From 0bcf10659b2bf8c7f9daf354252b55c473d5cc48 Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Fri, 15 Sep 2023 12:37:10 +0100 Subject: [PATCH] Cut down on macro nonsense - InertAtomic replaced atomic_inert! for improved tooling support - atomic_defaults! is easier to type out than to explain in a docstring - Changed rustfmt config to better support tiny functions such as as_any --- .github/workflows/rust.yml | 2 +- orchid.code-workspace | 3 + rust-toolchain.toml | 2 +- rustfmt.toml | 3 +- src/bin/orcx.rs | 4 +- src/error/import_all.rs | 4 +- src/error/no_targets.rs | 4 +- src/error/parse_error_with_tokens.rs | 8 +- src/error/project_error.rs | 8 +- src/error/too_many_supers.rs | 4 +- src/facade/environment.rs | 4 +- src/facade/pre_macro.rs | 8 +- src/facade/process.rs | 4 +- src/facade/system.rs | 4 +- src/foreign/atom.rs | 34 +++---- src/foreign/cps_box.rs | 12 +-- src/foreign/extern_fn.rs | 4 +- src/foreign/inert.rs | 46 ++++++++++ src/foreign/mod.rs | 2 + src/foreign_macros/atomic_defaults.rs | 16 ---- src/foreign_macros/atomic_impl.rs | 2 +- src/foreign_macros/atomic_inert.rs | 76 ---------------- src/foreign_macros/atomic_redirect.rs | 8 +- src/foreign_macros/externfn_impl.rs | 4 +- src/foreign_macros/mod.rs | 2 - src/foreign_macros/write_fn_step.rs | 4 +- src/interner/multitype.rs | 8 +- src/interner/token.rs | 8 +- src/interpreter/error.rs | 4 +- src/interpreter/handler.rs | 4 +- src/parse/context.rs | 12 +-- src/parse/errors.rs | 85 +++++------------- src/parse/lexer.rs | 13 ++- src/pipeline/file_loader.rs | 4 +- src/pipeline/source_loader/load_source.rs | 5 +- src/pipeline/source_loader/preparse.rs | 2 +- src/pipeline/source_loader/types.rs | 10 ++- src/representations/ast.rs | 4 +- src/representations/ast_to_postmacro.rs | 8 +- src/representations/interpreted.rs | 22 +++-- src/representations/literal.rs | 12 +-- src/representations/namelike.rs | 8 +- src/representations/project.rs | 4 +- src/representations/sourcefile.rs | 15 ++-- src/representations/string.rs | 12 +-- src/representations/tree.rs | 18 ++-- src/rule/matcher_vectree/shared.rs | 18 ++-- src/rule/rule_error.rs | 10 ++- src/systems/asynch/system.rs | 16 ++-- src/systems/cast_exprinst.rs | 94 +++++++------------- src/systems/io/bindings.rs | 8 +- src/systems/io/instances.rs | 14 +-- src/systems/mod.rs | 4 +- src/systems/scheduler/busy.rs | 13 +-- src/systems/scheduler/canceller.rs | 22 ++--- src/systems/scheduler/mod.rs | 3 +- src/systems/scheduler/system.rs | 103 +++++++++++++--------- src/systems/scheduler/take_and_drop.rs | 44 --------- src/systems/stl/bin.rs | 21 ++--- src/systems/stl/bool.rs | 13 +-- src/systems/stl/inspect.rs | 6 +- src/systems/stl/num.rs | 11 ++- src/systems/stl/state.rs | 12 +-- src/utils/boxed_iter.rs | 8 +- src/utils/ddispatch.rs | 29 ++++++ src/utils/delete_cell.rs | 12 +-- src/utils/id_map.rs | 20 ++--- src/utils/mod.rs | 9 +- src/utils/never.rs | 4 +- src/utils/poller.rs | 4 +- src/utils/side.rs | 8 +- src/utils/substack.rs | 16 +--- src/utils/thread_pool.rs | 20 ++--- 73 files changed, 418 insertions(+), 654 deletions(-) create mode 100644 src/foreign/inert.rs delete mode 100644 src/foreign_macros/atomic_defaults.rs delete mode 100644 src/foreign_macros/atomic_inert.rs delete mode 100644 src/systems/scheduler/take_and_drop.rs create mode 100644 src/utils/ddispatch.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a59b4ea..b527547 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,4 +26,4 @@ jobs: - name: Clippy run: cargo clippy - name: Formatting - run: cargo fmt --check + run: cargo fmt +nightly --check diff --git a/orchid.code-workspace b/orchid.code-workspace index 6d3c470..b492dee 100644 --- a/orchid.code-workspace +++ b/orchid.code-workspace @@ -29,6 +29,9 @@ "rust-analyzer.showUnlinkedFileNotification": false, "rust-analyzer.checkOnSave": true, "rust-analyzer.check.command": "clippy", + "rust-analyzer.rustfmt.extraArgs": [ + "+nightly" + ], "files.associations": { "*.mjsd": "markdown" }, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 271800c..292fe49 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly" \ No newline at end of file +channel = "stable" diff --git a/rustfmt.toml b/rustfmt.toml index f434b9d..a7a1b63 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -13,6 +13,7 @@ normalize_comments = true wrap_comments = true overflow_delimited_expr = true use_small_heuristics = "Max" +fn_single_line = true # literals hex_literal_case = "Lower" @@ -30,4 +31,4 @@ use_try_shorthand = true # Modules group_imports = "StdExternalCrate" imports_granularity = "Module" -reorder_modules = true \ No newline at end of file +reorder_modules = true diff --git a/src/bin/orcx.rs b/src/bin/orcx.rs index 28810bf..b7a25b4 100644 --- a/src/bin/orcx.rs +++ b/src/bin/orcx.rs @@ -61,9 +61,7 @@ impl Args { Ok(()) } - pub fn chk_proj(&self) -> Result<(), String> { - self.chk_dir_main() - } + pub fn chk_proj(&self) -> Result<(), String> { self.chk_dir_main() } } pub fn to_vname(data: &str, i: &Interner) -> VName { diff --git a/src/error/import_all.rs b/src/error/import_all.rs index b7f0a18..7f4b59c 100644 --- a/src/error/import_all.rs +++ b/src/error/import_all.rs @@ -15,9 +15,7 @@ pub struct ImportAll { pub offender_mod: Rc, } impl ProjectError for ImportAll { - fn description(&self) -> &str { - "a top-level glob import was used" - } + fn description(&self) -> &str { "a top-level glob import was used" } fn message(&self) -> String { format!("{} imports *", self.offender_mod.iter().join("::")) } diff --git a/src/error/no_targets.rs b/src/error/no_targets.rs index 6942187..049a4d4 100644 --- a/src/error/no_targets.rs +++ b/src/error/no_targets.rs @@ -16,7 +16,5 @@ impl ProjectError for NoTargets { "No targets were specified for layer parsing" } - fn positions(&self) -> BoxedIter { - box_empty() - } + fn positions(&self) -> BoxedIter { box_empty() } } diff --git a/src/error/parse_error_with_tokens.rs b/src/error/parse_error_with_tokens.rs index 55525f5..99bd92b 100644 --- a/src/error/parse_error_with_tokens.rs +++ b/src/error/parse_error_with_tokens.rs @@ -16,9 +16,7 @@ pub struct ParseErrorWithTokens { pub error: Rc, } impl ProjectError for ParseErrorWithTokens { - fn description(&self) -> &str { - self.error.description() - } + fn description(&self) -> &str { self.error.description() } fn message(&self) -> String { format!( "Failed to parse code: {}\nTokenized source for context:\n{}", @@ -26,7 +24,5 @@ impl ProjectError for ParseErrorWithTokens { self.tokens.iter().map(|t| t.to_string()).join(" "), ) } - fn positions(&self) -> BoxedIter { - self.error.positions() - } + fn positions(&self) -> BoxedIter { self.error.positions() } } diff --git a/src/error/project_error.rs b/src/error/project_error.rs index df9e5e8..4c9de44 100644 --- a/src/error/project_error.rs +++ b/src/error/project_error.rs @@ -20,9 +20,7 @@ pub trait ProjectError { /// A general description of this type of error fn description(&self) -> &str; /// A formatted message that includes specific parameters - fn message(&self) -> String { - self.description().to_string() - } + fn message(&self) -> String { self.description().to_string() } /// Code positions relevant to this error. If you don't implement this, you /// must implement [ProjectError::one_position] fn positions(&self) -> BoxedIter { @@ -30,9 +28,7 @@ pub trait ProjectError { } /// Short way to provide a single location. If you don't implement this, you /// must implement [ProjectError::positions] - fn one_position(&self) -> Location { - unimplemented!() - } + fn one_position(&self) -> Location { unimplemented!() } /// Convert the error into an `Rc` to be able to /// handle various errors together fn rc(self) -> Rc diff --git a/src/error/too_many_supers.rs b/src/error/too_many_supers.rs index d5fd27f..626d194 100644 --- a/src/error/too_many_supers.rs +++ b/src/error/too_many_supers.rs @@ -23,7 +23,5 @@ impl ProjectError for TooManySupers { ) } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } diff --git a/src/facade/environment.rs b/src/facade/environment.rs index bf43c1c..3a71f0b 100644 --- a/src/facade/environment.rs +++ b/src/facade/environment.rs @@ -23,9 +23,7 @@ pub struct Environment<'a> { } impl<'a> Environment<'a> { /// Initialize a new environment - pub fn new(i: &'a Interner) -> Self { - Self { i, systems: Vec::new() } - } + pub fn new(i: &'a Interner) -> Self { Self { i, systems: Vec::new() } } /// Register a new system in the environment pub fn add_system<'b: 'a>(mut self, is: impl IntoSystem<'b> + 'b) -> Self { diff --git a/src/facade/pre_macro.rs b/src/facade/pre_macro.rs index 376f598..2134218 100644 --- a/src/facade/pre_macro.rs +++ b/src/facade/pre_macro.rs @@ -116,9 +116,7 @@ pub struct MacroTimeout { limit: usize, } impl ProjectError for MacroTimeout { - fn description(&self) -> &str { - "Macro execution has not halted" - } + fn description(&self) -> &str { "Macro execution has not halted" } fn message(&self) -> String { format!( @@ -128,7 +126,5 @@ impl ProjectError for MacroTimeout { ) } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } diff --git a/src/facade/process.rs b/src/facade/process.rs index 41fee33..dd1cfbd 100644 --- a/src/facade/process.rs +++ b/src/facade/process.rs @@ -84,7 +84,5 @@ impl ProjectError for MissingSymbol { ) } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } diff --git a/src/facade/system.rs b/src/facade/system.rs index 1d07328..cdc7b10 100644 --- a/src/facade/system.rs +++ b/src/facade/system.rs @@ -59,9 +59,7 @@ impl ProjectError for MissingSystemCode { self.system.join("::") ) } - fn positions(&self) -> BoxedIter { - box_empty() - } + fn positions(&self) -> BoxedIter { box_empty() } } /// Trait for objects that can be converted into a [System] in the presence diff --git a/src/foreign/atom.rs b/src/foreign/atom.rs index 7934dd3..4222c87 100644 --- a/src/foreign/atom.rs +++ b/src/foreign/atom.rs @@ -6,6 +6,7 @@ use dyn_clone::DynClone; use crate::interpreted::ExprInst; use crate::interpreter::{Context, RuntimeError}; use crate::representations::interpreted::Clause; +use crate::utils::ddispatch::Responder; use crate::Primitive; /// Information returned by [Atomic::run]. This mirrors @@ -30,19 +31,18 @@ impl AtomicReturn { pub type AtomicResult = Result; /// Functionality the interpreter needs to handle a value -pub trait Atomic: Any + Debug + DynClone +pub trait Atomic: Any + Debug + DynClone + Responder where Self: 'static, { - /// A fully type-erased interface to issue a command to the unknown type - /// and see if it supports it - fn request(&self, _request: Box) -> Option> { - None - } - /// Casts this value to [Any] so that its original value can be salvaged - /// during introspection by other external code. There is no other way to - /// interact with values of unknown types at the moment. + /// during introspection by other external code. + /// + /// This function should be implemented in exactly one way: + /// + /// ``` + /// fn as_any(&self) -> &dyn Any { self } + /// ``` fn as_any(&self) -> &dyn Any; /// Attempt to normalize this value. If it wraps a value, this should report @@ -81,31 +81,23 @@ impl Atom { Self(Box::new(data) as Box) } /// 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 pub fn try_cast(&self) -> Option<&T> { self.data().as_any().downcast_ref() } /// Test the type of the contained data without downcasting - pub fn is(&self) -> bool { - self.data().as_any().is::() - } + pub fn is(&self) -> bool { self.data().as_any().is::() } /// Downcast contained data, panic if it isn't the specified type pub fn cast(&self) -> &T { self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast") } /// 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 { - fn clone(&self) -> Self { - Self(dyn_clone::clone_box(self.data())) - } + fn clone(&self) -> Self { Self(dyn_clone::clone_box(self.data())) } } impl Debug for Atom { diff --git a/src/foreign/cps_box.rs b/src/foreign/cps_box.rs index 518053d..a6eb702 100644 --- a/src/foreign/cps_box.rs +++ b/src/foreign/cps_box.rs @@ -4,11 +4,11 @@ use std::fmt::Debug; use trait_set::trait_set; -use super::{Atomic, ExternFn, XfnResult}; +use super::{Atomic, ExternFn, InertAtomic, XfnResult}; use crate::interpreted::{Clause, ExprInst}; use crate::interpreter::{Context, HandlerRes}; use crate::utils::pure_push::pushed_ref; -use crate::{ConstTree, atomic_inert}; +use crate::ConstTree; trait_set! { /// A "well behaved" type that can be used as payload in a CPS box @@ -34,9 +34,7 @@ impl CPSFn { } } impl ExternFn for CPSFn { - fn name(&self) -> &str { - "CPS function without argument" - } + fn name(&self) -> &str { "CPS function without argument" } fn apply(&self, arg: ExprInst, _ctx: Context) -> XfnResult { let payload = self.payload.clone(); let continuations = pushed_ref(&self.continuations, arg); @@ -93,7 +91,9 @@ impl CPSBox { } } -atomic_inert!(CPSBox(T:(CPSPayload)), typestr = "a CPS box"); +impl InertAtomic for CPSBox { + fn type_str() -> &'static str { "a CPS box" } +} /// Like [init_cps] but wrapped in a [ConstTree] for init-time usage pub fn const_cps(argc: usize, payload: T) -> ConstTree { diff --git a/src/foreign/extern_fn.rs b/src/foreign/extern_fn.rs index 9e7465e..d7a8700 100644 --- a/src/foreign/extern_fn.rs +++ b/src/foreign/extern_fn.rs @@ -55,9 +55,7 @@ pub trait ExternFn: DynClone { impl Eq for dyn ExternFn {} impl PartialEq for dyn ExternFn { - fn eq(&self, other: &Self) -> bool { - self.name() == other.name() - } + fn eq(&self, other: &Self) -> bool { self.name() == other.name() } } impl Hash for dyn ExternFn { fn hash(&self, state: &mut H) { diff --git a/src/foreign/inert.rs b/src/foreign/inert.rs new file mode 100644 index 0000000..38ebbec --- /dev/null +++ b/src/foreign/inert.rs @@ -0,0 +1,46 @@ +use std::any::Any; +use std::fmt::Debug; +use std::rc::Rc; + +use super::{AtomicResult, AtomicReturn, ExternError}; +#[allow(unused)] // for doc +use crate::define_fn; +use crate::foreign::Atomic; +use crate::interpreted::{ExprInst, TryFromExprInst}; +use crate::interpreter::Context; +use crate::systems::cast_exprinst::with_atomic; +use crate::utils::ddispatch::{Request, Responder}; + +/// 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 +/// parameters of functions defined with [define_fn] it also automatically +/// implements [TryFromExprInst] so that a conversion doesn't have to be +/// provided in argument lists. +pub trait InertAtomic: Debug + Clone + 'static { + /// Typename to be shown in the error when a conversion from [ExprInst] fails + fn type_str() -> &'static str; + /// Proxies to [Responder] so that you don't have to implmeent it manually if + /// you need it, but behaves exactly as the default implementation. + #[allow(unused_mut, unused_variables)] // definition should show likely usage + fn respond(&self, mut request: Request) {} +} +impl Responder for T { + fn respond(&self, request: Request) { self.respond(request) } +} +impl Atomic for T { + fn as_any(&self) -> &dyn Any { self } + + fn run(&self, ctx: Context) -> AtomicResult { + Ok(AtomicReturn { + clause: self.clone().atom_cls(), + gas: ctx.gas, + inert: true, + }) + } +} + +impl TryFromExprInst for T { + fn from_exi(exi: &ExprInst) -> Result> { + with_atomic(exi, Self::type_str(), |a: &T| Ok(a.clone())) + } +} diff --git a/src/foreign/mod.rs b/src/foreign/mod.rs index 771a45e..f474a6e 100644 --- a/src/foreign/mod.rs +++ b/src/foreign/mod.rs @@ -5,11 +5,13 @@ mod atom; pub mod cps_box; mod extern_fn; +mod inert; use std::rc::Rc; pub use atom::{Atom, Atomic, AtomicResult, AtomicReturn}; pub use extern_fn::{ExternError, ExternFn, XfnResult}; +pub use inert::InertAtomic; pub use crate::representations::interpreted::Clause; diff --git a/src/foreign_macros/atomic_defaults.rs b/src/foreign_macros/atomic_defaults.rs deleted file mode 100644 index 1e42283..0000000 --- a/src/foreign_macros/atomic_defaults.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[allow(unused)] // for the doc comments -use crate::foreign::Atomic; - -/// A macro that generates the straightforward, syntactically invariant part of -/// implementing [Atomic]. -/// -/// Currently implements -/// - [Atomic::as_any] -#[macro_export] -macro_rules! atomic_defaults { - () => { - fn as_any(&self) -> &dyn std::any::Any { - self - } - }; -} diff --git a/src/foreign_macros/atomic_impl.rs b/src/foreign_macros/atomic_impl.rs index 9d816b4..6e69840 100644 --- a/src/foreign_macros/atomic_impl.rs +++ b/src/foreign_macros/atomic_impl.rs @@ -79,7 +79,7 @@ macro_rules! atomic_impl { }; ($typ:ident, $next_phase:expr) => { impl $crate::foreign::Atomic for $typ { - $crate::atomic_defaults! {} + fn as_any(&self) -> &dyn std::any::Any { self } fn run( &self, diff --git a/src/foreign_macros/atomic_inert.rs b/src/foreign_macros/atomic_inert.rs deleted file mode 100644 index acb89bf..0000000 --- a/src/foreign_macros/atomic_inert.rs +++ /dev/null @@ -1,76 +0,0 @@ -#[allow(unused)] // for the doc comments -use std::any::Any; -#[allow(unused)] // for the doc comments -use std::fmt::Debug; - -#[allow(unused)] // for the doc comments -use dyn_clone::DynClone; - -#[allow(unused)] // for the doc comments -use crate::foreign::Atomic; - -/// Implement [Atomic] for a structure that cannot be transformed any further. -/// This would be optimal for atomics encapsulating raw data. [Atomic] depends -/// on [Any], [Debug] and [DynClone]. -/// -/// If the type in question is parametric, the angle brackets must be replaced -/// by parentheses, and the contraints must be parenthesized, for conenient -/// parsing. See the below example: -/// -/// ```ignore -/// use orchidlang::atomic_inert; -/// -/// struct MyContainer() -/// -/// atomic_inert!( MyContainer(T, U:(Clone), V:(Eq + Hash)), "my container" ); -/// ``` -#[macro_export] -macro_rules! atomic_inert { - ( $typ:ident $( ( - $( $typevar:ident $( : ( - $( $constraints:tt )* - ) )? ),+ ) )? - , typestr = $typename:expr $( , request = $reqhandler:expr )?) => { - impl $(< $($typevar : $( $($constraints)* + )? 'static ),+ >)? $crate::foreign::Atomic - for $typ $(< $($typevar),+ >)? { - $crate::atomic_defaults! {} - - fn run( - &self, - ctx: $crate::interpreter::Context, - ) -> $crate::foreign::AtomicResult { - Ok($crate::foreign::AtomicReturn { - clause: self.clone().atom_cls(), - gas: ctx.gas, - inert: true, - }) - } - - $( - fn request( - &self, - request: Box - ) -> Option> { - let lambda = $reqhandler; - lambda(request, self) - } - )? - } - - impl $(< $($typevar : $( $($constraints)* + )? 'static ),+ >)? - TryFrom<&$crate::interpreted::ExprInst> - for $typ $(< $($typevar),+ >)? { - type Error = std::rc::Rc; - - fn try_from( - value: &$crate::interpreted::ExprInst, - ) -> Result { - $crate::systems::cast_exprinst::with_atom( - value, - $typename, - |a: &$typ $(< $($typevar),+ >)?| Ok(a.clone()), - ) - } - } - }; -} diff --git a/src/foreign_macros/atomic_redirect.rs b/src/foreign_macros/atomic_redirect.rs index ceb202b..70c7db0 100644 --- a/src/foreign_macros/atomic_redirect.rs +++ b/src/foreign_macros/atomic_redirect.rs @@ -7,9 +7,7 @@ use crate::atomic_impl; macro_rules! atomic_redirect { ($typ:ident) => { impl AsRef<$crate::foreign::RcExpr> for $typ { - fn as_ref(&self) -> &Clause { - &self.0 - } + fn as_ref(&self) -> &Clause { &self.0 } } impl From<(&Self, $crate::foreign::RcExpr)> for $typ { fn from((old, clause): (&Self, Clause)) -> Self { @@ -19,9 +17,7 @@ macro_rules! atomic_redirect { }; ($typ:ident, $field:ident) => { impl AsRef<$crate::interpreted::ExprInst> for $typ { - fn as_ref(&self) -> &$crate::interpreted::ExprInst { - &self.$field - } + fn as_ref(&self) -> &$crate::interpreted::ExprInst { &self.$field } } impl From<(&Self, $crate::interpreted::ExprInst)> for $typ { #[allow(clippy::needless_update)] diff --git a/src/foreign_macros/externfn_impl.rs b/src/foreign_macros/externfn_impl.rs index 49165f6..bd7636c 100644 --- a/src/foreign_macros/externfn_impl.rs +++ b/src/foreign_macros/externfn_impl.rs @@ -25,9 +25,7 @@ use crate::{atomic_impl, atomic_redirect}; macro_rules! externfn_impl { ($typ:ident, $next_atomic:expr) => { impl $crate::foreign::ExternFn for $typ { - fn name(&self) -> &str { - stringify!($typ) - } + fn name(&self) -> &str { stringify!($typ) } fn apply( &self, arg: $crate::interpreted::ExprInst, diff --git a/src/foreign_macros/mod.rs b/src/foreign_macros/mod.rs index 2e75ed0..dcf77be 100644 --- a/src/foreign_macros/mod.rs +++ b/src/foreign_macros/mod.rs @@ -1,6 +1,4 @@ -mod atomic_defaults; mod atomic_impl; -mod atomic_inert; mod atomic_redirect; mod define_fn; mod externfn_impl; diff --git a/src/foreign_macros/write_fn_step.rs b/src/foreign_macros/write_fn_step.rs index 47a991c..5d1bdcd 100644 --- a/src/foreign_macros/write_fn_step.rs +++ b/src/foreign_macros/write_fn_step.rs @@ -107,6 +107,7 @@ macro_rules! write_fn_step { $( $arg: $typ, )* expr_inst: $crate::interpreted::ExprInst, } + impl $crate::utils::ddispatch::Responder for $name {} $crate::atomic_redirect!($name, expr_inst); $crate::atomic_impl!($name); $crate::externfn_impl!( @@ -136,6 +137,7 @@ macro_rules! write_fn_step { expr_inst: $crate::interpreted::ExprInst, } $crate::atomic_redirect!($name, expr_inst); + impl $crate::utils::ddispatch::Responder for $name {} $crate::atomic_impl!( $name, |Self{ $($arg, )* expr_inst }: &Self, _| { @@ -157,6 +159,6 @@ macro_rules! write_fn_step { } }; (@CONV $locxname:expr) => { - ($locxname).try_into()? + ($locxname).downcast()? }; } diff --git a/src/interner/multitype.rs b/src/interner/multitype.rs index e43446b..a02adaf 100644 --- a/src/interner/multitype.rs +++ b/src/interner/multitype.rs @@ -18,9 +18,7 @@ pub struct Interner { } impl Interner { /// Create a new interner - pub fn new() -> Self { - Self { interners: RefCell::new(HashMap::new()) } - } + pub fn new() -> Self { Self { interners: RefCell::new(HashMap::new()) } } /// Intern something pub fn i(&self, q: &Q) -> Tok @@ -62,9 +60,7 @@ impl Interner { } impl Default for Interner { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } /// Get or create an interner for a given type. diff --git a/src/interner/token.rs b/src/interner/token.rs index 3764f4b..b9529e3 100644 --- a/src/interner/token.rs +++ b/src/interner/token.rs @@ -27,9 +27,7 @@ impl Tok { .expect("Pointer can always be cast to nonzero") } /// Cast into usize - pub fn usize(&self) -> usize { - self.id().into() - } + pub fn usize(&self) -> usize { self.id().into() } /// pub fn assert_comparable(&self, other: &Self) { let iref = self.interner.as_ptr() as usize; @@ -50,9 +48,7 @@ impl Tok>> { impl Deref for Tok { type Target = T; - fn deref(&self) -> &Self::Target { - self.data.as_ref() - } + fn deref(&self) -> &Self::Target { self.data.as_ref() } } impl Debug for Tok { diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index 27fe8e2..3834231 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -17,9 +17,7 @@ pub enum RuntimeError { } impl From> for RuntimeError { - fn from(value: Rc) -> Self { - Self::Extern(value) - } + fn from(value: Rc) -> Self { Self::Extern(value) } } impl Display for RuntimeError { diff --git a/src/interpreter/handler.rs b/src/interpreter/handler.rs index 4f9e4fe..20a0f7a 100644 --- a/src/interpreter/handler.rs +++ b/src/interpreter/handler.rs @@ -20,9 +20,7 @@ pub struct HandlerTable<'a> { } impl<'a> HandlerTable<'a> { /// Create a new [HandlerTable] - pub fn new() -> Self { - Self { handlers: HashMap::new() } - } + pub fn new() -> Self { Self { handlers: HashMap::new() } } /// Add a handler function to interpret a type of atom and decide what happens /// next. This function can be impure. diff --git a/src/parse/context.rs b/src/parse/context.rs index b0e7dd7..b60ffa9 100644 --- a/src/parse/context.rs +++ b/src/parse/context.rs @@ -40,13 +40,7 @@ impl<'a> Clone for ParsingContext<'a> { } impl Context for ParsingContext<'_> { - fn interner(&self) -> &Interner { - self.interner - } - fn file(&self) -> Rc { - self.file.clone() - } - fn ops(&self) -> &[Tok] { - self.ops - } + fn interner(&self) -> &Interner { self.interner } + fn file(&self) -> Rc { self.file.clone() } + fn ops(&self) -> &[Tok] { self.ops } } diff --git a/src/parse/errors.rs b/src/parse/errors.rs index 6f7bac4..c6c8b05 100644 --- a/src/parse/errors.rs +++ b/src/parse/errors.rs @@ -13,15 +13,11 @@ pub struct LineNeedsPrefix { pub entry: Entry, } impl ProjectError for LineNeedsPrefix { - fn description(&self) -> &str { - "This linetype requires a prefix" - } + fn description(&self) -> &str { "This linetype requires a prefix" } fn message(&self) -> String { format!("{} cannot appear at the beginning of a line", self.entry) } - fn one_position(&self) -> Location { - self.entry.location() - } + fn one_position(&self) -> Location { self.entry.location() } } #[derive(Debug)] @@ -30,9 +26,7 @@ pub struct UnexpectedEOL { pub entry: Entry, } impl ProjectError for UnexpectedEOL { - fn description(&self) -> &str { - "The line ended abruptly" - } + fn description(&self) -> &str { "The line ended abruptly" } fn message(&self) -> String { "The line ends unexpectedly here. In Orchid, all line breaks outside \ @@ -40,21 +34,15 @@ impl ProjectError for UnexpectedEOL { .to_string() } - fn one_position(&self) -> Location { - self.entry.location() - } + fn one_position(&self) -> Location { self.entry.location() } } pub struct ExpectedEOL { pub location: Location, } impl ProjectError for ExpectedEOL { - fn description(&self) -> &str { - "Expected the end of the line" - } - fn one_position(&self) -> Location { - self.location.clone() - } + fn description(&self) -> &str { "Expected the end of the line" } + fn one_position(&self) -> Location { self.location.clone() } } #[derive(Debug)] @@ -85,9 +73,7 @@ impl ProjectError for ExpectedName { } } - fn one_position(&self) -> Location { - self.entry.location() - } + fn one_position(&self) -> Location { self.entry.location() } } #[derive()] @@ -115,16 +101,15 @@ impl ProjectError for Expected { &[] => return "Unsatisfiable expectation".to_string(), [only] => only.to_string(), [a, b] => format!("either {a} or {b}"), - [variants @ .., last] => - format!("any of {} or {last}", variants.iter().join(", ")), + [variants @ .., last] => { + format!("any of {} or {last}", variants.iter().join(", ")) + }, }; let or_name = if self.or_name { " or a name" } else { "" }; format!("Expected {list}{or_name} but found {}", self.found) } - fn one_position(&self) -> Location { - self.found.location() - } + fn one_position(&self) -> Location { self.found.location() } } pub struct ReservedToken { @@ -135,13 +120,9 @@ impl ProjectError for ReservedToken { "A token reserved for future use was found in the code" } - fn message(&self) -> String { - format!("{} is a reserved token", self.entry) - } + fn message(&self) -> String { format!("{} is a reserved token", self.entry) } - fn one_position(&self) -> Location { - self.entry.location() - } + fn one_position(&self) -> Location { self.entry.location() } } pub struct BadTokenInRegion { @@ -157,9 +138,7 @@ impl ProjectError for BadTokenInRegion { format!("{} cannot appear in {}", self.entry, self.region) } - fn one_position(&self) -> Location { - self.entry.location() - } + fn one_position(&self) -> Location { self.entry.location() } } pub struct NotFound { @@ -171,25 +150,17 @@ impl ProjectError for NotFound { "A specific lexeme was expected but not found in the given range" } - fn message(&self) -> String { - format!("{} was expected", self.expected) - } + fn message(&self) -> String { format!("{} was expected", self.expected) } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } pub struct LeadingNS { pub location: Location, } impl ProjectError for LeadingNS { - fn description(&self) -> &str { - ":: can only follow a name token" - } - fn one_position(&self) -> Location { - self.location.clone() - } + fn description(&self) -> &str { ":: can only follow a name token" } + fn one_position(&self) -> Location { self.location.clone() } } pub struct MisalignedParen { @@ -199,12 +170,8 @@ impl ProjectError for MisalignedParen { fn description(&self) -> &str { "Parentheses (), [] and {} must always pair up" } - fn message(&self) -> String { - format!("This {} has no pair", self.entry) - } - fn one_position(&self) -> Location { - self.entry.location() - } + fn message(&self) -> String { format!("This {} has no pair", self.entry) } + fn one_position(&self) -> Location { self.entry.location() } } pub struct NamespacedExport { @@ -214,9 +181,7 @@ impl ProjectError for NamespacedExport { fn description(&self) -> &str { "Exports can only refer to unnamespaced names in the local namespace" } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } pub struct GlobExport { @@ -226,9 +191,7 @@ impl ProjectError for GlobExport { fn description(&self) -> &str { "Exports can only refer to concrete names, globstars are not allowed" } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } pub struct LexError { @@ -237,9 +200,7 @@ pub struct LexError { pub file: VName, } impl ProjectError for LexError { - fn description(&self) -> &str { - "An error occured during tokenization" - } + fn description(&self) -> &str { "An error occured during tokenization" } fn positions(&self) -> BoxedIter { let file = self.file.clone(); Box::new(self.errors.iter().map(move |s| ErrorPosition { diff --git a/src/parse/lexer.rs b/src/parse/lexer.rs index 33f7360..35707cf 100644 --- a/src/parse/lexer.rs +++ b/src/parse/lexer.rs @@ -40,9 +40,7 @@ impl Entry { ) } - pub fn location(&self) -> Location { - self.location.clone() - } + pub fn location(&self) -> Location { self.location.clone() } pub fn range(&self) -> Range { self.location.range().expect("An Entry can only have a known location") @@ -84,9 +82,7 @@ impl Display for Entry { // } impl AsRef for Entry { - fn as_ref(&self) -> &Location { - &self.location - } + fn as_ref(&self) -> &Location { &self.location } } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -144,8 +140,9 @@ impl Display for Lexeme { Self::Module => write!(f, "module"), Self::Const => write!(f, "const"), Self::Macro => write!(f, "macro"), - Self::Operators(ops) => - write!(f, "operators[{}]", Interner::extern_all(ops).join(" ")), + Self::Operators(ops) => { + write!(f, "operators[{}]", Interner::extern_all(ops).join(" ")) + }, Self::Placeh(Placeholder { name, class }) => match *class { PHClass::Scalar => write!(f, "${}", **name), PHClass::Vec { nonzero, prio } => { diff --git a/src/pipeline/file_loader.rs b/src/pipeline/file_loader.rs index bbe5cc3..39b41fd 100644 --- a/src/pipeline/file_loader.rs +++ b/src/pipeline/file_loader.rs @@ -44,9 +44,7 @@ pub enum Loaded { } impl Loaded { /// Is the loaded item source code (not a collection)? - pub fn is_code(&self) -> bool { - matches!(self, Loaded::Code(_)) - } + pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) } } /// Returned by any source loading callback diff --git a/src/pipeline/source_loader/load_source.rs b/src/pipeline/source_loader/load_source.rs index 640a8dd..66becbb 100644 --- a/src/pipeline/source_loader/load_source.rs +++ b/src/pipeline/source_loader/load_source.rs @@ -87,8 +87,9 @@ fn load_abs_path_rec( // If the path is not within a file, load it as directory let coll = match get_source(abs_path) { Ok(Loaded::Collection(coll)) => coll, - Ok(Loaded::Code(_)) => - unreachable!("split_name returned None but the path is a file"), + Ok(Loaded::Code(_)) => { + unreachable!("split_name returned None but the path is a file") + }, Err(e) => { // todo: if this can actually be produced, return Err(ImportAll) instead let parent = abs_path.split_last().expect("import path nonzero").1; diff --git a/src/pipeline/source_loader/preparse.rs b/src/pipeline/source_loader/preparse.rs index 95233a6..ce66612 100644 --- a/src/pipeline/source_loader/preparse.rs +++ b/src/pipeline/source_loader/preparse.rs @@ -14,8 +14,8 @@ use crate::parse::{self, ParsingContext}; use crate::representations::sourcefile::{FileEntry, MemberKind}; use crate::representations::tree::{ModEntry, ModMember, Module}; use crate::sourcefile::{FileEntryKind, Import, Member, ModuleBlock}; -use crate::utils::pure_push::pushed; use crate::utils::get_or::{get_or_default, get_or_make}; +use crate::utils::pure_push::pushed; use crate::{Location, Tok, VName}; struct FileReport { diff --git a/src/pipeline/source_loader/types.rs b/src/pipeline/source_loader/types.rs index 7154e61..b57cc05 100644 --- a/src/pipeline/source_loader/types.rs +++ b/src/pipeline/source_loader/types.rs @@ -69,8 +69,9 @@ impl Add for PreExtra { fn add(self, rhs: Self) -> Self::Output { match (self, rhs) { (alt, Self::Dir) | (Self::Dir, alt) => Ok(alt), - (Self::File(_) | Self::Submod(_), Self::File(_) | Self::Submod(_)) => - panic!("Each file should be parsed once."), + (Self::File(_) | Self::Submod(_), Self::File(_) | Self::Submod(_)) => { + panic!("Each file should be parsed once.") + }, } } } @@ -79,8 +80,9 @@ impl Display for PreExtra { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Dir => write!(f, "Directory"), - Self::File(PreFileExt { name, .. }) => - write!(f, "File({}.orc)", Interner::extern_all(name).join("/")), + Self::File(PreFileExt { name, .. }) => { + write!(f, "File({}.orc)", Interner::extern_all(name).join("/")) + }, Self::Submod(_) => write!(f, "Submodule"), } } diff --git a/src/representations/ast.rs b/src/representations/ast.rs index 830c071..f54a1bc 100644 --- a/src/representations/ast.rs +++ b/src/representations/ast.rs @@ -57,9 +57,7 @@ impl Expr { } impl AsRef for Expr { - fn as_ref(&self) -> &Location { - &self.location - } + fn as_ref(&self) -> &Location { &self.location } } /// Visit all expression sequences including this sequence itself. Otherwise diff --git a/src/representations/ast_to_postmacro.rs b/src/representations/ast_to_postmacro.rs index 2857c1c..7fe02cc 100644 --- a/src/representations/ast_to_postmacro.rs +++ b/src/representations/ast_to_postmacro.rs @@ -50,9 +50,7 @@ impl ProjectError for Error { _ => self.description().to_string(), } } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } /// Try to convert an expression from AST format to typed lambda @@ -73,9 +71,7 @@ impl<'a> Context<'a> { Context { names: self.names.push(name) } } - fn new() -> Context<'static> { - Context { names: Substack::Bottom } - } + fn new() -> Context<'static> { Context { names: Substack::Bottom } } } /// Process an expression sequence diff --git a/src/representations/interpreted.rs b/src/representations/interpreted.rs index a9b3a79..e7603f1 100644 --- a/src/representations/interpreted.rs +++ b/src/representations/interpreted.rs @@ -13,6 +13,7 @@ use super::location::Location; use super::path_set::PathSet; use super::primitive::Primitive; use super::Literal; +use crate::foreign::ExternError; use crate::Sym; /// An expression with metadata @@ -45,6 +46,12 @@ impl Display for Expr { /// expression is not a literal pub struct NotALiteral; +/// Types automatically convertible from an [ExprInst] +pub trait TryFromExprInst: Sized { + /// Match and clone the value out of an [ExprInst] + fn from_exi(exi: &ExprInst) -> Result>; +} + /// A wrapper around expressions to handle their multiple occurences in /// the tree together #[derive(Clone)] @@ -134,6 +141,13 @@ impl ExprInst { Clause::Constant(_) | Clause::LambdaArg | Clause::P(_) => None, }) } + + /// Convert into any type that implements [FromExprInst]. Calls to this + /// function are generated wherever a conversion is elided in an extern + /// function. + pub fn downcast(&self) -> Result> { + T::from_exi(self) + } } impl Debug for ExprInst { @@ -208,13 +222,9 @@ impl Display for Clause { } impl> From for Clause { - fn from(value: T) -> Self { - Self::P(Primitive::Literal(value.into())) - } + fn from(value: T) -> Self { Self::P(Primitive::Literal(value.into())) } } impl> From for ExprInst { - fn from(value: T) -> Self { - value.into().wrap() - } + fn from(value: T) -> Self { value.into().wrap() } } diff --git a/src/representations/literal.rs b/src/representations/literal.rs index 2293029..ae17fec 100644 --- a/src/representations/literal.rs +++ b/src/representations/literal.rs @@ -27,17 +27,11 @@ impl Debug for Literal { } impl From> for Literal { - fn from(value: NotNan) -> Self { - Self::Num(value) - } + fn from(value: NotNan) -> Self { Self::Num(value) } } impl From for Literal { - fn from(value: u64) -> Self { - Self::Uint(value) - } + fn from(value: u64) -> Self { Self::Uint(value) } } impl From for Literal { - fn from(value: String) -> Self { - Self::Str(value.into()) - } + fn from(value: String) -> Self { Self::Str(value.into()) } } diff --git a/src/representations/namelike.rs b/src/representations/namelike.rs index 48d6b84..8d28e28 100644 --- a/src/representations/namelike.rs +++ b/src/representations/namelike.rs @@ -25,13 +25,9 @@ pub trait NameLike: 'static + Clone + Eq + Hash + Debug { } impl NameLike for Sym { - fn to_strv(&self) -> Vec { - self.extern_vec() - } + fn to_strv(&self) -> Vec { self.extern_vec() } } impl NameLike for VName { - fn to_strv(&self) -> Vec { - Interner::extern_all(self) - } + fn to_strv(&self) -> Vec { Interner::extern_all(self) } } diff --git a/src/representations/project.rs b/src/representations/project.rs index 49fb165..eb9d67e 100644 --- a/src/representations/project.rs +++ b/src/representations/project.rs @@ -25,9 +25,7 @@ pub enum ItemKind { } impl Default for ItemKind { - fn default() -> Self { - Self::None - } + fn default() -> Self { Self::None } } #[derive(Debug, Clone, Default)] diff --git a/src/representations/sourcefile.rs b/src/representations/sourcefile.rs index 31d64b6..49563c1 100644 --- a/src/representations/sourcefile.rs +++ b/src/representations/sourcefile.rs @@ -84,8 +84,9 @@ pub enum MemberKind { impl Display for MemberKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Operators(opv) => - write!(f, "operators[{}]", opv.iter().map(|t| &**t).join(" ")), + Self::Operators(opv) => { + write!(f, "operators[{}]", opv.iter().map(|t| &**t).join(" ")) + }, Self::Constant(c) => c.fmt(f), Self::Module(m) => m.fmt(f), Self::Rule(r) => r.fmt(f), @@ -130,11 +131,13 @@ impl Display for FileEntryKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Comment(s) => write!(f, "--[{s}]--"), - Self::Export(s) => - write!(f, "export ::({})", s.iter().map(|t| &**t.0).join(", ")), + Self::Export(s) => { + write!(f, "export ::({})", s.iter().map(|t| &**t.0).join(", ")) + }, Self::Member(member) => write!(f, "{member}"), - Self::Import(i) => - write!(f, "import ({})", i.iter().map(|i| i.to_string()).join(", ")), + Self::Import(i) => { + write!(f, "import ({})", i.iter().map(|i| i.to_string()).join(", ")) + }, } } } diff --git a/src/representations/string.rs b/src/representations/string.rs index 5d89c4d..7b910fc 100644 --- a/src/representations/string.rs +++ b/src/representations/string.rs @@ -25,9 +25,7 @@ impl Debug for OrcString { impl OrcString { /// Clone out the plain Rust [String] - pub fn get_string(&self) -> String { - self.as_str().to_owned() - } + pub fn get_string(&self) -> String { self.as_str().to_owned() } } impl Deref for OrcString { @@ -48,15 +46,11 @@ impl Hash for OrcString { } impl From for OrcString { - fn from(value: String) -> Self { - Self::Runtime(Rc::new(value)) - } + fn from(value: String) -> Self { Self::Runtime(Rc::new(value)) } } impl From> for OrcString { - fn from(value: Tok) -> Self { - Self::Interned(value) - } + fn from(value: Tok) -> Self { Self::Interned(value) } } impl PartialEq for OrcString { diff --git a/src/representations/tree.rs b/src/representations/tree.rs index 93a8849..72ca2dd 100644 --- a/src/representations/tree.rs +++ b/src/representations/tree.rs @@ -223,9 +223,7 @@ pub struct WalkError<'a> { } impl<'a> WalkError<'a> { /// Total length of the path represented by this error - pub fn depth(&self) -> usize { - self.prefix.len() + self.pos + 1 - } + pub fn depth(&self) -> usize { self.prefix.len() + self.pos + 1 } /// Attach a location to the error and convert into trait object for reporting pub fn at(self, location: &Location) -> Rc { @@ -263,15 +261,15 @@ impl ProjectError for WalkErrorWithLocation { let paths = Interner::extern_all(&self.path).join("::"); let options = Interner::extern_all(&self.options).join(", "); match &self.kind { - ErrKind::Missing => - format!("{paths} does not exist, options are {options}"), - ErrKind::NotModule => - format!("{paths} is not a module, options are {options}"), + ErrKind::Missing => { + format!("{paths} does not exist, options are {options}") + }, + ErrKind::NotModule => { + format!("{paths} is not a module, options are {options}") + }, ErrKind::Private => format!("{paths} is private, options are {options}"), } } - fn one_position(&self) -> Location { - self.location.clone() - } + fn one_position(&self) -> Location { self.location.clone() } } diff --git a/src/rule/matcher_vectree/shared.rs b/src/rule/matcher_vectree/shared.rs index 1191f3a..d913a72 100644 --- a/src/rule/matcher_vectree/shared.rs +++ b/src/rule/matcher_vectree/shared.rs @@ -56,9 +56,7 @@ pub enum AnyMatcher { Vec { left: Vec, mid: VecMatcher, right: Vec }, } impl Matcher for AnyMatcher { - fn new(pattern: Rc>) -> Self { - mk_any(&pattern) - } + fn new(pattern: Rc>) -> Self { mk_any(&pattern) } fn apply<'a>(&self, source: &'a [RuleExpr]) -> Option> { any_match(self, source) @@ -99,10 +97,12 @@ impl Display for VecMatcher { write!(f, "..${key}") }, Self::Scan { left, sep, right, direction } => match direction { - Side::Left => - write!(f, "Scan{{{left} <== {} <== {right}}}", sep.iter().join(" ")), - Side::Right => - write!(f, "Scan{{{left} ==> {} ==> {right}}}", sep.iter().join(" ")), + Side::Left => { + write!(f, "Scan{{{left} <== {} <== {right}}}", sep.iter().join(" ")) + }, + Side::Right => { + write!(f, "Scan{{{left} ==> {} ==> {right}}}", sep.iter().join(" ")) + }, }, Self::Middle { left, left_sep, mid, right_sep, right, .. } => { let left_sep_s = left_sep.iter().join(" "); @@ -134,9 +134,7 @@ impl Display for AnyMatcher { /// vectorial placeholders and handles the scalars on leaves. pub struct VectreeMatcher(AnyMatcher); impl Matcher for VectreeMatcher { - fn new(pattern: Rc>) -> Self { - Self(AnyMatcher::new(pattern)) - } + fn new(pattern: Rc>) -> Self { Self(AnyMatcher::new(pattern)) } fn apply<'a>(&self, source: &'a [RuleExpr]) -> Option> { self.0.apply(source) diff --git a/src/rule/rule_error.rs b/src/rule/rule_error.rs index 0be7d9f..66ff0c9 100644 --- a/src/rule/rule_error.rs +++ b/src/rule/rule_error.rs @@ -37,10 +37,12 @@ impl Display for RuleError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Missing(key) => write!(f, "Key {key} not in match pattern"), - Self::ArityMismatch(key) => - write!(f, "Key {key} used inconsistently with and without ellipsis"), - Self::Multiple(key) => - write!(f, "Key {key} appears multiple times in match pattern"), + Self::ArityMismatch(key) => { + write!(f, "Key {key} used inconsistently with and without ellipsis") + }, + Self::Multiple(key) => { + write!(f, "Key {key} appears multiple times in match pattern") + }, Self::VecNeighbors(left, right) => write!( f, "Keys {left} and {right} are two vectorials right next to each other" diff --git a/src/systems/asynch/system.rs b/src/systems/asynch/system.rs index 16c8e94..0837a72 100644 --- a/src/systems/asynch/system.rs +++ b/src/systems/asynch/system.rs @@ -11,14 +11,14 @@ use ordered_float::NotNan; use crate::facade::{IntoSystem, System}; use crate::foreign::cps_box::{init_cps, CPSBox}; -use crate::foreign::{Atomic, ExternError}; +use crate::foreign::{Atomic, ExternError, InertAtomic}; use crate::interpreted::ExprInst; use crate::interpreter::HandlerTable; use crate::systems::codegen::call; use crate::systems::stl::Boolean; use crate::utils::poller::{PollEvent, Poller}; use crate::utils::unwrap_or; -use crate::{atomic_inert, define_fn, ConstTree, Interner}; +use crate::{define_fn, ConstTree, Interner}; #[derive(Debug, Clone)] struct Timer { @@ -45,7 +45,9 @@ impl Debug for CancelTimer { #[derive(Clone, Debug)] struct Yield; -atomic_inert!(Yield, typestr = "a yield command"); +impl InertAtomic for Yield { + fn type_str() -> &'static str { "a yield command" } +} /// Error indicating a yield command when all event producers and timers had /// exited @@ -109,15 +111,11 @@ impl<'a> AsynchSystem<'a> { /// Obtain a message port for sending messages to the main thread. If an /// object is passed to the MessagePort that does not have a handler, the /// main thread panics. - pub fn get_port(&self) -> MessagePort { - MessagePort(self.sender.clone()) - } + pub fn get_port(&self) -> MessagePort { MessagePort(self.sender.clone()) } } impl<'a> Default for AsynchSystem<'a> { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } impl<'a> IntoSystem<'a> for AsynchSystem<'a> { diff --git a/src/systems/cast_exprinst.rs b/src/systems/cast_exprinst.rs index cb36e7f..462a179 100644 --- a/src/systems/cast_exprinst.rs +++ b/src/systems/cast_exprinst.rs @@ -5,8 +5,8 @@ use std::rc::Rc; use ordered_float::NotNan; use super::assertion_error::AssertionError; -use crate::foreign::{Atomic, ExternError}; -use crate::interpreted::Clause; +use crate::foreign::{Atom, Atomic, ExternError}; +use crate::interpreted::{Clause, TryFromExprInst}; use crate::representations::interpreted::ExprInst; use crate::representations::{Literal, OrcString}; use crate::Primitive; @@ -27,92 +27,66 @@ pub fn with_str( x: &ExprInst, predicate: impl FnOnce(&OrcString) -> Result>, ) -> Result> { - with_lit(x, |l| { - if let Literal::Str(s) = l { - predicate(s) - } else { - AssertionError::fail(x.clone(), "a string")? - } + with_lit(x, |l| match l { + Literal::Str(s) => predicate(s), + _ => AssertionError::fail(x.clone(), "a string"), }) } -/// Like [with_lit] but also unwraps [Literal::Uint] -pub fn with_uint( +/// If the [ExprInst] stores an [Atom], maps the predicate over it, otherwise +/// raises a runtime error. +pub fn with_atom( x: &ExprInst, - predicate: impl FnOnce(u64) -> Result>, + predicate: impl FnOnce(&Atom) -> Result>, ) -> Result> { - with_lit(x, |l| { - if let Literal::Uint(u) = l { - predicate(*u) - } else { - AssertionError::fail(x.clone(), "an uint")? - } - }) -} - -/// Like [with_lit] but also unwraps [Literal::Num] -pub fn with_num( - x: &ExprInst, - predicate: impl FnOnce(NotNan) -> Result>, -) -> Result> { - with_lit(x, |l| { - if let Literal::Num(n) = l { - predicate(*n) - } else { - AssertionError::fail(x.clone(), "a float")? - } + 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_atom( +pub fn with_atomic( x: &ExprInst, inexact_typename: &'static str, predicate: impl FnOnce(&T) -> Result>, ) -> Result> { - x.inspect(|c| { - if let Clause::P(Primitive::Atom(a)) = c { - a.try_cast() - .map(predicate) - .unwrap_or_else(|| AssertionError::fail(x.clone(), inexact_typename)) - } else { - AssertionError::fail(x.clone(), "an atom") - } + with_atom(x, |a| match a.try_cast() { + Some(atomic) => predicate(atomic), + _ => AssertionError::fail(x.clone(), inexact_typename), }) } // ######## Automatically ######## -impl TryFrom<&ExprInst> for Literal { - type Error = Rc; - - fn try_from(value: &ExprInst) -> Result { - with_lit(value, |l| Ok(l.clone())) +impl TryFromExprInst for Literal { + fn from_exi(exi: &ExprInst) -> Result> { + with_lit(exi, |l| Ok(l.clone())) } } -impl TryFrom<&ExprInst> for OrcString { - type Error = Rc; - - fn try_from(value: &ExprInst) -> Result { - with_str(value, |s| Ok(s.clone())) +impl TryFromExprInst for OrcString { + fn from_exi(exi: &ExprInst) -> Result> { + with_str(exi, |s| Ok(s.clone())) } } -impl TryFrom<&ExprInst> for u64 { - type Error = Rc; - - fn try_from(value: &ExprInst) -> Result { - with_uint(value, Ok) +impl TryFromExprInst for u64 { + fn from_exi(exi: &ExprInst) -> Result> { + with_lit(exi, |l| match l { + Literal::Uint(u) => Ok(*u), + _ => AssertionError::fail(exi.clone(), "an uint"), + }) } } -impl TryFrom<&ExprInst> for NotNan { - type Error = Rc; - - fn try_from(value: &ExprInst) -> Result { - with_num(value, Ok) +impl TryFromExprInst for NotNan { + fn from_exi(exi: &ExprInst) -> Result> { + with_lit(exi, |l| match l { + Literal::Num(n) => Ok(*n), + _ => AssertionError::fail(exi.clone(), "a float"), + }) } } diff --git a/src/systems/io/bindings.rs b/src/systems/io/bindings.rs index b2fa6f9..b09ec46 100644 --- a/src/systems/io/bindings.rs +++ b/src/systems/io/bindings.rs @@ -12,19 +12,19 @@ use crate::{ast, define_fn, ConstTree, Interner, Primitive}; define_fn! { ReadString = |x| Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RStr(SRead::All), - handle: x.try_into()? + handle: x.downcast()? })) } define_fn! { ReadLine = |x| Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RStr(SRead::Line), - handle: x.try_into()? + handle: x.downcast()? })) } define_fn! { ReadBin = |x| Ok(init_cps(3, IOCmdHandlePack{ cmd: ReadCmd::RBytes(BRead::All), - handle: x.try_into()? + handle: x.downcast()? })) } define_fn! { @@ -72,7 +72,7 @@ define_fn! { define_fn! { Flush = |x| Ok(init_cps(3, IOCmdHandlePack { cmd: WriteCmd::Flush, - handle: x.try_into()? + handle: x.downcast()? })) } diff --git a/src/systems/io/instances.rs b/src/systems/io/instances.rs index bf549e0..38b80df 100644 --- a/src/systems/io/instances.rs +++ b/src/systems/io/instances.rs @@ -81,23 +81,23 @@ pub enum ReadResult { impl ReadResult { pub fn dispatch(self, succ: ExprInst, fail: ExprInst) -> Vec { match self { - ReadResult::RBin(_, Err(e)) | ReadResult::RStr(_, Err(e)) => - vec![call(fail, vec![wrap_io_error(e)]).wrap()], + ReadResult::RBin(_, Err(e)) | ReadResult::RStr(_, Err(e)) => { + vec![call(fail, vec![wrap_io_error(e)]).wrap()] + }, ReadResult::RBin(_, Ok(bytes)) => { let arg = Binary(Arc::new(bytes)).atom_cls().wrap(); vec![call(succ, vec![arg]).wrap()] }, - ReadResult::RStr(_, Ok(text)) => - vec![call(succ, vec![Literal::Str(text.into()).into()]).wrap()], + ReadResult::RStr(_, Ok(text)) => { + vec![call(succ, vec![Literal::Str(text.into()).into()]).wrap()] + }, } } } /// Placeholder function for an eventual conversion from [io::Error] to Orchid /// data -fn wrap_io_error(_e: io::Error) -> ExprInst { - Literal::Uint(0u64).into() -} +fn wrap_io_error(_e: io::Error) -> ExprInst { Literal::Uint(0u64).into() } /// Writing command (string or binary) #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 23b1f52..fa0f603 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -3,11 +3,11 @@ mod assertion_error; pub mod asynch; pub mod cast_exprinst; pub mod codegen; +mod directfs; pub mod io; mod runtime_error; -pub mod stl; -mod directfs; pub mod scheduler; +pub mod stl; pub use assertion_error::AssertionError; pub use runtime_error::RuntimeError; diff --git a/src/systems/scheduler/busy.rs b/src/systems/scheduler/busy.rs index e377f6b..2fcf493 100644 --- a/src/systems/scheduler/busy.rs +++ b/src/systems/scheduler/busy.rs @@ -1,17 +1,14 @@ use std::any::Any; use std::collections::VecDeque; -use crate::interpreted::ExprInst; - use super::Canceller; +use crate::interpreted::ExprInst; pub type SyncResult = (T, Box); pub type SyncOperation = Box SyncResult + Send>; -pub type SyncOpResultHandler = Box< - dyn FnOnce(T, Box, Canceller) -> (T, Vec), ->; - +pub type SyncOpResultHandler = + Box, Canceller) -> (T, Vec)>; struct SyncQueueItem { cancelled: Canceller, @@ -89,9 +86,7 @@ impl BusyState { self.seal = Some(Box::new(recipient)) } - pub fn is_sealed(&self) -> bool { - self.seal.is_some() - } + pub fn is_sealed(&self) -> bool { self.seal.is_some() } pub fn rotate( mut self, diff --git a/src/systems/scheduler/canceller.rs b/src/systems/scheduler/canceller.rs index f80f496..009b611 100644 --- a/src/systems/scheduler/canceller.rs +++ b/src/systems/scheduler/canceller.rs @@ -1,32 +1,26 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use crate::atomic_inert; +use crate::foreign::InertAtomic; /// A single-fire thread-safe boolean flag with relaxed ordering #[derive(Debug, Clone)] pub struct Canceller(Arc); -atomic_inert!(Canceller, typestr = "a canceller"); +impl InertAtomic for Canceller { + fn type_str() -> &'static str { "a canceller" } +} impl Canceller { /// Create a new canceller - pub fn new() -> Self { - Canceller(Arc::new(AtomicBool::new(false))) - } + pub fn new() -> Self { Canceller(Arc::new(AtomicBool::new(false))) } /// Check whether the operation has been cancelled - pub fn is_cancelled(&self) -> bool { - self.0.load(Ordering::Relaxed) - } + pub fn is_cancelled(&self) -> bool { self.0.load(Ordering::Relaxed) } /// Cancel the operation - pub fn cancel(&self) { - self.0.store(true, Ordering::Relaxed) - } + pub fn cancel(&self) { self.0.store(true, Ordering::Relaxed) } } impl Default for Canceller { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } diff --git a/src/systems/scheduler/mod.rs b/src/systems/scheduler/mod.rs index 02df4f6..d4952ec 100644 --- a/src/systems/scheduler/mod.rs +++ b/src/systems/scheduler/mod.rs @@ -2,9 +2,8 @@ //! reference to a shared resource. mod busy; -mod system; mod canceller; -mod take_and_drop; +mod system; pub use canceller::Canceller; pub use system::{SealedOrTaken, SeqScheduler, SharedHandle, SharedState}; diff --git a/src/systems/scheduler/system.rs b/src/systems/scheduler/system.rs index 331d24c..c13bd85 100644 --- a/src/systems/scheduler/system.rs +++ b/src/systems/scheduler/system.rs @@ -8,17 +8,20 @@ use itertools::Itertools; use trait_set::trait_set; use super::busy::{BusyState, NextItemReportKind}; -use super::take_and_drop::{request, TakeAndDrop, TakeCmd}; use super::Canceller; use crate::facade::{IntoSystem, System}; -use crate::foreign::cps_box::CPSBox; +use crate::foreign::cps_box::{init_cps, CPSBox}; +use crate::foreign::InertAtomic; use crate::interpreted::ExprInst; use crate::interpreter::HandlerTable; use crate::systems::asynch::{AsynchSystem, MessagePort}; +use crate::systems::cast_exprinst::with_atom; use crate::systems::stl::Boolean; +use crate::systems::AssertionError; +use crate::utils::ddispatch::{request, Request}; use crate::utils::thread_pool::ThreadPool; use crate::utils::{take_with_output, unwrap_or, IdMap}; -use crate::{atomic_inert, define_fn, ConstTree}; +use crate::{define_fn, ConstTree}; enum SharedResource { Free(T), @@ -45,18 +48,17 @@ pub enum SharedState { /// A shared handle for a resource of type `T` that can be used with a /// [SeqScheduler] to execute mutating operations one by one in worker threads. -pub struct SharedHandle { - state: Rc>>, -} +pub struct SharedHandle(Rc>>); + impl SharedHandle { /// Wrap a value to be accessible to a [SeqScheduler]. pub fn wrap(t: T) -> Self { - Self { state: Rc::new(RefCell::new(SharedResource::Free(t))) } + Self(Rc::new(RefCell::new(SharedResource::Free(t)))) } /// Check the state of the handle pub fn state(&self) -> SharedState { - match &*self.state.as_ref().borrow() { + match &*self.0.as_ref().borrow() { SharedResource::Busy(b) if b.is_sealed() => SharedState::Sealed, SharedResource::Busy(_) => SharedState::Busy, SharedResource::Free(_) => SharedState::Free, @@ -69,32 +71,24 @@ impl SharedHandle { /// sense as eg. an optimization. You can return the value after processing /// via [SyncHandle::untake]. pub fn take(&self) -> Option { - take_with_output( - &mut *self.state.as_ref().borrow_mut(), - |state| match state { - SharedResource::Free(t) => (SharedResource::Taken, Some(t)), - _ => (state, None), - }, - ) + take_with_output(&mut *self.0.as_ref().borrow_mut(), |state| match state { + SharedResource::Free(t) => (SharedResource::Taken, Some(t)), + _ => (state, None), + }) } /// Return the value to a handle that doesn't have one. The intended use case /// is to return values synchronously after they have been removed with /// [SyncHandle::untake]. pub fn untake(&self, value: T) -> Result<(), T> { - take_with_output( - &mut *self.state.as_ref().borrow_mut(), - |state| match state { - SharedResource::Taken => (SharedResource::Free(value), Ok(())), - _ => (state, Err(value)), - }, - ) + take_with_output(&mut *self.0.as_ref().borrow_mut(), |state| match state { + SharedResource::Taken => (SharedResource::Free(value), Ok(())), + _ => (state, Err(value)), + }) } } impl Clone for SharedHandle { - fn clone(&self) -> Self { - Self { state: self.state.clone() } - } + fn clone(&self) -> Self { Self(self.0.clone()) } } impl Debug for SharedHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -104,23 +98,46 @@ impl Debug for SharedHandle { .finish() } } +impl InertAtomic for SharedHandle { + fn type_str() -> &'static str { "a SharedHandle" } + fn respond(&self, mut request: Request) { + request.serve_with(|| { + let this = self.clone(); + TakeCmd(Rc::new(move |sch| { + let _ = sch.seal(this.clone(), |_| Vec::new()); + })) + }) + } +} -atomic_inert! { - SharedHandle(T), - typestr = "a shared handle", - request = |req: Box, this: &SharedHandle| request(this, req) +#[derive(Clone)] +pub struct TakeCmd(pub Rc); +impl Debug for TakeCmd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "A command to drop a shared resource") + } +} +define_fn! { + pub TakeAndDrop = |x| with_atom(x, |a| match request(a.0.as_ref()) { + Some(t) => Ok(init_cps::(1, t)), + None => AssertionError::fail(x.clone(), "a SharedHandle"), + }) } /// Error produced when an operation is scheduled or a seal placed on a resource /// which is either already sealed or taken. #[derive(Debug, Clone)] pub struct SealedOrTaken; -atomic_inert!( - SealedOrTaken, - typestr = "a sealed-or-taken error for a shared resource" -); +impl InertAtomic for SealedOrTaken { + fn type_str() -> &'static str { + "a sealed-or-taken error for a shared resource" + } +} + define_fn! { - IsTakenError = |x| Ok(Boolean(SealedOrTaken::try_from(x).is_ok()).atom_cls()) + IsTakenError = |x| { + Ok(Boolean(x.downcast::().is_ok()).atom_cls()) + } } trait_set! { @@ -184,16 +201,17 @@ impl SeqScheduler { handler: impl FnOnce(T, U, Canceller) -> (T, Vec) + 'static, early_cancel: impl FnOnce(T) -> (T, Vec) + 'static, ) -> Result { - take_with_output(&mut *handle.state.as_ref().borrow_mut(), { + take_with_output(&mut *handle.0.as_ref().borrow_mut(), { let handle = handle.clone(); |state| { match state { SharedResource::Taken => (SharedResource::Taken, Err(SealedOrTaken)), - SharedResource::Busy(mut b) => + SharedResource::Busy(mut b) => { match b.enqueue(operation, handler, early_cancel) { Some(cancelled) => (SharedResource::Busy(b), Ok(cancelled)), None => (SharedResource::Busy(b), Err(SealedOrTaken)), - }, + } + }, SharedResource::Free(t) => { let cancelled = Canceller::new(); drop(early_cancel); // cannot possibly be useful @@ -212,8 +230,9 @@ impl SeqScheduler { handle: SharedHandle, seal: impl FnOnce(T) -> Vec + 'static, ) -> Result, SealedOrTaken> { - take_with_output(&mut *handle.state.as_ref().borrow_mut(), |state| { - match state { + take_with_output( + &mut *handle.0.as_ref().borrow_mut(), + |state| match state { SharedResource::Busy(mut b) if !b.is_sealed() => { b.seal(seal); (SharedResource::Busy(b), Ok(Vec::new())) @@ -221,8 +240,8 @@ impl SeqScheduler { SharedResource::Busy(_) => (state, Err(SealedOrTaken)), SharedResource::Taken => (SharedResource::Taken, Err(SealedOrTaken)), SharedResource::Free(t) => (SharedResource::Taken, Ok(seal(t))), - } - }) + }, + ) } /// Asynchronously recursive function to schedule a new task for execution and @@ -244,7 +263,7 @@ impl SeqScheduler { let (t, u): (T, U) = *data.downcast().expect("This is associated by ID"); let handle2 = handle.clone(); - take_with_output(&mut *handle.state.as_ref().borrow_mut(), |state| { + take_with_output(&mut *handle.0.as_ref().borrow_mut(), |state| { let busy = unwrap_or! { state => SharedResource::Busy; panic!("Handle with outstanding invocation must be busy") }; diff --git a/src/systems/scheduler/take_and_drop.rs b/src/systems/scheduler/take_and_drop.rs deleted file mode 100644 index a698c67..0000000 --- a/src/systems/scheduler/take_and_drop.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::any::Any; -use std::fmt::Debug; -use std::rc::Rc; - -use super::{SeqScheduler, SharedHandle}; -use crate::foreign::cps_box::{init_cps, CPSBox}; -use crate::foreign::Atom; -use crate::interpreted::Clause; -use crate::systems::AssertionError; -use crate::{define_fn, Primitive}; - -pub fn request( - handle: &SharedHandle, - request: Box, -) -> Option> { - if request.downcast::().is_ok() { - let handle = handle.clone(); - let cmd = TakeCmd(Rc::new(move |sch| { - let _ = sch.seal(handle.clone(), |_| Vec::new()); - })); - return Some(Box::new(init_cps(1, cmd))) - } - None -} - -pub struct TakerRequest; -#[derive(Clone)] -pub struct TakeCmd(pub Rc); -impl Debug for TakeCmd { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "A command to drop a shared resource") - } -} -define_fn! { - pub TakeAndDrop = |x| x.inspect(|c| match c { - Clause::P(Primitive::Atom(Atom(atomic))) => { - let t = atomic.request(Box::new(TakerRequest)) - .ok_or_else(|| AssertionError::ext(x.clone(), "a SharedHandle"))?; - let data: CPSBox = *t.downcast().expect("implied by request"); - Ok(data.atom_cls()) - }, - _ => AssertionError::fail(x.clone(), "an atom"), - }) -} diff --git a/src/systems/stl/bin.rs b/src/systems/stl/bin.rs index 6e4f52b..fca4950 100644 --- a/src/systems/stl/bin.rs +++ b/src/systems/stl/bin.rs @@ -4,16 +4,18 @@ use std::sync::Arc; use itertools::Itertools; use super::Boolean; -use crate::systems::cast_exprinst::with_uint; +use crate::foreign::InertAtomic; use crate::systems::codegen::{orchid_opt, tuple}; use crate::systems::RuntimeError; use crate::utils::{iter_find, unwrap_or}; -use crate::{atomic_inert, define_fn, ConstTree, Interner, Literal}; +use crate::{define_fn, ConstTree, Interner, Literal}; /// A block of binary data #[derive(Clone, Hash, PartialEq, Eq)] pub struct Binary(pub Arc>); -atomic_inert!(Binary, typestr = "a binary blob"); +impl InertAtomic for Binary { + fn type_str() -> &'static str { "a binary blob" } +} impl Debug for Binary { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -96,11 +98,7 @@ define_fn! {expr=x in define_fn! {expr=x in /// Extract a subsection of the binary data - pub Slice { - s: Binary, - i: u64 as with_uint(x, Ok), - len: u64 as with_uint(x, Ok) - } => { + pub Slice { s: Binary, i: u64, len: u64 } => { if i + len < s.0.len() as u64 { RuntimeError::fail( "Byte index out of bounds".to_string(), @@ -123,10 +121,7 @@ define_fn! {expr=x in define_fn! {expr=x in /// Split binary data block into two smaller blocks - pub Split { - bin: Binary, - i: u64 as with_uint(x, Ok) - } => { + pub Split { bin: Binary, i: u64 } => { if bin.0.len() < *i as usize { RuntimeError::fail( "Byte index out of bounds".to_string(), @@ -144,7 +139,7 @@ define_fn! {expr=x in define_fn! { /// Detect the number of bytes in the binary data block pub Size = |x| { - Ok(Literal::Uint(Binary::try_from(x)?.0.len() as u64).into()) + Ok(Literal::Uint(x.downcast::()?.0.len() as u64).into()) } } diff --git a/src/systems/stl/bool.rs b/src/systems/stl/bool.rs index 95c0663..3bdf185 100644 --- a/src/systems/stl/bool.rs +++ b/src/systems/stl/bool.rs @@ -1,19 +1,20 @@ use std::rc::Rc; +use crate::foreign::InertAtomic; use crate::interner::Interner; use crate::representations::interpreted::Clause; use crate::systems::AssertionError; -use crate::{atomic_inert, define_fn, ConstTree, Literal, PathSet}; +use crate::{define_fn, ConstTree, Literal, PathSet}; /// Booleans exposed to Orchid #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Boolean(pub bool); -atomic_inert!(Boolean, typestr = "a boolean"); +impl InertAtomic for Boolean { + fn type_str() -> &'static str { "a boolean" } +} impl From for Boolean { - fn from(value: bool) -> Self { - Self(value) - } + fn from(value: bool) -> Self { Self(value) } } define_fn! {expr=x in @@ -36,7 +37,7 @@ define_fn! {expr=x in define_fn! { /// Takes a boolean and two branches, runs the first if the bool is true, the /// second if it's false. - IfThenElse = |x| x.try_into() + IfThenElse = |x| x.downcast() .map_err(|_| AssertionError::ext(x.clone(), "a boolean")) .map(|b: Boolean| if b.0 {Clause::Lambda { args: Some(PathSet { steps: Rc::new(vec![]), next: None }), diff --git a/src/systems/stl/inspect.rs b/src/systems/stl/inspect.rs index 09381d7..75d1114 100644 --- a/src/systems/stl/inspect.rs +++ b/src/systems/stl/inspect.rs @@ -3,7 +3,8 @@ use std::fmt::Debug; use crate::foreign::{Atomic, AtomicReturn}; use crate::interpreter::Context; use crate::representations::interpreted::ExprInst; -use crate::{atomic_defaults, write_fn_step, ConstTree, Interner}; +use crate::utils::ddispatch::Responder; +use crate::{write_fn_step, ConstTree, Interner}; write_fn_step! { /// Print and return whatever expression is in the argument without @@ -15,8 +16,9 @@ write_fn_step! { struct Inspect1 { expr_inst: ExprInst, } +impl Responder for Inspect1 {} impl Atomic for Inspect1 { - atomic_defaults!(); + fn as_any(&self) -> &dyn std::any::Any { self } fn run(&self, ctx: Context) -> crate::foreign::AtomicResult { println!("{}", self.expr_inst); Ok(AtomicReturn { diff --git a/src/systems/stl/num.rs b/src/systems/stl/num.rs index a60f4f9..1900391 100644 --- a/src/systems/stl/num.rs +++ b/src/systems/stl/num.rs @@ -4,6 +4,7 @@ use ordered_float::NotNan; use super::ArithmeticError; use crate::foreign::ExternError; +use crate::interpreted::TryFromExprInst; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::{Literal, Primitive}; use crate::systems::cast_exprinst::with_lit; @@ -40,14 +41,12 @@ impl Numeric { } } } - -impl TryFrom<&ExprInst> for Numeric { - type Error = Rc; - fn try_from(value: &ExprInst) -> Result { - with_lit(value, |l| match l { +impl TryFromExprInst for Numeric { + fn from_exi(exi: &ExprInst) -> Result> { + with_lit(exi, |l| match l { Literal::Uint(i) => Ok(Numeric::Uint(*i)), Literal::Num(n) => Ok(Numeric::Num(*n)), - _ => AssertionError::fail(value.clone(), "an integer or number")?, + _ => AssertionError::fail(exi.clone(), "an integer or number")?, }) } } diff --git a/src/systems/stl/state.rs b/src/systems/stl/state.rs index 3e4be27..bffe8ba 100644 --- a/src/systems/stl/state.rs +++ b/src/systems/stl/state.rs @@ -2,15 +2,17 @@ use std::cell::RefCell; use std::rc::Rc; use crate::foreign::cps_box::{const_cps, init_cps, CPSBox}; -use crate::foreign::Atomic; +use crate::foreign::{Atomic, InertAtomic}; use crate::interpreted::ExprInst; use crate::interpreter::HandlerTable; use crate::systems::codegen::call; -use crate::{atomic_inert, define_fn, ConstTree, Interner}; +use crate::{define_fn, ConstTree, Interner}; #[derive(Debug, Clone)] pub struct State(Rc>); -atomic_inert!(State, typestr = "a state"); +impl InertAtomic for State { + fn type_str() -> &'static str { "a stateful container" } +} #[derive(Debug, Clone)] struct NewStateCmd; @@ -21,8 +23,8 @@ struct SetStateCmd(State); #[derive(Debug, Clone)] struct GetStateCmd(State); -define_fn! { SetState = |x| Ok(init_cps(2, SetStateCmd(x.try_into()?))) } -define_fn! { GetState = |x| Ok(init_cps(2, GetStateCmd(x.try_into()?))) } +define_fn! { SetState = |x| Ok(init_cps(2, SetStateCmd(x.downcast()?))) } +define_fn! { GetState = |x| Ok(init_cps(2, GetStateCmd(x.downcast()?))) } fn new_state_handler(cmd: &CPSBox) -> Result { let (_, default, handler) = cmd.unpack2(); diff --git a/src/utils/boxed_iter.rs b/src/utils/boxed_iter.rs index 4d6cce2..d538944 100644 --- a/src/utils/boxed_iter.rs +++ b/src/utils/boxed_iter.rs @@ -6,13 +6,9 @@ use std::iter; /// initialized form multiple iterators of incompatible types pub type BoxedIter<'a, T> = Box + 'a>; /// creates a [BoxedIter] of a single element -pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> { - Box::new(iter::once(t)) -} +pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> { Box::new(iter::once(t)) } /// creates an empty [BoxedIter] -pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { - Box::new(iter::empty()) -} +pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { Box::new(iter::empty()) } /// Chain various iterators into a [BoxedIter] macro_rules! box_chain { diff --git a/src/utils/ddispatch.rs b/src/utils/ddispatch.rs new file mode 100644 index 0000000..dc60e89 --- /dev/null +++ b/src/utils/ddispatch.rs @@ -0,0 +1,29 @@ +//! A variant of [std::any::Provider] + +use std::any::Any; + +/// A request for a value of an unknown type +pub struct Request<'a>(&'a mut dyn Any); +impl<'a> Request<'a> { + pub fn can_serve(&self) -> bool { self.0.is::>() } + + pub fn serve(&mut self, value: T) { self.serve_with(|| value) } + + pub fn serve_with(&mut self, provider: impl FnOnce() -> T) { + if let Some(slot) = self.0.downcast_mut() { + *slot = provider(); + } + } +} + +/// Trait for objects that can respond to type-erased commands. This trait is +/// a dependency of `Atomic` but the implementation can be left empty. +pub trait Responder { + fn respond(&self, _request: Request) {} +} + +pub fn request(responder: &(impl Responder + ?Sized)) -> Option { + let mut slot = None; + responder.respond(Request(&mut slot)); + slot +} diff --git a/src/utils/delete_cell.rs b/src/utils/delete_cell.rs index ce834be..18fc2e9 100644 --- a/src/utils/delete_cell.rs +++ b/src/utils/delete_cell.rs @@ -3,13 +3,9 @@ use std::rc::Rc; pub struct DeleteCell(pub Rc>>); impl DeleteCell { - pub fn new(t: T) -> Self { - Self(Rc::new(RefCell::new(Some(t)))) - } + pub fn new(t: T) -> Self { Self(Rc::new(RefCell::new(Some(t)))) } - pub fn take(&self) -> Option { - self.0.borrow_mut().take() - } + pub fn take(&self) -> Option { self.0.borrow_mut().take() } pub fn clone_out(&self) -> Option where @@ -19,7 +15,5 @@ impl DeleteCell { } } impl Clone for DeleteCell { - fn clone(&self) -> Self { - Self(self.0.clone()) - } + fn clone(&self) -> Self { Self(self.0.clone()) } } diff --git a/src/utils/id_map.rs b/src/utils/id_map.rs index 358aad9..f28dc54 100644 --- a/src/utils/id_map.rs +++ b/src/utils/id_map.rs @@ -14,14 +14,10 @@ pub struct IdMap { } impl IdMap { /// Create a new empty set - pub fn new() -> Self { - Self { next_id: 0, data: HashMap::new() } - } + pub fn new() -> Self { Self { next_id: 0, data: HashMap::new() } } /// Obtain a reference to the underlying map for iteration - pub fn map(&self) -> &HashMap { - &self.data - } + pub fn map(&self) -> &HashMap { &self.data } /// Insert an element with a new ID and return the ID pub fn insert(&mut self, t: T) -> u64 { @@ -33,9 +29,7 @@ impl IdMap { } /// Obtain a reference to the element with the given ID - pub fn get(&self, id: u64) -> Option<&T> { - self.data.get(&id) - } + pub fn get(&self, id: u64) -> Option<&T> { self.data.get(&id) } /// Obtain a mutable reference to the element with the given ID pub fn get_mut(&mut self, id: u64) -> Option<&mut T> { @@ -44,15 +38,11 @@ impl IdMap { /// Remove the element with the given ID from the set. The ID will not be /// reused. - pub fn remove(&mut self, id: u64) -> Option { - self.data.remove(&id) - } + pub fn remove(&mut self, id: u64) -> Option { self.data.remove(&id) } } impl Default for IdMap { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } #[cfg(test)] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 97701cf..17fa168 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,9 +1,11 @@ mod cache; +pub mod ddispatch; mod delete_cell; -pub mod poller; pub mod get_or; +mod id_map; mod iter_find; pub mod never; +pub mod poller; pub mod pure_push; pub mod rc_tools; mod replace_first; @@ -14,7 +16,6 @@ pub mod substack; mod take_with_output; pub mod thread_pool; mod unwrap_or; -mod id_map; pub use cache::Cache; pub use replace_first::replace_first; @@ -22,9 +23,9 @@ pub use side::Side; pub use split_max_prefix::split_max_prefix; pub(crate) use unwrap_or::unwrap_or; pub mod boxed_iter; -pub use delete_cell::DeleteCell; pub use boxed_iter::BoxedIter; +pub use delete_cell::DeleteCell; +pub use id_map::IdMap; pub use iter_find::iter_find; pub use string_from_charset::string_from_charset; pub use take_with_output::take_with_output; -pub use id_map::IdMap; diff --git a/src/utils/never.rs b/src/utils/never.rs index 8e3eae3..1fa6e9c 100644 --- a/src/utils/never.rs +++ b/src/utils/never.rs @@ -7,9 +7,7 @@ pub enum Never {} pub type Always = Result; /// Wrap value in a result with an impossible failure mode -pub fn always(t: T) -> Result { - Ok(t) -} +pub fn always(t: T) -> Result { Ok(t) } /// Take success value out of a result with an impossible failure mode pub fn unwrap_always(result: Result) -> T { diff --git a/src/utils/poller.rs b/src/utils/poller.rs index 50e7da7..a8c713a 100644 --- a/src/utils/poller.rs +++ b/src/utils/poller.rs @@ -37,9 +37,7 @@ impl Clone for Timer { } impl Eq for Timer {} impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.expires.eq(&other.expires) - } + fn eq(&self, other: &Self) -> bool { self.expires.eq(&other.expires) } } impl PartialOrd for Timer { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/src/utils/side.rs b/src/utils/side.rs index 3dcfbba..a9cb345 100644 --- a/src/utils/side.rs +++ b/src/utils/side.rs @@ -31,9 +31,7 @@ impl Side { } } /// Shorthand for opposite - pub fn inv(&self) -> Self { - self.opposite() - } + pub fn inv(&self) -> Self { self.opposite() } /// take N elements from this end of a slice pub fn slice<'a, T>(&self, size: usize, slice: &'a [T]) -> &'a [T] { match self { @@ -85,9 +83,7 @@ impl Side { impl Not for Side { type Output = Side; - fn not(self) -> Self::Output { - self.opposite() - } + fn not(self) -> Self::Output { self.opposite() } } #[cfg(test)] diff --git a/src/utils/substack.rs b/src/utils/substack.rs index 28c0d41..2e21399 100644 --- a/src/utils/substack.rs +++ b/src/utils/substack.rs @@ -32,13 +32,9 @@ impl<'a, T> Substack<'a, T> { } } /// Construct an iterator over the listlike, very fast O(1) - pub fn iter(&self) -> SubstackIterator { - SubstackIterator { curr: self } - } + pub fn iter(&self) -> SubstackIterator { SubstackIterator { curr: self } } /// Add the item to this substack - pub fn push(&'a self, item: T) -> Self { - Self::Frame(self.new_frame(item)) - } + pub fn push(&'a self, item: T) -> Self { Self::Frame(self.new_frame(item)) } /// Create a new frame on top of this substack pub fn new_frame(&'a self, item: T) -> Stackframe<'a, T> { Stackframe { item, prev: self, len: self.opt().map_or(1, |s| s.len + 1) } @@ -61,9 +57,7 @@ impl<'a, T> Substack<'a, T> { } } /// is this the bottom of the stack - pub fn is_empty(&self) -> bool { - self.len() == 0 - } + pub fn is_empty(&self) -> bool { self.len() == 0 } } impl<'a, T: Debug> Debug for Substack<'a, T> { @@ -104,9 +98,7 @@ impl<'a, T> SubstackIterator<'a, T> { impl<'a, T> Copy for SubstackIterator<'a, T> {} impl<'a, T> Clone for SubstackIterator<'a, T> { - fn clone(&self) -> Self { - Self { curr: self.curr } - } + fn clone(&self) -> Self { Self { curr: self.curr } } } impl<'a, T> Iterator for SubstackIterator<'a, T> { diff --git a/src/utils/thread_pool.rs b/src/utils/thread_pool.rs index 93db5ff..7ccd32e 100644 --- a/src/utils/thread_pool.rs +++ b/src/utils/thread_pool.rs @@ -16,9 +16,7 @@ pub trait Task: Send + 'static { } impl Task for F { - fn run(self) { - self() - } + fn run(self) { self() } } /// An async unit of work that produces some result, see [Task]. This can be @@ -49,9 +47,7 @@ pub trait Query: Send + 'static { impl R + Send + 'static, R: Send + 'static> Query for F { type Result = R; - fn run(self) -> Self::Result { - self() - } + fn run(self) -> Self::Result { self() } } /// A reporter that calls a statically known function with the result of a @@ -61,9 +57,7 @@ pub struct QueryTask { callback: F, } impl Task for QueryTask { - fn run(self) { - (self.callback)(self.query.run()) - } + fn run(self) { (self.callback)(self.query.run()) } } enum Message { @@ -91,9 +85,7 @@ struct ThreadPoolData { /// /// struct MyTask(&'static str); /// impl Task for MyTask { -/// fn run(self) { -/// println!("{}", self.0) -/// } +/// fn run(self) { println!("{}", self.0) } /// } /// /// let pool = ThreadPool::new(); @@ -167,9 +159,7 @@ impl ThreadPool { } impl Default for ThreadPool { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } impl Drop for ThreadPool {