add evaluation for terms
Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
parent
795d9ee028
commit
0bf93e59b5
|
@ -1,150 +0,0 @@
|
||||||
use flat_rs::de;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ast::{Constant, NamedDeBruijn, Term},
|
|
||||||
builtins::DefaultFunction,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
|
||||||
enum StepKind {
|
|
||||||
BConst = 0,
|
|
||||||
BVar = 1,
|
|
||||||
BLamAbs = 2,
|
|
||||||
BApply = 3,
|
|
||||||
BDelay = 4,
|
|
||||||
BForce = 5,
|
|
||||||
BBuiltin = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u8> for StepKind {
|
|
||||||
type Error = de::Error;
|
|
||||||
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
0 => Ok(StepKind::BConst),
|
|
||||||
1 => Ok(StepKind::BVar),
|
|
||||||
2 => Ok(StepKind::BLamAbs),
|
|
||||||
3 => Ok(StepKind::BApply),
|
|
||||||
4 => Ok(StepKind::BDelay),
|
|
||||||
5 => Ok(StepKind::BForce),
|
|
||||||
6 => Ok(StepKind::BBuiltin),
|
|
||||||
v => Err(de::Error::Message(format!(
|
|
||||||
"Default Function not found - {}",
|
|
||||||
v
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ExBudgetCategory {
|
|
||||||
BStep(StepKind),
|
|
||||||
BBuiltinApp(DefaultFunction),
|
|
||||||
BStartup,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Can be negative
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
struct ExBudget {
|
|
||||||
mem: i32,
|
|
||||||
cpu: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Mul<i32> for ExBudget {
|
|
||||||
type Output = ExBudget;
|
|
||||||
|
|
||||||
fn mul(self, rhs: i32) -> ExBudget {
|
|
||||||
ExBudget {
|
|
||||||
mem: self.mem * rhs,
|
|
||||||
cpu: self.cpu * rhs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CekValue {
|
|
||||||
VCon(Constant),
|
|
||||||
VDelay(Term<NamedDeBruijn>, Vec<CekValue>),
|
|
||||||
VLamAbs(NamedDeBruijn, Term<NamedDeBruijn>, Vec<CekValue>),
|
|
||||||
VBuiltin(
|
|
||||||
DefaultFunction,
|
|
||||||
Term<NamedDeBruijn>,
|
|
||||||
// Need to figure out run time stuff
|
|
||||||
// BuiltinRuntime (CekValue uni fun)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Context {
|
|
||||||
FrameApplyFun(Term<NamedDeBruijn>, Term<NamedDeBruijn>),
|
|
||||||
FrameApplyArg(Vec<CekValue>, Term<NamedDeBruijn>, Box<Context>),
|
|
||||||
FrameForce(Box<Context>),
|
|
||||||
NoFrame,
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now let's just start with running cek on term with generic params
|
|
||||||
fn run_cek_debruijn(term: Term<NamedDeBruijn>) -> (Term<NamedDeBruijn>, usize, Vec<String>) {
|
|
||||||
//paramerterize this
|
|
||||||
let mut initial_budget = ExBudget {
|
|
||||||
mem: 1000,
|
|
||||||
cpu: 1000,
|
|
||||||
};
|
|
||||||
let startup_budget = ExBudget { mem: 10, cpu: 10 };
|
|
||||||
spend_budget_cek(
|
|
||||||
&mut initial_budget,
|
|
||||||
ExBudgetCategory::BStartup,
|
|
||||||
startup_budget,
|
|
||||||
);
|
|
||||||
let evaluation = enter_compute_cek(Context::NoFrame, Vec::new(), term, &mut initial_budget);
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn enter_compute_cek(
|
|
||||||
frame: Context,
|
|
||||||
env: Vec<CekValue>,
|
|
||||||
term: Term<NamedDeBruijn>,
|
|
||||||
current_budget: &mut ExBudget,
|
|
||||||
) -> (Term<NamedDeBruijn>, usize, Vec<u32>) {
|
|
||||||
//enter compute
|
|
||||||
compute_cek(&mut Vec::with_capacity(8), frame, env, term, current_budget)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_cek(
|
|
||||||
unbudgeted_steps: &mut Vec<u32>,
|
|
||||||
frame: Context,
|
|
||||||
env: Vec<CekValue>,
|
|
||||||
term: Term<NamedDeBruijn>,
|
|
||||||
current_budget: &mut ExBudget,
|
|
||||||
) -> (Term<NamedDeBruijn>, usize, Vec<u32>) {
|
|
||||||
//TODO: parameterize slippage
|
|
||||||
let slippage = 200;
|
|
||||||
match (term) {
|
|
||||||
a @ Term::Var(_) => {
|
|
||||||
unbudgeted_steps[2] += 1;
|
|
||||||
unbudgeted_steps[7] += 1;
|
|
||||||
if unbudgeted_steps[7] >= slippage {
|
|
||||||
spend_accumulated_budget_cek(unbudgeted_steps, current_budget);
|
|
||||||
}
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
Term::Delay(_) => todo!(),
|
|
||||||
Term::Lambda {
|
|
||||||
parameter_name,
|
|
||||||
body,
|
|
||||||
} => todo!(),
|
|
||||||
Term::Apply { function, argument } => todo!(),
|
|
||||||
Term::Constant(_) => todo!(),
|
|
||||||
Term::Force(_) => todo!(),
|
|
||||||
Term::Error => todo!(),
|
|
||||||
Term::Builtin(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spend_accumulated_budget_cek(unbudgeted_steps: &mut Vec<u32>, current_budget: &mut ExBudget) {
|
|
||||||
//only spend each step kind and not total which is index
|
|
||||||
for i in 0..unbudgeted_steps.len() - 1 {
|
|
||||||
let step = StepKind::try_from(i as u8).unwrap();
|
|
||||||
spend_budget_cek(
|
|
||||||
current_budget,
|
|
||||||
ExBudgetCategory::BStep(step),
|
|
||||||
get_cost_by_step(step) * unbudgeted_steps[i] as i32,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@ pub mod ast;
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
mod debruijn;
|
mod debruijn;
|
||||||
mod flat;
|
mod flat;
|
||||||
pub mod interpreter;
|
|
||||||
pub mod machine;
|
pub mod machine;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
mod pretty;
|
mod pretty;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, DeBruijn, Term},
|
ast::{Constant, NamedDeBruijn, Term},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ pub struct Machine {
|
||||||
frames: Vec<Context>,
|
frames: Vec<Context>,
|
||||||
slippage: u32,
|
slippage: u32,
|
||||||
env: Vec<Value>,
|
env: Vec<Value>,
|
||||||
|
unbudgeted_steps: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
|
@ -23,23 +24,172 @@ impl Machine {
|
||||||
slippage,
|
slippage,
|
||||||
frames: vec![],
|
frames: vec![],
|
||||||
env: vec![],
|
env: vec![],
|
||||||
|
unbudgeted_steps: vec![0; 8],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
&mut self,
|
&mut self,
|
||||||
term: &Term<DeBruijn>,
|
term: &Term<NamedDeBruijn>,
|
||||||
) -> Result<(Term<DeBruijn>, usize, Vec<String>), Error> {
|
) -> Result<(Term<NamedDeBruijn>, usize, Vec<String>), Error> {
|
||||||
let startup_budget = self.costs.get(StepKind::StartUp);
|
let startup_budget = self.costs.get(StepKind::StartUp);
|
||||||
|
|
||||||
self.spend_budget(startup_budget)?;
|
self.spend_budget(startup_budget)?;
|
||||||
|
|
||||||
self.push_frame(Context::NoFrame);
|
self.push_frame(Context::NoFrame);
|
||||||
|
|
||||||
self.enter_compute(term)
|
self.enter_compute(term)?;
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_compute(&mut self, term: &Term<DeBruijn>) {}
|
fn enter_compute(&mut self, term: &Term<NamedDeBruijn>) -> Result<Term<NamedDeBruijn>, Error> {
|
||||||
|
match term {
|
||||||
|
Term::Var(name) => {
|
||||||
|
self.unbudgeted_steps[1] += 1;
|
||||||
|
self.unbudgeted_steps[7] += 1;
|
||||||
|
if self.unbudgeted_steps[7] >= self.slippage {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
let val = self.lookup_var(name.clone())?;
|
||||||
|
self.return_compute(val)
|
||||||
|
}
|
||||||
|
Term::Delay(body) => {
|
||||||
|
self.unbudgeted_steps[4] += 1;
|
||||||
|
self.unbudgeted_steps[7] += 1;
|
||||||
|
if self.unbudgeted_steps[7] >= self.slippage {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
self.return_compute(Value::Delay(*body.clone()))
|
||||||
|
}
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
self.unbudgeted_steps[2] += 1;
|
||||||
|
self.unbudgeted_steps[7] += 1;
|
||||||
|
if self.unbudgeted_steps[7] >= self.slippage {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
self.return_compute(Value::Lambda {
|
||||||
|
parameter_name: parameter_name.clone(),
|
||||||
|
body: *body.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Term::Apply { function, argument } => {
|
||||||
|
self.unbudgeted_steps[3] += 1;
|
||||||
|
self.unbudgeted_steps[7] += 1;
|
||||||
|
if self.unbudgeted_steps[7] >= self.slippage {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
self.push_frame(Context::FrameApplyArg(*argument.clone()));
|
||||||
|
self.enter_compute(function)
|
||||||
|
}
|
||||||
|
Term::Constant(x) => {
|
||||||
|
self.unbudgeted_steps[0] += 1;
|
||||||
|
self.unbudgeted_steps[7] += 1;
|
||||||
|
if self.unbudgeted_steps[7] >= self.slippage {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
self.return_compute(Value::Con(x.clone()))
|
||||||
|
}
|
||||||
|
Term::Force(body) => {
|
||||||
|
self.unbudgeted_steps[5] += 1;
|
||||||
|
self.unbudgeted_steps[7] += 1;
|
||||||
|
if self.unbudgeted_steps[7] >= self.slippage {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
self.push_frame(Context::FrameForce);
|
||||||
|
self.enter_compute(body)
|
||||||
|
}
|
||||||
|
Term::Error => Err(Error::EvaluationFailure),
|
||||||
|
Term::Builtin(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_compute(&mut self, value: Value) -> Result<Term<NamedDeBruijn>, Error> {
|
||||||
|
let frame = self.frames.last().cloned().unwrap();
|
||||||
|
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.enter_compute(&arg)
|
||||||
|
}
|
||||||
|
Context::FrameForce => {
|
||||||
|
self.pop_frame();
|
||||||
|
self.force_evaluate(value)
|
||||||
|
}
|
||||||
|
Context::NoFrame => {
|
||||||
|
if self.unbudgeted_steps[7] > 0 {
|
||||||
|
self.spend_unbudgeted_steps()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let term = self.discharge_value(value);
|
||||||
|
Ok(term)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discharge_value(&mut self, value: Value) -> Term<NamedDeBruijn> {
|
||||||
|
match value {
|
||||||
|
Value::Con(x) => Term::Constant(x),
|
||||||
|
Value::Builtin(_, t) => t,
|
||||||
|
Value::Delay(_) => todo!(),
|
||||||
|
Value::Lambda {
|
||||||
|
parameter_name,
|
||||||
|
body,
|
||||||
|
} => self.discharge_value_env(Term::Lambda {
|
||||||
|
parameter_name: NamedDeBruijn {
|
||||||
|
text: parameter_name.text,
|
||||||
|
index: 0.into(),
|
||||||
|
},
|
||||||
|
body: Box::new(body),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discharge_value_env(&mut self, term: Term<NamedDeBruijn>) -> Term<NamedDeBruijn> {
|
||||||
|
fn rec(i: u32, t: Term<NamedDeBruijn>) -> Term<NamedDeBruijn> {
|
||||||
|
match t {
|
||||||
|
Term::Var(x) => todo!(),
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name,
|
||||||
|
body,
|
||||||
|
} => Term::Lambda {
|
||||||
|
parameter_name,
|
||||||
|
body: Box::new(rec(i + 1, *body)),
|
||||||
|
},
|
||||||
|
Term::Apply { function, argument } => Term::Apply {
|
||||||
|
function: Box::new(rec(i, *function)),
|
||||||
|
argument: Box::new(rec(i, *argument)),
|
||||||
|
},
|
||||||
|
|
||||||
|
Term::Delay(x) => Term::Delay(Box::new(rec(i, *x))),
|
||||||
|
Term::Force(x) => Term::Force(Box::new(rec(i, *x))),
|
||||||
|
rest => rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rec(0, term)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn force_evaluate(&mut self, value: Value) -> Result<Term<NamedDeBruijn>, Error> {
|
||||||
|
match value {
|
||||||
|
Value::Delay(body) => self.enter_compute(&body),
|
||||||
|
Value::Builtin(_, _) => todo!(),
|
||||||
|
rest => Err(Error::NonPolymorphicInstantiation(rest)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_evaluate(
|
||||||
|
&mut self,
|
||||||
|
function: Value,
|
||||||
|
argument: Value,
|
||||||
|
) -> Result<Term<NamedDeBruijn>, Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
fn spend_budget(&mut self, spend_budget: ExBudget) -> Result<(), Error> {
|
fn spend_budget(&mut self, spend_budget: ExBudget) -> Result<(), Error> {
|
||||||
self.ex_budget.mem -= spend_budget.mem;
|
self.ex_budget.mem -= spend_budget.mem;
|
||||||
|
@ -55,30 +205,56 @@ impl Machine {
|
||||||
fn push_frame(&mut self, frame: Context) {
|
fn push_frame(&mut self, frame: Context) {
|
||||||
self.frames.push(frame);
|
self.frames.push(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pop_frame(&mut self) {
|
||||||
|
self.frames.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lookup_var(&mut self, name: NamedDeBruijn) -> Result<Value, Error> {
|
||||||
|
self.env
|
||||||
|
.get::<usize>(name.index.into())
|
||||||
|
.cloned()
|
||||||
|
.ok_or(Error::OpenTermEvaluated(Term::Var(name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spend_unbudgeted_steps(&mut self) -> Result<(), Error> {
|
||||||
|
for i in 0..self.unbudgeted_steps.len() - 1 {
|
||||||
|
let mut unspent_step_budget = self.costs.get(StepKind::try_from(i as u8)?);
|
||||||
|
unspent_step_budget.occurence(self.unbudgeted_steps[i] as i32);
|
||||||
|
self.spend_budget(unspent_step_budget)?;
|
||||||
|
}
|
||||||
|
self.unbudgeted_steps = vec![0; 8];
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
enum Context {
|
enum Context {
|
||||||
FrameApplyFun(Term<DeBruijn>, Term<DeBruijn>),
|
FrameApplyFun(Value),
|
||||||
FrameApplyArg(Vec<Value>, Term<DeBruijn>, Box<Context>),
|
FrameApplyArg(Term<NamedDeBruijn>),
|
||||||
FrameForce(Box<Context>),
|
FrameForce,
|
||||||
NoFrame,
|
NoFrame,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Value {
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Value {
|
||||||
Con(Constant),
|
Con(Constant),
|
||||||
Delay(Term<DeBruijn>, Vec<Value>),
|
Delay(Term<NamedDeBruijn>),
|
||||||
Lambda(DeBruijn, Term<DeBruijn>, Vec<Value>),
|
Lambda {
|
||||||
|
parameter_name: NamedDeBruijn,
|
||||||
|
body: Term<NamedDeBruijn>,
|
||||||
|
},
|
||||||
Builtin(
|
Builtin(
|
||||||
DefaultFunction,
|
DefaultFunction,
|
||||||
Term<DeBruijn>,
|
Term<NamedDeBruijn>,
|
||||||
// Need to figure out run time stuff
|
// Need to figure out run time stuff
|
||||||
// BuiltinRuntime (CekValue uni fun)
|
// BuiltinRuntime (CekValue uni fun)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can be negative
|
/// Can be negative
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
struct ExBudget {
|
pub struct ExBudget {
|
||||||
mem: i32,
|
mem: i32,
|
||||||
cpu: i32,
|
cpu: i32,
|
||||||
}
|
}
|
||||||
|
@ -90,7 +266,7 @@ impl ExBudget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StepKind {
|
pub enum StepKind {
|
||||||
Constant,
|
Constant,
|
||||||
Var,
|
Var,
|
||||||
Lambda,
|
Lambda,
|
||||||
|
@ -101,9 +277,26 @@ enum StepKind {
|
||||||
StartUp,
|
StartUp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for StepKind {
|
||||||
|
type Error = error::Error;
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(StepKind::Constant),
|
||||||
|
1 => Ok(StepKind::Var),
|
||||||
|
2 => Ok(StepKind::Lambda),
|
||||||
|
3 => Ok(StepKind::Apply),
|
||||||
|
4 => Ok(StepKind::Delay),
|
||||||
|
5 => Ok(StepKind::Force),
|
||||||
|
6 => Ok(StepKind::Builtin),
|
||||||
|
v => Err(error::Error::InvalidStepKind(v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// There's no entry for Error since we'll be exiting anyway; also, what would
|
/// 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?
|
/// happen if calling 'Error' caused the budget to be exceeded?
|
||||||
struct Costs {
|
pub struct Costs {
|
||||||
startup: ExBudget,
|
startup: ExBudget,
|
||||||
var: ExBudget,
|
var: ExBudget,
|
||||||
constant: ExBudget,
|
constant: ExBudget,
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::ExBudget;
|
use crate::ast::{NamedDeBruijn, Term};
|
||||||
|
|
||||||
|
use super::{ExBudget, Value};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)]
|
#[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)]
|
||||||
OutOfExError(ExBudget),
|
OutOfExError(ExBudget),
|
||||||
|
#[error("Invalid Stepkind: {0}")]
|
||||||
|
InvalidStepKind(u8),
|
||||||
|
#[error("Cannot evaluate an open term: {0:#?}")]
|
||||||
|
OpenTermEvaluated(Term<NamedDeBruijn>),
|
||||||
|
#[error("The provided Plutus code called 'error'.")]
|
||||||
|
EvaluationFailure,
|
||||||
|
#[error("Attempted to instantiate a non-polymorphic term: {0:#?}")]
|
||||||
|
NonPolymorphicInstantiation(Value),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue