Try out recursion for environment and frames

Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
Kasey White 2022-07-25 02:14:58 -04:00 committed by Lucas
parent be38d1eebe
commit 234ab7c7cb
4 changed files with 150 additions and 109 deletions

View File

@ -1,5 +1,5 @@
(program (program
1.0.0 1.0.0
[ (force (builtin ifThenElse)) (con string "yo") (con integer 3) (con integer 4) ] [ (builtin subtractInteger ) (con integer 7) (con integer 4) ]
) )

View File

@ -15,9 +15,7 @@ use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime};
pub struct Machine { pub struct Machine {
costs: CostModel, costs: CostModel,
pub ex_budget: ExBudget, pub ex_budget: ExBudget,
frames: Vec<Context>,
slippage: u32, slippage: u32,
env: Vec<Value>,
unbudgeted_steps: [u32; 8], unbudgeted_steps: [u32; 8],
pub logs: Vec<String>, pub logs: Vec<String>,
} }
@ -28,8 +26,6 @@ impl Machine {
costs, costs,
ex_budget: initial_budget, ex_budget: initial_budget,
slippage, slippage,
frames: vec![Context::NoFrame],
env: vec![],
unbudgeted_steps: [0; 8], unbudgeted_steps: [0; 8],
logs: vec![], logs: vec![],
} }
@ -40,22 +36,27 @@ impl Machine {
self.spend_budget(startup_budget)?; self.spend_budget(startup_budget)?;
self.compute(term) self.compute(Context::NoFrame, vec![], term)
} }
fn compute(&mut self, term: &Term<NamedDeBruijn>) -> Result<Term<NamedDeBruijn>, Error> { fn compute(
&mut self,
context: Context,
env: Vec<Value>,
term: &Term<NamedDeBruijn>,
) -> Result<Term<NamedDeBruijn>, 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())?; let val = self.lookup_var(name.clone(), env)?;
self.return_compute(val) self.return_compute(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(Value::Delay(*body.clone())) self.return_compute(context, Value::Delay(*body.clone(), env))
} }
Term::Lambda { Term::Lambda {
parameter_name, parameter_name,
@ -63,29 +64,33 @@ impl Machine {
} => { } => {
self.step_and_maybe_spend(StepKind::Lambda)?; self.step_and_maybe_spend(StepKind::Lambda)?;
self.return_compute(Value::Lambda { self.return_compute(
parameter_name: parameter_name.clone(), context,
body: *body.clone(), Value::Lambda {
}) parameter_name: parameter_name.clone(),
body: *body.clone(),
env,
},
)
} }
Term::Apply { function, argument } => { Term::Apply { function, argument } => {
self.step_and_maybe_spend(StepKind::Apply)?; self.step_and_maybe_spend(StepKind::Apply)?;
self.push_frame(Context::FrameApplyArg(*argument.clone())); self.compute(
Context::FrameApplyArg(env.clone(), *argument.clone(), Box::new(context)),
self.compute(function) env,
function,
)
} }
Term::Constant(x) => { Term::Constant(x) => {
self.step_and_maybe_spend(StepKind::Constant)?; self.step_and_maybe_spend(StepKind::Constant)?;
self.return_compute(Value::Con(x.clone())) self.return_compute(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.push_frame(Context::FrameForce); self.compute(Context::FrameForce(Box::new(context)), env, body)
self.compute(body)
} }
Term::Error => Err(Error::EvaluationFailure), Term::Error => Err(Error::EvaluationFailure),
Term::Builtin(fun) => { Term::Builtin(fun) => {
@ -93,42 +98,29 @@ impl Machine {
let runtime: BuiltinRuntime = (*fun).into(); let runtime: BuiltinRuntime = (*fun).into();
self.return_compute(Value::Builtin { self.return_compute(
fun: *fun, context,
term: term.clone(), Value::Builtin {
runtime, fun: *fun,
}) term: term.clone(),
runtime,
},
)
} }
} }
} }
fn return_compute(&mut self, value: Value) -> Result<Term<NamedDeBruijn>, Error> { fn return_compute(
// frames should never be empty anyways because Machine &mut self,
// is initialized with `Context::NoFrame`. context: Context,
let frame = self value: Value,
.frames ) -> Result<Term<NamedDeBruijn>, Error> {
.last() match context {
.cloned() Context::FrameApplyFun(function, ctx) => self.apply_evaluate(*ctx, function, value),
.expect("frames should never be empty"); Context::FrameApplyArg(arg_var_env, arg, ctx) => {
self.compute(Context::FrameApplyFun(value, ctx), arg_var_env, &arg)
match frame {
Context::FrameApplyFun(function) => {
self.pop_frame();
self.apply_evaluate(function, value)
}
Context::FrameApplyArg(arg) => {
self.pop_frame();
self.push_frame(Context::FrameApplyFun(value));
self.compute(&arg)
}
Context::FrameForce => {
self.pop_frame();
self.force_evaluate(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()?;
@ -145,57 +137,76 @@ impl Machine {
match value { match value {
Value::Con(x) => Term::Constant(x), Value::Con(x) => Term::Constant(x),
Value::Builtin { term, .. } => term, Value::Builtin { term, .. } => term,
Value::Delay(body) => self.discharge_value_env(Term::Delay(Box::new(body))), Value::Delay(body, env) => self.discharge_value_env(env, Term::Delay(Box::new(body))),
Value::Lambda { Value::Lambda {
parameter_name, parameter_name,
body, body,
} => self.discharge_value_env(Term::Lambda { env,
parameter_name: NamedDeBruijn { } => self.discharge_value_env(
text: parameter_name.text, env,
index: 0.into(), Term::Lambda {
parameter_name: NamedDeBruijn {
text: parameter_name.text,
index: 0.into(),
},
body: Box::new(body),
}, },
body: Box::new(body), ),
}),
} }
} }
fn discharge_value_env(&mut self, term: Term<NamedDeBruijn>) -> Term<NamedDeBruijn> { fn discharge_value_env(
fn rec(lam_cnt: usize, t: Term<NamedDeBruijn>, this: &mut Machine) -> Term<NamedDeBruijn> { &mut self,
match t { env: Vec<Value>,
term: Term<NamedDeBruijn>,
) -> Term<NamedDeBruijn> {
let mut lam_cnt = 0;
let mut term = term;
loop {
match term {
Term::Var(name) => { Term::Var(name) => {
let index: usize = name.index.into(); let index: usize = name.index.into();
if lam_cnt >= index { if lam_cnt >= index {
Term::Var(name) return Term::Var(name);
} else { } else {
this.env return env
.get::<usize>(index - lam_cnt - 1) .get::<usize>(index - lam_cnt)
.cloned() .cloned()
.map_or(Term::Var(name), |v| this.discharge_value(v)) .map_or(Term::Var(name), |v| self.discharge_value(v));
} }
} }
Term::Lambda { Term::Lambda {
parameter_name, parameter_name,
body, body,
} => Term::Lambda { } => {
parameter_name, term = *body;
body: Box::new(rec(lam_cnt + 1, *body, this)), lam_cnt += 1;
},
return Term::Lambda {
parameter_name,
body: Box::new(rec(lam_cnt + 1, *body, env, this)),
};
}
Term::Apply { function, argument } => Term::Apply { Term::Apply { function, argument } => Term::Apply {
function: Box::new(rec(lam_cnt, *function, this)), function: Box::new(rec(lam_cnt, *function, env, this)),
argument: Box::new(rec(lam_cnt, *argument, this)), argument: Box::new(rec(lam_cnt, *argument, env, this)),
}, },
Term::Delay(x) => Term::Delay(Box::new(rec(lam_cnt, *x, this))), Term::Delay(x) => Term::Delay(Box::new(rec(lam_cnt, *x, env, this))),
Term::Force(x) => Term::Force(Box::new(rec(lam_cnt, *x, this))), Term::Force(x) => Term::Force(Box::new(rec(lam_cnt, *x, env, this))),
rest => rest, rest => rest,
} }
} }
rec(0, term, self)
} }
fn force_evaluate(&mut self, value: Value) -> Result<Term<NamedDeBruijn>, Error> { fn force_evaluate(
&mut self,
context: Context,
value: Value,
) -> Result<Term<NamedDeBruijn>, Error> {
match value { match value {
Value::Delay(body) => self.compute(&body), Value::Delay(body, env) => self.compute(context, env, &body),
Value::Builtin { Value::Builtin {
fun, fun,
term, term,
@ -208,7 +219,7 @@ 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(res) self.return_compute(context, res)
} else { } else {
Err(Error::BuiltinTermArgumentExpected(force_term)) Err(Error::BuiltinTermArgumentExpected(force_term))
} }
@ -219,15 +230,17 @@ impl Machine {
fn apply_evaluate( fn apply_evaluate(
&mut self, &mut self,
context: Context,
function: Value, function: Value,
argument: Value, argument: Value,
) -> Result<Term<NamedDeBruijn>, Error> { ) -> Result<Term<NamedDeBruijn>, Error> {
match function { match function {
Value::Lambda { body, .. } => { Value::Lambda { body, env, .. } => {
self.env.push(argument); let mut e = env;
let term = self.compute(&body)?;
self.env.pop(); e.push(argument);
Ok(term)
self.compute(context, e, &body)
} }
Value::Builtin { Value::Builtin {
fun, fun,
@ -246,7 +259,7 @@ impl Machine {
let res = self.eval_builtin_app(fun, t, runtime)?; let res = self.eval_builtin_app(fun, t, runtime)?;
self.return_compute(res) self.return_compute(context, res)
} else { } else {
Err(Error::UnexpectedBuiltinTermArgument(t)) Err(Error::UnexpectedBuiltinTermArgument(t))
} }
@ -272,17 +285,8 @@ impl Machine {
} }
} }
fn push_frame(&mut self, frame: Context) { fn lookup_var(&mut self, name: NamedDeBruijn, env: Vec<Value>) -> Result<Value, Error> {
self.frames.push(frame); env.get::<usize>(env.len() - usize::from(name.index))
}
fn pop_frame(&mut self) {
self.frames.pop();
}
fn lookup_var(&mut self, name: NamedDeBruijn) -> Result<Value, Error> {
self.env
.get::<usize>(usize::from(name.index) - 1)
.cloned() .cloned()
.ok_or(Error::OpenTermEvaluated(Term::Var(name))) .ok_or(Error::OpenTermEvaluated(Term::Var(name)))
} }
@ -330,19 +334,20 @@ impl Machine {
#[derive(Clone)] #[derive(Clone)]
enum Context { enum Context {
FrameApplyFun(Value), FrameApplyFun(Value, Box<Context>),
FrameApplyArg(Term<NamedDeBruijn>), FrameApplyArg(Vec<Value>, Term<NamedDeBruijn>, Box<Context>),
FrameForce, FrameForce(Box<Context>),
NoFrame, NoFrame,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Value { pub enum Value {
Con(Constant), Con(Constant),
Delay(Term<NamedDeBruijn>), Delay(Term<NamedDeBruijn>, Vec<Value>),
Lambda { Lambda {
parameter_name: NamedDeBruijn, parameter_name: NamedDeBruijn,
body: Term<NamedDeBruijn>, body: Term<NamedDeBruijn>,
env: Vec<Value>,
}, },
Builtin { Builtin {
fun: DefaultFunction, fun: DefaultFunction,
@ -375,7 +380,7 @@ impl Value {
Constant::Unit => 1, Constant::Unit => 1,
Constant::Bool(_) => 1, Constant::Bool(_) => 1,
}, },
Value::Delay(_) => 1, Value::Delay(_, _) => 1,
Value::Lambda { .. } => 1, Value::Lambda { .. } => 1,
Value::Builtin { .. } => 1, Value::Builtin { .. } => 1,
} }

View File

@ -542,7 +542,16 @@ impl BuiltinCosts {
.cpu .cpu
.cost(args[0].to_ex_mem(), args[1].to_ex_mem()), .cost(args[0].to_ex_mem(), args[1].to_ex_mem()),
}, },
DefaultFunction::SubtractInteger => todo!(), DefaultFunction::SubtractInteger => ExBudget {
mem: self
.subtract_integer
.mem
.cost(args[0].to_ex_mem(), args[1].to_ex_mem()),
cpu: self
.subtract_integer
.cpu
.cost(args[0].to_ex_mem(), args[1].to_ex_mem()),
},
DefaultFunction::MultiplyInteger => todo!(), DefaultFunction::MultiplyInteger => todo!(),
DefaultFunction::DivideInteger => todo!(), DefaultFunction::DivideInteger => todo!(),
DefaultFunction::QuotientInteger => todo!(), DefaultFunction::QuotientInteger => todo!(),
@ -550,7 +559,16 @@ impl BuiltinCosts {
DefaultFunction::ModInteger => todo!(), DefaultFunction::ModInteger => todo!(),
DefaultFunction::EqualsInteger => todo!(), DefaultFunction::EqualsInteger => todo!(),
DefaultFunction::LessThanInteger => todo!(), DefaultFunction::LessThanInteger => todo!(),
DefaultFunction::LessThanEqualsInteger => todo!(), DefaultFunction::LessThanEqualsInteger => ExBudget {
mem: self
.less_than_equals_integer
.mem
.cost(args[0].to_ex_mem(), args[1].to_ex_mem()),
cpu: self
.less_than_equals_integer
.cpu
.cost(args[0].to_ex_mem(), args[1].to_ex_mem()),
},
DefaultFunction::AppendByteString => todo!(), DefaultFunction::AppendByteString => todo!(),
DefaultFunction::ConsByteString => todo!(), DefaultFunction::ConsByteString => todo!(),
DefaultFunction::SliceByteString => todo!(), DefaultFunction::SliceByteString => todo!(),

View File

@ -71,7 +71,7 @@ impl DefaultFunction {
pub fn arity(&self) -> usize { pub fn arity(&self) -> usize {
match self { match self {
DefaultFunction::AddInteger => 2, DefaultFunction::AddInteger => 2,
DefaultFunction::SubtractInteger => todo!(), DefaultFunction::SubtractInteger => 2,
DefaultFunction::MultiplyInteger => todo!(), DefaultFunction::MultiplyInteger => todo!(),
DefaultFunction::DivideInteger => todo!(), DefaultFunction::DivideInteger => todo!(),
DefaultFunction::QuotientInteger => todo!(), DefaultFunction::QuotientInteger => todo!(),
@ -79,7 +79,7 @@ impl DefaultFunction {
DefaultFunction::ModInteger => todo!(), DefaultFunction::ModInteger => todo!(),
DefaultFunction::EqualsInteger => todo!(), DefaultFunction::EqualsInteger => todo!(),
DefaultFunction::LessThanInteger => todo!(), DefaultFunction::LessThanInteger => todo!(),
DefaultFunction::LessThanEqualsInteger => todo!(), DefaultFunction::LessThanEqualsInteger => 2,
DefaultFunction::AppendByteString => todo!(), DefaultFunction::AppendByteString => todo!(),
DefaultFunction::ConsByteString => todo!(), DefaultFunction::ConsByteString => todo!(),
DefaultFunction::SliceByteString => todo!(), DefaultFunction::SliceByteString => todo!(),
@ -138,7 +138,7 @@ impl DefaultFunction {
DefaultFunction::ModInteger => todo!(), DefaultFunction::ModInteger => todo!(),
DefaultFunction::EqualsInteger => todo!(), DefaultFunction::EqualsInteger => todo!(),
DefaultFunction::LessThanInteger => todo!(), DefaultFunction::LessThanInteger => todo!(),
DefaultFunction::LessThanEqualsInteger => todo!(), DefaultFunction::LessThanEqualsInteger => 0,
DefaultFunction::AppendByteString => todo!(), DefaultFunction::AppendByteString => todo!(),
DefaultFunction::ConsByteString => todo!(), DefaultFunction::ConsByteString => todo!(),
DefaultFunction::SliceByteString => todo!(), DefaultFunction::SliceByteString => todo!(),
@ -189,7 +189,7 @@ impl DefaultFunction {
pub fn check_type(&self, arg: &Value, args: &[Value]) -> Result<(), Error> { pub fn check_type(&self, arg: &Value, args: &[Value]) -> Result<(), Error> {
match self { match self {
DefaultFunction::AddInteger => arg.expect_type(Type::Integer), DefaultFunction::AddInteger => arg.expect_type(Type::Integer),
DefaultFunction::SubtractInteger => todo!(), DefaultFunction::SubtractInteger => arg.expect_type(Type::Integer),
DefaultFunction::MultiplyInteger => todo!(), DefaultFunction::MultiplyInteger => todo!(),
DefaultFunction::DivideInteger => todo!(), DefaultFunction::DivideInteger => todo!(),
DefaultFunction::QuotientInteger => todo!(), DefaultFunction::QuotientInteger => todo!(),
@ -197,7 +197,7 @@ impl DefaultFunction {
DefaultFunction::ModInteger => todo!(), DefaultFunction::ModInteger => todo!(),
DefaultFunction::EqualsInteger => todo!(), DefaultFunction::EqualsInteger => todo!(),
DefaultFunction::LessThanInteger => todo!(), DefaultFunction::LessThanInteger => todo!(),
DefaultFunction::LessThanEqualsInteger => todo!(), DefaultFunction::LessThanEqualsInteger => arg.expect_type(Type::Integer),
DefaultFunction::AppendByteString => todo!(), DefaultFunction::AppendByteString => todo!(),
DefaultFunction::ConsByteString => todo!(), DefaultFunction::ConsByteString => todo!(),
DefaultFunction::SliceByteString => todo!(), DefaultFunction::SliceByteString => todo!(),
@ -266,7 +266,16 @@ impl DefaultFunction {
_ => unreachable!(), _ => unreachable!(),
} }
} }
DefaultFunction::SubtractInteger => todo!(), DefaultFunction::SubtractInteger => {
let args = (&args[0], &args[1]);
match args {
(Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => {
Ok(Value::Con(Constant::Integer(arg1 - arg2)))
}
_ => unreachable!(),
}
}
DefaultFunction::MultiplyInteger => todo!(), DefaultFunction::MultiplyInteger => todo!(),
DefaultFunction::DivideInteger => todo!(), DefaultFunction::DivideInteger => todo!(),
DefaultFunction::QuotientInteger => todo!(), DefaultFunction::QuotientInteger => todo!(),
@ -274,7 +283,16 @@ impl DefaultFunction {
DefaultFunction::ModInteger => todo!(), DefaultFunction::ModInteger => todo!(),
DefaultFunction::EqualsInteger => todo!(), DefaultFunction::EqualsInteger => todo!(),
DefaultFunction::LessThanInteger => todo!(), DefaultFunction::LessThanInteger => todo!(),
DefaultFunction::LessThanEqualsInteger => todo!(), DefaultFunction::LessThanEqualsInteger => {
let args = (&args[0], &args[1]);
match args {
(Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => {
Ok(Value::Con(Constant::Bool(arg1 <= arg2)))
}
_ => unreachable!(),
}
}
DefaultFunction::AppendByteString => todo!(), DefaultFunction::AppendByteString => todo!(),
DefaultFunction::ConsByteString => todo!(), DefaultFunction::ConsByteString => todo!(),
DefaultFunction::SliceByteString => todo!(), DefaultFunction::SliceByteString => todo!(),