switch to stack model for plutus eval
Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
parent
4acbd704e0
commit
a3a175d74b
|
@ -12,12 +12,30 @@ pub use error::Error;
|
||||||
|
|
||||||
use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime};
|
use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime};
|
||||||
|
|
||||||
|
enum MachineStep {
|
||||||
|
Return(Context, Value),
|
||||||
|
Compute(Context, Vec<Value>, Term<NamedDeBruijn>),
|
||||||
|
Done(Term<NamedDeBruijn>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Option<MachineStep>> for Term<NamedDeBruijn> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: Option<MachineStep>) -> Result<Self, Error> {
|
||||||
|
match value {
|
||||||
|
Some(MachineStep::Done(term)) => Ok(term),
|
||||||
|
_ => Err(Error::MachineNeverReachedDone),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Machine {
|
pub struct Machine {
|
||||||
costs: CostModel,
|
costs: CostModel,
|
||||||
pub ex_budget: ExBudget,
|
pub ex_budget: ExBudget,
|
||||||
slippage: u32,
|
slippage: u32,
|
||||||
unbudgeted_steps: [u32; 8],
|
unbudgeted_steps: [u32; 8],
|
||||||
pub logs: Vec<String>,
|
pub logs: Vec<String>,
|
||||||
|
stack: Vec<MachineStep>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
|
@ -28,15 +46,37 @@ impl Machine {
|
||||||
slippage,
|
slippage,
|
||||||
unbudgeted_steps: [0; 8],
|
unbudgeted_steps: [0; 8],
|
||||||
logs: vec![],
|
logs: vec![],
|
||||||
|
stack: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, term: &Term<NamedDeBruijn>) -> Result<Term<NamedDeBruijn>, Error> {
|
pub fn run(&mut self, term: &Term<NamedDeBruijn>) -> Result<Term<NamedDeBruijn>, Error> {
|
||||||
|
use MachineStep::*;
|
||||||
|
|
||||||
let startup_budget = self.costs.machine_costs.get(StepKind::StartUp);
|
let startup_budget = self.costs.machine_costs.get(StepKind::StartUp);
|
||||||
|
|
||||||
self.spend_budget(startup_budget)?;
|
self.spend_budget(startup_budget)?;
|
||||||
|
|
||||||
self.compute(Context::NoFrame, vec![], term)
|
self.stack
|
||||||
|
.push(Compute(Context::NoFrame, vec![], term.clone()));
|
||||||
|
|
||||||
|
while let Some(step) = self.stack.pop() {
|
||||||
|
match step {
|
||||||
|
Compute(context, env, t) => {
|
||||||
|
self.compute(context, env, &t)?;
|
||||||
|
}
|
||||||
|
Return(context, value) => {
|
||||||
|
self.return_compute(context, value)?;
|
||||||
|
}
|
||||||
|
d @ Done(_) => {
|
||||||
|
self.stack.push(d);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stack.pop().try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(
|
fn compute(
|
||||||
|
@ -44,19 +84,22 @@ impl Machine {
|
||||||
context: Context,
|
context: Context,
|
||||||
env: Vec<Value>,
|
env: Vec<Value>,
|
||||||
term: &Term<NamedDeBruijn>,
|
term: &Term<NamedDeBruijn>,
|
||||||
) -> Result<Term<NamedDeBruijn>, Error> {
|
) -> Result<(), Error> {
|
||||||
match term {
|
match term {
|
||||||
Term::Var(name) => {
|
Term::Var(name) => {
|
||||||
self.step_and_maybe_spend(StepKind::Var)?;
|
self.step_and_maybe_spend(StepKind::Var)?;
|
||||||
|
|
||||||
let val = self.lookup_var(name.clone(), env)?;
|
let val = self.lookup_var(name.clone(), env)?;
|
||||||
|
|
||||||
self.return_compute(context, val)
|
self.stack.push(MachineStep::Return(context, val));
|
||||||
}
|
}
|
||||||
Term::Delay(body) => {
|
Term::Delay(body) => {
|
||||||
self.step_and_maybe_spend(StepKind::Delay)?;
|
self.step_and_maybe_spend(StepKind::Delay)?;
|
||||||
|
|
||||||
self.return_compute(context, Value::Delay(*body.clone(), env))
|
self.stack.push(MachineStep::Return(
|
||||||
|
context,
|
||||||
|
Value::Delay(*body.clone(), env),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Term::Lambda {
|
Term::Lambda {
|
||||||
parameter_name,
|
parameter_name,
|
||||||
|
@ -64,63 +107,70 @@ impl Machine {
|
||||||
} => {
|
} => {
|
||||||
self.step_and_maybe_spend(StepKind::Lambda)?;
|
self.step_and_maybe_spend(StepKind::Lambda)?;
|
||||||
|
|
||||||
self.return_compute(
|
self.stack.push(MachineStep::Return(
|
||||||
context,
|
context,
|
||||||
Value::Lambda {
|
Value::Lambda {
|
||||||
parameter_name: parameter_name.clone(),
|
parameter_name: parameter_name.clone(),
|
||||||
body: *body.clone(),
|
body: *body.clone(),
|
||||||
env,
|
env,
|
||||||
},
|
},
|
||||||
)
|
));
|
||||||
}
|
}
|
||||||
Term::Apply { function, argument } => {
|
Term::Apply { function, argument } => {
|
||||||
self.step_and_maybe_spend(StepKind::Apply)?;
|
self.step_and_maybe_spend(StepKind::Apply)?;
|
||||||
|
|
||||||
self.compute(
|
self.stack.push(MachineStep::Compute(
|
||||||
Context::FrameApplyArg(env.clone(), *argument.clone(), Box::new(context)),
|
Context::FrameApplyArg(env.clone(), *argument.clone(), Box::new(context)),
|
||||||
env,
|
env,
|
||||||
function,
|
*function.clone(),
|
||||||
)
|
));
|
||||||
}
|
}
|
||||||
Term::Constant(x) => {
|
Term::Constant(x) => {
|
||||||
self.step_and_maybe_spend(StepKind::Constant)?;
|
self.step_and_maybe_spend(StepKind::Constant)?;
|
||||||
|
|
||||||
self.return_compute(context, Value::Con(x.clone()))
|
self.stack
|
||||||
|
.push(MachineStep::Return(context, Value::Con(x.clone())));
|
||||||
}
|
}
|
||||||
Term::Force(body) => {
|
Term::Force(body) => {
|
||||||
self.step_and_maybe_spend(StepKind::Force)?;
|
self.step_and_maybe_spend(StepKind::Force)?;
|
||||||
|
|
||||||
self.compute(Context::FrameForce(Box::new(context)), env, body)
|
self.stack.push(MachineStep::Compute(
|
||||||
|
Context::FrameForce(Box::new(context)),
|
||||||
|
env,
|
||||||
|
*body.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Term::Error => Err(Error::EvaluationFailure),
|
Term::Error => return Err(Error::EvaluationFailure),
|
||||||
Term::Builtin(fun) => {
|
Term::Builtin(fun) => {
|
||||||
self.step_and_maybe_spend(StepKind::Builtin)?;
|
self.step_and_maybe_spend(StepKind::Builtin)?;
|
||||||
|
|
||||||
let runtime: BuiltinRuntime = (*fun).into();
|
let runtime: BuiltinRuntime = (*fun).into();
|
||||||
|
|
||||||
self.return_compute(
|
self.stack.push(MachineStep::Return(
|
||||||
context,
|
context,
|
||||||
Value::Builtin {
|
Value::Builtin {
|
||||||
fun: *fun,
|
fun: *fun,
|
||||||
term: term.clone(),
|
term: term.clone(),
|
||||||
runtime,
|
runtime,
|
||||||
},
|
},
|
||||||
)
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_compute(
|
fn return_compute(&mut self, context: Context, value: Value) -> Result<(), Error> {
|
||||||
&mut self,
|
|
||||||
context: Context,
|
|
||||||
value: Value,
|
|
||||||
) -> Result<Term<NamedDeBruijn>, Error> {
|
|
||||||
match context {
|
match context {
|
||||||
Context::FrameApplyFun(function, ctx) => self.apply_evaluate(*ctx, function, value),
|
Context::FrameApplyFun(function, ctx) => self.apply_evaluate(*ctx, function, value)?,
|
||||||
Context::FrameApplyArg(arg_var_env, arg, ctx) => {
|
Context::FrameApplyArg(arg_var_env, arg, ctx) => {
|
||||||
self.compute(Context::FrameApplyFun(value, ctx), arg_var_env, &arg)
|
self.stack.push(MachineStep::Compute(
|
||||||
|
Context::FrameApplyFun(value, ctx),
|
||||||
|
arg_var_env,
|
||||||
|
arg,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Context::FrameForce(ctx) => self.force_evaluate(*ctx, value),
|
Context::FrameForce(ctx) => self.force_evaluate(*ctx, value)?,
|
||||||
Context::NoFrame => {
|
Context::NoFrame => {
|
||||||
if self.unbudgeted_steps[7] > 0 {
|
if self.unbudgeted_steps[7] > 0 {
|
||||||
self.spend_unbudgeted_steps()?;
|
self.spend_unbudgeted_steps()?;
|
||||||
|
@ -128,9 +178,11 @@ impl Machine {
|
||||||
|
|
||||||
let term = self.discharge_value(value);
|
let term = self.discharge_value(value);
|
||||||
|
|
||||||
Ok(term)
|
self.stack.push(MachineStep::Done(term));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discharge_value(&mut self, value: Value) -> Term<NamedDeBruijn> {
|
fn discharge_value(&mut self, value: Value) -> Term<NamedDeBruijn> {
|
||||||
|
@ -197,13 +249,13 @@ impl Machine {
|
||||||
rec(0, term, self, &env)
|
rec(0, term, self, &env)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn force_evaluate(
|
fn force_evaluate(&mut self, context: Context, value: Value) -> Result<(), Error> {
|
||||||
&mut self,
|
|
||||||
context: Context,
|
|
||||||
value: Value,
|
|
||||||
) -> Result<Term<NamedDeBruijn>, Error> {
|
|
||||||
match value {
|
match value {
|
||||||
Value::Delay(body, env) => self.compute(context, env, &body),
|
Value::Delay(body, env) => {
|
||||||
|
self.stack.push(MachineStep::Compute(context, env, body));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Value::Builtin {
|
Value::Builtin {
|
||||||
fun,
|
fun,
|
||||||
term,
|
term,
|
||||||
|
@ -216,7 +268,9 @@ impl Machine {
|
||||||
|
|
||||||
let res = self.eval_builtin_app(fun, force_term, runtime)?;
|
let res = self.eval_builtin_app(fun, force_term, runtime)?;
|
||||||
|
|
||||||
self.return_compute(context, res)
|
self.stack.push(MachineStep::Return(context, res));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BuiltinTermArgumentExpected(force_term))
|
Err(Error::BuiltinTermArgumentExpected(force_term))
|
||||||
}
|
}
|
||||||
|
@ -230,14 +284,16 @@ impl Machine {
|
||||||
context: Context,
|
context: Context,
|
||||||
function: Value,
|
function: Value,
|
||||||
argument: Value,
|
argument: Value,
|
||||||
) -> Result<Term<NamedDeBruijn>, Error> {
|
) -> Result<(), Error> {
|
||||||
match function {
|
match function {
|
||||||
Value::Lambda { body, env, .. } => {
|
Value::Lambda { body, env, .. } => {
|
||||||
let mut e = env;
|
let mut e = env;
|
||||||
|
|
||||||
e.push(argument);
|
e.push(argument);
|
||||||
|
|
||||||
self.compute(context, e, &body)
|
self.stack.push(MachineStep::Compute(context, e, body));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Value::Builtin {
|
Value::Builtin {
|
||||||
fun,
|
fun,
|
||||||
|
@ -256,7 +312,9 @@ impl Machine {
|
||||||
|
|
||||||
let res = self.eval_builtin_app(fun, t, runtime)?;
|
let res = self.eval_builtin_app(fun, t, runtime)?;
|
||||||
|
|
||||||
self.return_compute(context, res)
|
self.stack.push(MachineStep::Return(context, res));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnexpectedBuiltinTermArgument(t))
|
Err(Error::UnexpectedBuiltinTermArgument(t))
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ pub enum Error {
|
||||||
BuiltinTermArgumentExpected(Term<NamedDeBruijn>),
|
BuiltinTermArgumentExpected(Term<NamedDeBruijn>),
|
||||||
#[error("Unable to unlift value because it is not a constant:\n\n{0:#?}")]
|
#[error("Unable to unlift value because it is not a constant:\n\n{0:#?}")]
|
||||||
NotAConstant(Value),
|
NotAConstant(Value),
|
||||||
|
#[error("The evaluation never reached a final state")]
|
||||||
|
MachineNeverReachedDone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
Loading…
Reference in New Issue