diff --git a/Cargo.lock b/Cargo.lock index 1bed437..37a7ff4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -298,8 +298,8 @@ dependencies = [ "hashbrown 0.13.2", "itertools", "ordered-float", - "smallvec", "thiserror", + "trait-set", ] [[package]] @@ -352,12 +352,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - [[package]] name = "stacker" version = "0.1.15" @@ -377,6 +371,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.13" @@ -405,7 +410,18 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", +] + +[[package]] +name = "trait-set" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4bba97b..fcd4d6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,15 @@ authors = [ "Lawrence Bethlenfalvy " ] +[lib] +name = "orchid" +path = "src/lib.rs" + +[[bin]] +name = "orcx" +path = "src/bin/main.rs" +doc = false + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -14,6 +23,6 @@ chumsky = "0.9.2" hashbrown = "0.13.2" ordered-float = "3.0" itertools = "0.10" -smallvec = { version = "1.10.0", features = ['const_generics'] } dyn-clone = "1.0.11" clap = { version = "4.2.4", features = ["derive"] } +trait-set = "0.3.0" diff --git a/src/run_dir.rs b/src/bin/main.rs similarity index 75% rename from src/run_dir.rs rename to src/bin/main.rs index f243fab..695059a 100644 --- a/src/run_dir.rs +++ b/src/bin/main.rs @@ -1,20 +1,45 @@ -use std::path::Path; +use std::borrow::Borrow; +use std::fs::File; +use std::path::{Path, PathBuf}; use std::rc::Rc; +use clap::Parser; use hashbrown::HashMap; use itertools::Itertools; - -use crate::external::handle; -use crate::interner::{InternedDisplay, Interner, Sym}; -use crate::interpreter::Return; -use crate::pipeline::file_loader::{mk_cache, Loaded}; -use crate::pipeline::{ +use orchid::interner::{InternedDisplay, Interner, Sym}; +use orchid::pipeline::file_loader::{mk_cache, Loaded}; +use orchid::pipeline::{ collect_consts, collect_rules, from_const_tree, parse_layer, ProjectTree, }; -use crate::representations::sourcefile::{FileEntry, Import}; -use crate::representations::{ast_to_postmacro, postmacro_to_interpreted}; -use crate::rule::Repo; -use crate::{external, interpreter, xloop}; +use orchid::rule::Repo; +use orchid::sourcefile::{FileEntry, Import}; +use orchid::{ast_to_interpreted, interpreter, stl}; + +/// Orchid interpreter +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Folder containing main.orc + #[arg(short, long, default_value = ".")] + pub project: String, +} +impl Args { + pub fn chk_proj(&self) -> Result<(), String> { + let mut path = PathBuf::from(&self.project); + path.push(PathBuf::from("main.orc")); + if File::open(&path).is_ok() { + Ok(()) + } else { + Err(format!("{} not found", path.display())) + } + } +} + +fn main() { + let args = Args::parse(); + args.chk_proj().unwrap_or_else(|e| panic!("{e}")); + run_dir(PathBuf::try_from(args.project).unwrap().borrow()); +} static PRELUDE_TXT: &str = r#" import std::( @@ -63,7 +88,7 @@ fn entrypoint(i: &Interner) -> Sym { fn load_environment(i: &Interner) -> ProjectTree { let env = from_const_tree( - HashMap::from([(i.i("std"), external::std::std(i))]), + HashMap::from([(i.i("std"), stl::mk_stl(i))]), &[i.i("std")], i, ); @@ -90,7 +115,6 @@ fn load_dir(i: &Interner, dir: &Path) -> ProjectTree { .expect("Failed to load source code") } -#[allow(unused)] pub fn run_dir(dir: &Path) { let i = Interner::new(); let project = load_dir(&i, dir); @@ -113,7 +137,11 @@ pub fn run_dir(dir: &Path) { let displayname = i.extern_vec(*name).join("::"); let macro_timeout = 100; println!("Executing macros in {displayname}...",); - let unmatched = xloop!(let mut idx = 0; idx < macro_timeout; idx += 1; { + let mut idx = 0; + let unmatched = loop { + if idx == macro_timeout { + panic!("Macro execution in {displayname} didn't halt") + } match repo.step(&tree) { None => break tree, Some(phase) => { @@ -121,10 +149,10 @@ pub fn run_dir(dir: &Path) { tree = phase; }, } - }; panic!("Macro execution in {displayname} didn't halt")); - let pmtree = ast_to_postmacro::expr(&unmatched) + idx += 1; + }; + let runtree = ast_to_interpreted(&unmatched) .unwrap_or_else(|e| panic!("Postmacro conversion error: {e}")); - let runtree = postmacro_to_interpreted::expr(&pmtree); exec_table.insert(*name, runtree); } println!("macro execution complete"); @@ -139,9 +167,9 @@ pub fn run_dir(dir: &Path) { .join(", ") ) }); - let io_handler = handle; + let io_handler = orchid::stl::handleIO; let ret = interpreter::run_handler(entrypoint.clone(), io_handler, ctx); - let Return { gas, state, inert } = + let interpreter::Return { gas, state, inert } = ret.unwrap_or_else(|e| panic!("Runtime error: {}", e)); if inert { println!("Settled at {}", state.expr().clause.bundle(&i)); diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index e961ed3..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::fmt::Display; -use std::io::{stdin, stdout, BufRead, Write}; - -pub fn prompt( - prompt: &str, - default: T, - mut try_cast: impl FnMut(String) -> Result, -) -> T { - loop { - print!("{prompt} ({default}): "); - stdout().lock().flush().unwrap(); - let mut input = String::with_capacity(100); - stdin().lock().read_line(&mut input).unwrap(); - if input.is_empty() { - return default; - } - match try_cast(input) { - Ok(t) => return t, - Err(e) => println!("Error: {e}"), - } - } -} diff --git a/src/foreign.rs b/src/foreign.rs index 2661a98..7eb5aaf 100644 --- a/src/foreign.rs +++ b/src/foreign.rs @@ -1,3 +1,7 @@ +//! Interaction with foreign code +//! +//! Structures and traits used in the exposure of external functions and values +//! to Orchid code use std::any::Any; use std::error::Error; use std::fmt::{Debug, Display}; @@ -11,6 +15,8 @@ pub use crate::representations::interpreted::Clause; use crate::representations::interpreted::ExprInst; use crate::representations::Primitive; +/// Information returned by [Atomic::run]. This mirrors +/// [crate::interpreter::Return] but with a clause instead of an Expr. pub struct AtomicReturn { pub clause: Clause, pub gas: Option, @@ -23,12 +29,14 @@ impl AtomicReturn { } } -// Aliases for concise macros +/// A type-erased error in external code pub type RcError = Rc; +/// Returned by [Atomic::run] pub type AtomicResult = Result; +/// Returned by [ExternFn::apply] pub type XfnResult = Result; -pub type RcExpr = ExprInst; +/// Errors produced by external code pub trait ExternError: Display { fn into_extern(self) -> Rc where @@ -50,11 +58,15 @@ impl Error for dyn ExternError {} /// the executor. Since Orchid lacks basic numerical operations, /// these are also external functions. pub trait ExternFn: DynClone { + /// Display name of the function fn name(&self) -> &str; + /// Combine the function with an argument to produce a new clause fn apply(&self, arg: ExprInst, ctx: Context) -> XfnResult; - fn hash(&self, state: &mut dyn std::hash::Hasher) { - state.write_str(self.name()) + /// Hash the name to get a somewhat unique hash. + fn hash(&self, mut state: &mut dyn std::hash::Hasher) { + self.name().hash(&mut state) } + /// Wrap this function in a clause to be placed in an [AtomicResult]. fn to_xfn_cls(self) -> Clause where Self: Sized + 'static, @@ -80,12 +92,20 @@ impl Debug for dyn ExternFn { } } +/// Functionality the interpreter needs to handle a value pub trait Atomic: Any + Debug + DynClone where Self: 'static, { + /// 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. fn as_any(&self) -> &dyn Any; + /// Attempt to normalize this value. If it wraps a value, this should report + /// inert. If it wraps a computation, it should execute one logical step of + /// the computation and return a structure representing the ntext. fn run(&self, ctx: Context) -> AtomicResult; + /// Wrap the atom in a clause to be placed in an [AtomicResult]. fn to_atom_cls(self) -> Clause where Self: Sized, @@ -96,12 +116,11 @@ where /// Represents a black box unit of code with its own normalization steps. /// Typically [ExternFn] will produce an [Atom] when applied to a [Clause], -/// this [Atom] will then forward `run_*` calls to the argument until it -/// yields [InternalError::NonReducible] at which point the [Atom] will -/// validate and process the argument, returning a different [Atom] -/// intended for processing by external code, a new [ExternFn] to capture -/// an additional argument, or an Orchid expression -/// to pass control back to the interpreter. +/// this [Atom] will then forward `run` calls to the argument until it becomes +/// inert at which point the [Atom] will validate and process the argument, +/// returning a different [Atom] intended for processing by external code, a new +/// [ExternFn] to capture an additional argument, or an Orchid expression +/// to pass control back to the interpreter.btop pub struct Atom(pub Box); impl Atom { pub fn new(data: T) -> Self { @@ -110,8 +129,8 @@ impl Atom { pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic } - pub fn try_cast(&self) -> Result<&T, ()> { - self.data().as_any().downcast_ref().ok_or(()) + pub fn try_cast(&self) -> Option<&T> { + self.data().as_any().downcast_ref() } pub fn is(&self) -> bool { self.data().as_any().is::() diff --git a/src/foreign_macros/atomic_defaults.rs b/src/foreign_macros/atomic_defaults.rs index 1b2dd8a..1e42283 100644 --- a/src/foreign_macros/atomic_defaults.rs +++ b/src/foreign_macros/atomic_defaults.rs @@ -2,10 +2,10 @@ use crate::foreign::Atomic; /// A macro that generates the straightforward, syntactically invariant part of -/// implementing [Atomic]. Implemented fns are [Atomic::as_any], -/// [Atomic::definitely_eq] and [Atomic::hash]. +/// implementing [Atomic]. /// -/// It depends on [Eq] and [Hash] +/// Currently implements +/// - [Atomic::as_any] #[macro_export] macro_rules! atomic_defaults { () => { diff --git a/src/foreign_macros/atomic_impl.rs b/src/foreign_macros/atomic_impl.rs index d075ff9..6ac785f 100644 --- a/src/foreign_macros/atomic_impl.rs +++ b/src/foreign_macros/atomic_impl.rs @@ -14,10 +14,10 @@ use crate::representations::Primitive; /// A macro that generates implementations of [Atomic] to simplify the /// development of external bindings for Orchid. /// -/// The macro depends on implementations of [AsRef] and [From<(&Self, -/// Clause)>] for extracting the clause to be processed and then reconstructing -/// the [Atomic]. Naturally, supertraits of [Atomic] are also dependencies. -/// These are [Any], [Debug] and [DynClone]. +/// The macro depends on implementations of [`AsRef`] and +/// [`From<(&Self, Clause)>`] for extracting the clause to be processed and then +/// reconstructing the [Atomic]. Naturally, supertraits of [Atomic] are also +/// dependencies. These are [Any], [Debug] and [DynClone]. /// /// The simplest form just requires the typename to be specified. This /// additionally depends on an implementation of [ExternFn] because after the @@ -37,8 +37,9 @@ use crate::representations::Primitive; /// ``` /// // excerpt from the exact implementation of Multiply /// atomic_impl!(Multiply0, |Self(a, cls): &Self| { -/// let b: Numeric = cls.clone().try_into().map_err(AssertionError::into_extern)?; -/// Ok(*a * b).into()) +/// let b: Numeric = +/// cls.clone().try_into().map_err(AssertionError::into_extern)?; +/// Ok(*a * b).into() /// }) /// ``` #[macro_export] @@ -58,14 +59,18 @@ macro_rules! atomic_impl { ctx: $crate::interpreter::Context, ) -> $crate::foreign::AtomicResult { // extract the expression - let expr = - >::as_ref(self).clone(); + let expr = >::as_ref(self) + .clone(); // run the expression let ret = $crate::interpreter::run(expr, ctx.clone())?; let $crate::interpreter::Return { gas, state, inert } = ret; // rebuild the atomic - let next_self = - >::from((self, state)); + let next_self = >::from((self, state)); // branch off or wrap up let clause = if inert { let closure = $next_phase; diff --git a/src/foreign_macros/atomic_redirect.rs b/src/foreign_macros/atomic_redirect.rs index 32e3b05..353a9a3 100644 --- a/src/foreign_macros/atomic_redirect.rs +++ b/src/foreign_macros/atomic_redirect.rs @@ -1,7 +1,7 @@ #[allow(unused)] -use super::atomic_impl; +use crate::atomic_impl; -/// Implement the traits required by [atomic_impl] to redirect run_* functions +/// Implement the traits required by [atomic_impl] to redirect run calls /// to a field with a particular name. #[macro_export] macro_rules! atomic_redirect { @@ -18,14 +18,18 @@ macro_rules! atomic_redirect { } }; ($typ:ident, $field:ident) => { - impl AsRef<$crate::foreign::RcExpr> for $typ { - fn as_ref(&self) -> &$crate::foreign::RcExpr { + impl AsRef<$crate::representations::interpreted::ExprInst> for $typ { + fn as_ref(&self) -> &$crate::representations::interpreted::ExprInst { &self.$field } } - impl From<(&Self, $crate::foreign::RcExpr)> for $typ { + impl From<(&Self, $crate::representations::interpreted::ExprInst)> + for $typ + { #[allow(clippy::needless_update)] - fn from((old, $field): (&Self, $crate::foreign::RcExpr)) -> Self { + fn from( + (old, $field): (&Self, $crate::representations::interpreted::ExprInst), + ) -> Self { Self { $field, ..old.clone() } } } diff --git a/src/foreign_macros/externfn_impl.rs b/src/foreign_macros/externfn_impl.rs index 36e2f92..ca5f094 100644 --- a/src/foreign_macros/externfn_impl.rs +++ b/src/foreign_macros/externfn_impl.rs @@ -28,7 +28,7 @@ macro_rules! externfn_impl { } fn apply( &self, - arg: $crate::foreign::RcExpr, + arg: $crate::representations::interpreted::ExprInst, _ctx: $crate::interpreter::Context, ) -> $crate::foreign::XfnResult { let closure = $next_atomic; diff --git a/src/interner/display.rs b/src/interner/display.rs index 54a9dcf..9a9f826 100644 --- a/src/interner/display.rs +++ b/src/interner/display.rs @@ -20,13 +20,7 @@ pub trait InternedDisplay { /// Converts the value to a string to be displayed fn to_string_i(&self, i: &Interner) -> String { - // Copied from - let mut buf = String::new(); - let mut formatter = Formatter::new(&mut buf); - // Bypass format_args!() to avoid write_str with zero-length strs - Self::fmt_i(self, &mut formatter, i) - .expect("a Display implementation returned an error unexpectedly"); - buf + self.bundle(i).to_string() } fn bundle<'a>(&'a self, interner: &'a Interner) -> DisplayBundle<'a, Self> { @@ -47,12 +41,14 @@ where } } +/// A reference to an [InternedDisplay] type and an [Interner] tied together +/// to implement [Display] pub struct DisplayBundle<'a, T: InternedDisplay + ?Sized> { interner: &'a Interner, data: &'a T, } -impl<'a, T: InternedDisplay> Display for DisplayBundle<'a, T> { +impl<'a, T: InternedDisplay + ?Sized> Display for DisplayBundle<'a, T> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.data.fmt_i(f, self.interner) } diff --git a/src/interner/mod.rs b/src/interner/mod.rs index 54a5e36..a81a612 100644 --- a/src/interner/mod.rs +++ b/src/interner/mod.rs @@ -1,3 +1,7 @@ +//! A type-agnostic interner +//! +//! Can be used to deduplicate various structures for fast equality comparisons. +//! The parser uses it to intern strings. mod display; mod monotype; mod multitype; @@ -10,9 +14,9 @@ pub use token::Tok; /// A symbol, nsname, nname or namespaced name is a sequence of namespaces /// and an identifier. The [Vec] can never be empty. -/// +/// /// Throughout different stages of processing, these names can be -/// +/// /// - local names to be prefixed with the current module /// - imported names starting with a segment /// - ending a single import or diff --git a/src/interner/monotype.rs b/src/interner/monotype.rs index bc22135..39eaa76 100644 --- a/src/interner/monotype.rs +++ b/src/interner/monotype.rs @@ -8,7 +8,8 @@ use hashbrown::HashMap; use super::token::Tok; /// An interner for any type that implements [Borrow]. This is inspired by -/// Lasso but much simpler, in part because not much can be known about the type. +/// Lasso but much simpler, in part because not much can be known about the +/// type. pub struct TypedInterner { tokens: RefCell>>, values: RefCell>, @@ -45,7 +46,7 @@ impl TypedInterner { *kv.1 } - /// Resolve a token, obtaining an object + /// Resolve a token, obtaining a reference to the held object. /// It is illegal to use a token obtained from one interner with /// another. pub fn r(&self, t: Tok) -> &T { @@ -74,6 +75,12 @@ impl TypedInterner { } } +impl Default for TypedInterner { + fn default() -> Self { + Self::new() + } +} + impl Drop for TypedInterner { fn drop(&mut self) { // make sure all values leaked by us are dropped diff --git a/src/interner/multitype.rs b/src/interner/multitype.rs index 432ce5d..a65fcbd 100644 --- a/src/interner/multitype.rs +++ b/src/interner/multitype.rs @@ -16,10 +16,12 @@ pub struct Interner { interners: RefCell>>, } impl Interner { + /// Create a new interner pub fn new() -> Self { Self { interners: RefCell::new(HashMap::new()) } } + /// Intern something pub fn i(&self, q: &Q) -> Tok where Q::Owned: 'static + Eq + Hash + Clone + Borrow, @@ -29,6 +31,7 @@ impl Interner { interner.i(q) } + /// Resolve a token to a reference pub fn r(&self, t: Tok) -> &T { let mut interners = self.interners.borrow_mut(); let interner = get_interner(&mut interners); @@ -36,7 +39,7 @@ impl Interner { unsafe { (interner.r(t) as *const T).as_ref().unwrap() } } - /// Fully resolve + /// Fully resolve an interned list of interned things /// TODO: make this generic over containers pub fn extern_vec( &self, @@ -49,6 +52,7 @@ impl Interner { v.iter().map(|t| t_int.r(*t)).cloned().collect() } + /// Fully resolve a list of interned things. pub fn extern_all( &self, s: &[Tok], diff --git a/src/interpreter/apply.rs b/src/interpreter/apply.rs index de42931..9f238f6 100644 --- a/src/interpreter/apply.rs +++ b/src/interpreter/apply.rs @@ -53,12 +53,16 @@ fn map_at( .map(|p| p.0) } +/// TODO replace when `!` gets stabilized +#[derive(Debug)] +enum Never {} + /// Replace the [Clause::LambdaArg] placeholders at the ends of the [PathSet] /// with the value in the body. Note that a path may point to multiple /// placeholders. fn substitute(paths: &PathSet, value: Clause, body: ExprInst) -> ExprInst { let PathSet { steps, next } = paths; - map_at(steps, body, &mut |checkpoint| -> Result { + map_at(steps, body, &mut |checkpoint| -> Result { match (checkpoint, next) { (Clause::Lambda { .. }, _) => unreachable!("Handled by map_at"), (Clause::Apply { f, x }, Some((left, right))) => Ok(Clause::Apply { @@ -72,7 +76,7 @@ fn substitute(paths: &PathSet, value: Clause, body: ExprInst) -> ExprInst { panic!("Substitution path leads into something other than Apply"), } }) - .into_ok() + .unwrap() } /// Apply a function-like expression to a parameter. diff --git a/src/interpreter/context.rs b/src/interpreter/context.rs index 396b423..8439182 100644 --- a/src/interpreter/context.rs +++ b/src/interpreter/context.rs @@ -8,8 +8,8 @@ use crate::representations::interpreted::ExprInst; pub struct Context<'a> { /// Table used to resolve constants pub symbols: &'a HashMap, - /// The interner used for strings internally, so external functions can deduce - /// referenced constant names on the fly + /// The interner used for strings internally, so external functions can + /// deduce referenced constant names on the fly pub interner: &'a Interner, /// The number of reduction steps the interpreter can take before returning pub gas: Option, diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index ebbeb8b..8058d59 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -7,7 +7,9 @@ use crate::representations::interpreted::ExprInst; /// Problems in the process of execution #[derive(Clone, Debug)] pub enum RuntimeError { + /// A Rust function encountered an error Extern(Rc), + /// Primitive applied as function NonFunctionApplication(ExprInst), } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3de9f22..92dad7c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,3 +1,4 @@ +//! functions to interact with Orchid code mod apply; mod context; mod error; @@ -5,4 +6,4 @@ mod run; pub use context::{Context, Return}; pub use error::RuntimeError; -pub use run::{run, run_handler, Handler, HandlerParm, HandlerRes}; +pub use run::{run, run_handler, Handler, HandlerErr, HandlerParm, HandlerRes}; diff --git a/src/interpreter/run.rs b/src/interpreter/run.rs index 2f8e21f..ad27513 100644 --- a/src/interpreter/run.rs +++ b/src/interpreter/run.rs @@ -78,23 +78,24 @@ impl From for HandlerErr { } } -/// Various possible outcomes of a [Handler] execution. +/// Various possible outcomes of a [Handler] execution. Ok returns control to +/// the interpreter. The meaning of Err is decided by the value in it. pub type HandlerRes = Result; /// A trait for things that may be able to handle commands returned by Orchid -/// code. This trait is implemented for [FnMut(HandlerParm) -> HandlerRes] and -/// [(Handler, Handler)], users are not supposed to implement it themselves. +/// code. This trait is implemented for `FnMut(HandlerParm) -> HandlerRes` and +/// `(Handler, Handler)`, users are not supposed to implement it themselves. /// /// A handler receives an arbitrary inert [Atomic] and uses [Atomic::as_any] -/// then [std::any::Any::downcast_ref] to obtain a known type. If this fails, it -/// returns the box in [HandlerErr::NA] which will be passed to the next +/// then downcast_ref of [std::any::Any] to obtain a known type. If this fails, +/// it returns the box in [HandlerErr::NA] which will be passed to the next /// handler. pub trait Handler { /// Attempt to resolve a command with this handler. fn resolve(&mut self, data: HandlerParm) -> HandlerRes; /// If this handler isn't applicable, try the other one. - fn or(self, t: T) -> impl Handler + fn or(self, t: T) -> (Self, T) where Self: Sized, { diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8ddd1a3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,16 @@ +pub mod foreign; +mod foreign_macros; +pub mod interner; +pub mod interpreter; +mod parse; +pub mod pipeline; +mod representations; +pub mod rule; +pub mod stl; +mod utils; + +pub use representations::ast_to_interpreted::ast_to_interpreted; +pub use representations::{ + ast, interpreted, sourcefile, tree, Literal, Location, PathSet, Primitive, +}; +pub use utils::{Side, Substack}; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 5e37f65..0000000 --- a/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![feature(generators, generator_trait)] -#![feature(never_type)] -#![feature(unwrap_infallible)] -#![feature(arc_unwrap_or_clone)] -#![feature(hasher_prefixfree_extras)] -#![feature(closure_lifetime_binder)] -#![feature(generic_arg_infer)] -#![feature(array_chunks)] -#![feature(fmt_internals)] -#![feature(map_try_insert)] -#![feature(slice_group_by)] -#![feature(trait_alias)] -#![feature(return_position_impl_trait_in_trait)] - -mod cli; -mod external; -pub(crate) mod foreign; -mod foreign_macros; -mod interner; -mod interpreter; -mod parse; -mod pipeline; -mod representations; -mod rule; -mod run_dir; -mod utils; -use std::fs::File; -use std::path::PathBuf; - -use clap::Parser; -use cli::prompt; -pub use representations::ast; -use run_dir::run_dir; - -/// Orchid interpreter -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Args { - /// Folder containing main.orc - #[arg(short, long)] - pub project: Option, -} - -fn main() { - let args = Args::parse(); - let path = args.project.unwrap_or_else(|| { - prompt("Enter a project root", ".".to_string(), |p| { - let mut path: PathBuf = p.trim().into(); - path.push("main.orc"); - match File::open(&path) { - Ok(_) => Ok(p), - Err(e) => Err(format!("{}: {e}", path.display())), - } - }) - }); - run_dir(&PathBuf::try_from(path).unwrap()); -} diff --git a/src/parse/decls.rs b/src/parse/decls.rs index 7a44d36..d89ea33 100644 --- a/src/parse/decls.rs +++ b/src/parse/decls.rs @@ -3,10 +3,13 @@ use std::hash::Hash; use chumsky::prelude::Simple; use chumsky::recursive::Recursive; use chumsky::{BoxedParser, Parser}; +use trait_set::trait_set; -/// Wrapper around [Parser] with [Simple] error to avoid repeating the input -pub trait SimpleParser = - Parser>; +trait_set! { + /// Wrapper around [Parser] with [Simple] error to avoid repeating the input + pub trait SimpleParser = + Parser>; +} /// Boxed version of [SimpleParser] pub type BoxedSimpleParser<'a, I, O> = BoxedParser<'a, I, O, Simple>; /// [Recursive] specialization of [SimpleParser] to parameterize calls to diff --git a/src/parse/enum_filter.rs b/src/parse/enum_filter.rs index 5e63b03..af015f6 100644 --- a/src/parse/enum_filter.rs +++ b/src/parse/enum_filter.rs @@ -8,7 +8,6 @@ /// // Foo::Bar(T) into Quz::Bar(T) /// // Foo::Baz(U) into Quz::Baz(U) /// ``` -#[macro_export] macro_rules! enum_filter { ($p:path | $m:tt) => { { @@ -48,3 +47,5 @@ macro_rules! enum_filter { enum_filter!($p | {concat!("Expected ", stringify!($p))}) }; } + +pub(crate) use enum_filter; diff --git a/src/parse/expression.rs b/src/parse/expression.rs index 02ce1e0..4020f2f 100644 --- a/src/parse/expression.rs +++ b/src/parse/expression.rs @@ -6,8 +6,8 @@ use chumsky::{self, Parser}; use super::context::Context; use super::decls::SimpleParser; +use super::enum_filter::enum_filter; use super::lexer::{filter_map_lex, Entry, Lexeme}; -use crate::enum_filter; use crate::interner::Sym; use crate::representations::ast::{Clause, Expr}; use crate::representations::location::Location; diff --git a/src/parse/import.rs b/src/parse/import.rs index 4308d74..3c5daff 100644 --- a/src/parse/import.rs +++ b/src/parse/import.rs @@ -4,18 +4,17 @@ use itertools::Itertools; use super::context::Context; use super::decls::{SimpleParser, SimpleRecursive}; +use super::enum_filter::enum_filter; use super::lexer::{filter_map_lex, Lexeme}; use super::Entry; use crate::interner::Tok; use crate::representations::sourcefile::Import; use crate::utils::iter::{ - box_flatten, box_once, into_boxed_iter, BoxedIterIter, + box_chain, box_flatten, box_once, into_boxed_iter, BoxedIterIter, }; -use crate::{box_chain, enum_filter}; -/// initialize a BoxedIter> with a single element. +/// initialize an iterator of iterator with a single element. fn init_table(name: Tok) -> BoxedIterIter<'static, Tok> { - // I'm not at all confident that this is a good approach. box_once(box_once(name)) } @@ -24,7 +23,7 @@ fn init_table(name: Tok) -> BoxedIterIter<'static, Tok> { /// semi and the delimiters are plain parentheses. Namespaces should /// preferably contain crossplatform filename-legal characters but the /// symbols are explicitly allowed to go wild. -/// There's a blacklist in [name] +/// There's a blacklist in [crate::parse::name::NOT_NAME_CHAR] pub fn import_parser<'a>( ctx: impl Context + 'a, ) -> impl SimpleParser> + 'a { diff --git a/src/parse/name.rs b/src/parse/name.rs index de7cf77..ffab6c2 100644 --- a/src/parse/name.rs +++ b/src/parse/name.rs @@ -24,7 +24,7 @@ fn op_parser<'a>( /// Characters that cannot be parsed as part of an operator /// /// The initial operator list overrides this. -static NOT_NAME_CHAR: &[char] = &[ +pub static NOT_NAME_CHAR: &[char] = &[ ':', // used for namespacing and type annotations '\\', '@', // parametric expression starters '"', '\'', // parsed as primitives and therefore would never match diff --git a/src/parse/sourcefile.rs b/src/parse/sourcefile.rs index 868c398..4384d6c 100644 --- a/src/parse/sourcefile.rs +++ b/src/parse/sourcefile.rs @@ -7,12 +7,12 @@ use itertools::Itertools; use super::context::Context; use super::decls::{SimpleParser, SimpleRecursive}; +use super::enum_filter::enum_filter; use super::expression::xpr_parser; use super::import::import_parser; use super::lexer::{filter_map_lex, Lexeme}; use super::Entry; use crate::ast::{Clause, Constant, Expr, Rule}; -use crate::enum_filter; use crate::representations::location::Location; use crate::representations::sourcefile::{FileEntry, Member, Namespace}; @@ -24,10 +24,10 @@ fn rule_parser<'a>( .at_least(1) .then(filter_map_lex(enum_filter!(Lexeme::Rule))) .then(xpr_parser(ctx).repeated().at_least(1)) - .map(|((s, (prio, _)), t)| Rule { - source: Rc::new(s), + .map(|((p, (prio, _)), t)| Rule { + pattern: Rc::new(p), prio, - target: Rc::new(t), + template: Rc::new(t), }) .labelled("Rule") } diff --git a/src/pipeline/error/mod.rs b/src/pipeline/error/mod.rs index 6975c90..3fceafc 100644 --- a/src/pipeline/error/mod.rs +++ b/src/pipeline/error/mod.rs @@ -1,3 +1,4 @@ +//! Various errors the pipeline can produce mod module_not_found; mod not_exported; mod parse_error_with_path; diff --git a/src/pipeline/error/not_exported.rs b/src/pipeline/error/not_exported.rs index 176ed59..edd264f 100644 --- a/src/pipeline/error/not_exported.rs +++ b/src/pipeline/error/not_exported.rs @@ -4,6 +4,7 @@ use super::{ErrorPosition, ProjectError}; use crate::representations::location::Location; use crate::utils::BoxedIter; +/// An import refers to a symbol which exists but is not exported. #[derive(Debug)] pub struct NotExported { pub file: Vec, diff --git a/src/pipeline/error/project_error.rs b/src/pipeline/error/project_error.rs index 8f09de0..f0ab621 100644 --- a/src/pipeline/error/project_error.rs +++ b/src/pipeline/error/project_error.rs @@ -29,7 +29,7 @@ pub trait ProjectError: Debug { } /// Code positions relevant to this error fn positions(&self) -> BoxedIter; - /// Convert the error into an [Rc] to be able to + /// Convert the error into an `Rc` to be able to /// handle various errors together fn rc(self) -> Rc where diff --git a/src/pipeline/error/visibility_mismatch.rs b/src/pipeline/error/visibility_mismatch.rs index 9cc93e0..3d95013 100644 --- a/src/pipeline/error/visibility_mismatch.rs +++ b/src/pipeline/error/visibility_mismatch.rs @@ -5,6 +5,7 @@ use crate::representations::location::Location; use crate::utils::iter::box_once; use crate::utils::BoxedIter; +/// Multiple occurences of the same namespace with different visibility #[derive(Debug)] pub struct VisibilityMismatch { pub namespace: Vec, diff --git a/src/pipeline/file_loader.rs b/src/pipeline/file_loader.rs index 51ee1ac..60b0131 100644 --- a/src/pipeline/file_loader.rs +++ b/src/pipeline/file_loader.rs @@ -1,3 +1,4 @@ +//! File system implementation of the source loader callback use std::path::{Path, PathBuf}; use std::rc::Rc; use std::{fs, io}; @@ -9,6 +10,7 @@ use crate::pipeline::error::{ use crate::utils::iter::box_once; use crate::utils::{BoxedIter, Cache}; +/// All the data available about a failed source load call #[derive(Debug)] pub struct FileLoadingError { file: io::Error, @@ -40,10 +42,9 @@ impl Loaded { } } +/// Returned by any source loading callback pub type IOResult = Result>; -pub type FileCache<'a> = Cache<'a, Sym, IOResult>; - /// Load a file from a path expressed in Rust strings, but relative to /// a root expressed as an OS Path. pub fn load_file(root: &Path, path: &[impl AsRef]) -> IOResult { @@ -81,7 +82,7 @@ pub fn load_file(root: &Path, path: &[impl AsRef]) -> IOResult { } /// Generates a cached file loader for a directory -pub fn mk_cache(root: PathBuf, i: &Interner) -> FileCache { +pub fn mk_cache(root: PathBuf, i: &Interner) -> Cache { Cache::new(move |token: Sym, _this| -> IOResult { let path = i.r(token).iter().map(|t| i.r(*t).as_str()).collect::>(); load_file(&root, &path) diff --git a/src/pipeline/import_resolution/apply_aliases.rs b/src/pipeline/import_resolution/apply_aliases.rs index de65d63..7535f54 100644 --- a/src/pipeline/import_resolution/apply_aliases.rs +++ b/src/pipeline/import_resolution/apply_aliases.rs @@ -90,17 +90,17 @@ fn apply_aliases_rec( .rules .iter() .map(|rule| { - let Rule { source, prio, target } = rule; + let Rule { pattern, prio, template } = rule; Rule { prio: *prio, - source: Rc::new( - source + pattern: Rc::new( + pattern .iter() .map(|expr| process_expr(expr, alias_map, injected_as, i)) .collect::>(), ), - target: Rc::new( - target + template: Rc::new( + template .iter() .map(|expr| process_expr(expr, alias_map, injected_as, i)) .collect::>(), diff --git a/src/pipeline/import_resolution/decls.rs b/src/pipeline/import_resolution/decls.rs index 00d21be..27fc3c2 100644 --- a/src/pipeline/import_resolution/decls.rs +++ b/src/pipeline/import_resolution/decls.rs @@ -1,3 +1,7 @@ +use trait_set::trait_set; + use crate::interner::{Sym, Tok}; -pub trait InjectedAsFn = Fn(&[Tok]) -> Option; +trait_set! { + pub trait InjectedAsFn = Fn(&[Tok]) -> Option; +} diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index e792773..6c68d5b 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -1,3 +1,4 @@ +//! Loading Orchid modules from source pub mod error; pub mod file_loader; mod import_abs_path; diff --git a/src/pipeline/project_tree/build_tree.rs b/src/pipeline/project_tree/build_tree.rs index 6abf964..ff58ce6 100644 --- a/src/pipeline/project_tree/build_tree.rs +++ b/src/pipeline/project_tree/build_tree.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use hashbrown::HashMap; +use itertools::Itertools; use super::collect_ops::InjectedOperatorsFn; use super::parse_file::parse_file; @@ -87,7 +88,7 @@ fn source_to_module( Member::Namespace(ns) => box_once(mk_ent(ns.name)), Member::Rule(rule) => { let mut names = Vec::new(); - for e in rule.source.iter() { + for e in rule.pattern.iter() { e.visit_names(Substack::Bottom, &mut |n| { if let Some([name]) = i.r(n).strip_prefix(&path_v[..]) { names.push((*name, n)) @@ -177,7 +178,7 @@ fn source_to_module( fn files_to_module( path: Substack>, - files: &[ParsedSource], + files: Vec, i: &Interner, ) -> Rc> { let lvl = path.len(); @@ -192,11 +193,13 @@ fn files_to_module( ); } let items = files - .group_by(|a, b| a.path[lvl] == b.path[lvl]) - .map(|files| { - let namespace = files[0].path[lvl]; + .into_iter() + .group_by(|f| f.path[lvl]) + .into_iter() + .map(|(namespace, files)| { let subpath = path.push(namespace); - let module = files_to_module(subpath, files, i); + let files_v = files.collect::>(); + let module = files_to_module(subpath, files_v, i); let member = ModMember::Sub(module); (namespace, ModEntry { exported: true, member }) }) @@ -206,11 +209,6 @@ fn files_to_module( .copied() .map(|name| (name, i.i(&pushed(&path_v, name)))) .collect(); - // println!( - // "Constructing module {} with items ({})", - // i.extern_all(&path_v[..]).join("::"), - // exports.keys().map(|t| i.r(*t)).join(", ") - // ); Rc::new(Module { items, imports: vec![], @@ -250,5 +248,5 @@ pub fn build_tree( path: path.clone(), }) .collect::>(); - Ok(ProjectTree(files_to_module(Substack::Bottom, &files, i))) + Ok(ProjectTree(files_to_module(Substack::Bottom, files, i))) } diff --git a/src/pipeline/project_tree/collect_ops/exported_ops.rs b/src/pipeline/project_tree/collect_ops/exported_ops.rs index fc6ddec..88319fe 100644 --- a/src/pipeline/project_tree/collect_ops/exported_ops.rs +++ b/src/pipeline/project_tree/collect_ops/exported_ops.rs @@ -3,6 +3,7 @@ use std::rc::Rc; use hashbrown::HashSet; use itertools::Itertools; +use trait_set::trait_set; use crate::interner::{Interner, Sym, Tok}; use crate::pipeline::error::{ModuleNotFound, ProjectError}; @@ -14,7 +15,9 @@ use crate::utils::Cache; pub type OpsResult = Result>>, Rc>; pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>; -pub trait InjectedOperatorsFn = Fn(Sym) -> Option>>>; +trait_set! { + pub trait InjectedOperatorsFn = Fn(Sym) -> Option>>>; +} fn coprefix( l: impl Iterator, diff --git a/src/pipeline/project_tree/const_tree.rs b/src/pipeline/project_tree/const_tree.rs index 667ffdc..0e6300e 100644 --- a/src/pipeline/project_tree/const_tree.rs +++ b/src/pipeline/project_tree/const_tree.rs @@ -12,23 +12,30 @@ use crate::representations::tree::{ModEntry, ModMember, Module}; use crate::representations::Primitive; use crate::utils::{pushed, Substack}; +/// A lightweight module tree that can be built declaratively by hand to +/// describe libraries of external functions in Rust. It implements [Add] for +/// added convenience pub enum ConstTree { Const(Expr), Tree(HashMap, ConstTree>), } impl ConstTree { + /// Describe a [Primitive] pub fn primitive(primitive: Primitive) -> Self { Self::Const(Expr { location: Location::Unknown, value: Clause::P(primitive), }) } + /// Describe an [ExternFn] pub fn xfn(xfn: impl ExternFn + 'static) -> Self { Self::primitive(Primitive::ExternFn(Box::new(xfn))) } + /// Describe an [Atomic] pub fn atom(atom: impl Atomic + 'static) -> Self { Self::primitive(Primitive::Atom(Atom(Box::new(atom)))) } + /// Describe a module pub fn tree(arr: impl IntoIterator, Self)>) -> Self { Self::Tree(arr.into_iter().collect()) } @@ -89,6 +96,8 @@ fn from_const_tree_rec( } } +/// Convert a map of [ConstTree] into a [ProjectTree] that can be used with the +/// layered parsing system pub fn from_const_tree( consts: HashMap, ConstTree>, file: &[Tok], diff --git a/src/pipeline/project_tree/prefix.rs b/src/pipeline/project_tree/prefix.rs index d292dfe..4192dfb 100644 --- a/src/pipeline/project_tree/prefix.rs +++ b/src/pipeline/project_tree/prefix.rs @@ -35,11 +35,11 @@ fn member_rec( }), Member::Rule(rule) => Member::Rule(Rule { prio: rule.prio, - source: Rc::new( - rule.source.iter().map(|e| e.prefix(prefix, i, &except)).collect(), + pattern: Rc::new( + rule.pattern.iter().map(|e| e.prefix(prefix, i, &except)).collect(), ), - target: Rc::new( - rule.target.iter().map(|e| e.prefix(prefix, i, &except)).collect(), + template: Rc::new( + rule.template.iter().map(|e| e.prefix(prefix, i, &except)).collect(), ), }), } diff --git a/src/pipeline/project_tree/tree.rs b/src/pipeline/project_tree/tree.rs index 0384030..f1d40dc 100644 --- a/src/pipeline/project_tree/tree.rs +++ b/src/pipeline/project_tree/tree.rs @@ -8,11 +8,13 @@ use crate::interner::{Interner, Sym, Tok}; use crate::representations::tree::{ModMember, Module}; use crate::utils::Substack; +/// Additional data about a loaded module beyond the list of constants and +/// submodules #[derive(Clone, Debug, Default)] pub struct ProjectExt { /// Pairs each foreign token to the module it was imported from pub imports_from: HashMap, Sym>, - /// Pairs each exported token to its original full name. + /// Pairs each exported token to its original full name pub exports: HashMap, Sym>, /// All rules defined in this module, exported or not pub rules: Vec, @@ -35,7 +37,10 @@ impl Add for ProjectExt { } } +/// A node in the tree describing the project pub type ProjectModule = Module; + +/// Module corresponding to the root of a project pub struct ProjectTree(pub Rc); fn collect_rules_rec(bag: &mut Vec, module: &ProjectModule) { @@ -47,6 +52,8 @@ fn collect_rules_rec(bag: &mut Vec, module: &ProjectModule) { } } +/// Collect the complete list of rules to be used by the rule repository from +/// the [ProjectTree] pub fn collect_rules(project: &ProjectTree) -> Vec { let mut rules = Vec::new(); collect_rules_rec(&mut rules, project.0.as_ref()); @@ -72,6 +79,7 @@ fn collect_consts_rec( } } +/// Extract the symbol table from a [ProjectTree] pub fn collect_consts( project: &ProjectTree, i: &Interner, diff --git a/src/representations/ast.rs b/src/representations/ast.rs index 10dee87..7eb0dcd 100644 --- a/src/representations/ast.rs +++ b/src/representations/ast.rs @@ -1,3 +1,10 @@ +//! Datastructures representing syntax as written +//! +//! These structures are produced by the parser, processed by the macro +//! executor, and then converted to other usable formats. This module is public +//! in order to allow users to define injected libraries programmatically, +//! although going through the parser is usually preferable. + use std::hash::Hash; use std::rc::Rc; @@ -9,7 +16,7 @@ use super::primitive::Primitive; use crate::interner::{InternedDisplay, Interner, Sym, Tok}; use crate::utils::Substack; -/// An S-expression with a type +/// A [Clause] with associated metadata #[derive(Clone, Debug, PartialEq)] pub struct Expr { pub value: Clause, @@ -17,10 +24,12 @@ pub struct Expr { } impl Expr { + /// Obtain the contained clause pub fn into_clause(self) -> Clause { self.value } + /// Call the function on every name in this expression pub fn visit_names(&self, binds: Substack, cb: &mut impl FnMut(Sym)) { let Expr { value, .. } = self; value.visit_names(binds, cb); @@ -61,12 +70,21 @@ impl InternedDisplay for Expr { } } +/// Various types of placeholders #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum PHClass { - Vec { nonzero: bool, prio: u64 }, + /// Matches multiple tokens, lambdas or parenthesized groups + Vec { + /// If true, must match at least one clause + nonzero: bool, + /// Greediness in the allocation of tokens + prio: u64, + }, + /// Matches exactly one token, lambda or parenthesized group Scalar, } +/// Properties of a placeholder that matches unknown tokens in macros #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Placeholder { pub name: Tok, @@ -95,6 +113,7 @@ impl InternedDisplay for Placeholder { /// An S-expression as read from a source file #[derive(Debug, PartialEq, Clone)] pub enum Clause { + /// A primitive P(Primitive), /// A c-style name or an operator, eg. `+`, `i`, `foo::bar` Name(Sym), @@ -257,7 +276,7 @@ fn fmt_expr_seq<'a>( Ok(()) } -pub fn fmt_name( +pub(crate) fn fmt_name( name: Sym, f: &mut std::fmt::Formatter, i: &Interner, @@ -302,15 +321,17 @@ impl InternedDisplay for Clause { /// A substitution rule as read from the source #[derive(Debug, Clone, PartialEq)] pub struct Rule { - pub source: Rc>, + pub pattern: Rc>, pub prio: NotNan, - pub target: Rc>, + pub template: Rc>, } impl Rule { + /// Return a list of all names that don't contain a namespace separator `::`. + /// These are exported when the rule is exported pub fn collect_single_names(&self, i: &Interner) -> Vec> { let mut names = Vec::new(); - for e in self.source.iter() { + for e in self.pattern.iter() { e.visit_names(Substack::Bottom, &mut |tok| { let ns_name = i.r(tok); let (name, excess) = @@ -324,6 +345,7 @@ impl Rule { names } + /// Namespace all tokens in the rule pub fn prefix( &self, prefix: Sym, @@ -332,11 +354,11 @@ impl Rule { ) -> Self { Self { prio: self.prio, - source: Rc::new( - self.source.iter().map(|e| e.prefix(prefix, i, except)).collect(), + pattern: Rc::new( + self.pattern.iter().map(|e| e.prefix(prefix, i, except)).collect(), ), - target: Rc::new( - self.target.iter().map(|e| e.prefix(prefix, i, except)).collect(), + template: Rc::new( + self.template.iter().map(|e| e.prefix(prefix, i, except)).collect(), ), } } @@ -348,12 +370,12 @@ impl InternedDisplay for Rule { f: &mut std::fmt::Formatter<'_>, i: &Interner, ) -> std::fmt::Result { - for e in self.source.iter() { + for e in self.pattern.iter() { e.fmt_i(f, i)?; write!(f, " ")?; } write!(f, "={}=>", self.prio)?; - for e in self.target.iter() { + for e in self.template.iter() { write!(f, " ")?; e.fmt_i(f, i)?; } diff --git a/src/representations/ast_to_interpreted.rs b/src/representations/ast_to_interpreted.rs new file mode 100644 index 0000000..6e208a8 --- /dev/null +++ b/src/representations/ast_to_interpreted.rs @@ -0,0 +1,13 @@ +use super::{ast, ast_to_postmacro, interpreted, postmacro_to_interpreted}; + +#[allow(unused)] +pub type AstError = ast_to_postmacro::Error; + +/// Attempt to convert the AST processed by macros into an executable format +#[allow(unused)] +pub fn ast_to_interpreted( + ast: &ast::Expr, +) -> Result { + let pmtree = ast_to_postmacro::expr(ast)?; + Ok(postmacro_to_interpreted::expr(&pmtree)) +} diff --git a/src/representations/ast_to_postmacro.rs b/src/representations/ast_to_postmacro.rs index 4fe8ccd..0b6f66b 100644 --- a/src/representations/ast_to_postmacro.rs +++ b/src/representations/ast_to_postmacro.rs @@ -63,7 +63,7 @@ impl<'a> Context<'a> { } } -/// Recursive state of [exprv] +/// Process an expression sequence fn exprv_rec<'a>( v: &'a [ast::Expr], ctx: Context<'a>, @@ -78,7 +78,7 @@ fn exprv_rec<'a>( Ok(postmacro::Expr { value, location: Location::Unknown }) } -/// Recursive state of [expr] +/// Process an expression fn expr_rec<'a>( ast::Expr { value, location }: &'a ast::Expr, ctx: Context<'a>, @@ -95,11 +95,7 @@ fn expr_rec<'a>( } } -// (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail -// (@T. \t:Pair T T. t \left.\right. left) @number -- this is the correct -// phrasing - -/// Recursive state of [clause] +/// Process a clause fn clause_rec<'a>( cls: &'a ast::Clause, ctx: Context<'a>, diff --git a/src/representations/interpreted.rs b/src/representations/interpreted.rs index 798da76..e854717 100644 --- a/src/representations/interpreted.rs +++ b/src/representations/interpreted.rs @@ -1,3 +1,7 @@ +//! The interpreter's changing internal representation of the code at runtime +//! +//! This code may be generated to minimize the number of states external +//! functions have to define use std::cell::RefCell; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; @@ -12,6 +16,7 @@ use crate::utils::sym2string; // TODO: implement Debug, Eq and Hash with cycle detection +/// An expression with metadata pub struct Expr { pub clause: Clause, pub location: Location, @@ -43,8 +48,12 @@ impl InternedDisplay for Expr { } } +/// [ExprInst::with_literal] produces this marker unit to indicate that the +/// expression is not a literal +pub struct NotALiteral; + /// A wrapper around expressions to handle their multiple occurences in -/// the tree +/// the tree together #[derive(Clone)] pub struct ExprInst(pub Rc>); impl ExprInst { @@ -88,15 +97,17 @@ impl ExprInst { predicate(&self.expr().clause) } + /// Call the predicate on the value inside this expression if it is a + /// primitive pub fn with_literal( &self, predicate: impl FnOnce(&Literal) -> T, - ) -> Result { + ) -> Result { let expr = self.expr(); if let Clause::P(Primitive::Literal(l)) = &expr.clause { Ok(predicate(l)) } else { - Err(()) + Err(NotALiteral) } } } @@ -123,12 +134,18 @@ impl InternedDisplay for ExprInst { } } +/// Distinct types of expressions recognized by the interpreter #[derive(Debug, Clone)] pub enum Clause { + /// An unintrospectable unit P(Primitive), + /// A function application Apply { f: ExprInst, x: ExprInst }, + /// A name to be looked up in the interpreter's symbol table Constant(Sym), + /// A function Lambda { args: Option, body: ExprInst }, + /// A placeholder within a function that will be replaced upon application LambdaArg, } impl Clause { diff --git a/src/representations/literal.rs b/src/representations/literal.rs index e630438..73fdc39 100644 --- a/src/representations/literal.rs +++ b/src/representations/literal.rs @@ -2,8 +2,8 @@ use std::fmt::Debug; use ordered_float::NotNan; -/// An exact value, read from the AST and unmodified in shape until -/// compilation +/// Exact values read from the AST which have a shared meaning recognized by all +/// external functions #[derive(Clone, PartialEq, Eq, Hash)] pub enum Literal { Num(NotNan), diff --git a/src/representations/location.rs b/src/representations/location.rs index d9e7b25..e5b0a5c 100644 --- a/src/representations/location.rs +++ b/src/representations/location.rs @@ -4,6 +4,8 @@ use std::rc::Rc; use itertools::Itertools; +/// A location in a file, identifies a sequence of suspect characters for any +/// error. Meaningful within the context of a project. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Location { Unknown, diff --git a/src/representations/mod.rs b/src/representations/mod.rs index 852dce7..f17e123 100644 --- a/src/representations/mod.rs +++ b/src/representations/mod.rs @@ -1,14 +1,17 @@ pub mod ast; +pub mod ast_to_interpreted; pub mod ast_to_postmacro; pub mod interpreted; pub mod literal; pub mod location; pub mod path_set; pub mod postmacro; +pub mod postmacro_to_interpreted; pub mod primitive; pub mod sourcefile; pub mod tree; + +pub use literal::Literal; +pub use location::Location; pub use path_set::PathSet; pub use primitive::Primitive; -pub mod postmacro_to_interpreted; -pub use literal::Literal; diff --git a/src/representations/path_set.rs b/src/representations/path_set.rs index afab1bc..ea5a278 100644 --- a/src/representations/path_set.rs +++ b/src/representations/path_set.rs @@ -4,7 +4,8 @@ use std::rc::Rc; use crate::utils::Side; -/// A set of paths into a Lambda expression +/// A branching path selecting some placeholders (but at least one) in a Lambda +/// expression #[derive(Clone, PartialEq, Eq, Hash)] pub struct PathSet { /// The definite steps @@ -40,7 +41,8 @@ impl Add for PathSet { type Output = Self; fn add(self, rhs: Side) -> Self::Output { let PathSet { steps, next } = self; - let mut new_steps = Rc::unwrap_or_clone(steps); + let mut new_steps = + Rc::try_unwrap(steps).unwrap_or_else(|rc| rc.as_ref().clone()); new_steps.insert(0, rhs); Self { steps: Rc::new(new_steps), next } } diff --git a/src/representations/primitive.rs b/src/representations/primitive.rs index 59033a3..e25db35 100644 --- a/src/representations/primitive.rs +++ b/src/representations/primitive.rs @@ -3,12 +3,13 @@ use std::fmt::Debug; use super::Literal; use crate::foreign::{Atom, ExternFn}; +/// A value the interpreter can't inspect pub enum Primitive { /// A literal value, eg. `1`, `"hello"` Literal(Literal), - /// An opaque function, eg. an effectful function employing CPS. + /// An opaque function, eg. an effectful function employing CPS ExternFn(Box), - /// An opaque non-callable value, eg. a file handle. + /// An opaque non-callable value, eg. a file handle Atom(Atom), } diff --git a/src/representations/sourcefile.rs b/src/representations/sourcefile.rs index e7ce41e..c84be6b 100644 --- a/src/representations/sourcefile.rs +++ b/src/representations/sourcefile.rs @@ -1,10 +1,12 @@ +//! Building blocks of a source file use itertools::{Either, Itertools}; use crate::ast::{Constant, Rule}; use crate::interner::{Interner, Sym, Tok}; -use crate::unwrap_or; -use crate::utils::BoxedIter; +use crate::utils::{unwrap_or, BoxedIter}; +/// An import pointing at another module, either specifying the symbol to be +/// imported or importing all available symbols with a globstar (*) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Import { pub path: Sym, @@ -112,6 +114,11 @@ pub fn normalize_namespaces( Ok(rest) } +/// Produced by [absolute_path] if there are more `super` segments in the +/// import than the length of the current absolute path +#[derive(Debug, Clone)] +pub struct TooManySupers; + /// Turn a relative (import) path into an absolute path. /// If the import path is empty, the return value is also empty. /// @@ -123,12 +130,12 @@ pub fn absolute_path( abs_location: &[Tok], rel_path: &[Tok], i: &Interner, -) -> Result>, ()> { +) -> Result>, TooManySupers> { let (head, tail) = unwrap_or!(rel_path.split_first(); return Ok(vec![]) ); if *head == i.i("super") { - let (_, new_abs) = abs_location.split_last().ok_or(())?; + let (_, new_abs) = abs_location.split_last().ok_or(TooManySupers)?; if tail.is_empty() { Ok(new_abs.to_vec()) } else { diff --git a/src/representations/tree.rs b/src/representations/tree.rs index 0ebe260..7fba86b 100644 --- a/src/representations/tree.rs +++ b/src/representations/tree.rs @@ -1,3 +1,6 @@ +//! Generic module tree structure +//! +//! Used by various stages of the pipeline with different parameters use std::ops::Add; use std::rc::Rc; @@ -27,20 +30,29 @@ pub struct Module { pub extra: TExt, } +/// Possible causes why the path could not be walked #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum WalkErrorKind { + /// `require_exported` was set to `true` and a module wasn't exported Private, + /// A module was not found Missing, } +/// Error produced by [Module::walk] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct WalkError { + /// The 0-based index of the offending segment pub pos: usize, + /// The cause of the error pub kind: WalkErrorKind, } +/// The path taken to reach a given module pub type ModPath<'a> = Substack<'a, Tok>; + impl Module { + /// Return the module at the end of the given path. pub fn walk( self: &Rc, path: &[Tok], @@ -78,6 +90,8 @@ impl Module { Ok(()) } + /// Call the provided function on every import in the tree. Can be + /// short-circuited by returning Err pub fn visit_all_imports( &self, callback: &mut impl FnMut(ModPath, &Self, &Import) -> Result<(), E>, diff --git a/src/rule/matcher.rs b/src/rule/matcher.rs index e29f9f8..ec45a22 100644 --- a/src/rule/matcher.rs +++ b/src/rule/matcher.rs @@ -3,7 +3,11 @@ use std::rc::Rc; use super::state::State; use crate::ast::Expr; +/// Cacheable optimized structures for matching patterns on slices. This is +/// injected to allow experimentation in the matcher implementation. pub trait Matcher { + /// Build matcher for a pattern fn new(pattern: Rc>) -> Self; + /// Apply matcher to a token sequence fn apply<'a>(&self, source: &'a [Expr]) -> Option>; } diff --git a/src/rule/matcher_second/any_match.rs b/src/rule/matcher_vectree/any_match.rs similarity index 100% rename from src/rule/matcher_second/any_match.rs rename to src/rule/matcher_vectree/any_match.rs diff --git a/src/rule/matcher_second/build.rs b/src/rule/matcher_vectree/build.rs similarity index 94% rename from src/rule/matcher_second/build.rs rename to src/rule/matcher_vectree/build.rs index b886987..da1dfc4 100644 --- a/src/rule/matcher_second/build.rs +++ b/src/rule/matcher_vectree/build.rs @@ -25,9 +25,7 @@ fn scal_cnt<'a>(iter: impl Iterator) -> usize { iter.take_while(|expr| vec_attrs(expr).is_none()).count() } -/// Recursively convert this pattern into a matcher that can be -/// efficiently applied to slices. -pub fn mk_matcher(pattern: &[Expr]) -> AnyMatcher { +pub fn mk_any(pattern: &[Expr]) -> AnyMatcher { let left_split = scal_cnt(pattern.iter()); if pattern.len() <= left_split { return AnyMatcher::Scalar(mk_scalv(pattern)); @@ -113,9 +111,9 @@ fn mk_scalar(pattern: &Expr) -> ScalMatcher { ); ScalMatcher::Placeh(*name) }, - Clause::S(c, body) => ScalMatcher::S(*c, Box::new(mk_matcher(body))), + Clause::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body))), Clause::Lambda(arg, body) => - ScalMatcher::Lambda(Box::new(mk_scalar(arg)), Box::new(mk_matcher(body))), + ScalMatcher::Lambda(Box::new(mk_scalar(arg)), Box::new(mk_any(body))), } } @@ -123,7 +121,7 @@ fn mk_scalar(pattern: &Expr) -> ScalMatcher { mod test { use std::rc::Rc; - use super::mk_matcher; + use super::mk_any; use crate::ast::{Clause, PHClass, Placeholder}; use crate::interner::{InternedDisplay, Interner}; @@ -160,7 +158,7 @@ mod test { }) .into_expr(), ]; - let matcher = mk_matcher(&pattern); + let matcher = mk_any(&pattern); println!("{}", matcher.bundle(&i)); } } diff --git a/src/rule/matcher_second/mod.rs b/src/rule/matcher_vectree/mod.rs similarity index 89% rename from src/rule/matcher_second/mod.rs rename to src/rule/matcher_vectree/mod.rs index 1fe701c..4fce5b3 100644 --- a/src/rule/matcher_second/mod.rs +++ b/src/rule/matcher_vectree/mod.rs @@ -16,5 +16,5 @@ mod scal_match; mod shared; mod vec_match; -pub use build::mk_matcher; -pub use shared::AnyMatcher; +// pub use build::mk_matcher; +pub use shared::VectreeMatcher; diff --git a/src/rule/matcher_second/scal_match.rs b/src/rule/matcher_vectree/scal_match.rs similarity index 100% rename from src/rule/matcher_second/scal_match.rs rename to src/rule/matcher_vectree/scal_match.rs diff --git a/src/rule/matcher_second/shared.rs b/src/rule/matcher_vectree/shared.rs similarity index 85% rename from src/rule/matcher_second/shared.rs rename to src/rule/matcher_vectree/shared.rs index 2d971de..ac1d339 100644 --- a/src/rule/matcher_second/shared.rs +++ b/src/rule/matcher_vectree/shared.rs @@ -2,14 +2,13 @@ use std::fmt::Write; use std::rc::Rc; use super::any_match::any_match; -use super::build::mk_matcher; +use super::build::mk_any; use crate::ast::Expr; use crate::interner::{InternedDisplay, Interner, Sym, Tok}; use crate::representations::Primitive; use crate::rule::matcher::Matcher; use crate::rule::state::State; -use crate::unwrap_or; -use crate::utils::{sym2string, Side}; +use crate::utils::{sym2string, unwrap_or, Side}; pub enum ScalMatcher { P(Primitive), @@ -56,7 +55,7 @@ pub enum AnyMatcher { } impl Matcher for AnyMatcher { fn new(pattern: Rc>) -> Self { - mk_matcher(&pattern) + mk_any(&pattern) } fn apply<'a>(&self, source: &'a [Expr]) -> Option> { @@ -177,3 +176,27 @@ impl InternedDisplay for AnyMatcher { } } } + +// ################ External ################ + +/// A [Matcher] implementation that builds a priority-order tree of the +/// 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 apply<'a>(&self, source: &'a [Expr]) -> Option> { + self.0.apply(source) + } +} +impl InternedDisplay for VectreeMatcher { + fn fmt_i( + &self, + f: &mut std::fmt::Formatter<'_>, + i: &Interner, + ) -> std::fmt::Result { + self.0.fmt_i(f, i) + } +} diff --git a/src/rule/matcher_second/vec_match.rs b/src/rule/matcher_vectree/vec_match.rs similarity index 99% rename from src/rule/matcher_second/vec_match.rs rename to src/rule/matcher_vectree/vec_match.rs index bfaca95..0b155f9 100644 --- a/src/rule/matcher_second/vec_match.rs +++ b/src/rule/matcher_vectree/vec_match.rs @@ -6,7 +6,7 @@ use super::scal_match::scalv_match; use super::shared::VecMatcher; use crate::ast::Expr; use crate::rule::state::{State, StateEntry}; -use crate::unwrap_or; +use crate::utils::unwrap_or; pub fn vec_match<'a>( matcher: &VecMatcher, diff --git a/src/rule/mod.rs b/src/rule/mod.rs index 1619d33..cf5c6fb 100644 --- a/src/rule/mod.rs +++ b/src/rule/mod.rs @@ -1,5 +1,6 @@ +//! Substitution rule processing mod matcher; -mod matcher_second; +mod matcher_vectree; mod prepare_rule; mod repository; mod rule_error; @@ -7,6 +8,7 @@ mod state; mod update_first_seq; mod vec_attrs; -pub use matcher_second::AnyMatcher; +pub use matcher::Matcher; +pub use matcher_vectree::VectreeMatcher; pub use repository::{Repo, Repository}; pub use rule_error::RuleError; diff --git a/src/rule/prepare_rule.rs b/src/rule/prepare_rule.rs index 38dda41..e466f57 100644 --- a/src/rule/prepare_rule.rs +++ b/src/rule/prepare_rule.rs @@ -22,24 +22,24 @@ fn pad(mut rule: Rule, i: &Interner) -> Rule { location: Location::Unknown, value: Clause::Placeh(Placeholder { name: i.i("::suffix"), class }), }]; - let rule_head = rule.source.first().expect("Src can never be empty!"); + let rule_head = rule.pattern.first().expect("Src can never be empty!"); let prefix_explicit = vec_attrs(rule_head).is_some(); - let rule_tail = rule.source.last().expect("Unreachable branch!"); + let rule_tail = rule.pattern.last().expect("Unreachable branch!"); let suffix_explicit = vec_attrs(rule_tail).is_some(); let prefix_v = if prefix_explicit { empty } else { prefix }; let suffix_v = if suffix_explicit { empty } else { suffix }; - rule.source = Rc::new( + rule.pattern = Rc::new( prefix_v .iter() - .chain(rule.source.iter()) + .chain(rule.pattern.iter()) .chain(suffix_v.iter()) .cloned() .collect(), ); - rule.target = Rc::new( + rule.template = Rc::new( prefix_v .iter() - .chain(rule.target.iter()) + .chain(rule.template.iter()) .chain(suffix_v.iter()) .cloned() .collect(), @@ -118,8 +118,8 @@ fn check_rec_exprv( pub fn prepare_rule(rule: Rule, i: &Interner) -> Result { // Dimension check let mut types = HashMap::new(); - check_rec_exprv(&rule.source, &mut types, false)?; - check_rec_exprv(&rule.target, &mut types, true)?; + check_rec_exprv(&rule.pattern, &mut types, false)?; + check_rec_exprv(&rule.template, &mut types, true)?; // Padding Ok(pad(rule, i)) } diff --git a/src/rule/repository.rs b/src/rule/repository.rs index 9cf679f..f3c3d95 100644 --- a/src/rule/repository.rs +++ b/src/rule/repository.rs @@ -8,7 +8,7 @@ use ordered_float::NotNan; use super::matcher::Matcher; use super::prepare_rule::prepare_rule; use super::state::apply_exprv; -use super::{update_first_seq, AnyMatcher, RuleError}; +use super::{update_first_seq, RuleError, VectreeMatcher}; use crate::ast::{Expr, Rule}; use crate::interner::{InternedDisplay, Interner, Sym}; use crate::utils::Substack; @@ -16,7 +16,7 @@ use crate::utils::Substack; #[derive(Debug)] pub struct CachedRule { matcher: M, - source: Rc>, + pattern: Rc>, template: Rc>, } @@ -26,7 +26,7 @@ impl InternedDisplay for CachedRule { f: &mut std::fmt::Formatter<'_>, i: &Interner, ) -> std::fmt::Result { - for item in self.source.iter() { + for item in self.pattern.iter() { item.fmt_i(f, i)?; f.write_char(' ')?; } @@ -35,8 +35,13 @@ impl InternedDisplay for CachedRule { } } -/// Manages a priority queue of substitution rules and allows to apply -/// them +/// Substitution rule scheduler +/// +/// Manages a priority queue of rules and offers functions to apply them. The +/// rules are stored in an optimized structure but the repository is generic +/// over the implementation of this optimized form. +/// +/// If you don't know what to put in the generic parameter, use [Repo] pub struct Repository { cache: Vec<(CachedRule, HashSet, NotNan)>, } @@ -52,14 +57,17 @@ impl Repository { let prio = r.prio; let rule = prepare_rule(r.clone(), i).map_err(|e| (r, e))?; let mut glossary = HashSet::new(); - for e in rule.source.iter() { + for e in rule.pattern.iter() { e.visit_names(Substack::Bottom, &mut |op| { glossary.insert(op); }) } - let matcher = M::new(rule.source.clone()); - let prep = - CachedRule { matcher, source: rule.source, template: rule.target }; + let matcher = M::new(rule.pattern.clone()); + let prep = CachedRule { + matcher, + pattern: rule.pattern, + template: rule.template, + }; Ok((prep, glossary, prio)) }) .collect::, _>>()?; @@ -163,4 +171,5 @@ impl InternedDisplay for Repository { } } -pub type Repo = Repository; +/// Repository with the default matcher implementation +pub type Repo = Repository; diff --git a/src/rule/rule_error.rs b/src/rule/rule_error.rs index 701c7ee..f0a0f36 100644 --- a/src/rule/rule_error.rs +++ b/src/rule/rule_error.rs @@ -2,13 +2,16 @@ use std::fmt; use crate::interner::{InternedDisplay, Interner, Tok}; +/// Various reasons why a substitution rule may be invalid #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RuleError { + /// A key is present in the template but not the pattern Missing(Tok), + /// A key uses a different arity in the template and in the pattern TypeMismatch(Tok), - /// Multiple occurences of a placeholder in a pattern are no longer - /// supported. + /// Multiple occurences of a placeholder in a pattern Multiple(Tok), + /// Two vectorial placeholders are next to each other VecNeighbors(Tok, Tok), } diff --git a/src/rule/state.rs b/src/rule/state.rs index e0055b3..f04e413 100644 --- a/src/rule/state.rs +++ b/src/rule/state.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use crate::ast::{Clause, Expr, PHClass, Placeholder}; use crate::interner::Tok; -use crate::unwrap_or; +use crate::utils::unwrap_or; #[derive(Clone, Copy, Debug, PartialEq)] pub enum StateEntry<'a> { diff --git a/src/external/assertion_error.rs b/src/stl/assertion_error.rs similarity index 93% rename from src/external/assertion_error.rs rename to src/stl/assertion_error.rs index 85e554b..efad123 100644 --- a/src/external/assertion_error.rs +++ b/src/stl/assertion_error.rs @@ -13,10 +13,10 @@ pub struct AssertionError { } impl AssertionError { - pub fn fail( + pub fn fail( value: ExprInst, assertion: &'static str, - ) -> Result> { + ) -> Result> { return Err(Self { value, assertion }.into_extern()); } diff --git a/src/external/bool/boolean.rs b/src/stl/bool/boolean.rs similarity index 100% rename from src/external/bool/boolean.rs rename to src/stl/bool/boolean.rs diff --git a/src/external/bool/equals.rs b/src/stl/bool/equals.rs similarity index 97% rename from src/external/bool/equals.rs rename to src/stl/bool/equals.rs index 3b9fd57..4d53712 100644 --- a/src/external/bool/equals.rs +++ b/src/stl/bool/equals.rs @@ -1,14 +1,14 @@ use std::fmt::Debug; use super::super::assertion_error::AssertionError; +use super::super::litconv::with_lit; use super::boolean::Boolean; -use crate::external::litconv::with_lit; use crate::representations::interpreted::ExprInst; use crate::representations::Literal; use crate::{atomic_impl, atomic_redirect, externfn_impl}; /// Compares the inner values if -/// +/// /// - both values are char, /// - both are string, /// - both are either uint or num diff --git a/src/external/bool/ifthenelse.rs b/src/stl/bool/ifthenelse.rs similarity index 95% rename from src/external/bool/ifthenelse.rs rename to src/stl/bool/ifthenelse.rs index df4ce03..2960120 100644 --- a/src/external/bool/ifthenelse.rs +++ b/src/stl/bool/ifthenelse.rs @@ -1,8 +1,8 @@ use std::fmt::Debug; use std::rc::Rc; +use super::super::assertion_error::AssertionError; use super::Boolean; -use crate::external::assertion_error::AssertionError; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::PathSet; use crate::{atomic_impl, atomic_redirect, externfn_impl}; diff --git a/src/external/bool/mod.rs b/src/stl/bool/mod.rs similarity index 100% rename from src/external/bool/mod.rs rename to src/stl/bool/mod.rs diff --git a/src/external/conv/mod.rs b/src/stl/conv/mod.rs similarity index 100% rename from src/external/conv/mod.rs rename to src/stl/conv/mod.rs diff --git a/src/external/conv/parse_float.rs b/src/stl/conv/parse_float.rs similarity index 96% rename from src/external/conv/parse_float.rs rename to src/stl/conv/parse_float.rs index 9048ec6..d0e1cb7 100644 --- a/src/external/conv/parse_float.rs +++ b/src/stl/conv/parse_float.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use chumsky::Parser; use super::super::assertion_error::AssertionError; -use crate::external::litconv::with_lit; +use super::super::litconv::with_lit; use crate::parse::float_parser; use crate::representations::interpreted::ExprInst; use crate::representations::Literal; diff --git a/src/external/conv/parse_uint.rs b/src/stl/conv/parse_uint.rs similarity index 92% rename from src/external/conv/parse_uint.rs rename to src/stl/conv/parse_uint.rs index 029a20e..336a45b 100644 --- a/src/external/conv/parse_uint.rs +++ b/src/stl/conv/parse_uint.rs @@ -2,8 +2,8 @@ use std::fmt::Debug; use chumsky::Parser; -use crate::external::assertion_error::AssertionError; -use crate::external::litconv::with_lit; +use super::super::assertion_error::AssertionError; +use super::super::litconv::with_lit; use crate::parse::int_parser; use crate::representations::interpreted::ExprInst; use crate::representations::Literal; diff --git a/src/external/conv/to_string.rs b/src/stl/conv/to_string.rs similarity index 95% rename from src/external/conv/to_string.rs rename to src/stl/conv/to_string.rs index f9aa89d..c1cab36 100644 --- a/src/external/conv/to_string.rs +++ b/src/stl/conv/to_string.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use crate::external::litconv::with_lit; +use super::super::litconv::with_lit; use crate::representations::interpreted::ExprInst; use crate::representations::Literal; use crate::{atomic_impl, atomic_redirect, externfn_impl}; diff --git a/src/external/cpsio/debug.rs b/src/stl/cpsio/debug.rs similarity index 100% rename from src/external/cpsio/debug.rs rename to src/stl/cpsio/debug.rs diff --git a/src/external/cpsio/io.rs b/src/stl/cpsio/io.rs similarity index 91% rename from src/external/cpsio/io.rs rename to src/stl/cpsio/io.rs index 4597d19..f69b1c6 100644 --- a/src/external/cpsio/io.rs +++ b/src/stl/cpsio/io.rs @@ -1,10 +1,11 @@ use std::io::{self, Write}; -use crate::external::runtime_error::RuntimeError; +use super::super::runtime_error::RuntimeError; +use crate::atomic_inert; use crate::interpreter::{HandlerParm, HandlerRes}; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::{Literal, Primitive}; -use crate::{atomic_inert, unwrap_or}; +use crate::utils::unwrap_or; /// An IO command to be handled by the host application. #[derive(Clone, Debug)] diff --git a/src/external/cpsio/mod.rs b/src/stl/cpsio/mod.rs similarity index 100% rename from src/external/cpsio/mod.rs rename to src/stl/cpsio/mod.rs diff --git a/src/external/cpsio/panic.rs b/src/stl/cpsio/panic.rs similarity index 96% rename from src/external/cpsio/panic.rs rename to src/stl/cpsio/panic.rs index 8c5b8c6..39f3fcd 100644 --- a/src/external/cpsio/panic.rs +++ b/src/stl/cpsio/panic.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use crate::external::litconv::with_str; +use super::super::litconv::with_str; use crate::foreign::ExternError; use crate::representations::interpreted::ExprInst; use crate::{atomic_impl, atomic_redirect, externfn_impl}; diff --git a/src/external/cpsio/print.rs b/src/stl/cpsio/print.rs similarity index 96% rename from src/external/cpsio/print.rs rename to src/stl/cpsio/print.rs index df194ce..ed56cd9 100644 --- a/src/external/cpsio/print.rs +++ b/src/stl/cpsio/print.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; +use super::super::litconv::with_str; use super::io::IO; -use crate::external::litconv::with_str; use crate::foreign::{Atomic, AtomicResult, AtomicReturn}; use crate::interpreter::Context; use crate::representations::interpreted::ExprInst; diff --git a/src/external/cpsio/readline.rs b/src/stl/cpsio/readline.rs similarity index 100% rename from src/external/cpsio/readline.rs rename to src/stl/cpsio/readline.rs diff --git a/src/external/litconv.rs b/src/stl/litconv.rs similarity index 90% rename from src/external/litconv.rs rename to src/stl/litconv.rs index 34d563b..37ec1e0 100644 --- a/src/external/litconv.rs +++ b/src/stl/litconv.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::external::assertion_error::AssertionError; +use super::assertion_error::AssertionError; use crate::foreign::ExternError; use crate::representations::interpreted::ExprInst; use crate::representations::Literal; @@ -12,7 +12,7 @@ pub fn with_lit( predicate: impl FnOnce(&Literal) -> Result>, ) -> Result> { x.with_literal(predicate) - .map_err(|()| AssertionError::ext(x.clone(), "a literal value")) + .map_err(|_| AssertionError::ext(x.clone(), "a literal value")) .and_then(|r| r) } diff --git a/src/external/std.rs b/src/stl/mk_stl.rs similarity index 84% rename from src/external/std.rs rename to src/stl/mk_stl.rs index fb84dd9..1ac7c77 100644 --- a/src/external/std.rs +++ b/src/stl/mk_stl.rs @@ -6,6 +6,6 @@ use super::str::str; use crate::interner::Interner; use crate::pipeline::ConstTree; -pub fn std(i: &Interner) -> ConstTree { +pub fn mk_stl(i: &Interner) -> ConstTree { cpsio(i) + conv(i) + bool(i) + str(i) + num(i) } diff --git a/src/external/mod.rs b/src/stl/mod.rs similarity index 57% rename from src/external/mod.rs rename to src/stl/mod.rs index e5f610b..8a29177 100644 --- a/src/external/mod.rs +++ b/src/stl/mod.rs @@ -3,9 +3,10 @@ mod bool; mod conv; mod cpsio; mod litconv; +mod mk_stl; mod num; mod runtime_error; -pub mod std; mod str; -pub use cpsio::{handle, IO}; +pub use cpsio::{handle as handleIO, IO}; +pub use mk_stl::mk_stl; diff --git a/src/external/num/mod.rs b/src/stl/num/mod.rs similarity index 100% rename from src/external/num/mod.rs rename to src/stl/num/mod.rs diff --git a/src/external/num/numeric.rs b/src/stl/num/numeric.rs similarity index 97% rename from src/external/num/numeric.rs rename to src/stl/num/numeric.rs index 76de87d..10b1c0f 100644 --- a/src/external/num/numeric.rs +++ b/src/stl/num/numeric.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use ordered_float::NotNan; -use crate::external::assertion_error::AssertionError; -use crate::external::litconv::with_lit; +use super::super::assertion_error::AssertionError; +use super::super::litconv::with_lit; use crate::foreign::ExternError; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::{Literal, Primitive}; diff --git a/src/external/num/operators/add.rs b/src/stl/num/operators/add.rs similarity index 100% rename from src/external/num/operators/add.rs rename to src/stl/num/operators/add.rs diff --git a/src/external/num/operators/divide.rs b/src/stl/num/operators/divide.rs similarity index 100% rename from src/external/num/operators/divide.rs rename to src/stl/num/operators/divide.rs diff --git a/src/external/num/operators/mod.rs b/src/stl/num/operators/mod.rs similarity index 100% rename from src/external/num/operators/mod.rs rename to src/stl/num/operators/mod.rs diff --git a/src/external/num/operators/multiply.rs b/src/stl/num/operators/multiply.rs similarity index 100% rename from src/external/num/operators/multiply.rs rename to src/stl/num/operators/multiply.rs diff --git a/src/external/num/operators/remainder.rs b/src/stl/num/operators/remainder.rs similarity index 100% rename from src/external/num/operators/remainder.rs rename to src/stl/num/operators/remainder.rs diff --git a/src/external/num/operators/subtract.rs b/src/stl/num/operators/subtract.rs similarity index 100% rename from src/external/num/operators/subtract.rs rename to src/stl/num/operators/subtract.rs diff --git a/src/external/runtime_error.rs b/src/stl/runtime_error.rs similarity index 92% rename from src/external/runtime_error.rs rename to src/stl/runtime_error.rs index d47a45a..ad427a5 100644 --- a/src/external/runtime_error.rs +++ b/src/stl/runtime_error.rs @@ -11,10 +11,10 @@ pub struct RuntimeError { } impl RuntimeError { - pub fn fail( + pub fn fail( message: String, operation: &'static str, - ) -> Result> { + ) -> Result> { return Err(Self { message, operation }.into_extern()); } diff --git a/src/external/str/char_at.rs b/src/stl/str/char_at.rs similarity index 91% rename from src/external/str/char_at.rs rename to src/stl/str/char_at.rs index 8fdf6c3..cc88d53 100644 --- a/src/external/str/char_at.rs +++ b/src/stl/str/char_at.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; -use crate::external::litconv::{with_str, with_uint}; -use crate::external::runtime_error::RuntimeError; +use super::super::litconv::{with_str, with_uint}; +use super::super::runtime_error::RuntimeError; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::{Literal, Primitive}; use crate::{atomic_impl, atomic_redirect, externfn_impl}; diff --git a/src/external/str/concatenate.rs b/src/stl/str/concatenate.rs similarity index 96% rename from src/external/str/concatenate.rs rename to src/stl/str/concatenate.rs index aad41f1..221d71d 100644 --- a/src/external/str/concatenate.rs +++ b/src/stl/str/concatenate.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use crate::external::litconv::with_str; +use super::super::litconv::with_str; use crate::representations::interpreted::{Clause, ExprInst}; use crate::representations::{Literal, Primitive}; use crate::{atomic_impl, atomic_redirect, externfn_impl}; diff --git a/src/external/str/mod.rs b/src/stl/str/mod.rs similarity index 100% rename from src/external/str/mod.rs rename to src/stl/str/mod.rs diff --git a/src/utils/cache.rs b/src/utils/cache.rs index 4823cd5..0afcd7e 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -2,10 +2,12 @@ use std::cell::RefCell; use std::hash::Hash; use hashbrown::HashMap; +use trait_set::trait_set; // TODO: make this a crate -pub trait Callback<'a, I, O: 'static> = Fn(I, &Cache<'a, I, O>) -> O; - +trait_set! { + pub trait Callback<'a, I, O: 'static> = Fn(I, &Cache<'a, I, O>) -> O; +} pub type CbBox<'a, I, O> = Box + 'a>; /// Cache the return values of an effectless closure in a hashmap diff --git a/src/utils/iter.rs b/src/utils/iter.rs index 3672a3e..c3fbe64 100644 --- a/src/utils/iter.rs +++ b/src/utils/iter.rs @@ -17,7 +17,6 @@ pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { } /// Chain various iterators into a [BoxedIter] -#[macro_export] macro_rules! box_chain { ($curr:expr) => { Box::new($curr) as BoxedIter<_> @@ -27,6 +26,8 @@ macro_rules! box_chain { }; } +pub(crate) use box_chain; + /// Flatten an iterator of iterators into a boxed iterator of the inner /// nested values pub fn box_flatten< @@ -40,7 +41,7 @@ pub fn box_flatten< Box::new(i.flatten()) } -/// Convert an iterator into a Box +/// Convert an iterator into a `Box` pub fn into_boxed_iter<'a, T: 'a + IntoIterator>( t: T, ) -> BoxedIter<'a, ::Item> { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index b7955c2..d6ad685 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -6,7 +6,6 @@ mod side; mod string_from_charset; mod substack; mod unwrap_or; -mod xloop; pub use cache::Cache; pub use print_nname::sym2string; @@ -14,6 +13,7 @@ pub use pushed::pushed; pub use replace_first::replace_first; pub use side::Side; pub use substack::{Stackframe, Substack, SubstackIterator}; +pub(crate) use unwrap_or::unwrap_or; pub mod iter; pub use iter::BoxedIter; pub use string_from_charset::string_from_charset; diff --git a/src/utils/side.rs b/src/utils/side.rs index 620fa78..8d7ce18 100644 --- a/src/utils/side.rs +++ b/src/utils/side.rs @@ -66,8 +66,8 @@ impl Side { Side::Right => (opposite, this), } } - /// Produces an increasing sequence on Right, and a decreasing sequence - /// on Left + /// Walk a double ended iterator (assumed to be left-to-right) in this + /// direction pub fn walk<'a, I: DoubleEndedIterator + 'a>( &self, iter: I, diff --git a/src/utils/substack.rs b/src/utils/substack.rs index f1c9571..eed6eee 100644 --- a/src/utils/substack.rs +++ b/src/utils/substack.rs @@ -56,6 +56,9 @@ impl<'a, T> Substack<'a, T> { Self::Bottom => 0, } } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } impl<'a, T: Debug> Debug for Substack<'a, T> { diff --git a/src/utils/unwrap_or.rs b/src/utils/unwrap_or.rs index acad1de..0a24a12 100644 --- a/src/utils/unwrap_or.rs +++ b/src/utils/unwrap_or.rs @@ -1,8 +1,9 @@ /// A macro version of [Option::unwrap_or_else] which supports flow /// control statements such as `return` and `break` in the "else" branch. -#[macro_export] macro_rules! unwrap_or { ($m:expr; $fail:expr) => {{ if let Some(res) = ($m) { res } else { $fail } }}; } + +pub(crate) use unwrap_or; diff --git a/src/utils/xloop.rs b/src/utils/xloop.rs deleted file mode 100644 index e4b6c1d..0000000 --- a/src/utils/xloop.rs +++ /dev/null @@ -1,95 +0,0 @@ -/// Imitates a regular for loop with an exit clause using Rust's `loop` -/// keyword. This macro brings the break value to all existing Rust loops, -/// by allowing you to specify an exit expression in case the loop was -/// broken by the condition and not an explicit `break`. -/// -/// Since the exit expression can also be a block, this also allows you to -/// execute other code when the condition fails. This can also be used to -/// re-enter the loop with an explicit `continue` statement. -/// -/// The macro also adds support for classic for loops familiar to everyone -/// since C, except with the addition of an exit statement these too can -/// be turned into expressions. -/// -/// ``` -/// xloop!(for i in 0..10; { -/// connection.try_connect() -/// if connection.ready() { -/// break Some(connection) -/// } -/// }; None) -/// ``` -/// -/// While loop with reentry. This is a very convoluted example but -/// displays the idea quite clearly. -/// -/// ``` -/// xloop!(while socket.is_open(); { -/// let (data, is_end) = socket.read(); -/// all_data.append(data) -/// if is_end { break Ok(all_data) } -/// }; { -/// if let Ok(new_sock) = open_socket(socket.position()) { -/// new_sock.set_position(socket.position()); -/// socket = new_sock; -/// continue -/// } else { -/// Err(DownloadError::ConnectionLost) -/// } -/// }) -/// ``` -/// -/// CUDA algorythm for O(log n) summation using a C loop -/// -/// ``` -/// xloop!(let mut leap = 1; own_id*2 + leap < batch_size; leap *= 2; { -/// batch[own_id*2] += batch[own_id*2 + leap] -/// }) -/// ``` -/// -/// The above loop isn't used as an expression, but an exit expression - -/// or block - can be added to these as well just like the others. In all -/// cases the exit expression is optional, its default value is `()`. -/// -/// TODO: find a valid use case for While let for a demo -/// TODO: break out into crate -#[macro_export] -macro_rules! xloop { - (for $p:pat in $it:expr; $body:stmt) => { - xloop!(for $p in $it; $body; ()) - }; - (for $p:pat in $iit:expr; $body:stmt; $exit:stmt) => { - { - let mut i = $iit.into_iter(); - xloop!(let Some($p) = i.next(); $body; $exit) - } - }; - (let $p:pat = $e:expr; $body:stmt) => { - xloop!(let $p = $e; $body; ()) - }; - (let $p:pat = $e:expr; $body:stmt; $exit:stmt) => { - { - loop { - if let $p = $e { $body } - else { break { $exit } } - } - } - }; - (while $cond:expr; $body:stmt) => { - xloop!($cond; $body; ()) - }; - (while $cond:expr; $body:stmt; $exit:stmt) => { - { - loop { - if $cond { $body } - else { break { $exit } } - } - } - }; - ($init:stmt; $cond:expr; $step:stmt; $body:stmt) => { - xloop!(for ( $init; $cond; $step ) $body; ()) - }; - ($init:stmt; $cond:expr; $step:stmt; $body:stmt; $exit:stmt) => { - { $init; xloop!(while $cond; { $body; $step }; $exit) } - }; -}