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:
2023-05-08 20:27:52 +01:00
parent a604e40bad
commit 6a381c5b57
28 changed files with 112 additions and 445 deletions

184
Cargo.lock generated
View File

@@ -79,12 +79,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -103,12 +97,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "chumsky"
version = "0.9.2"
@@ -152,7 +140,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.13",
"syn",
]
[[package]]
@@ -167,16 +155,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "dashmap"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
dependencies = [
"cfg-if",
"num_cpus",
]
[[package]]
name = "dyn-clone"
version = "1.0.11"
@@ -221,15 +199,6 @@ dependencies = [
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -254,37 +223,19 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
dependencies = [
"hermit-abi 0.3.1",
"hermit-abi",
"libc",
"windows-sys",
]
@@ -295,7 +246,7 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi 0.3.1",
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
@@ -310,16 +261,6 @@ dependencies = [
"either",
]
[[package]]
name = "lasso"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb7b21a526375c5ca55f1a6dfd4e1fad9fa4edd750f530252a718a44b2608f0"
dependencies = [
"dashmap",
"hashbrown 0.11.2",
]
[[package]]
name = "libc"
version = "0.2.142"
@@ -332,28 +273,6 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "mappable-rc"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "204651f31b0a6a7b2128d2b92c372cd94607b210c3a6b6e542c57a8cfd4db996"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "num-traits"
version = "0.2.15"
@@ -363,16 +282,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@@ -381,19 +290,15 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "orchid"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"base64",
"chumsky",
"clap",
"dyn-clone",
"hashbrown 0.13.2",
"itertools",
"lasso",
"mappable-rc",
"ordered-float",
"smallvec",
"static_init",
"thiserror",
]
@@ -406,31 +311,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "proc-macro2"
version = "1.0.56"
@@ -458,15 +338,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "rustix"
version = "0.37.19"
@@ -481,12 +352,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "smallvec"
version = "1.10.0"
@@ -506,51 +371,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "static_init"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6"
dependencies = [
"bitflags",
"cfg_aliases",
"libc",
"parking_lot",
"parking_lot_core",
"static_init_macro",
"winapi",
]
[[package]]
name = "static_init_macro"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf"
dependencies = [
"cfg_aliases",
"memchr",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.13"
@@ -579,7 +405,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
"syn",
]
[[package]]

View File

