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