feat: clean up the machine a bit

This commit is contained in:
rvcas 2022-07-03 16:38:55 -04:00 committed by Kasey White
parent de476c801b
commit 795d9ee028
5 changed files with 170 additions and 35 deletions

View File

@ -14,6 +14,32 @@ pub struct Program<T> {
pub term: Term<T>, pub term: Term<T>,
} }
impl<T> Program<T>
where
T: Clone,
{
/// We use this to apply the validator to Datum,
/// then redeemer, then ScriptContext. If datum is
/// even necessary (i.e. minting policy).
///
/// ```rust
/// program.apply(&datum)
/// .apply(&redeemer)
/// .apply(&script_context);
/// ```
pub fn apply(&self, program: &Self) -> Self {
let applied_term = Term::Apply {
function: Box::new(self.term.clone()),
argument: Box::new(program.term.clone()),
};
Program {
version: self.version,
term: applied_term,
}
}
}
/// This represents a term in Untyped Plutus Core. /// This represents a term in Untyped Plutus Core.
/// We need a generic type for the different forms that a program may be in. /// We need a generic type for the different forms that a program may be in.
/// Specifically, `Var` and `parameter_name` in `Lambda` can be a `Name`, /// Specifically, `Var` and `parameter_name` in `Lambda` can be a `Name`,
@ -374,14 +400,3 @@ impl From<Term<FakeNamedDeBruijn>> for Term<NamedDeBruijn> {
converter.fake_named_debruijn_to_named_debruijn(value) converter.fake_named_debruijn_to_named_debruijn(value)
} }
} }
pub fn apply_program<T>(p1: Program<T>, p2: Program<T>) -> Program<T> {
let applied_term = Term::Apply {
function: Box::new(p1.term),
argument: Box::new(p2.term),
};
Program {
version: p1.version,
term: applied_term,
}
}

View File

@ -16,6 +16,7 @@ enum StepKind {
BForce = 5, BForce = 5,
BBuiltin = 6, BBuiltin = 6,
} }
impl TryFrom<u8> for StepKind { impl TryFrom<u8> for StepKind {
type Error = de::Error; type Error = de::Error;
@ -41,6 +42,7 @@ enum ExBudgetCategory {
BBuiltinApp(DefaultFunction), BBuiltinApp(DefaultFunction),
BStartup, BStartup,
} }
/// Can be negative /// Can be negative
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct ExBudget { struct ExBudget {
@ -146,27 +148,3 @@ fn spend_accumulated_budget_cek(unbudgeted_steps: &mut Vec<u32>, current_budget:
); );
} }
} }
fn spend_budget_cek(
current_budget: &mut ExBudget,
category: ExBudgetCategory,
spend_budget: ExBudget,
) {
current_budget.mem -= spend_budget.mem;
current_budget.cpu -= spend_budget.cpu;
if current_budget.mem < 0 || current_budget.cpu < 0 {
panic!("Budget exhausted {:?}", current_budget);
}
}
fn get_cost_by_step(step: StepKind) -> ExBudget {
match (step) {
StepKind::BConst => ExBudget { mem: 100, cpu: 100 },
StepKind::BVar => ExBudget { mem: 100, cpu: 100 },
StepKind::BLamAbs => ExBudget { mem: 100, cpu: 100 },
StepKind::BApply => ExBudget { mem: 100, cpu: 100 },
StepKind::BDelay => ExBudget { mem: 100, cpu: 100 },
StepKind::BForce => ExBudget { mem: 100, cpu: 100 },
StepKind::BBuiltin => ExBudget { mem: 100, cpu: 100 },
}
}

View File

@ -3,6 +3,7 @@ pub mod builtins;
mod debruijn; mod debruijn;
mod flat; mod flat;
pub mod interpreter; pub mod interpreter;
pub mod machine;
pub mod parser; pub mod parser;
mod pretty; mod pretty;
pub mod program_builder; pub mod program_builder;

132
crates/uplc/src/machine.rs Normal file
View File

@ -0,0 +1,132 @@
use crate::{
ast::{Constant, DeBruijn, Term},
builtins::DefaultFunction,
};
mod error;
pub use error::Error;
pub struct Machine {
costs: Costs,
ex_budget: ExBudget,
frames: Vec<Context>,
slippage: u32,
env: Vec<Value>,
}
impl Machine {
pub fn new(costs: Costs, initial_budget: ExBudget, slippage: u32) -> Machine {
Machine {
costs,
ex_budget: initial_budget,
slippage,
frames: vec![],
env: vec![],
}
}
pub fn run(
&mut self,
term: &Term<DeBruijn>,
) -> Result<(Term<DeBruijn>, usize, Vec<String>), Error> {
let startup_budget = self.costs.get(StepKind::StartUp);
self.spend_budget(startup_budget)?;
self.push_frame(Context::NoFrame);
self.enter_compute(term)
}
fn enter_compute(&mut self, term: &Term<DeBruijn>) {}
fn spend_budget(&mut self, spend_budget: ExBudget) -> Result<(), Error> {
self.ex_budget.mem -= spend_budget.mem;
self.ex_budget.cpu -= spend_budget.cpu;
if self.ex_budget.mem < 0 || self.ex_budget.cpu < 0 {
Err(Error::OutOfExError(self.ex_budget))
} else {
Ok(())
}
}
fn push_frame(&mut self, frame: Context) {
self.frames.push(frame);
}
}
enum Context {
FrameApplyFun(Term<DeBruijn>, Term<DeBruijn>),
FrameApplyArg(Vec<Value>, Term<DeBruijn>, Box<Context>),
FrameForce(Box<Context>),
NoFrame,
}
enum Value {
Con(Constant),
Delay(Term<DeBruijn>, Vec<Value>),
Lambda(DeBruijn, Term<DeBruijn>, Vec<Value>),
Builtin(
DefaultFunction,
Term<DeBruijn>,
// Need to figure out run time stuff
// BuiltinRuntime (CekValue uni fun)
),
}
/// Can be negative
#[derive(Debug, Clone, PartialEq)]
struct ExBudget {
mem: i32,
cpu: i32,
}
impl ExBudget {
pub fn occurence(&mut self, n: i32) {
self.mem *= n;
self.cpu *= n;
}
}
enum StepKind {
Constant,
Var,
Lambda,
Apply,
Delay,
Force,
Builtin,
StartUp,
}
/// There's no entry for Error since we'll be exiting anyway; also, what would
/// happen if calling 'Error' caused the budget to be exceeded?
struct Costs {
startup: ExBudget,
var: ExBudget,
constant: ExBudget,
lambda: ExBudget,
delay: ExBudget,
force: ExBudget,
apply: ExBudget,
/// Just the cost of evaluating a Builtin node, not the builtin itself.
builtin: ExBudget,
}
impl Costs {
/// Get the cost of a step
pub fn get(&self, step: StepKind) -> ExBudget {
match step {
StepKind::Constant => self.constant,
StepKind::Var => self.var,
StepKind::Lambda => self.lambda,
StepKind::Apply => self.apply,
StepKind::Delay => self.delay,
StepKind::Force => self.force,
StepKind::Builtin => self.builtin,
StepKind::StartUp => self.startup,
}
}
}

View File

@ -0,0 +1,9 @@
use thiserror::Error;
use super::ExBudget;
#[derive(Error, Debug)]
pub enum Error {
#[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)]
OutOfExError(ExBudget),
}