@@ -1,6 +1,6 @@
[package]
name = "orchid"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
authors = [
"Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"
@@ -12,12 +12,8 @@ authors = [
thiserror = "1.0"
chumsky = "0.9.2"
hashbrown = "0.13.2"
mappable-rc = "0.1"
ordered-float = "3.0"
itertools = "0.10"
smallvec = { version = "1.10.0", features = ['const_generics'] }
dyn-clone = "1.0.11"
lasso = { version = "0.6.0", features = ['multi-threaded'] }
base64 = "0.21.0"
static_init = "1.0.3"
clap = { version = "4.2.7", features = ["derive"] }
clap = { version = "4.2.4", features = ["derive"] }

View File

@@ -13,7 +13,7 @@ use super::boolean::Boolean;
#[derive(Clone)]
pub struct Equals2;
externfn_impl!(Equals2, |_: &Self, x: ExprInst| {Ok(Equals1{x})});
externfn_impl!(Equals2, |_: &Self, x: ExprInst| Ok(Equals1{x}));
/// Partially applied Equals function
///

View File

@@ -13,7 +13,7 @@ use super::Boolean;
#[derive(Clone)]
pub struct IfThenElse1;
externfn_impl!(IfThenElse1, |_: &Self, x: ExprInst| {Ok(IfThenElse0{x})});
externfn_impl!(IfThenElse1, |_: &Self, x: ExprInst| Ok(IfThenElse0{x}));
/// Partially applied IfThenElse function
///

View File

@@ -15,7 +15,7 @@ use crate::{atomic_impl, atomic_redirect, externfn_impl};
#[derive(Clone)]
pub struct ParseFloat1;
externfn_impl!(ParseFloat1, |_: &Self, x: ExprInst| {Ok(ParseFloat0{x})});
externfn_impl!(ParseFloat1, |_: &Self, x: ExprInst| Ok(ParseFloat0{x}));
/// Applied to_string function
///

View File

@@ -14,7 +14,7 @@ use crate::parse::int_parser;
#[derive(Clone)]
pub struct ParseUint1;
externfn_impl!(ParseUint1, |_: &Self, x: ExprInst| {Ok(ParseUint0{x})});
externfn_impl!(ParseUint1, |_: &Self, x: ExprInst| Ok(ParseUint0{x}));
/// Applied ParseUint function
///

View File

@@ -11,7 +11,7 @@ use crate::{atomic_impl, atomic_redirect, externfn_impl};
#[derive(Clone)]
pub struct ToString1;
externfn_impl!(ToString1, |_: &Self, x: ExprInst| {Ok(ToString0{x})});
externfn_impl!(ToString1, |_: &Self, x: ExprInst| Ok(ToString0{x}));
/// Applied ToString function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::{Clause, ExprInst};
#[derive(Clone)]
pub struct Print2;
externfn_impl!(Print2, |_: &Self, x: ExprInst| {Ok(Print1{x})});
externfn_impl!(Print2, |_: &Self, x: ExprInst| Ok(Print1{x}));
/// Partially applied Print function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::{Clause, ExprInst};
#[derive(Clone)]
pub struct Readln2;
externfn_impl!(Readln2, |_: &Self, x: ExprInst| {Ok(Readln1{x})});
externfn_impl!(Readln2, |_: &Self, x: ExprInst| Ok(Readln1{x}));
/// Partially applied Readln function
///
@@ -23,7 +23,8 @@ pub struct Readln1{ x: ExprInst }
atomic_redirect!(Readln1, x);
atomic_impl!(Readln1, |Self{ x }: &Self| {
let mut buf = String::new();
stdin().read_line(&mut buf).map_err(|e| RuntimeError::ext(e.to_string(), "reading from stdin"))?;
stdin().read_line(&mut buf)
.map_err(|e| RuntimeError::ext(e.to_string(), "reading from stdin"))?;
buf.pop();
Ok(Clause::Apply {
f: x.clone(),

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::ExprInst;
#[derive(Clone)]
pub struct Add2;
externfn_impl!(Add2, |_: &Self, x: ExprInst| {Ok(Add1{x})});
externfn_impl!(Add2, |_: &Self, x: ExprInst| Ok(Add1{x}));
/// Partially applied Add function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::ExprInst;
#[derive(Clone)]
pub struct Divide2;
externfn_impl!(Divide2, |_: &Self, x: ExprInst| {Ok(Divide1{x})});
externfn_impl!(Divide2, |_: &Self, x: ExprInst| Ok(Divide1{x}));
/// Partially applied Divide function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::ExprInst;
#[derive(Clone)]
pub struct Multiply2;
externfn_impl!(Multiply2, |_: &Self, x: ExprInst| {Ok(Multiply1{x})});
externfn_impl!(Multiply2, |_: &Self, x: ExprInst| Ok(Multiply1{x}));
/// Partially applied Multiply function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::ExprInst;
#[derive(Clone)]
pub struct Remainder2;
externfn_impl!(Remainder2, |_: &Self, x: ExprInst| {Ok(Remainder1{x})});
externfn_impl!(Remainder2, |_: &Self, x: ExprInst| Ok(Remainder1{x}));
/// Partially applied Remainder function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::ExprInst;
#[derive(Clone)]
pub struct Subtract2;
externfn_impl!(Subtract2, |_: &Self, x: ExprInst| {Ok(Subtract1{x})});
externfn_impl!(Subtract2, |_: &Self, x: ExprInst| Ok(Subtract1{x}));
/// Partially applied Subtract function
///

View File

@@ -12,7 +12,7 @@ use crate::representations::interpreted::{Clause, ExprInst};
#[derive(Clone)]
pub struct CharAt2;
externfn_impl!(CharAt2, |_: &Self, x: ExprInst| {Ok(CharAt1{x})});
externfn_impl!(CharAt2, |_: &Self, x: ExprInst| Ok(CharAt1{x}));
/// Partially applied CharAt function
///

View File

@@ -11,7 +11,7 @@ use crate::representations::interpreted::{Clause, ExprInst};
#[derive(Clone)]
pub struct Concatenate2;
externfn_impl!(Concatenate2, |_: &Self, c: ExprInst| {Ok(Concatenate1{c})});
externfn_impl!(Concatenate2, |_: &Self, c: ExprInst| Ok(Concatenate1{c}));
/// Partially applied Concatenate function
///

View File

@@ -1,4 +1,5 @@
use std::any::Any;
use std::error::Error;
use std::fmt::{Display, Debug};
use std::hash::Hash;
use std::rc::Rc;
@@ -11,10 +12,16 @@ use crate::representations::Primitive;
pub use crate::representations::interpreted::Clause;
use crate::representations::interpreted::ExprInst;
pub struct AtomicReturn {
pub clause: Clause,
pub gas: Option<usize>,
pub inert: bool
}
// Aliases for concise macros
pub type RcError = Rc<dyn ExternError>;
pub type AtomicResult = Result<(Clause, Option<usize>), RuntimeError>;
pub type XfnResult = Result<(Clause, Option<usize>), RcError>;
pub type AtomicResult = Result<AtomicReturn, RuntimeError>;
pub type XfnResult = Result<Clause, RcError>;
pub type RcExpr = ExprInst;
pub trait ExternError: Display {
@@ -24,6 +31,14 @@ pub trait ExternError: Display {
}
}
impl Debug for dyn ExternError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
impl Error for dyn ExternError {}
/// Represents an externally defined function from the perspective of
/// the executor. Since Orchid lacks basic numerical operations,
/// these are also external functions.
@@ -85,6 +100,9 @@ impl Atom {
self.data().as_any().downcast_ref()
.expect("Type mismatch on Atom::cast")
}
pub fn run(&self, ctx: Context) -> AtomicResult {
self.0.run(ctx)
}
}
impl Clone for Atom {

View File

@@ -56,14 +56,14 @@ macro_rules! atomic_impl {
AsRef<$crate::foreign::RcExpr>
>::as_ref(self).clone();
// run the expression
let ret = $crate::interpreter::run(expr, ctx)?;
let $crate::interpreter::Return{ gas, state } = ret;
let ret = $crate::interpreter::run(expr, ctx.clone())?;
let $crate::interpreter::Return{ gas, state, inert } = ret;
// rebuild the atomic
let next_self = <Self as
From<(&Self, $crate::foreign::RcExpr)>
>::from((self, state));
// branch off or wrap up
let next_clause = if gas.map(|g| g > 0).unwrap_or(true) {
let clause = if inert {
match ($next_phase)(&next_self) {
Ok(r) => r,
Err(e) => return Err(
@@ -72,7 +72,7 @@ macro_rules! atomic_impl {
}
} else { next_self.to_atom_cls() };
// package and return
Ok((next_clause, gas))
Ok($crate::foreign::AtomicReturn{ clause, gas, inert: false })
}
}
};

View File

@@ -18,7 +18,11 @@ macro_rules! atomic_inert {
fn run(&self, ctx: $crate::interpreter::Context)
-> $crate::foreign::AtomicResult
{
Ok((self.clone().to_atom_cls(), ctx.gas))
Ok($crate::foreign::AtomicReturn{
clause: self.clone().to_atom_cls(),
gas: ctx.gas,
inert: true
})
}
}
};

View File

@@ -23,17 +23,16 @@ macro_rules! externfn_impl {
fn name(&self) -> &str {stringify!($typ)}
fn apply(&self,
arg: $crate::foreign::RcExpr,
ctx: $crate::interpreter::Context
_ctx: $crate::interpreter::Context
) -> $crate::foreign::XfnResult {
match ($next_atomic)(self, arg) { // ? casts the result but we want to strictly forward it
Ok(r) => Ok((
Ok(r) => Ok(
$crate::representations::interpreted::Clause::P(
$crate::representations::Primitive::Atom(
$crate::foreign::Atom::new(r)
)
),
ctx.gas.map(|g| g - 1)
)),
)
),
Err(e) => Err(e)
}
}

View File

@@ -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 })
}

View File

@@ -24,4 +24,5 @@ impl Context<'_> {
pub struct Return {
pub state: ExprInst,
pub gas: Option<usize>,
pub inert: bool,
}

View File

@@ -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),

View File

@@ -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 })
}

