diff --git a/src/foreign/atom.rs b/src/foreign/atom.rs index 4222c87..9f66dcb 100644 --- a/src/foreign/atom.rs +++ b/src/foreign/atom.rs @@ -20,12 +20,6 @@ pub struct AtomicReturn { /// [Atomic::run] pub inert: bool, } -impl AtomicReturn { - /// Wrap an inert atomic for delivery to the supervisor - pub fn from_data(d: D, c: Context) -> Self { - AtomicReturn { clause: d.atom_cls(), gas: c.gas, inert: false } - } -} /// Returned by [Atomic::run] pub type AtomicResult = Result; @@ -40,15 +34,17 @@ where /// /// This function should be implemented in exactly one way: /// + /// ```ignore + /// fn as_any(self: Box) -> Box { self } /// ``` - /// fn as_any(&self) -> &dyn Any { self } - /// ``` - fn as_any(&self) -> &dyn Any; + fn as_any(self: Box) -> Box; + /// See [Atomic::as_any], exactly the same but for references + fn as_any_ref(&self) -> &dyn Any; /// Attempt to normalize this value. If it wraps a value, this should report /// inert. If it wraps a computation, it should execute one logical step of /// the computation and return a structure representing the ntext. - fn run(&self, ctx: Context) -> AtomicResult; + fn run(self: Box, ctx: Context) -> AtomicResult; /// Wrap the atom in a clause to be placed in an [AtomicResult]. fn atom_cls(self) -> Clause @@ -83,17 +79,20 @@ impl Atom { /// Get the contained data pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic } /// Attempt to downcast contained data to a specific type - pub fn try_cast(&self) -> Option<&T> { - self.data().as_any().downcast_ref() + pub fn try_cast(self) -> Result { + match self.0.as_any_ref().is::() { + true => Ok(*self.0.as_any().downcast().expect("checked just above")), + false => Err(self), + } } /// Test the type of the contained data without downcasting - pub fn is(&self) -> bool { self.data().as_any().is::() } + pub fn is(&self) -> bool { self.data().as_any_ref().is::() } /// Downcast contained data, panic if it isn't the specified type - pub fn cast(&self) -> &T { - self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast") + pub fn cast(self) -> T { + *self.0.as_any().downcast().expect("Type mismatch on Atom::cast") } /// Normalize the contained data - pub fn run(&self, ctx: Context) -> AtomicResult { self.0.run(ctx) } + pub fn run(self, ctx: Context) -> AtomicResult { self.0.run(ctx) } } impl Clone for Atom { diff --git a/src/foreign/cps_box.rs b/src/foreign/cps_box.rs index a6eb702..0ff4c6c 100644 --- a/src/foreign/cps_box.rs +++ b/src/foreign/cps_box.rs @@ -35,7 +35,7 @@ impl CPSFn { } impl ExternFn for CPSFn { fn name(&self) -> &str { "CPS function without argument" } - fn apply(&self, arg: ExprInst, _ctx: Context) -> XfnResult { + fn apply(self: Box, arg: ExprInst, _ctx: Context) -> XfnResult { let payload = self.payload.clone(); let continuations = pushed_ref(&self.continuations, arg); if self.argc == 1 { @@ -68,26 +68,27 @@ impl CPSBox { ) } /// Unpack the wrapped command and the continuation - pub fn unpack1(&self) -> (&T, &ExprInst) { + pub fn unpack1(self) -> (T, ExprInst) { self.assert_count(1); - (&self.payload, &self.continuations[0]) + let [cont]: [ExprInst; 1] = + self.continuations.try_into().expect("size checked"); + (self.payload, cont) } /// Unpack the wrapped command and 2 continuations (usually an async and a /// sync) - pub fn unpack2(&self) -> (&T, &ExprInst, &ExprInst) { + pub fn unpack2(self) -> (T, ExprInst, ExprInst) { self.assert_count(2); - (&self.payload, &self.continuations[0], &self.continuations[1]) + let [c1, c2]: [ExprInst; 2] = + self.continuations.try_into().expect("size checked"); + (self.payload, c1, c2) } /// Unpack the wrapped command and 3 continuations (usually an async success, /// an async fail and a sync) - pub fn unpack3(&self) -> (&T, &ExprInst, &ExprInst, &ExprInst) { + pub fn unpack3(self) -> (T, ExprInst, ExprInst, ExprInst) { self.assert_count(3); - ( - &self.payload, - &self.continuations[0], - &self.continuations[1], - &self.continuations[2], - ) + let [c1, c2, c3]: [ExprInst; 3] = + self.continuations.try_into().expect("size checked"); + (self.payload, c1, c2, c3) } } diff --git a/src/foreign/extern_fn.rs b/src/foreign/extern_fn.rs index d7a8700..fd4f6a0 100644 --- a/src/foreign/extern_fn.rs +++ b/src/foreign/extern_fn.rs @@ -39,7 +39,7 @@ pub trait ExternFn: DynClone { /// Display name of the function fn name(&self) -> &str; /// Combine the function with an argument to produce a new clause - fn apply(&self, arg: ExprInst, ctx: Context) -> XfnResult; + fn apply(self: Box, arg: ExprInst, ctx: Context) -> XfnResult; /// Hash the name to get a somewhat unique hash. fn hash(&self, mut state: &mut dyn std::hash::Hasher) { self.name().hash(&mut state) diff --git a/src/foreign/inert.rs b/src/foreign/inert.rs index 38ebbec..c425421 100644 --- a/src/foreign/inert.rs +++ b/src/foreign/inert.rs @@ -6,10 +6,11 @@ use super::{AtomicResult, AtomicReturn, ExternError}; #[allow(unused)] // for doc use crate::define_fn; use crate::foreign::Atomic; -use crate::interpreted::{ExprInst, TryFromExprInst}; +use crate::interpreted::{Clause, Expr, ExprInst, TryFromExprInst}; use crate::interpreter::Context; -use crate::systems::cast_exprinst::with_atomic; +use crate::systems::AssertionError; use crate::utils::ddispatch::{Request, Responder}; +use crate::Primitive; /// A proxy trait that implements [Atomic] for blobs of data in Rust code that /// cannot be processed and always report inert. Since these are expected to be @@ -28,19 +29,23 @@ impl Responder for T { fn respond(&self, request: Request) { self.respond(request) } } impl Atomic for T { - fn as_any(&self) -> &dyn Any { self } + fn as_any(self: Box) -> Box { self } + fn as_any_ref(&self) -> &dyn Any { self } - fn run(&self, ctx: Context) -> AtomicResult { - Ok(AtomicReturn { - clause: self.clone().atom_cls(), - gas: ctx.gas, - inert: true, - }) + fn run(self: Box, ctx: Context) -> AtomicResult { + Ok(AtomicReturn { gas: ctx.gas, inert: true, clause: self.atom_cls() }) } } impl TryFromExprInst for T { - fn from_exi(exi: &ExprInst) -> Result> { - with_atomic(exi, Self::type_str(), |a: &T| Ok(a.clone())) + fn from_exi(exi: ExprInst) -> Result> { + let Expr { clause, location } = exi.expr_val(); + match clause { + Clause::P(Primitive::Atom(a)) => match a.0.as_any().downcast() { + Ok(t) => Ok(*t), + Err(_) => AssertionError::fail(location, Self::type_str()), + }, + _ => AssertionError::fail(location, "atom"), + } } } diff --git a/src/foreign_macros/atomic_impl.rs b/src/foreign_macros/atomic_impl.rs index 6e69840..c116f77 100644 --- a/src/foreign_macros/atomic_impl.rs +++ b/src/foreign_macros/atomic_impl.rs @@ -41,8 +41,9 @@ use crate::Primitive; /// ``` /// use orchidlang::{Literal}; /// use orchidlang::interpreted::{ExprInst, Clause}; -/// use orchidlang::systems::cast_exprinst::with_lit; +/// use orchidlang::systems::cast_exprinst::get_literal; /// use orchidlang::{atomic_impl, atomic_redirect, externfn_impl}; +/// use orchidlang::ddispatch::Responder; /// /// /// Convert a literal to a string using Rust's conversions for floats, chars and /// /// uints respectively @@ -50,65 +51,67 @@ use crate::Primitive; /// struct ToString; /// /// externfn_impl!{ -/// ToString, |_: &Self, expr_inst: ExprInst|{ -/// Ok(InternalToString { -/// expr_inst -/// }) +/// ToString, |_: Self, expr_inst: ExprInst|{ +/// Ok(InternalToString { expr_inst }) /// } /// } /// #[derive(std::fmt::Debug,Clone)] /// struct InternalToString { /// expr_inst: ExprInst, /// } +/// impl Responder for InternalToString {} /// atomic_redirect!(InternalToString, expr_inst); -/// atomic_impl!(InternalToString, |Self { expr_inst }: &Self, _|{ -/// with_lit(expr_inst, |l| Ok(match l { -/// Literal::Uint(i) => Literal::Str(i.to_string().into()), -/// Literal::Num(n) => Literal::Str(n.to_string().into()), -/// s@Literal::Str(_) => s.clone(), -/// })).map(Clause::from) +/// atomic_impl!(InternalToString, |Self { expr_inst }: Self, _|{ +/// Ok(match get_literal(expr_inst)?.0 { +/// Literal::Uint(i) => Clause::from(Literal::Str(i.to_string().into())), +/// Literal::Num(n) => Clause::from(Literal::Str(n.to_string().into())), +/// s@Literal::Str(_) => Clause::from(s), +/// }) /// }); /// ``` #[macro_export] macro_rules! atomic_impl { ($typ:ident) => { - $crate::atomic_impl! {$typ, |this: &Self, _: $crate::interpreter::Context| { + $crate::atomic_impl! {$typ, |this: Self, _: $crate::interpreter::Context| { use $crate::foreign::ExternFn; - Ok(this.clone().xfn_cls()) + Ok(this.xfn_cls()) }} }; ($typ:ident, $next_phase:expr) => { impl $crate::foreign::Atomic for $typ { - fn as_any(&self) -> &dyn std::any::Any { self } + fn as_any(self: Box) -> Box { self } + fn as_any_ref(&self) -> &dyn std::any::Any { self } fn run( - &self, + self: Box, ctx: $crate::interpreter::Context, ) -> $crate::foreign::AtomicResult { // extract the expression + let mut this = *self; let expr = - >::as_ref(self).clone(); + >::as_mut(&mut this); // run the expression - let ret = $crate::interpreter::run(expr, ctx.clone())?; - let $crate::interpreter::Return { gas, state, inert } = ret; - // rebuild the atomic - let next_self = >::from((self, state)); + let (gas, inert) = + $crate::take_with_output( + expr, + |expr| match $crate::interpreter::run(expr, ctx.clone()) { + Ok(ret) => (ret.state, Ok((ret.gas, ret.inert))), + Err(e) => ($crate::interpreted::Clause::Bottom.wrap(), Err(e)), + }, + )?; // branch off or wrap up let clause = if inert { let closure = $next_phase; let res: Result< $crate::interpreted::Clause, std::rc::Rc, - > = closure(&next_self, ctx); + > = closure(this, ctx); match res { Ok(r) => r, Err(e) => return Err($crate::interpreter::RuntimeError::Extern(e)), } } else { - next_self.atom_cls() + this.atom_cls() }; // package and return Ok($crate::foreign::AtomicReturn { clause, gas, inert: false }) diff --git a/src/foreign_macros/atomic_redirect.rs b/src/foreign_macros/atomic_redirect.rs index 70c7db0..2ae4bd2 100644 --- a/src/foreign_macros/atomic_redirect.rs +++ b/src/foreign_macros/atomic_redirect.rs @@ -6,23 +6,14 @@ use crate::atomic_impl; #[macro_export] macro_rules! atomic_redirect { ($typ:ident) => { - impl AsRef<$crate::foreign::RcExpr> for $typ { - fn as_ref(&self) -> &Clause { &self.0 } - } - impl From<(&Self, $crate::foreign::RcExpr)> for $typ { - fn from((old, clause): (&Self, Clause)) -> Self { - Self { 0: clause, ..old.clone() } - } + impl AsMut<$crate::interpreted::ExprInst> for $typ { + fn as_mut(&mut self) -> &mut $crate::interpreted::ExprInst { &mut self.0 } } }; ($typ:ident, $field:ident) => { - impl AsRef<$crate::interpreted::ExprInst> for $typ { - fn as_ref(&self) -> &$crate::interpreted::ExprInst { &self.$field } - } - impl From<(&Self, $crate::interpreted::ExprInst)> for $typ { - #[allow(clippy::needless_update)] - fn from((old, $field): (&Self, $crate::interpreted::ExprInst)) -> Self { - Self { $field, ..old.clone() } + impl AsMut<$crate::interpreted::ExprInst> for $typ { + fn as_mut(&mut self) -> &mut $crate::interpreted::ExprInst { + &mut self.$field } } }; diff --git a/src/foreign_macros/define_fn.rs b/src/foreign_macros/define_fn.rs index 8c05dc6..bf6e424 100644 --- a/src/foreign_macros/define_fn.rs +++ b/src/foreign_macros/define_fn.rs @@ -1,7 +1,7 @@ #[allow(unused)] // for doc use crate::foreign::ExternFn; #[allow(unused)] // for doc -use crate::interpreted::ExprInst; +use crate::interpreted::{ExprInst, TryFromExprInst}; #[allow(unused)] // for doc use crate::write_fn_step; @@ -27,9 +27,7 @@ use crate::write_fn_step; /// `Rc`. /// /// To avoid typing the same expression a lot, the conversion is optional. -/// If it is omitted, the field is initialized with a [TryInto::try_into] call -/// from `&ExprInst` to the target type. In this case, the error is -/// short-circuited using `?` so conversions through `FromResidual` are allowed. +/// If it is omitted, the field is initialized using [TryFromExprInst]. /// The optional syntax starts with `as`. /// /// If all conversions are omitted, the alias definition (`expr=$ident in`) has @@ -41,14 +39,13 @@ use crate::write_fn_step; /// /// ``` /// use orchidlang::interpreted::Clause; -/// use orchidlang::systems::cast_exprinst::with_str; /// use orchidlang::{define_fn, Literal, OrcString, Primitive}; /// /// define_fn! {expr=x in /// /// Append a string to another /// pub Concatenate { -/// a: OrcString as with_str(x, |s| Ok(s.clone())), -/// b: OrcString as with_str(x, |s| Ok(s.clone())) +/// a: OrcString as x.downcast(), +/// b: OrcString /// } => { /// Ok(Clause::P(Primitive::Literal(Literal::Str( /// OrcString::from(a.get_string() + &b) @@ -61,23 +58,26 @@ use crate::write_fn_step; /// /// ``` /// use orchidlang::interpreted::Clause; -/// use orchidlang::systems::cast_exprinst::with_lit; +/// use orchidlang::systems::cast_exprinst::get_literal; /// use orchidlang::{define_fn, Literal}; /// /// define_fn! { /// /// Convert a literal to a string using Rust's conversions for floats, /// /// chars and uints respectively -/// ToString = |x| with_lit(x, |l| Ok(match l { -/// Literal::Uint(i) => Literal::Str(i.to_string().into()), -/// Literal::Num(n) => Literal::Str(n.to_string().into()), -/// s@Literal::Str(_) => s.clone(), -/// })).map(Clause::from) +/// ToString = |x| Ok(match get_literal(x)?.0 { +/// Literal::Uint(i) => Clause::from(Literal::Str(i.to_string().into())), +/// Literal::Num(n) => Clause::from(Literal::Str(n.to_string().into())), +/// s@Literal::Str(_) => Clause::from(s), +/// }) /// } /// ``` #[macro_export] macro_rules! define_fn { // Unary function entry - ($( #[ $attr:meta ] )* $qual:vis $name:ident = |$x:ident| $body:expr) => { + ( + $( #[ $attr:meta ] )* $qual:vis $name:ident = |$x:ident| $body:expr + $(; $( $next:tt )+ )? + ) => { paste::paste!{ $crate::write_fn_step!( $( #[ $attr ] )* $qual $name @@ -89,21 +89,28 @@ macro_rules! define_fn { {} out = expr => Ok(expr); { - let lambda = |$x: &$crate::interpreted::ExprInst| $body; + let lambda = |$x: $crate::interpreted::ExprInst| $body; lambda(out) } ); } + + $( $crate::define_fn!{ $( $next )+ } )? }; // xname is optional only if every conversion is implicit - ($( #[ $attr:meta ] )* $qual:vis $name:ident { - $( $arg:ident: $typ:ty ),+ $(,)? - } => $body:expr) => { + ( + $( #[ $attr:meta ] )* $qual:vis $name:ident { + $( $arg:ident: $typ:ty ),+ $(,)? + } => $body:expr + $(; $( $next:tt )+ )? + ) => { $crate::define_fn!{expr=expr in $( #[ $attr ] )* $qual $name { $( $arg: $typ ),* } => $body } + + $( $crate::define_fn!{ $( $next )+ } )? }; // multi-parameter function entry (expr=$xname:ident in @@ -112,24 +119,29 @@ macro_rules! define_fn { $arg0:ident: $typ0:ty $( as $parse0:expr )? $(, $arg:ident: $typ:ty $( as $parse:expr )? )* $(,)? } => $body:expr - ) => {paste::paste!{ - // Generate initial state - $crate::write_fn_step!( - $( #[ $attr ] )* $qual $name - > - [< Internal $name >] - ); - // Enter loop to generate intermediate states - $crate::define_fn!(@MIDDLE $xname [< Internal $name >] ($body) - () - ( - ( $arg0: $typ0 $( as $parse0)? ) - $( - ( $arg: $typ $( as $parse)? ) - )* - ) - ); - }}; + $(; $( $next:tt )+ )? + ) => { + paste::paste!{ + // Generate initial state + $crate::write_fn_step!( + $( #[ $attr ] )* $qual $name + > + [< Internal $name >] + ); + // Enter loop to generate intermediate states + $crate::define_fn!(@MIDDLE $xname [< Internal $name >] ($body) + () + ( + ( $arg0: $typ0 $( as $parse0)? ) + $( + ( $arg: $typ $( as $parse)? ) + )* + ) + ); + } + + $( $crate::define_fn!{ expr = $xname in $( $next )+ } )? + }; // Recursive case (@MIDDLE $xname:ident $name:ident ($body:expr) // fields that should be included in this struct diff --git a/src/foreign_macros/externfn_impl.rs b/src/foreign_macros/externfn_impl.rs index bd7636c..e14fb22 100644 --- a/src/foreign_macros/externfn_impl.rs +++ b/src/foreign_macros/externfn_impl.rs @@ -27,12 +27,12 @@ macro_rules! externfn_impl { impl $crate::foreign::ExternFn for $typ { fn name(&self) -> &str { stringify!($typ) } fn apply( - &self, + self: Box, arg: $crate::interpreted::ExprInst, _ctx: $crate::interpreter::Context, ) -> $crate::foreign::XfnResult { let closure = $next_atomic; - match closure(self, arg) { + match closure(*self, arg) { // ? casts the result but we want to strictly forward it Ok(r) => Ok($crate::interpreted::Clause::P($crate::Primitive::Atom( $crate::foreign::Atom::new(r), diff --git a/src/foreign_macros/write_fn_step.rs b/src/foreign_macros/write_fn_step.rs index 5d1bdcd..628c7c7 100644 --- a/src/foreign_macros/write_fn_step.rs +++ b/src/foreign_macros/write_fn_step.rs @@ -23,7 +23,6 @@ use crate::interpreted::ExprInst; /// /// use orchidlang::{write_fn_step, Literal, Primitive, OrcString}; /// use orchidlang::interpreted::Clause; -/// use orchidlang::systems::cast_exprinst::{with_str, with_uint}; /// use orchidlang::systems::RuntimeError; /// /// // Initial state @@ -31,14 +30,14 @@ use crate::interpreted::ExprInst; /// // Middle state /// write_fn_step!( /// CharAt1 {} -/// CharAt0 where s: OrcString = x => with_str(x, |s| Ok(s.clone())); +/// CharAt0 where s: OrcString = x => x.downcast::(); /// ); /// // Exit state /// write_fn_step!( /// CharAt0 { s: OrcString } -/// i = x => with_uint(x, Ok); +/// i = x => x.downcast::(); /// { -/// if let Some(c) = s.graphemes(true).nth(*i as usize) { +/// if let Some(c) = s.graphemes(true).nth(i as usize) { /// Ok(Literal::Str(OrcString::from(c.to_string())).into()) /// } else { /// RuntimeError::fail( @@ -88,7 +87,7 @@ macro_rules! write_fn_step { $quant struct $name; $crate::externfn_impl!{ $name, - |_: &Self, expr_inst: $crate::interpreted::ExprInst| { + |_: Self, expr_inst: $crate::interpreted::ExprInst| { Ok($next{ expr_inst }) } } @@ -107,14 +106,14 @@ macro_rules! write_fn_step { $( $arg: $typ, )* expr_inst: $crate::interpreted::ExprInst, } - impl $crate::utils::ddispatch::Responder for $name {} + impl $crate::ddispatch::Responder for $name {} $crate::atomic_redirect!($name, expr_inst); $crate::atomic_impl!($name); $crate::externfn_impl!( $name, - |this: &Self, expr_inst: $crate::interpreted::ExprInst| { + |this: Self, expr_inst: $crate::interpreted::ExprInst| { let $added $( :$added_typ )? = - $crate::write_fn_step!(@CONV &this.expr_inst $(, $xname $extract )?); + $crate::write_fn_step!(@CONV this.expr_inst $(, $xname $extract )?); Ok($next{ $( $arg: this.$arg.clone(), )* $added, expr_inst @@ -137,13 +136,12 @@ macro_rules! write_fn_step { expr_inst: $crate::interpreted::ExprInst, } $crate::atomic_redirect!($name, expr_inst); - impl $crate::utils::ddispatch::Responder for $name {} + impl $crate::ddispatch::Responder for $name {} $crate::atomic_impl!( $name, - |Self{ $($arg, )* expr_inst }: &Self, _| { - let added $(: $added_typ )? = + |Self{ $($arg, )* expr_inst }, _| { + let $added $(: $added_typ )? = $crate::write_fn_step!(@CONV expr_inst $(, $xname $extract )?); - let $added = &added; $process } ); diff --git a/src/interpreter/apply.rs b/src/interpreter/apply.rs index bb2c95f..041cfae 100644 --- a/src/interpreter/apply.rs +++ b/src/interpreter/apply.rs @@ -13,17 +13,14 @@ use crate::utils::Side; fn map_at( path: &[Side], source: ExprInst, - mapper: &mut impl FnMut(&Clause) -> Result, + mapper: &mut impl FnMut(Clause) -> Result, ) -> Result { source .try_update(|value, _loc| { // Pass right through lambdas if let Clause::Lambda { args, body } = value { return Ok(( - Clause::Lambda { - args: args.clone(), - body: map_at(path, body.clone(), mapper)?, - }, + Clause::Lambda { args, body: map_at(path, body, mapper)? }, (), )); } @@ -37,14 +34,8 @@ fn map_at( if let Clause::Apply { f, x } = value { return Ok(( match head { - Side::Left => Clause::Apply { - f: map_at(tail, f.clone(), mapper)?, - x: x.clone(), - }, - Side::Right => Clause::Apply { - f: f.clone(), - x: map_at(tail, x.clone(), mapper)?, - }, + Side::Left => Clause::Apply { f: map_at(tail, f, mapper)?, x }, + Side::Right => Clause::Apply { f, x: map_at(tail, x, mapper)? }, }, (), )); @@ -63,8 +54,8 @@ fn substitute(paths: &PathSet, value: Clause, body: ExprInst) -> ExprInst { match (checkpoint, next) { (Clause::Lambda { .. }, _) => unreachable!("Handled by map_at"), (Clause::Apply { f, x }, Some((left, right))) => Ok(Clause::Apply { - f: substitute(left, value.clone(), f.clone()), - x: substitute(right, value.clone(), x.clone()), + f: substitute(left, value.clone(), f), + x: substitute(right, value.clone(), x), }), (Clause::LambdaArg, None) => Ok(value.clone()), (_, None) => { @@ -91,20 +82,19 @@ pub fn apply( 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(); - let new_xpr_inst = substitute(args, x_cls, body.clone()); - let new_xpr = new_xpr_inst.expr(); + let x_cls = x.expr_val().clause; + let result = substitute(&args, x_cls, body); // cost of substitution // XXX: should this be the number of occurrences instead? - (new_xpr.clause.clone(), (ctx.gas.map(|x| x - 1), false)) + (result.expr_val().clause, (ctx.gas.map(|x| x - 1), false)) } else { - (body.expr().clause.clone(), (ctx.gas, false)) + (body.expr_val().clause, (ctx.gas, false)) }), Clause::Constant(name) => - if let Some(sym) = ctx.symbols.get(name) { + if let Some(sym) = ctx.symbols.get(&name) { Ok((Clause::Apply { f: sym.clone(), x }, (ctx.gas, false))) } else { - Err(RuntimeError::MissingSymbol(name.clone(), loc.clone())) + Err(RuntimeError::MissingSymbol(name.clone(), loc)) }, Clause::P(Primitive::Atom(atom)) => { // take a step in expanding atom @@ -113,11 +103,11 @@ pub fn apply( }, Clause::Apply { f: fun, x: arg } => { // take a step in resolving pre-function - let ret = apply(fun.clone(), arg.clone(), ctx.clone())?; + let ret = apply(fun, arg, ctx.clone())?; let Return { state, inert, gas } = ret; Ok((Clause::Apply { f: state, x }, (gas, inert))) }, - _ => Err(RuntimeError::NonFunctionApplication(f.clone())), + _ => Err(RuntimeError::NonFunctionApplication(loc)), })?; Ok(Return { state, gas, inert }) } diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index 3834231..ede768b 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -2,7 +2,6 @@ use std::fmt::Display; use std::rc::Rc; use crate::foreign::ExternError; -use crate::representations::interpreted::ExprInst; use crate::{Location, Sym}; /// Problems in the process of execution @@ -11,7 +10,7 @@ pub enum RuntimeError { /// A Rust function encountered an error Extern(Rc), /// Primitive applied as function - NonFunctionApplication(ExprInst), + NonFunctionApplication(Location), /// Symbol not in context MissingSymbol(Sym, Location), } @@ -24,8 +23,8 @@ impl Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Extern(e) => write!(f, "Error in external function: {e}"), - Self::NonFunctionApplication(expr) => { - write!(f, "Primitive applied as function at {}", expr.expr().location) + Self::NonFunctionApplication(location) => { + write!(f, "Primitive applied as function at {}", location) }, Self::MissingSymbol(sym, loc) => { write!( diff --git a/src/interpreter/handler.rs b/src/interpreter/handler.rs index 20a0f7a..3ee238f 100644 --- a/src/interpreter/handler.rs +++ b/src/interpreter/handler.rs @@ -5,12 +5,13 @@ use hashbrown::HashMap; use trait_set::trait_set; use super::{run, Context, Return, RuntimeError}; -use crate::foreign::ExternError; -use crate::interpreted::{Clause, ExprInst}; +use crate::foreign::{Atom, Atomic, ExternError}; +use crate::interpreted::{Clause, Expr, ExprInst}; +use crate::utils::take_with_output; use crate::Primitive; trait_set! { - trait Handler = for<'b> FnMut(&'b dyn Any) -> HandlerRes; + trait Handler = FnMut(Box) -> HandlerRes; } /// A table of command handlers @@ -26,16 +27,22 @@ impl<'a> HandlerTable<'a> { /// next. This function can be impure. pub fn register( &mut self, - mut f: impl for<'b> FnMut(&'b T) -> HandlerRes + 'a, + mut f: impl FnMut(Box) -> HandlerRes + 'a, ) { - let cb = move |a: &dyn Any| f(a.downcast_ref().expect("found by TypeId")); + let cb = move |a: Box| f(a.downcast().expect("found by TypeId")); let prev = self.handlers.insert(TypeId::of::(), Box::new(cb)); assert!(prev.is_none(), "A handler for this type is already registered"); } /// Find and execute the corresponding handler for this type - pub fn dispatch(&mut self, arg: &dyn Any) -> Option { - self.handlers.get_mut(&arg.type_id()).map(|f| f(arg)) + pub fn dispatch( + &mut self, + arg: Box, + ) -> Result> { + match self.handlers.get_mut(&arg.as_any_ref().type_id()) { + Some(f) => Ok(f(arg.as_any())), + None => Err(arg), + } } /// Combine two non-overlapping handler sets @@ -60,16 +67,23 @@ pub fn run_handler( mut ctx: Context, ) -> Result { loop { - let ret = run(expr.clone(), ctx.clone())?; - if let Clause::P(Primitive::Atom(a)) = &ret.state.expr().clause { - if let Some(e) = handlers.dispatch(a.0.as_any()) { - expr = e?; - ctx.gas = ret.gas; - if ret.gas.map_or(true, |g| g > 0) { - continue; + let mut ret = run(expr, ctx.clone())?; + let quit = take_with_output(&mut ret.state, |exi| match exi.expr_val() { + Expr { clause: Clause::P(Primitive::Atom(a)), .. } => { + match handlers.dispatch(a.0) { + Err(b) => (Clause::P(Primitive::Atom(Atom(b))).wrap(), Ok(true)), + Ok(e) => match e { + Ok(expr) => (expr, Ok(false)), + Err(e) => (Clause::Bottom.wrap(), Err(e)), + }, } - } + }, + expr => (ExprInst::new(expr), Ok(true)), + })?; + if quit | ret.gas.map_or(false, |g| g == 0) { + return Ok(ret); } - return Ok(ret); + ctx.gas = ret.gas; + expr = ret.state; } } diff --git a/src/interpreter/run.rs b/src/interpreter/run.rs index 5664d68..02d66ed 100644 --- a/src/interpreter/run.rs +++ b/src/interpreter/run.rs @@ -7,41 +7,40 @@ use crate::representations::Primitive; /// Normalize an expression using beta reduction with memoization pub fn run(expr: ExprInst, mut ctx: Context) -> Result { - let (state, (gas, inert)) = - expr.try_normalize(|cls, loc| -> Result<(Clause, _), RuntimeError> { - let mut i = cls.clone(); + let (state, (gas, inert)) = expr.try_normalize( + |mut cls, loc| -> Result<(Clause, _), RuntimeError> { while ctx.gas.map(|g| g > 0).unwrap_or(true) { - match &i { + match cls { Clause::Apply { f, x } => { - let res = apply(f.clone(), x.clone(), ctx.clone())?; + let res = apply(f, x, ctx.clone())?; if res.inert { - return Ok((i, (res.gas, true))); + return Ok((res.state.expr_val().clause, (res.gas, true))); } ctx.gas = res.gas; - i = res.state.expr().clause.clone(); + cls = res.state.expr().clause.clone(); }, Clause::P(Primitive::Atom(data)) => { - let ret = data.run(ctx.clone())?; - let AtomicReturn { clause, gas, inert } = ret; + let AtomicReturn { clause, gas, inert } = data.run(ctx.clone())?; if inert { - return Ok((i, (gas, true))); + return Ok((clause, (gas, true))); } ctx.gas = gas; - i = clause.clone(); + cls = clause; }, Clause::Constant(c) => { - let symval = (ctx.symbols.get(c)).ok_or_else(|| { + let symval = (ctx.symbols.get(&c)).ok_or_else(|| { RuntimeError::MissingSymbol(c.clone(), loc.clone()) })?; ctx.gas = ctx.gas.map(|g| g - 1); // cost of lookup - i = symval.expr().clause.clone(); + cls = symval.expr().clause.clone(); }, // non-reducible - _ => return Ok((i, (ctx.gas, true))), + _ => return Ok((cls, (ctx.gas, true))), } } // out of gas - Ok((i, (ctx.gas, false))) - })?; + Ok((cls, (ctx.gas, false))) + }, + )?; Ok(Return { state, gas, inert }) } diff --git a/src/lib.rs b/src/lib.rs index 7cb29ca..e0688ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,4 +34,4 @@ pub use representations::{ Location, NameLike, OrcString, PathSet, Primitive, Sym, VName, }; pub use utils::substack::Substack; -pub use utils::{IdMap, Side}; +pub use utils::{ddispatch, take_with_output, thread_pool, IdMap, Side}; diff --git a/src/representations/interpreted.rs b/src/representations/interpreted.rs index e7603f1..283fc58 100644 --- a/src/representations/interpreted.rs +++ b/src/representations/interpreted.rs @@ -13,10 +13,15 @@ use super::location::Location; use super::path_set::PathSet; use super::primitive::Primitive; use super::Literal; +#[allow(unused)] // for doc +use crate::foreign::Atomic; use crate::foreign::ExternError; +use crate::utils::ddispatch::request; +use crate::utils::take_with_output; use crate::Sym; /// An expression with metadata +#[derive(Clone)] pub struct Expr { /// The actual value pub clause: Clause, @@ -49,7 +54,7 @@ pub struct NotALiteral; /// Types automatically convertible from an [ExprInst] pub trait TryFromExprInst: Sized { /// Match and clone the value out of an [ExprInst] - fn from_exi(exi: &ExprInst) -> Result>; + fn from_exi(exi: ExprInst) -> Result>; } /// A wrapper around expressions to handle their multiple occurences in @@ -57,6 +62,18 @@ pub trait TryFromExprInst: Sized { #[derive(Clone)] pub struct ExprInst(pub Rc>); impl ExprInst { + /// Wrap an [Expr] in a shared container so that normalizatoin steps are + /// applied to all references + pub fn new(expr: Expr) -> Self { Self(Rc::new(RefCell::new(expr))) } + + /// Take the [Expr] out of this container if it's the last reference to it, or + /// clone it out. + pub fn expr_val(self) -> Expr { + Rc::try_unwrap(self.0) + .map(|c| c.into_inner()) + .unwrap_or_else(|rc| rc.as_ref().borrow().deref().clone()) + } + /// Read-only access to the shared expression instance /// /// # Panics @@ -80,12 +97,15 @@ impl ExprInst { /// across the tree. pub fn try_normalize( &self, - mapper: impl FnOnce(&Clause, &Location) -> Result<(Clause, T), E>, + mapper: impl FnOnce(Clause, &Location) -> Result<(Clause, T), E>, ) -> Result<(Self, T), E> { - let expr = self.expr(); - let (new_clause, extra) = mapper(&expr.clause, &expr.location)?; - drop(expr); - self.expr_mut().clause = new_clause; + let extra = take_with_output(&mut *self.expr_mut(), |expr| { + let Expr { clause, location } = expr; + match mapper(clause, &location) { + Ok((clause, t)) => (Expr { clause, location }, Ok(t)), + Err(e) => (Expr { clause: Clause::Bottom, location }, Err(e)), + } + })?; Ok((self.clone(), extra)) } @@ -93,13 +113,12 @@ impl ExprInst { /// distinct expression. The new expression shares location info with /// the original but is normalized independently. pub fn try_update( - &self, - mapper: impl FnOnce(&Clause, &Location) -> Result<(Clause, T), E>, + self, + mapper: impl FnOnce(Clause, Location) -> Result<(Clause, T), E>, ) -> Result<(Self, T), E> { - let expr = self.expr(); - let (clause, extra) = mapper(&expr.clause, &expr.location)?; - let new_expr = Expr { clause, location: expr.location.clone() }; - Ok((Self(Rc::new(RefCell::new(new_expr))), extra)) + let Expr { clause, location } = self.expr_val(); + let (clause, extra) = mapper(clause, location.clone())?; + Ok((Self::new(Expr { clause, location }), extra)) } /// Call a predicate on the expression, returning whatever the @@ -111,16 +130,22 @@ impl ExprInst { /// Call the predicate on the value inside this expression if it is a /// primitive - pub fn with_literal( - &self, - predicate: impl FnOnce(&Literal) -> T, - ) -> Result { - let expr = self.expr(); - if let Clause::P(Primitive::Literal(l)) = &expr.clause { - Ok(predicate(l)) - } else { - Err(NotALiteral) - } + pub fn get_literal(self) -> Result<(Literal, Location), Self> { + Rc::try_unwrap(self.0).map_or_else( + |rc| { + if let Expr { clause: Clause::P(Primitive::Literal(li)), location } = + rc.as_ref().borrow().deref() + { + return Ok((li.clone(), location.clone())); + } + Err(Self(rc)) + }, + |cell| match cell.into_inner() { + Expr { clause: Clause::P(Primitive::Literal(li)), location } => + Ok((li, location)), + expr => Err(Self::new(expr)), + }, + ) } /// Visit all expressions in the tree. The search can be exited early by @@ -138,16 +163,31 @@ impl ExprInst { Clause::Apply { f, x } => f.search_all(predicate).or_else(|| x.search_all(predicate)), Clause::Lambda { body, .. } => body.search_all(predicate), - Clause::Constant(_) | Clause::LambdaArg | Clause::P(_) => None, + Clause::Constant(_) + | Clause::LambdaArg + | Clause::P(_) + | Clause::Bottom => None, }) } /// Convert into any type that implements [FromExprInst]. Calls to this /// function are generated wherever a conversion is elided in an extern /// function. - pub fn downcast(&self) -> Result> { + pub fn downcast(self) -> Result> { T::from_exi(self) } + + /// Get the code location data associated with this expresssion directly + pub fn location(&self) -> Location { self.expr().location.clone() } + + /// If this expression is an [Atomic], request an object of the given type. + /// If it's not an atomic, fail the request automatically. + pub fn request(&self) -> Option { + match &self.expr().clause { + Clause::P(Primitive::Atom(a)) => request(&*a.0), + _ => None, + } + } } impl Debug for ExprInst { @@ -171,6 +211,8 @@ impl Display for ExprInst { /// Distinct types of expressions recognized by the interpreter #[derive(Debug, Clone)] pub enum Clause { + /// An expression that causes an error + Bottom, /// An unintrospectable unit P(Primitive), /// A function application @@ -210,6 +252,7 @@ impl Display for Clause { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Clause::P(p) => write!(f, "{p:?}"), + Clause::Bottom => write!(f, "bottom"), Clause::LambdaArg => write!(f, "arg"), Clause::Apply { f: fun, x } => write!(f, "({fun} {x})"), Clause::Lambda { args, body } => match args { diff --git a/src/representations/string.rs b/src/representations/string.rs index 7b910fc..d723c91 100644 --- a/src/representations/string.rs +++ b/src/representations/string.rs @@ -25,7 +25,13 @@ impl Debug for OrcString { impl OrcString { /// Clone out the plain Rust [String] - pub fn get_string(&self) -> String { self.as_str().to_owned() } + pub fn get_string(self) -> String { + match self { + Self::Interned(s) => s.as_str().to_owned(), + Self::Runtime(rc) => + Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()), + } + } } impl Deref for OrcString { diff --git a/src/systems/assertion_error.rs b/src/systems/assertion_error.rs index 6046a22..5086966 100644 --- a/src/systems/assertion_error.rs +++ b/src/systems/assertion_error.rs @@ -2,35 +2,39 @@ use std::fmt::Display; use std::rc::Rc; use crate::foreign::ExternError; -use crate::representations::interpreted::ExprInst; +use crate::Location; /// Some expectation (usually about the argument types of a function) did not /// hold. #[derive(Clone)] pub struct AssertionError { - value: ExprInst, - assertion: &'static str, + location: Location, + message: &'static str, } impl AssertionError { /// Construct, upcast and wrap in a Result that never succeeds for easy /// short-circuiting pub fn fail( - value: ExprInst, - assertion: &'static str, + location: Location, + message: &'static str, ) -> Result> { - return Err(Self { value, assertion }.into_extern()); + return Err(Self { location, message }.into_extern()); } /// Construct and upcast to [ExternError] - pub fn ext(value: ExprInst, assertion: &'static str) -> Rc { - return Self { value, assertion }.into_extern(); + pub fn ext(location: Location, message: &'static str) -> Rc { + return Self { location, message }.into_extern(); } } impl Display for AssertionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Error: {:?} is not {}", self.value, self.assertion) + write!(f, "Error: expected {}", self.message)?; + if self.location != Location::Unknown { + write!(f, " at {}", self.location)?; + } + Ok(()) } } diff --git a/src/systems/asynch/system.rs b/src/systems/asynch/system.rs index 0837a72..06fe66f 100644 --- a/src/systems/asynch/system.rs +++ b/src/systems/asynch/system.rs @@ -29,10 +29,7 @@ define_fn! {expr=x in SetTimer { recurring: Boolean, duration: NotNan - } => Ok(init_cps(2, Timer{ - recurring: *recurring, - duration: *duration - })) + } => Ok(init_cps(2, Timer{ recurring, duration })) } #[derive(Clone)] @@ -125,27 +122,27 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> { let polly = Rc::new(RefCell::new(poller)); handler_table.register({ let polly = polly.clone(); - move |t: &CPSBox| { + move |t: Box>| { let mut polly = polly.borrow_mut(); let (timeout, action, cont) = t.unpack2(); let duration = Duration::from_secs_f64(*timeout.duration); let cancel_timer = if timeout.recurring.0 { - CancelTimer(Rc::new(polly.set_interval(duration, action.clone()))) + CancelTimer(Rc::new(polly.set_interval(duration, action))) } else { - CancelTimer(Rc::new(polly.set_timeout(duration, action.clone()))) + CancelTimer(Rc::new(polly.set_timeout(duration, action))) }; - Ok(call(cont.clone(), [init_cps(1, cancel_timer).wrap()]).wrap()) + Ok(call(cont, [init_cps(1, cancel_timer).wrap()]).wrap()) } }); - handler_table.register(move |t: &CPSBox| { + handler_table.register(move |t: Box>| { let (command, cont) = t.unpack1(); command.0.as_ref()(); - Ok(cont.clone()) + Ok(cont) }); handler_table.register({ let polly = polly.clone(); let mut microtasks = VecDeque::new(); - move |_: &Yield| { + move |_: Box| { if let Some(expr) = microtasks.pop_front() { return Ok(expr); } diff --git a/src/systems/cast_exprinst.rs b/src/systems/cast_exprinst.rs index 462a179..99adba7 100644 --- a/src/systems/cast_exprinst.rs +++ b/src/systems/cast_exprinst.rs @@ -5,88 +5,61 @@ use std::rc::Rc; use ordered_float::NotNan; use super::assertion_error::AssertionError; -use crate::foreign::{Atom, Atomic, ExternError}; -use crate::interpreted::{Clause, TryFromExprInst}; +use crate::foreign::{Atom, ExternError}; +use crate::interpreted::{Clause, Expr, TryFromExprInst}; use crate::representations::interpreted::ExprInst; use crate::representations::{Literal, OrcString}; -use crate::Primitive; +use crate::{Location, Primitive}; -/// Tries to cast the [ExprInst] as a [Literal], calls the provided function on -/// it if successful. Returns a generic [AssertionError] if not. -pub fn with_lit( - x: &ExprInst, - predicate: impl FnOnce(&Literal) -> Result>, -) -> Result> { - x.with_literal(predicate) - .map_err(|_| AssertionError::ext(x.clone(), "a literal value")) - .and_then(|r| r) -} - -/// Like [with_lit] but also unwraps [Literal::Str] -pub fn with_str( - x: &ExprInst, - predicate: impl FnOnce(&OrcString) -> Result>, -) -> Result> { - with_lit(x, |l| match l { - Literal::Str(s) => predicate(s), - _ => AssertionError::fail(x.clone(), "a string"), - }) -} - -/// If the [ExprInst] stores an [Atom], maps the predicate over it, otherwise -/// raises a runtime error. -pub fn with_atom( - x: &ExprInst, - predicate: impl FnOnce(&Atom) -> Result>, -) -> Result> { - x.inspect(|c| match c { - Clause::P(Primitive::Atom(a)) => predicate(a), - _ => AssertionError::fail(x.clone(), "an atom"), - }) -} - -/// Tries to cast the [ExprInst] into the specified atom type. Throws an -/// assertion error if unsuccessful, or calls the provided function on the -/// extracted atomic type. -pub fn with_atomic( - x: &ExprInst, - inexact_typename: &'static str, - predicate: impl FnOnce(&T) -> Result>, -) -> Result> { - with_atom(x, |a| match a.try_cast() { - Some(atomic) => predicate(atomic), - _ => AssertionError::fail(x.clone(), inexact_typename), - }) +/// [ExprInst::get_literal] except the error is mapped to an [ExternError] +pub fn get_literal( + exi: ExprInst, +) -> Result<(Literal, Location), Rc> { + (exi.get_literal()) + .map_err(|exi| AssertionError::ext(exi.location(), "literal")) } // ######## Automatically ######## impl TryFromExprInst for Literal { - fn from_exi(exi: &ExprInst) -> Result> { - with_lit(exi, |l| Ok(l.clone())) + fn from_exi(exi: ExprInst) -> Result> { + get_literal(exi).map(|(l, _)| l) } } impl TryFromExprInst for OrcString { - fn from_exi(exi: &ExprInst) -> Result> { - with_str(exi, |s| Ok(s.clone())) + fn from_exi(exi: ExprInst) -> Result> { + match get_literal(exi)? { + (Literal::Str(s), _) => Ok(s), + (_, location) => AssertionError::fail(location, "string"), + } } } impl TryFromExprInst for u64 { - fn from_exi(exi: &ExprInst) -> Result> { - with_lit(exi, |l| match l { - Literal::Uint(u) => Ok(*u), - _ => AssertionError::fail(exi.clone(), "an uint"), - }) + fn from_exi(exi: ExprInst) -> Result> { + match get_literal(exi)? { + (Literal::Uint(u), _) => Ok(u), + (_, location) => AssertionError::fail(location, "uint"), + } } } impl TryFromExprInst for NotNan { - fn from_exi(exi: &ExprInst) -> Result> { - with_lit(exi, |l| match l { - Literal::Num(n) => Ok(*n), - _ => AssertionError::fail(exi.clone(), "a float"), - }) + fn from_exi(exi: ExprInst) -> Result> { + match get_literal(exi)? { + (Literal::Num(n), _) => Ok(n), + (_, location) => AssertionError::fail(location, "float"), + } + } +} + +impl TryFromExprInst for Atom { + fn from_exi(exi: ExprInst) -> Result> { + let Expr { clause, location } = exi.expr_val(); + match clause { + Clause::P(Primitive::Atom(a)) => Ok(a), + _ => AssertionError::fail(location, "atom"), + } } } diff --git a/src/systems/directfs/commands.rs b/src/systems/directfs/commands.rs new file mode 100644 index 0000000..36aef94 --- /dev/null +++ b/src/systems/directfs/commands.rs @@ -0,0 +1,19 @@ +use crate::foreign::cps_box::init_cps; +use crate::foreign::InertAtomic; +use crate::systems::asynch::MessagePort; +use crate::systems::scheduler::SeqScheduler; +use crate::{define_fn, OrcString}; + +#[derive(Debug, Clone)] +struct ReadFile(OrcString); +impl InertAtomic for ReadFile { + fn type_str() -> &'static str { "a readfile command" } +} + +pub fn read_file(port: MessagePort, cmd: ReadFile) -> Vec { + let new_file = +} + +define_fn! { + pub OpenFileRead = |x| Ok(init_cps(3, ReadFile(x.downcast()?))) +} diff --git a/src/systems/directfs/mod.rs b/src/systems/directfs/mod.rs new file mode 100644 index 0000000..6de2606 --- /dev/null +++ b/src/systems/directfs/mod.rs @@ -0,0 +1,2 @@ + +mod commands; diff --git a/src/systems/io/bindings.rs b/src/systems/io/bindings.rs index b09ec46..80977e7 100644 --- a/src/systems/io/bindings.rs +++ b/src/systems/io/bindings.rs @@ -13,63 +13,49 @@ define_fn! { ReadString = |x| Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RStr(SRead::All), handle: x.downcast()? - })) -} -define_fn! { + })); ReadLine = |x| Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RStr(SRead::Line), handle: x.downcast()? - })) -} -define_fn! { + })); ReadBin = |x| Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RBytes(BRead::All), handle: x.downcast()? - })) -} -define_fn! { + })); ReadBytes { stream: SourceHandle, n: u64 } => Ok(init_cps(3, IOCmdHandlePack{ - cmd: ReadCmd::RBytes(BRead::N((*n).try_into().unwrap())), + cmd: ReadCmd::RBytes(BRead::N(n.try_into().unwrap())), handle: stream.clone() - })) -} -define_fn! { + })); ReadUntil { stream: SourceHandle, pattern: u64 } => { - let delim = (*pattern).try_into().map_err(|_| RuntimeError::ext( + let delim = pattern.try_into().map_err(|_| RuntimeError::ext( "greater than 255".to_string(), "converting number to byte" ))?; Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RBytes(BRead::Until(delim)), - handle: stream.clone() + handle: stream })) - } -} -define_fn! { + }; WriteStr { stream: SinkHandle, string: OrcString } => Ok(init_cps(3, IOCmdHandlePack { cmd: WriteCmd::WStr(string.get_string()), handle: stream.clone(), - })) -} -define_fn! { + })); WriteBin { stream: SinkHandle, bytes: Binary } => Ok(init_cps(3, IOCmdHandlePack { - cmd: WriteCmd::WBytes(bytes.clone()), + cmd: WriteCmd::WBytes(bytes), handle: stream.clone(), - })) -} -define_fn! { + })); Flush = |x| Ok(init_cps(3, IOCmdHandlePack { cmd: WriteCmd::Flush, handle: x.downcast()? diff --git a/src/systems/io/service.rs b/src/systems/io/service.rs index 611cf08..b2d397e 100644 --- a/src/systems/io/service.rs +++ b/src/systems/io/service.rs @@ -56,30 +56,29 @@ impl<'a, ST: IntoIterator> IntoSystem<'static> fn into_system(self, i: &crate::Interner) -> crate::facade::System<'static> { let scheduler = self.scheduler.clone(); let mut handlers = HandlerTable::new(); - handlers.register(move |cps: &CPSBox>| { + handlers.register(move |cps: Box>>| { let (IOCmdHandlePack { cmd, handle }, succ, fail, tail) = cps.unpack3(); - let (cmd, succ1, fail1) = (*cmd, succ.clone(), fail.clone()); + let fail1 = fail.clone(); let result = scheduler.schedule( - handle.clone(), + handle, move |mut stream, cancel| { let ret = cmd.execute(&mut stream, cancel); (stream, ret) }, - move |stream, res, _cancel| (stream, res.dispatch(succ1, fail1)), + move |stream, res, _cancel| (stream, res.dispatch(succ, fail1)), |stream| (stream, Vec::new()), ); match result { - Ok(cancel) => - Ok(call(tail.clone(), vec![init_cps(1, cancel).wrap()]).wrap()), - Err(e) => Ok(call(fail.clone(), vec![e.atom_exi()]).wrap()), + Ok(cancel) => Ok(call(tail, vec![init_cps(1, cancel).wrap()]).wrap()), + Err(e) => Ok(call(fail, vec![e.atom_exi()]).wrap()), } }); let scheduler = self.scheduler.clone(); - handlers.register(move |cps: &CPSBox>| { + handlers.register(move |cps: Box>>| { let (IOCmdHandlePack { cmd, handle }, succ, fail, tail) = cps.unpack3(); - let (cmd, succ1, fail1) = (cmd.clone(), succ.clone(), fail.clone()); + let (succ1, fail1) = (succ, fail.clone()); let result = scheduler.schedule( - handle.clone(), + handle, move |mut stream, cancel| { let ret = cmd.execute(&mut stream, cancel); (stream, ret) @@ -88,9 +87,8 @@ impl<'a, ST: IntoIterator> IntoSystem<'static> |stream| (stream, Vec::new()), ); match result { - Ok(cancel) => - Ok(call(tail.clone(), vec![init_cps(1, cancel).wrap()]).wrap()), - Err(e) => Ok(call(fail.clone(), vec![e.atom_exi()]).wrap()), + Ok(cancel) => Ok(call(tail, vec![init_cps(1, cancel).wrap()]).wrap()), + Err(e) => Ok(call(fail, vec![e.atom_exi()]).wrap()), } }); let streams = self.global_streams.into_iter().map(|(n, stream)| { diff --git a/src/systems/mod.rs b/src/systems/mod.rs index fa0f603..9380219 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -3,7 +3,7 @@ mod assertion_error; pub mod asynch; pub mod cast_exprinst; pub mod codegen; -mod directfs; +// mod directfs; pub mod io; mod runtime_error; pub mod scheduler; diff --git a/src/systems/scheduler/system.rs b/src/systems/scheduler/system.rs index c13bd85..7eec7b9 100644 --- a/src/systems/scheduler/system.rs +++ b/src/systems/scheduler/system.rs @@ -15,10 +15,9 @@ use crate::foreign::InertAtomic; use crate::interpreted::ExprInst; use crate::interpreter::HandlerTable; use crate::systems::asynch::{AsynchSystem, MessagePort}; -use crate::systems::cast_exprinst::with_atom; use crate::systems::stl::Boolean; use crate::systems::AssertionError; -use crate::utils::ddispatch::{request, Request}; +use crate::utils::ddispatch::Request; use crate::utils::thread_pool::ThreadPool; use crate::utils::{take_with_output, unwrap_or, IdMap}; use crate::{define_fn, ConstTree}; @@ -117,12 +116,6 @@ impl Debug for TakeCmd { write!(f, "A command to drop a shared resource") } } -define_fn! { - pub TakeAndDrop = |x| with_atom(x, |a| match request(a.0.as_ref()) { - Some(t) => Ok(init_cps::(1, t)), - None => AssertionError::fail(x.clone(), "a SharedHandle"), - }) -} /// Error produced when an operation is scheduled or a seal placed on a resource /// which is either already sealed or taken. @@ -135,6 +128,13 @@ impl InertAtomic for SealedOrTaken { } define_fn! { + pub TakeAndDrop = |x| { + let location = x.location(); + match x.request() { + Some(t) => Ok(init_cps::(1, t)), + None => AssertionError::fail(location, "SharedHandle"), + } + }; IsTakenError = |x| { Ok(Boolean(x.downcast::().is_ok()).atom_cls()) } @@ -296,15 +296,15 @@ impl SeqScheduler { impl IntoSystem<'static> for SeqScheduler { fn into_system(self, i: &crate::Interner) -> crate::facade::System<'static> { let mut handlers = HandlerTable::new(); - handlers.register(|cmd: &CPSBox| { + handlers.register(|cmd: Box>| { let (canceller, cont) = cmd.unpack1(); canceller.cancel(); - Ok(cont.clone()) + Ok(cont) }); - handlers.register(move |cmd: &CPSBox| { + handlers.register(move |cmd: Box>| { let (TakeCmd(cb), cont) = cmd.unpack1(); cb(self.clone()); - Ok(cont.clone()) + Ok(cont) }); System { name: ["system", "scheduler"].into_iter().map_into().collect(), diff --git a/src/systems/stl/bin.rs b/src/systems/stl/bin.rs index fca4950..78ee66b 100644 --- a/src/systems/stl/bin.rs +++ b/src/systems/stl/bin.rs @@ -33,29 +33,34 @@ impl Debug for Binary { } } -define_fn! {expr=x in +define_fn! { + /// Detect the number of bytes in the binary data block + pub Size = |x| { + Ok(Literal::Uint(x.downcast::()?.0.len() as u64).into()) + }; + +expr=x in + /// Convert a number into a binary blob pub FromNum { size: u64, is_little_endian: Boolean, data: u64 } => { - if size > &8 { + if size > 8 { RuntimeError::fail( "more than 8 bytes requested".to_string(), "converting number to binary" )? } let bytes = if is_little_endian.0 { - data.to_le_bytes()[0..*size as usize].to_vec() + data.to_le_bytes()[0..size as usize].to_vec() } else { - data.to_be_bytes()[8 - *size as usize..].to_vec() + data.to_be_bytes()[8 - size as usize..].to_vec() }; Ok(Binary(Arc::new(bytes)).atom_cls()) - } -} + }; -define_fn! {expr=x in /// Read a number from a binary blob pub GetNum { buf: Binary, @@ -69,34 +74,30 @@ define_fn! {expr=x in "reading number from binary data" )? } - if 8 < *size { + if 8 < size { RuntimeError::fail( "more than 8 bytes provided".to_string(), "reading number from binary data" )? } let mut data = [0u8; 8]; - let section = &buf.0[*loc as usize..(loc + size) as usize]; + let section = &buf.0[loc as usize..(loc + size) as usize]; let num = if is_little_endian.0 { - data[0..*size as usize].copy_from_slice(section); + data[0..size as usize].copy_from_slice(section); u64::from_le_bytes(data) } else { - data[8 - *size as usize..].copy_from_slice(section); + data[8 - size as usize..].copy_from_slice(section); u64::from_be_bytes(data) }; Ok(Literal::Uint(num).into()) - } -} + }; -define_fn! {expr=x in /// Append two binary data blocks pub Concatenate { a: Binary, b: Binary } => { let data = a.0.iter().chain(b.0.iter()).copied().collect(); Ok(Binary(Arc::new(data)).atom_cls()) - } -} + }; -define_fn! {expr=x in /// Extract a subsection of the binary data pub Slice { s: Binary, i: u64, len: u64 } => { if i + len < s.0.len() as u64 { @@ -105,30 +106,25 @@ define_fn! {expr=x in "indexing binary" )? } - let data = s.0[*i as usize..*i as usize + *len as usize].to_vec(); + let data = s.0[i as usize..i as usize + len as usize].to_vec(); Ok(Binary(Arc::new(data)).atom_cls()) - } -} + }; -define_fn! {expr=x in /// Return the index where the first argument first contains the second, /// if any pub Find { haystack: Binary, needle: Binary } => { let found = iter_find(haystack.0.iter(), needle.0.iter()); Ok(orchid_opt(found.map(|x| Literal::Uint(x as u64).into()))) - } -} - -define_fn! {expr=x in + }; /// Split binary data block into two smaller blocks pub Split { bin: Binary, i: u64 } => { - if bin.0.len() < *i as usize { + if bin.0.len() < i as usize { RuntimeError::fail( "Byte index out of bounds".to_string(), "splitting binary" )? } - let (asl, bsl) = bin.0.split_at(*i as usize); + let (asl, bsl) = bin.0.split_at(i as usize); Ok(tuple(vec![ Binary(Arc::new(asl.to_vec())).atom_cls().into(), Binary(Arc::new(bsl.to_vec())).atom_cls().into(), @@ -136,13 +132,6 @@ define_fn! {expr=x in } } -define_fn! { - /// Detect the number of bytes in the binary data block - pub Size = |x| { - Ok(Literal::Uint(x.downcast::()?.0.len() as u64).into()) - } -} - pub fn bin(i: &Interner) -> ConstTree { ConstTree::tree([( i.i("bin"), diff --git a/src/systems/stl/bool.rs b/src/systems/stl/bool.rs index 3bdf185..98e6e3d 100644 --- a/src/systems/stl/bool.rs +++ b/src/systems/stl/bool.rs @@ -4,7 +4,7 @@ use crate::foreign::InertAtomic; use crate::interner::Interner; use crate::representations::interpreted::Clause; use crate::systems::AssertionError; -use crate::{define_fn, ConstTree, Literal, PathSet}; +use crate::{define_fn, ConstTree, Literal, Location, PathSet}; /// Booleans exposed to Orchid #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -17,7 +17,26 @@ impl From for Boolean { fn from(value: bool) -> Self { Self(value) } } -define_fn! {expr=x in +define_fn! { + /// Takes a boolean and two branches, runs the first if the bool is true, the + /// second if it's false. + // Even though it's a ternary function, IfThenElse is implemented as an unary + // foreign function, as the rest of the logic can be defined in Orchid. + IfThenElse = |x| x.downcast().map(|Boolean(b)| if b {Clause::Lambda { + args: Some(PathSet { steps: Rc::new(vec![]), next: None }), + body: Clause::Lambda { + args: None, + body: Clause::LambdaArg.wrap() + }.wrap(), + }} else {Clause::Lambda { + args: None, + body: Clause::Lambda { + args: Some(PathSet { steps: Rc::new(vec![]), next: None }), + body: Clause::LambdaArg.wrap(), + }.wrap(), + }}); + +expr=x in /// Compares the inner values if /// /// - both are string, @@ -26,34 +45,12 @@ define_fn! {expr=x in (Literal::Str(s1), Literal::Str(s2)) => s1 == s2, (Literal::Num(n1), Literal::Num(n2)) => n1 == n2, (Literal::Uint(i1), Literal::Uint(i2)) => i1 == i2, - (Literal::Num(n1), Literal::Uint(u1)) => *n1 == (*u1 as f64), - (Literal::Uint(u1), Literal::Num(n1)) => *n1 == (*u1 as f64), - (..) => AssertionError::fail(b.clone().into(), "the expected type")?, + (Literal::Num(n1), Literal::Uint(u1)) => *n1 == (u1 as f64), + (Literal::Uint(u1), Literal::Num(n1)) => *n1 == (u1 as f64), + (..) => AssertionError::fail(Location::Unknown, "the expected type")?, }).atom_cls()) } -// Even though it's a ternary function, IfThenElse is implemented as an unary -// foreign function, as the rest of the logic can be defined in Orchid. -define_fn! { - /// Takes a boolean and two branches, runs the first if the bool is true, the - /// second if it's false. - IfThenElse = |x| x.downcast() - .map_err(|_| AssertionError::ext(x.clone(), "a boolean")) - .map(|b: Boolean| if b.0 {Clause::Lambda { - args: Some(PathSet { steps: Rc::new(vec![]), next: None }), - body: Clause::Lambda { - args: None, - body: Clause::LambdaArg.wrap() - }.wrap(), - }} else {Clause::Lambda { - args: None, - body: Clause::Lambda { - args: Some(PathSet { steps: Rc::new(vec![]), next: None }), - body: Clause::LambdaArg.wrap(), - }.wrap(), - }}) -} - pub fn bool(i: &Interner) -> ConstTree { ConstTree::tree([( i.i("bool"), diff --git a/src/systems/stl/conv.rs b/src/systems/stl/conv.rs index b1a83c9..cf33e57 100644 --- a/src/systems/stl/conv.rs +++ b/src/systems/stl/conv.rs @@ -6,48 +6,38 @@ use crate::foreign::ExternError; use crate::interner::Interner; use crate::interpreted::Clause; use crate::parse::{float_parser, int_parser}; -use crate::systems::cast_exprinst::with_lit; +use crate::systems::cast_exprinst::get_literal; use crate::systems::AssertionError; use crate::{define_fn, ConstTree, Literal}; define_fn! { /// parse a number. Accepts the same syntax Orchid does. - ToFloat = |x| with_lit(x, |l| match l { - Literal::Str(s) => float_parser() + ToFloat = |x| match get_literal(x)? { + (Literal::Str(s), loc) => float_parser() .parse(s.as_str()) - .map_err(|_| AssertionError::ext( - x.clone(), - "cannot be parsed into a float" - )), - Literal::Num(n) => Ok(*n), - Literal::Uint(i) => NotNan::new(*i as f64) + .map_err(|_| AssertionError::ext(loc, "float syntax")), + (Literal::Num(n), _) => Ok(n), + (Literal::Uint(i), _) => NotNan::new(i as f64) .map_err(|_| ArithmeticError::NaN.into_extern()), - }).map(|nn| Literal::Num(nn).into()) -} + }.map(|nn| Literal::Num(nn).into()); -define_fn! { /// Parse an unsigned integer. Accepts the same formats Orchid does. If the /// input is a number, floors it. - ToUint = |x| with_lit(x, |l| match l { - Literal::Str(s) => int_parser() + ToUint = |x| match get_literal(x)? { + (Literal::Str(s), loc) => int_parser() .parse(s.as_str()) - .map_err(|_| AssertionError::ext( - x.clone(), - "cannot be parsed into an unsigned int", - )), - Literal::Num(n) => Ok(n.floor() as u64), - Literal::Uint(i) => Ok(*i), - }).map(|u| Literal::Uint(u).into()) -} + .map_err(|_| AssertionError::ext(loc, "int syntax")), + (Literal::Num(n), _) => Ok(n.floor() as u64), + (Literal::Uint(i), _) => Ok(i), + }.map(|u| Literal::Uint(u).into()); -define_fn! { /// Convert a literal to a string using Rust's conversions for floats, chars and /// uints respectively - ToString = |x| with_lit(x, |l| Ok(match l { - Literal::Uint(i) => Literal::Str(i.to_string().into()), - Literal::Num(n) => Literal::Str(n.to_string().into()), - s@Literal::Str(_) => s.clone(), - })).map(Clause::from) + ToString = |x| Ok(match get_literal(x)?.0 { + Literal::Uint(i) => Clause::from(Literal::Str(i.to_string().into())), + Literal::Num(n) => Clause::from(Literal::Str(n.to_string().into())), + s@Literal::Str(_) => Clause::from(s), + }) } pub fn conv(i: &Interner) -> ConstTree { diff --git a/src/systems/stl/inspect.rs b/src/systems/stl/inspect.rs index 75d1114..6fdf7ab 100644 --- a/src/systems/stl/inspect.rs +++ b/src/systems/stl/inspect.rs @@ -18,8 +18,9 @@ struct Inspect1 { } impl Responder for Inspect1 {} impl Atomic for Inspect1 { - fn as_any(&self) -> &dyn std::any::Any { self } - fn run(&self, ctx: Context) -> crate::foreign::AtomicResult { + fn as_any(self: Box) -> Box { self } + fn as_any_ref(&self) -> &dyn std::any::Any { self } + fn run(self: Box, ctx: Context) -> crate::foreign::AtomicResult { println!("{}", self.expr_inst); Ok(AtomicReturn { clause: self.expr_inst.expr().clause.clone(), diff --git a/src/systems/stl/num.rs b/src/systems/stl/num.rs index 1900391..969d62e 100644 --- a/src/systems/stl/num.rs +++ b/src/systems/stl/num.rs @@ -7,7 +7,7 @@ use crate::foreign::ExternError; use crate::interpreted::TryFromExprInst; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::{Literal, Primitive}; -use crate::systems::cast_exprinst::with_lit; +use crate::systems::cast_exprinst::get_literal; use crate::systems::AssertionError; use crate::{define_fn, ConstTree, Interner}; @@ -42,12 +42,12 @@ impl Numeric { } } impl TryFromExprInst for Numeric { - fn from_exi(exi: &ExprInst) -> Result> { - with_lit(exi, |l| match l { - Literal::Uint(i) => Ok(Numeric::Uint(*i)), - Literal::Num(n) => Ok(Numeric::Num(*n)), - _ => AssertionError::fail(exi.clone(), "an integer or number")?, - }) + fn from_exi(exi: ExprInst) -> Result> { + match get_literal(exi)? { + (Literal::Uint(i), _) => Ok(Numeric::Uint(i)), + (Literal::Num(n), _) => Ok(Numeric::Num(n)), + (_, location) => AssertionError::fail(location, "an integer or number")?, + } } } @@ -69,42 +69,36 @@ define_fn! { /// number, the output is number. Add { a: Numeric, b: Numeric } => match (a, b) { (Numeric::Uint(a), Numeric::Uint(b)) => { - a.checked_add(*b) + a.checked_add(b) .map(Numeric::Uint) .ok_or_else(|| ArithmeticError::Overflow.into_extern()) } (Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a + b)), (Numeric::Num(a), Numeric::Uint(b)) | (Numeric::Uint(b), Numeric::Num(a)) - => Numeric::num(a.into_inner() + *b as f64), - }.map(Numeric::into) -} + => Numeric::num(*a + b as f64), + }.map(Numeric::into); -define_fn! { /// Subtract a number from another. Always returns Number. Subtract { a: Numeric, b: Numeric } => match (a, b) { - (Numeric::Uint(a), Numeric::Uint(b)) => Numeric::num(*a as f64 - *b as f64), + (Numeric::Uint(a), Numeric::Uint(b)) => Numeric::num(a as f64 - b as f64), (Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a - b)), - (Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(**a - *b as f64), - (Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(*a as f64 - **b), - }.map(Numeric::into) -} + (Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a - b as f64), + (Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 - *b), + }.map(Numeric::into); -define_fn! { /// Multiply two numbers. If they're both uint, the output is uint. If either /// is number, the output is number. Multiply { a: Numeric, b: Numeric } => match (a, b) { (Numeric::Uint(a), Numeric::Uint(b)) => { - a.checked_mul(*b) + a.checked_mul(b) .map(Numeric::Uint) .ok_or_else(|| ArithmeticError::Overflow.into_extern()) } (Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a * b)), (Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a)) - => Numeric::num(*a as f64 * **b), - }.map(Numeric::into) -} + => Numeric::num(a as f64 * *b), + }.map(Numeric::into); -define_fn! { /// Divide a number by another. Always returns Number. Divide { a: Numeric, b: Numeric } => { let a: f64 = a.as_f64(); @@ -113,21 +107,19 @@ define_fn! { return Err(ArithmeticError::DivByZero.into_extern()) } Numeric::num(a / b).map(Numeric::into) - } -} + }; -define_fn! { /// Take the remainder of two numbers. If they're both uint, the output is /// uint. If either is number, the output is number. Remainder { a: Numeric, b: Numeric } => match (a, b) { (Numeric::Uint(a), Numeric::Uint(b)) => { - a.checked_rem(*b) + a.checked_rem(b) .map(Numeric::Uint) .ok_or_else(|| ArithmeticError::DivByZero.into_extern()) } (Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a % b)), - (Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(*a as f64 % **b), - (Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(**a % *b as f64), + (Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 % *b), + (Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a % b as f64), }.map(Numeric::into) } diff --git a/src/systems/stl/panic.rs b/src/systems/stl/panic.rs index 55ee24b..afc9cf3 100644 --- a/src/systems/stl/panic.rs +++ b/src/systems/stl/panic.rs @@ -2,8 +2,7 @@ use std::fmt::Display; use std::rc::Rc; use crate::foreign::ExternError; -use crate::systems::cast_exprinst::with_str; -use crate::{define_fn, ConstTree, Interner}; +use crate::{define_fn, ConstTree, Interner, OrcString}; /// An unrecoverable error in Orchid land. Because Orchid is lazy, this only /// invalidates expressions that reference the one that generated it. @@ -19,10 +18,10 @@ impl ExternError for OrchidPanic {} define_fn! { /// Takes a message, returns an [ExternError] unconditionally. - Panic = |x| with_str(x, |s| { - let msg = Rc::new(s.get_string()); + Panic = |x| { + let msg = Rc::new(x.downcast::()?.get_string()); Err(OrchidPanic(msg).into_extern()) - }) + } } pub fn panic(i: &Interner) -> ConstTree { diff --git a/src/systems/stl/state.rs b/src/systems/stl/state.rs index bffe8ba..759392b 100644 --- a/src/systems/stl/state.rs +++ b/src/systems/stl/state.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::ops::Deref; use std::rc::Rc; use crate::foreign::cps_box::{const_cps, init_cps, CPSBox}; @@ -23,31 +24,37 @@ struct SetStateCmd(State); #[derive(Debug, Clone)] struct GetStateCmd(State); -define_fn! { SetState = |x| Ok(init_cps(2, SetStateCmd(x.downcast()?))) } -define_fn! { GetState = |x| Ok(init_cps(2, GetStateCmd(x.downcast()?))) } +define_fn! { + SetState = |x| Ok(init_cps(2, SetStateCmd(x.downcast()?))); + GetState = |x| Ok(init_cps(2, GetStateCmd(x.downcast()?))) +} -fn new_state_handler(cmd: &CPSBox) -> Result { +fn new_state_handler(cmd: CPSBox) -> Result { let (_, default, handler) = cmd.unpack2(); - let state = State(Rc::new(RefCell::new(default.clone()))); - Ok(call(handler.clone(), [state.atom_exi()]).wrap()) + let state = State(Rc::new(RefCell::new(default))); + Ok(call(handler, [state.atom_exi()]).wrap()) } -fn set_state_handler(cmd: &CPSBox) -> Result { +fn set_state_handler(cmd: CPSBox) -> Result { let (SetStateCmd(state), value, handler) = cmd.unpack2(); - *state.0.as_ref().borrow_mut() = value.clone(); - Ok(handler.clone()) + *state.0.as_ref().borrow_mut() = value; + Ok(handler) } -fn get_state_handler(cmd: &CPSBox) -> Result { +fn get_state_handler(cmd: CPSBox) -> Result { let (GetStateCmd(state), handler) = cmd.unpack1(); - Ok(call(handler.clone(), [state.0.as_ref().borrow().clone()]).wrap()) + let val = match Rc::try_unwrap(state.0) { + Ok(cell) => cell.into_inner(), + Err(rc) => rc.as_ref().borrow().deref().clone(), + }; + Ok(call(handler, [val]).wrap()) } pub fn state_handlers() -> HandlerTable<'static> { let mut handlers = HandlerTable::new(); - handlers.register(new_state_handler); - handlers.register(get_state_handler); - handlers.register(set_state_handler); + handlers.register(|b| new_state_handler(*b)); + handlers.register(|b| get_state_handler(*b)); + handlers.register(|b| set_state_handler(*b)); handlers } diff --git a/src/systems/stl/str.rs b/src/systems/stl/str.rs index 9ed956c..ec3828b 100644 --- a/src/systems/stl/str.rs +++ b/src/systems/stl/str.rs @@ -2,26 +2,37 @@ use unicode_segmentation::UnicodeSegmentation; use crate::interner::Interner; use crate::representations::OrcString; -use crate::systems::cast_exprinst::with_str; use crate::systems::codegen::{orchid_opt, tuple}; use crate::systems::RuntimeError; use crate::utils::iter_find; use crate::{define_fn, ConstTree, Literal}; -define_fn! {expr=x in - /// Append a string to another - pub Concatenate { a: OrcString, b: OrcString } - => Ok(Literal::Str((a.get_string() + b.as_str()).into()).into()) -} +define_fn! { + pub Len = |x| Ok(Literal::Uint( + (*x.downcast::()?) + .graphemes(true) + .count() as u64 + ).into()); + + pub Size = |x| Ok(Literal::Uint( + (*x.downcast::()?) + .as_bytes() + .len() as u64 + ).into()); + +expr=x in + /// Append a string to another + pub Concatenate { a: OrcString, b: OrcString } => Ok( + Literal::Str((a.get_string() + b.as_str()).into()).into() + ); -define_fn! {expr=x in pub Slice { s: OrcString, i: u64, len: u64 } => { let graphs = s.as_str().graphemes(true); - if *i == 0 { - let orc_str = graphs.take(*len as usize).collect::().into(); + if i == 0 { + let orc_str = graphs.take(len as usize).collect::().into(); Ok(Literal::Str(orc_str).into()) } else { - let mut prefix = graphs.skip(*i as usize - 1); + let mut prefix = graphs.skip(i as usize - 1); if prefix.next().is_none() { RuntimeError::fail( "Character index out of bounds".to_string(), @@ -29,10 +40,10 @@ define_fn! {expr=x in ) } else { let mut count = 0; - let ret = (prefix.take(*len as usize)) + let ret = (prefix.take(len as usize)) .map(|x| { count+=1; x }) .collect::().into(); - if count == *len { + if count == len { Ok(Literal::Str(ret).into()) } else { RuntimeError::fail( @@ -42,38 +53,22 @@ define_fn! {expr=x in } } } - } -} + }; -define_fn! {expr=x in pub Find { haystack: OrcString, needle: OrcString } => { let haystack_graphs = haystack.as_str().graphemes(true); let found = iter_find(haystack_graphs, needle.as_str().graphemes(true)); Ok(orchid_opt(found.map(|x| Literal::Uint(x as u64).into()))) - } -} + }; -define_fn! {expr=x in pub Split { s: OrcString, i: u64 } => { let mut graphs = s.as_str().graphemes(true); - let a = graphs.by_ref().take(*i as usize).collect::(); + let a = graphs.by_ref().take(i as usize).collect::(); let b = graphs.collect::(); Ok(tuple(vec![a.into(), b.into()])) } } -define_fn! { - pub Len = |x| with_str(x, |s| { - Ok(Literal::Uint(s.graphemes(true).count() as u64).into()) - }) -} - -define_fn! { - pub Size = |x| with_str(x, |s| { - Ok(Literal::Uint(s.as_bytes().len() as u64).into()) - }) -} - pub fn str(i: &Interner) -> ConstTree { ConstTree::tree([( i.i("str"), diff --git a/src/utils/ddispatch.rs b/src/utils/ddispatch.rs index dc60e89..a171a3d 100644 --- a/src/utils/ddispatch.rs +++ b/src/utils/ddispatch.rs @@ -5,10 +5,13 @@ use std::any::Any; /// A request for a value of an unknown type pub struct Request<'a>(&'a mut dyn Any); impl<'a> Request<'a> { + /// Checks whether a value of the given type would serve the request pub fn can_serve(&self) -> bool { self.0.is::>() } + /// Serve a value if it's the correct type pub fn serve(&mut self, value: T) { self.serve_with(|| value) } + /// Invoke the callback to serve the request only if the return type matches pub fn serve_with(&mut self, provider: impl FnOnce() -> T) { if let Some(slot) = self.0.downcast_mut() { *slot = provider(); @@ -19,9 +22,11 @@ impl<'a> Request<'a> { /// Trait for objects that can respond to type-erased commands. This trait is /// a dependency of `Atomic` but the implementation can be left empty. pub trait Responder { + /// Try to provide as many types as we support fn respond(&self, _request: Request) {} } +/// Request a specific contract type from a responder pub fn request(responder: &(impl Responder + ?Sized)) -> Option { let mut slot = None; responder.respond(Request(&mut slot)); diff --git a/src/utils/take_with_output.rs b/src/utils/take_with_output.rs index c4778de..6ce49e7 100644 --- a/src/utils/take_with_output.rs +++ b/src/utils/take_with_output.rs @@ -1,3 +1,4 @@ +/// A variation on [take_mut::take] that allows the callback to return a value pub fn take_with_output(src: &mut T, cb: impl FnOnce(T) -> (T, U)) -> U { take_mut::scoped::scope(|scope| { let (old, hole) = scope.take(src); diff --git a/src/utils/thread_pool.rs b/src/utils/thread_pool.rs index 7ccd32e..f360ead 100644 --- a/src/utils/thread_pool.rs +++ b/src/utils/thread_pool.rs @@ -168,9 +168,8 @@ impl Drop for ThreadPool { self.data.stopping.store(true, Ordering::SeqCst); let mut rdv_point = self.data.rdv_point.lock().unwrap(); if let Some(pending) = rdv_point.take() { - pending - .try_send(Message::Stop) - .expect("The channel is always removed before push") + // the worker has read the value of `stopping` + let _ = pending.send(Message::Stop); } } }