forked from Orchid/orchid
Fixing some showstoppers
- inertness now tracked separately from gas - atomic_impl now correctly rolls over when the argument is inert - syntax fixes - tree shaking
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::foreign::Atom;
|
||||
use crate::foreign::AtomicReturn;
|
||||
use crate::representations::Primitive;
|
||||
use crate::representations::PathSet;
|
||||
use crate::representations::interpreted::{ExprInst, Clause};
|
||||
@@ -19,18 +19,18 @@ fn map_at<E>(
|
||||
source.try_update(|value| {
|
||||
// Pass right through lambdas
|
||||
if let Clause::Lambda { args, body } = value {
|
||||
return Ok(Clause::Lambda {
|
||||
return Ok((Clause::Lambda {
|
||||
args: args.clone(),
|
||||
body: map_at(path, body.clone(), mapper)?
|
||||
})
|
||||
}, ()))
|
||||
}
|
||||
// If the path ends here, process the next (non-lambda) node
|
||||
let (head, tail) = if let Some(sf) = path.split_first() {sf} else {
|
||||
return Ok(mapper(value)?)
|
||||
return Ok((mapper(value)?, ()))
|
||||
};
|
||||
// If it's an Apply, execute the next step in the path
|
||||
if let Clause::Apply { f, x } = value {
|
||||
return Ok(match head {
|
||||
return Ok((match head {
|
||||
Side::Left => Clause::Apply {
|
||||
f: map_at(tail, f.clone(), mapper)?,
|
||||
x: x.clone(),
|
||||
@@ -39,10 +39,10 @@ fn map_at<E>(
|
||||
f: f.clone(),
|
||||
x: map_at(tail, x.clone(), mapper)?,
|
||||
}
|
||||
})
|
||||
}, ()))
|
||||
}
|
||||
panic!("Invalid path")
|
||||
})
|
||||
}).map(|p| p.0)
|
||||
}
|
||||
|
||||
fn substitute(paths: &PathSet, value: Clause, body: ExprInst) -> ExprInst {
|
||||
@@ -64,15 +64,14 @@ fn substitute(paths: &PathSet, value: Clause, body: ExprInst) -> ExprInst {
|
||||
/// Apply a function-like expression to a parameter.
|
||||
/// If any work is being done, gas will be deducted.
|
||||
pub fn apply(
|
||||
f: ExprInst, x: ExprInst, mut ctx: Context
|
||||
f: ExprInst, x: ExprInst, ctx: Context
|
||||
) -> Result<Return, RuntimeError> {
|
||||
let state = f.clone().try_update(|clause| match clause {
|
||||
let (state, (gas, inert)) = f.clone().try_update(|clause| match clause {
|
||||
// apply an ExternFn or an internal function
|
||||
Clause::P(Primitive::ExternFn(f)) => {
|
||||
let (clause, gas) = f.apply(x, ctx.clone())
|
||||
let clause = f.apply(x, ctx.clone())
|
||||
.map_err(|e| RuntimeError::Extern(e))?;
|
||||
ctx.gas = gas.map(|g| g - 1); // cost of extern call
|
||||
Ok(clause)
|
||||
Ok((clause, (ctx.gas.map(|g| g - 1), false)))
|
||||
}
|
||||
Clause::Lambda{args, body} => Ok(if let Some(args) = args {
|
||||
let x_cls = x.expr().clause.clone();
|
||||
@@ -80,25 +79,22 @@ pub fn apply(
|
||||
let new_xpr = new_xpr_inst.expr();
|
||||
// cost of substitution
|
||||
// XXX: should this be the number of occurrences instead?
|
||||
ctx.gas = ctx.gas.map(|x| x - 1);
|
||||
new_xpr.clause.clone()
|
||||
} else {body.expr().clause.clone()}),
|
||||
(new_xpr.clause.clone(), (ctx.gas.map(|x| x - 1), false))
|
||||
} else {(body.expr().clause.clone(), (ctx.gas, false))}),
|
||||
Clause::Constant(name) => {
|
||||
let symval = ctx.symbols.get(name).expect("missing symbol for function").clone();
|
||||
ctx.gas = ctx.gas.map(|x| x - 1); // cost of lookup
|
||||
Ok(Clause::Apply { f: symval, x, })
|
||||
Ok((Clause::Apply { f: symval, x, }, (ctx.gas, false)))
|
||||
}
|
||||
Clause::P(Primitive::Atom(Atom(atom))) => { // take a step in expanding atom
|
||||
let (clause, gas) = atom.run(ctx.clone())?;
|
||||
ctx.gas = gas.map(|x| x - 1); // cost of dispatch
|
||||
Ok(Clause::Apply { f: clause.wrap(), x })
|
||||
Clause::P(Primitive::Atom(atom)) => { // take a step in expanding atom
|
||||
let AtomicReturn { clause, gas, inert } = atom.run(ctx.clone())?;
|
||||
Ok((Clause::Apply { f: clause.wrap(), x }, (gas, inert)))
|
||||
},
|
||||
Clause::Apply{ f: fun, x: arg } => { // take a step in resolving pre-function
|
||||
let res = apply(fun.clone(), arg.clone(), ctx.clone())?;
|
||||
ctx.gas = res.gas; // if work has been done, it has been paid
|
||||
Ok(Clause::Apply{ f: res.state, x })
|
||||
let ret = apply(fun.clone(), arg.clone(), ctx.clone())?;
|
||||
let Return { state, inert, gas } = ret;
|
||||
Ok((Clause::Apply{ f: state, x }, (gas, inert)))
|
||||
},
|
||||
_ => Err(RuntimeError::NonFunctionApplication(f.clone()))
|
||||
})?;
|
||||
Ok(Return { state, gas: ctx.gas })
|
||||
Ok(Return { state, gas, inert })
|
||||
}
|
||||
@@ -24,4 +24,5 @@ impl Context<'_> {
|
||||
pub struct Return {
|
||||
pub state: ExprInst,
|
||||
pub gas: Option<usize>,
|
||||
pub inert: bool,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::representations::interpreted::ExprInst;
|
||||
use crate::foreign::ExternError;
|
||||
|
||||
/// Problems in the process of execution
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RuntimeError {
|
||||
Extern(Rc<dyn ExternError>),
|
||||
NonFunctionApplication(ExprInst),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::foreign::Atom;
|
||||
use crate::foreign::AtomicReturn;
|
||||
use crate::representations::Primitive;
|
||||
use crate::representations::interpreted::{Clause, ExprInst};
|
||||
|
||||
@@ -9,19 +9,20 @@ use super::context::{Context, Return};
|
||||
pub fn run(expr: ExprInst, mut ctx: Context)
|
||||
-> Result<Return, RuntimeError>
|
||||
{
|
||||
let state = expr.try_normalize(|cls| -> Result<Clause, RuntimeError> {
|
||||
let (state, (gas, inert)) = expr.try_normalize(|cls| -> Result<(Clause, _), RuntimeError> {
|
||||
let mut i = cls.clone();
|
||||
while ctx.gas.map(|g| g > 0).unwrap_or(true) {
|
||||
match &i {
|
||||
Clause::Apply { f, x } => {
|
||||
let res = apply(f.clone(), x.clone(), ctx.clone())?;
|
||||
if ctx.is_stuck(res.gas) {return Ok(i)}
|
||||
if res.inert {return Ok((i, (res.gas, true)))}
|
||||
ctx.gas = res.gas;
|
||||
i = res.state.expr().clause.clone();
|
||||
}
|
||||
Clause::P(Primitive::Atom(Atom(data))) => {
|
||||
let (clause, gas) = data.run(ctx.clone())?;
|
||||
if ctx.is_stuck(gas) {return Ok(i)}
|
||||
Clause::P(Primitive::Atom(data)) => {
|
||||
let ret = data.run(ctx.clone())?;
|
||||
let AtomicReturn { clause, gas, inert } = ret;
|
||||
if inert {return Ok((i, (gas, true)))}
|
||||
ctx.gas = gas;
|
||||
i = clause.clone();
|
||||
}
|
||||
@@ -30,10 +31,12 @@ pub fn run(expr: ExprInst, mut ctx: Context)
|
||||
ctx.gas = ctx.gas.map(|g| g - 1); // cost of lookup
|
||||
i = symval.expr().clause.clone();
|
||||
}
|
||||
_ => return Ok(i)
|
||||
// non-reducible
|
||||
_ => return Ok((i, (ctx.gas, true)))
|
||||
}
|
||||
}
|
||||
Ok(i)
|
||||
// out of gas
|
||||
Ok((i, (ctx.gas, false)))
|
||||
})?;
|
||||
Ok(Return { state, gas: ctx.gas })
|
||||
Ok(Return { state, gas, inert })
|
||||
}
|
||||
Reference in New Issue
Block a user