View File

@@ -56,26 +56,27 @@ impl ExprInst {
/// Call a normalization function on the expression. The expr is
/// updated with the new clause which affects all copies of it
/// across the tree.
pub fn try_normalize<E>(&self,
mapper: impl FnOnce(&Clause) -> Result<Clause, E>
) -> Result<Self, E> {
let new_clause = mapper(&self.expr().clause)?;
pub fn try_normalize<T, E>(&self,
mapper: impl FnOnce(&Clause) -> Result<(Clause, T), E>
) -> Result<(Self, T), E> {
let (new_clause, extra) = mapper(&self.expr().clause)?;
self.expr_mut().clause = new_clause;
Ok(self.clone())
Ok((self.clone(), extra))
}
/// Run a mutation function on the expression, producing a new,
/// distinct expression. The new expression shares location info with
/// the original but is normalized independently.
pub fn try_update<E>(&self,
mapper: impl FnOnce(&Clause) -> Result<Clause, E>
) -> Result<Self, E> {
pub fn try_update<T, E>(&self,
mapper: impl FnOnce(&Clause) -> Result<(Clause, T), E>
) -> Result<(Self, T), E> {
let expr = self.expr();
let (clause, extra) = mapper(&expr.clause)?;
let new_expr = Expr{
clause: mapper(&expr.clause)?,
clause,
location: expr.location.clone(),
};
Ok(Self(Rc::new(RefCell::new(new_expr))))
Ok((Self(Rc::new(RefCell::new(new_expr))), extra))
}
/// Call a predicate on the expression, returning whatever the

View File

@@ -34,7 +34,6 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a Expr>) -> usize {
/// Recursively convert this pattern into a matcher that can be
/// efficiently applied to slices.
pub fn mk_matcher(pattern: &[Expr]) -> AnyMatcher {
println!("matcher from {:?}", pattern);
let left_split = scal_cnt(pattern.iter());
if pattern.len() <= left_split {
return AnyMatcher::Scalar(mk_scalv(pattern))
@@ -51,13 +50,11 @@ pub fn mk_matcher(pattern: &[Expr]) -> AnyMatcher {
/// Pattern MUST NOT contain vectorial placeholders
fn mk_scalv(pattern: &[Expr]) -> Vec<ScalMatcher> {
println!("Scalv from {:?}", pattern);
pattern.iter().map(mk_scalar).collect()
}
/// Pattern MUST start and end with a vectorial placeholder
fn mk_vec(pattern: &[Expr]) -> VecMatcher {
println!("Vec from {:?}", pattern);
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
debug_assert!(pattern.last().map(vec_attrs).is_some(), "pattern must end with a vectorial");
@@ -103,7 +100,6 @@ fn mk_vec(pattern: &[Expr]) -> VecMatcher {
/// Pattern MUST NOT be a vectorial placeholder
fn mk_scalar(pattern: &Expr) -> ScalMatcher {
println!("Scalar from {:?}", pattern);
match &pattern.value {
Clause::P(p) => ScalMatcher::P(p.clone()),
Clause::Name(n) => ScalMatcher::Name(*n),

View File

@@ -112,7 +112,7 @@ pub fn run_dir(dir: &Path) {
match repo.step(&tree) {
None => break tree,
Some(phase) => {
// println!("Step {idx}/{macro_timeout}: {}", phase.bundle(&i));
println!("Step {idx}/{macro_timeout}: {}", phase.bundle(&i));
tree = phase;
},
}
@@ -135,9 +135,16 @@ pub fn run_dir(dir: &Path) {
.join(", ")
)
});
let Return{ gas, state } = interpreter::run(entrypoint.clone(), ctx)
let Return{ gas, state, inert } = interpreter::run(entrypoint.clone(), ctx)
.unwrap_or_else(|e| panic!("Runtime error: {}", e));
println!("Settled at {}", state.bundle(&i));
if inert {
println!("Expression not reducible");
println!("Settled at {}", state.expr().clause.bundle(&i));
println!("Remaining gas: {}",
gas.map(|g| g.to_string())
.unwrap_or(String::from(""))
);
}
if gas == Some(0) {println!("Ran out of gas!")}
else {println!("Expression not reducible.")}
}

View File

@@ -1,181 +0,0 @@
use std::sync::{Mutex, Arc};
use std::num::NonZeroU32;
use std::hash::Hash;
use lasso::{Rodeo, Spur, Key};
use base64::{engine::general_purpose::STANDARD_NO_PAD as BASE64, Engine};
/// A token representing an interned string or sequence in an interner
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Token<const RANK: u8>(pub Spur);
/// An interner that can intern strings, and sequences of things it
/// interned as long as they have the same rank
pub trait Interner: Sync {
fn str2tok(&self, str: &str) -> Token<String>;
fn tok2str(&self, token: Token<String>) -> String;
fn slc2tok<const RANK: u8>(&self, slice: &[Token<RANK>]) -> Token<{RANK + 1}>;
fn tok2slc<const RANK: u8>(&self, token: Token<RANK>) -> Vec<Token<{RANK - 1}>>;
fn tok2strv(&self, token: Token<Vec<Token<String>>>) -> Vec<String> {
self.tok2slc(token).into_iter().map(|t| self.tok2str(t)).collect()
}
fn tokv2strv(&self, slice: &[Token<String>]) -> Vec<String> {
slice.iter().map(|t| self.tok2str(*t)).collect()
}
/// Get the first token of a sequence
fn head<const RANK: u8>(&self, token: Token<RANK>) -> Token<{RANK - 1}>;
/// Returns the length of a sequence
fn len<const RANK: u8>(&self, token: Token<RANK>) -> usize
where Token<{RANK - 1}>: Clone;
/// Returns the length of the longest identical prefix of the two sequences
fn coprefix<const RANK: u8>(&self, a: Token<RANK>, b: Token<RANK>) -> usize
where Token<{RANK - 1}>: Clone;
}
fn serialize_seq<const RANK: u8>(seq: &[Token<RANK>]) -> String {
let data: Vec<u8> = seq.iter()
.map(|t| u32::from(t.0.into_inner()).to_le_bytes().into_iter())
.flatten()
.collect();
BASE64.encode(data)
}
fn deserialize_seq<const RANK: u8>(string: &str) -> Vec<Token<RANK>> {
let data = BASE64.decode(string)
.expect("String is not valid base64");
assert!(data.len() % 4 == 0, "Tokens should serialize to 3 bytes each");
data.array_chunks::<4>().map(|a| {
let bytes = [a[0], a[1], a[2], a[3]];
let nz32 = NonZeroU32::new(u32::from_le_bytes(bytes))
.expect("Token representation should never be zero");
Token(Spur::try_from_usize(u32::from(nz32) as usize).unwrap())
}).collect()
}
/// An interner that delegates the actual work to Lasso
#[derive(Clone)]
pub struct LassoInterner {
strings: Arc<Mutex<Rodeo>>,
slices: Arc<Mutex<Rodeo>>
}
impl LassoInterner {
/// Create an empty interner. Called to create the singleton.
fn new() -> Self {
Self{
slices: Arc::new(Mutex::new(Rodeo::new())),
strings: Arc::new(Mutex::new(Rodeo::new()))
}
}
}
impl Interner for LassoInterner {
fn str2tok(&self, str: &str) -> Token<String> {
let mut strings = self.strings.lock().unwrap();
let key = strings.get_or_intern(str);
Token(key)
}
fn tok2str<'a>(&'a self, token: Token<String>) -> String {
let key = token.0;
let strings = self.strings.lock().unwrap();
strings.resolve(&key).to_string()
}
fn slc2tok<const RANK: u8>(&self, slice: &[Token<RANK>]) -> Token<{RANK + 1}> {
let data = serialize_seq(slice);
let mut slices = self.slices.lock().unwrap();
let key = slices.get_or_intern(data);
Token(key)
}
fn tok2slc<'a, const RANK: u8>(&'a self, token: Token<RANK>) -> Vec<Token<{RANK - 1}>> {
let key = token.0;
let slices = self.slices.lock().unwrap();
let string = slices.resolve(&key);
deserialize_seq(string)
}
fn head<const RANK: u8>(&self, token: Token<RANK>) -> Token<{RANK - 1}> {
let key = token.0;
let slices = self.slices.lock().unwrap();
let string = slices.resolve(&key);
deserialize_seq(&string[0..5])[0]
}
fn len<const RANK: u8>(&self, token: Token<RANK>) -> usize where Token<{RANK - 1}>: Clone {
let key = token.0;
let slices = self.slices.lock().unwrap();
let string = slices.resolve(&key);
assert!(string.len() % 4 == 0, "Tokens should serialize to 3 characters");
string.len() / 4
}
fn coprefix<const RANK: u8>(&self, a: Token<RANK>, b: Token<RANK>) -> usize where Token<{RANK - 1}>: Clone {
let keya = a.0;
let keyb = b.0;
let slices = self.slices.lock().unwrap();
let sa = slices.resolve(&keya);
let sb = slices.resolve(&keyb);
sa.bytes()
.zip(sb.bytes())
.take_while(|(a, b)| a == b)
.count() / 4
}
}
/// Create an interner that inherits the singleton's data, and
/// block all future interaction with the singleton.
///
/// DO NOT call within [dynamic] or elsewhere pre-main
pub fn mk_interner() -> impl Interner {
LassoInterner::new()
}
pub trait StringLike: Clone + Eq + Hash {
fn into_str(self, i: &Interner) -> String;
fn into_tok(self, i: &Interner) -> Token<String>;
}
impl StringLike for String {
fn into_str(self, _i: &Interner) -> String {self}
fn into_tok(self, i: &Interner) -> Token<String> {i.str2tok(&self)}
}
impl StringLike for Token<String> {
fn into_str(self, i: &Interner) -> String {i.tok2str(self)}
fn into_tok(self, _i: &Interner) -> Token<String> {self}
}
pub trait StringVLike: Clone + Eq + Hash {
fn into_strv(self, i: &Interner) -> Vec<String>;
fn into_tok(self, i: &Interner) -> Token<Vec<Token<String>>>;
fn into_tokv(self, i: &Interner) -> Vec<Token<String>>;
}
impl StringVLike for Vec<String> {
fn into_strv(self, _i: &Interner) -> Vec<String> {self}
fn into_tok(self, i: &Interner) -> Token<Vec<Token<String>>> {
let tokv = self.into_iter()
.map(|s| i.str2tok(&s))
.collect::<Vec<_>>();
i.slc2tok(&tokv)
}
fn into_tokv(self, i: &Interner) -> Vec<Token<String>> {
self.into_iter()
.map(|s| i.str2tok(&s))
.collect()
}
}
impl StringVLike for Vec<Token<String>> {
fn into_strv(self, i: &Interner) -> Vec<String> {i.tokv2strv(&self)}
fn into_tok(self, i: &Interner) -> Token<Vec<Token<String>>> {i.slc2tok(&self)}
fn into_tokv(self, _i: &Interner) -> Vec<Token<String>> {self}
}
impl StringVLike for Token<Vec<Token<String>>> {
fn into_strv(self, i: &Interner) -> Vec<String> {i.tok2strv(self)}
fn into_tok(self, _i: &Interner) -> Token<Vec<Token<String>>> {self}
fn into_tokv(self, i: &Interner) -> Vec<Token<String>> {i.tok2slc(self)}
}