Massive feature drop

- pattern matching seems to be correct
- dynamic dispatch works with the to_string example
- template strings as a last-minute addition
- interpreter revamp, virtual stack for abort safety
This commit is contained in:
2024-01-29 18:26:56 +00:00
parent a8887227e5
commit c279301583
71 changed files with 947 additions and 932 deletions

View File

@@ -1,9 +1,10 @@
import std::exit_status import std::exit_status
import std::conv import std::conv
import std::number
import std::tuple
import std::list
const main2 := ( const main := match t["set", "foo", 1] {
println "Hello, world!" t[= "set", key, val] =>
exit_status::success $"Setting ${ key ++ $"${1 + 1}" } to ${val}"
) }
const main := conv::to_string t[1, 2, 3]

View File

@@ -24,7 +24,9 @@
"editor.formatOnType": true, "editor.formatOnType": true,
}, },
"[rust]": { "[rust]": {
"editor.rulers": [80], "editor.rulers": [
100
],
}, },
"rust-analyzer.showUnlinkedFileNotification": false, "rust-analyzer.showUnlinkedFileNotification": false,
"rust-analyzer.checkOnSave": true, "rust-analyzer.checkOnSave": true,

View File

@@ -5,7 +5,7 @@ version = "Two"
# space # space
tab_spaces = 2 tab_spaces = 2
max_width = 80 max_width = 100
error_on_line_overflow = true error_on_line_overflow = true
format_macro_matchers = true format_macro_matchers = true
newline_style = "Unix" newline_style = "Unix"

View File

@@ -48,6 +48,7 @@ enum Command {
}, },
#[command(arg_required_else_help = true)] #[command(arg_required_else_help = true)]
MacroDebug { MacroDebug {
#[arg(long, short)]
symbol: String, symbol: String,
}, },
ListMacros, ListMacros,

View File

@@ -1,8 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use hashbrown::HashMap; use hashbrown::HashMap;
use never::Never;
use substack::Substack;
use super::system::System; use super::system::System;
use crate::error::ProjectResult; use crate::error::ProjectResult;
@@ -10,9 +8,10 @@ use crate::intermediate::ast_to_ir::ast_to_ir;
use crate::intermediate::ir_to_nort::ir_to_nort; use crate::intermediate::ir_to_nort::ir_to_nort;
use crate::interpreter::nort; use crate::interpreter::nort;
use crate::location::{CodeGenInfo, CodeLocation}; use crate::location::{CodeGenInfo, CodeLocation};
use crate::name::{Sym, VPath}; use crate::name::Sym;
use crate::pipeline::project::ConstReport; use crate::pipeline::project::ConstReport;
use crate::tree::{ModMember, ModMemberRef, TreeTransforms}; use crate::tree::{ModMemberRef, TreeTransforms};
use crate::utils::unwrap_or::unwrap_or;
/// Equivalent of [crate::pipeline::project::ConstReport] for the interpreter's /// Equivalent of [crate::pipeline::project::ConstReport] for the interpreter's
/// representation, [crate::interpreter::nort]. /// representation, [crate::interpreter::nort].
@@ -33,28 +32,27 @@ pub fn merge_trees<'a: 'b, 'b>(
) -> ProjectResult<impl IntoIterator<Item = (Sym, NortConst)> + 'static> { ) -> ProjectResult<impl IntoIterator<Item = (Sym, NortConst)> + 'static> {
let mut out = HashMap::new(); let mut out = HashMap::new();
for (name, rep) in source { for (name, rep) in source {
let ir = ast_to_ir(rep.value, name.clone())?;
// if name == Sym::literal("tree::main::main") {
// panic!("{ir:?}");
// }
out.insert(name.clone(), NortConst { out.insert(name.clone(), NortConst {
value: ir_to_nort(&ast_to_ir(rep.value, name)?), value: ir_to_nort(&ir),
location: CodeLocation::Source(rep.range), location: CodeLocation::Source(rep.range),
comments: rep.comments, comments: rep.comments,
}); });
} }
for sys in systems { for system in systems {
let const_module = sys.constants.unwrap_mod_ref(); let const_module = system.constants.unwrap_mod_ref();
const_module.search_all((), |path, node, ()| { const_module.search_all((), |stack, node, ()| {
let m = if let ModMemberRef::Mod(m) = node { m } else { return }; let c = unwrap_or!(node => ModMemberRef::Item; return);
for (key, ent) in &m.entries {
if let ModMember::Item(c) = &ent.member {
let path = VPath::new(path.unreverse()).as_prefix_of(key.clone());
let location = CodeLocation::Gen(CodeGenInfo::details( let location = CodeLocation::Gen(CodeGenInfo::details(
"constant from", "constant from",
format!("system.name={}", sys.name), format!("system.name={}", system.name),
)); ));
let value = c.gen_nort(location.clone()); let value = c.clone().gen_nort(stack.clone(), location.clone());
let crep = NortConst { value, comments: vec![], location }; let crep = NortConst { value, comments: vec![], location };
out.insert(path.to_sym(), crep); out.insert(Sym::new(stack.unreverse()).expect("root item is forbidden"), crep);
}
}
}); });
} }
Ok(out) Ok(out)

View File

@@ -9,7 +9,6 @@ use crate::interpreter::handler::{run_handler, HandlerTable};
use crate::interpreter::nort::{Clause, Expr}; use crate::interpreter::nort::{Clause, Expr};
use crate::location::CodeLocation; use crate::location::CodeLocation;
use crate::name::Sym; use crate::name::Sym;
use crate::utils::boxed_iter::BoxedIter;
/// This struct ties the state of systems to loaded code, and allows to call /// This struct ties the state of systems to loaded code, and allows to call
/// Orchid-defined functions /// Orchid-defined functions
@@ -37,7 +36,7 @@ impl<'a> Process<'a> {
prompt: Expr, prompt: Expr,
gas: Option<usize>, gas: Option<usize>,
) -> Result<Halt, RunError> { ) -> Result<Halt, RunError> {
let ctx = RunContext { gas, symbols: &self.symbols }; let ctx = RunContext { gas, symbols: &self.symbols, stack_size: 1000 };
run_handler(prompt, &mut self.handlers, ctx) run_handler(prompt, &mut self.handlers, ctx)
} }
@@ -48,7 +47,7 @@ impl<'a> Process<'a> {
let mut errors = Vec::new(); let mut errors = Vec::new();
let sym = self.symbols.get(&key).expect("symbol must exist"); let sym = self.symbols.get(&key).expect("symbol must exist");
sym.search_all(&mut |s: &Expr| { sym.search_all(&mut |s: &Expr| {
if let Clause::Constant(sym) = &*s.clause.cls() { if let Clause::Constant(sym) = &*s.cls() {
if !self.symbols.contains_key(sym) { if !self.symbols.contains_key(sym) {
errors.push((sym.clone(), s.location())) errors.push((sym.clone(), s.location()))
} }

View File

@@ -5,35 +5,27 @@ use std::sync::{Arc, Mutex};
use never::Never; use never::Never;
use super::error::{ExternError, ExternResult}; use super::error::{ExternError, ExternResult};
use crate::interpreter::apply::CallData;
use crate::interpreter::context::RunContext; use crate::interpreter::context::RunContext;
use crate::interpreter::error::RunError; use crate::interpreter::error::RunError;
use crate::interpreter::nort; use crate::interpreter::nort;
use crate::interpreter::run::RunData;
use crate::location::{CodeLocation, SourceRange}; use crate::location::{CodeLocation, SourceRange};
use crate::name::NameLike; use crate::name::NameLike;
use crate::parse::parsed; use crate::parse::parsed;
use crate::utils::ddispatch::{request, Request, Responder}; use crate::utils::ddispatch::{request, Request, Responder};
/// Information returned by [Atomic::run]. This mirrors /// Information returned by [Atomic::run].
/// [crate::interpreter::Return] but with a clause instead of an Expr. pub enum AtomicReturn {
pub struct AtomicReturn { /// No work was done. If the atom takes an argument, it can be provided now
/// The next form of the expression Inert(nort::Clause),
pub clause: nort::Clause, /// Work was done, returns new clause and consumed gas. 1 gas is already
/// Remaining gas /// consumed by the virtual call, so nonzero values indicate expensive
pub gas: Option<usize>, /// operations.
/// Whether further normalization is possible by repeated calls to Change(usize, nort::Clause),
/// [Atomic::run]
pub inert: bool,
} }
impl AtomicReturn { impl AtomicReturn {
/// Report indicating that the value is inert /// Report indicating that the value is inert
pub fn inert<T: Atomic, E>(this: T, ctx: RunContext) -> Result<Self, E> { pub fn inert<T: Atomic, E>(this: T) -> Result<Self, E> {
Ok(Self { clause: this.atom_cls(), gas: ctx.gas, inert: true }) Ok(Self::Inert(this.atom_cls()))
}
/// Report indicating that the value has been processed
pub fn run<E>(clause: nort::Clause, run: RunData) -> Result<Self, E> {
Ok(Self { clause, gas: run.ctx.gas, inert: false })
} }
} }
@@ -51,6 +43,25 @@ impl Display for NotAFunction {
} }
} }
/// Information about a function call presented to an external function
pub struct CallData<'a> {
/// Location of the function expression
pub location: CodeLocation,
/// The argument the function was called on. Functions are curried
pub arg: nort::Expr,
/// Information relating to this interpreter run
pub ctx: RunContext<'a>,
}
/// Information about a normalization run presented to an atom
#[derive(Clone)]
pub struct RunData<'a> {
/// Location of the atom
pub location: CodeLocation,
/// Information about the execution
pub ctx: RunContext<'a>,
}
/// Functionality the interpreter needs to handle a value /// Functionality the interpreter needs to handle a value
/// ///
/// # Lifecycle methods /// # Lifecycle methods
@@ -91,7 +102,7 @@ where Self: 'static
/// Returns a reference to a possible expression held inside the atom which /// Returns a reference to a possible expression held inside the atom which
/// can be reduced. For an overview of the lifecycle see [Atomic] /// can be reduced. For an overview of the lifecycle see [Atomic]
fn redirect(&mut self) -> Option<&mut nort::ClauseInst>; fn redirect(&mut self) -> Option<&mut nort::Expr>;
/// Attempt to normalize this value. If it wraps a value, this should report /// Attempt to normalize this value. If it wraps a value, this should report
/// inert. If it wraps a computation, it should execute one logical step of /// inert. If it wraps a computation, it should execute one logical step of
@@ -172,7 +183,7 @@ impl AtomGenerator {
} }
impl Debug for AtomGenerator { impl Debug for AtomGenerator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AtomGenerator").finish_non_exhaustive() write!(f, "{:?}", self.run())
} }
} }
@@ -239,7 +250,7 @@ impl Responder for Never {
impl Atomic for Never { impl Atomic for Never {
fn as_any(self: Box<Self>) -> Box<dyn Any> { match *self {} } fn as_any(self: Box<Self>) -> Box<dyn Any> { match *self {} }
fn as_any_ref(&self) -> &dyn Any { match *self {} } fn as_any_ref(&self) -> &dyn Any { match *self {} }
fn redirect(&mut self) -> Option<&mut nort::ClauseInst> { match *self {} } fn redirect(&mut self) -> Option<&mut nort::Expr> { match *self {} }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { match *self {} } fn run(self: Box<Self>, _: RunData) -> AtomicResult { match *self {} }
fn apply_ref(&self, _: CallData) -> ExternResult<nort::Clause> { fn apply_ref(&self, _: CallData) -> ExternResult<nort::Clause> {
match *self {} match *self {}

View File

@@ -4,11 +4,11 @@ use std::fmt::Debug;
use trait_set::trait_set; use trait_set::trait_set;
use super::atom::{Atomic, AtomicResult, AtomicReturn, NotAFunction}; use super::atom::{
Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData,
};
use super::error::{ExternError, ExternResult}; use super::error::{ExternError, ExternResult};
use crate::interpreter::apply::CallData; use crate::interpreter::nort::{Clause, Expr};
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
use crate::location::CodeLocation; use crate::location::CodeLocation;
use crate::utils::ddispatch::{Request, Responder}; use crate::utils::ddispatch::{Request, Responder};
use crate::utils::pure_seq::pushed_ref; use crate::utils::pure_seq::pushed_ref;
@@ -78,9 +78,9 @@ impl<T: CPSPayload> Atomic for CPSBox<T> {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self } fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self } fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn parser_eq(&self, _: &dyn std::any::Any) -> bool { false } fn parser_eq(&self, _: &dyn std::any::Any) -> bool { false }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None } fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult { fn run(self: Box<Self>, _: RunData) -> AtomicResult {
AtomicReturn::inert(*self, run.ctx) AtomicReturn::inert(*self)
} }
fn apply(mut self: Box<Self>, call: CallData) -> ExternResult<Clause> { fn apply(mut self: Box<Self>, call: CallData) -> ExternResult<Clause> {
self.assert_applicable(&call.location)?; self.assert_applicable(&call.location)?;

View File

@@ -10,7 +10,7 @@ use crate::location::CodeLocation;
pub trait ExternError: Display + Send + Sync + DynClone { pub trait ExternError: Display + Send + Sync + DynClone {
/// Convert into trait object /// Convert into trait object
#[must_use] #[must_use]
fn rc(self) -> Arc<dyn ExternError> fn rc(self) -> ExternErrorObj
where Self: 'static + Sized { where Self: 'static + Sized {
Arc::new(self) Arc::new(self)
} }
@@ -25,7 +25,10 @@ impl Debug for dyn ExternError {
impl Error for dyn ExternError {} impl Error for dyn ExternError {}
/// An error produced by Rust code called form Orchid. The error is type-erased. /// An error produced by Rust code called form Orchid. The error is type-erased.
pub type ExternResult<T> = Result<T, Arc<dyn ExternError>>; pub type ExternErrorObj = Arc<dyn ExternError>;
/// A result produced by Rust code called from Orchid.
pub type ExternResult<T> = Result<T, ExternErrorObj>;
/// Some expectation (usually about the argument types of a function) did not /// Some expectation (usually about the argument types of a function) did not
/// hold. /// hold.
@@ -52,7 +55,7 @@ impl AssertionError {
location: CodeLocation, location: CodeLocation,
message: &'static str, message: &'static str,
details: String, details: String,
) -> Arc<dyn ExternError> { ) -> ExternErrorObj {
Self { location, message, details }.rc() Self { location, message, details }.rc()
} }
} }

View File

@@ -1,15 +1,14 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::fmt::Debug; use std::fmt::{Debug, Display};
use std::marker::PhantomData; use std::marker::PhantomData;
use super::atom::{Atomic, AtomicResult, AtomicReturn}; use intern_all::{i, Tok};
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
use super::error::ExternResult; use super::error::ExternResult;
use super::to_clause::ToClause; use super::to_clause::ToClause;
use super::try_from_expr::TryFromExpr; use super::try_from_expr::TryFromExpr;
use crate::interpreter::apply::CallData; use crate::interpreter::nort::{Clause, Expr};
use crate::interpreter::context::Halt;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::{run, RunData};
use crate::utils::ddispatch::Responder; use crate::utils::ddispatch::Responder;
/// Return a unary lambda wrapped in this struct to take an additional argument /// Return a unary lambda wrapped in this struct to take an additional argument
@@ -27,22 +26,31 @@ use crate::utils::ddispatch::Responder;
/// type's [TryFromExpr] impl. /// type's [TryFromExpr] impl.
pub struct Param<T, U, F> { pub struct Param<T, U, F> {
data: F, data: F,
name: Tok<String>,
_t: PhantomData<T>, _t: PhantomData<T>,
_u: PhantomData<U>, _u: PhantomData<U>,
} }
unsafe impl<T, U, F: Send> Send for Param<T, U, F> {} unsafe impl<T, U, F: Send> Send for Param<T, U, F> {}
impl<T, U, F> Param<T, U, F> { impl<T, U, F> Param<T, U, F> {
/// Wrap a new function in a parametric struct /// Wrap a new function in a parametric struct
pub fn new(f: F) -> Self pub fn new(name: Tok<String>, f: F) -> Self
where F: FnOnce(T) -> U { where F: FnOnce(T) -> U {
Self { data: f, _t: PhantomData, _u: PhantomData } Self { name, data: f, _t: PhantomData, _u: PhantomData }
} }
/// Take out the function /// Take out the function
pub fn get(self) -> F { self.data } pub fn get(self) -> F { self.data }
} }
impl<T, U, F: Clone> Clone for Param<T, U, F> { impl<T, U, F: Clone> Clone for Param<T, U, F> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { data: self.data.clone(), _t: PhantomData, _u: PhantomData } Self { name: self.name.clone(), data: self.data.clone(), _t: PhantomData, _u: PhantomData }
}
}
impl<T, U, F> Display for Param<T, U, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.name) }
}
impl<T, U, F> Debug for Param<T, U, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Param").field(&*self.name).finish()
} }
} }
@@ -65,9 +73,7 @@ impl<T, U, F: Clone> Clone for FnMiddleStage<T, U, F> {
} }
impl<T, U, F> Debug for FnMiddleStage<T, U, F> { impl<T, U, F> Debug for FnMiddleStage<T, U, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FnMiddleStage") write!(f, "FnMiddleStage({} {})", self.f, self.arg)
.field("argument", &self.arg)
.finish_non_exhaustive()
} }
} }
impl<T, U, F> Responder for FnMiddleStage<T, U, F> {} impl<T, U, F> Responder for FnMiddleStage<T, U, F> {}
@@ -79,26 +85,18 @@ impl<
{ {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self } fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self } fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { fn redirect(&mut self) -> Option<&mut Expr> {
// this should be ctfe'd // this should be ctfe'd
(TypeId::of::<T>() != TypeId::of::<Thunk>()).then(|| &mut self.arg.clause) (TypeId::of::<T>() != TypeId::of::<Thunk>()).then_some(&mut self.arg)
} }
fn run(self: Box<Self>, r: RunData) -> AtomicResult { fn run(self: Box<Self>, r: RunData) -> AtomicResult {
let Self { arg, f: Param { data: f, .. } } = *self; let Self { arg, f: Param { data: f, .. } } = *self;
let clause = f(arg.downcast()?).to_clause(r.location); Ok(AtomicReturn::Change(0, f(arg.downcast()?).to_clause(r.location)))
Ok(AtomicReturn { gas: r.ctx.gas, inert: false, clause })
}
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> {
panic!("Atom should have decayed")
} }
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> { panic!("Atom should have decayed") }
} }
impl<T, U, F> Responder for Param<T, U, F> {} impl<T, U, F> Responder for Param<T, U, F> {}
impl<T, U, F> Debug for Param<T, U, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Param")
}
}
impl< impl<
T: 'static + TryFromExpr + Clone, T: 'static + TryFromExpr + Clone,
@@ -108,10 +106,8 @@ impl<
{ {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self } fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self } fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None } fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, r: RunData) -> AtomicResult { fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
AtomicReturn::inert(*self, r.ctx)
}
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> { fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
Ok(FnMiddleStage { arg: call.arg, f: self.clone() }.atom_cls()) Ok(FnMiddleStage { arg: call.arg, f: self.clone() }.atom_cls())
} }
@@ -120,17 +116,42 @@ impl<
} }
} }
/// Convert a Rust function to Orchid. If you can, register your Rust functions
/// statically with functions in [crate::gen::tree].
pub fn xfn<const N: usize, Argv, Ret>(
name: &str,
x: impl Xfn<N, Argv, Ret>,
) -> impl Atomic + Clone {
x.to_atomic(i(name))
}
/// Trait for functions that can be directly passed to Orchid. Constraints in a
/// nutshell:
///
/// - the function must live as long as ['static]
/// - All arguments must implement [TryFromExpr]
/// - all but the last argument must implement [Clone] and [Send]
/// - the return type must implement [ToClause]
///
/// Take [Thunk] to consume the argument as-is, without normalization.
pub trait Xfn<const N: usize, Argv, Ret>: Clone + 'static {
/// Convert Rust type to Orchid function, given a name for logging
fn to_atomic(self, name: Tok<String>) -> impl Atomic + Clone;
}
/// Conversion functions from [Fn] traits into [Atomic]. Since Rust's type /// Conversion functions from [Fn] traits into [Atomic]. Since Rust's type
/// system allows overloaded [Fn] implementations, we must specify the arity and /// system allows overloaded [Fn] implementations, we must specify the arity and
/// argument types for this process. Arities are only defined up to 9, but the /// argument types for this process. Arities are only defined up to 9, but the
/// function can always return another call to `xfn_`N`ary` to consume more /// function can always return another call to `xfn_`N`ary` to consume more
/// arguments. /// arguments.
pub mod constructors { pub mod xfn_impls {
use intern_all::{i, Tok};
use super::super::atom::Atomic; use super::super::atom::Atomic;
use super::super::try_from_expr::TryFromExpr; use super::super::try_from_expr::TryFromExpr;
#[allow(unused)] // for doc #[allow(unused)] // for doc
use super::Thunk; use super::Thunk;
use super::{Param, ToClause}; use super::{Param, ToClause, Xfn};
macro_rules! xfn_variant { macro_rules! xfn_variant {
( (
@@ -139,42 +160,40 @@ pub mod constructors {
($($alt:expr)*) ($($alt:expr)*)
) => { ) => {
paste::paste!{ paste::paste!{
#[doc = "Convert a function of " $number " argument(s) into a curried" impl<
" Orchid function. See also Constraints summarized:\n\n"
"- the callback must live as long as `'static`\n"
"- All arguments must implement [TryFromExpr]\n"
"- all but the last argument must implement [Clone] and [Send]\n"
"- the return type must implement [ToClause].\n\n"
]
#[doc = "Take [Lazy] to take the argument as-is,\n"
"without normalization\n\n"
]
#[doc = "Other arities: " $( "[xfn_" $alt "ary], " )+ ]
pub fn [< xfn_ $number ary >] <
$( $t : TryFromExpr + Clone + Send + 'static, )* $( $t : TryFromExpr + Clone + Send + 'static, )*
TLast: TryFromExpr + Clone + 'static, TLast: TryFromExpr + Clone + 'static,
TReturn: ToClause + Send + 'static, TReturn: ToClause + Send + 'static,
TFunction: FnOnce( $( $t , )* TLast ) TFunction: FnOnce( $( $t , )* TLast )
-> TReturn + Clone + Send + 'static -> TReturn + Clone + Send + 'static
>(function: TFunction) -> impl Atomic + Clone { > Xfn<$number, ($($t,)* TLast,), TReturn> for TFunction {
xfn_variant!(@BODY_LOOP function fn to_atomic(self, name: Tok<String>) -> impl Atomic + Clone {
#[allow(unused_variables)]
let argc = 0;
let stage_n = name.clone();
xfn_variant!(@BODY_LOOP self name stage_n argc
( $( ( $t [< $t:lower >] ) )* ) ( $( ( $t [< $t:lower >] ) )* )
( $( [< $t:lower >] )* ) ( $( [< $t:lower >] )* )
) )
} }
} }
}
}; };
(@BODY_LOOP $function:ident ( (@BODY_LOOP $function:ident $name:ident $stage_n:ident $argc:ident (
( $Next:ident $next:ident ) ( $Next:ident $next:ident )
$( ( $T:ident $t:ident ) )* $( ( $T:ident $t:ident ) )*
) $full:tt) => { ) $full:tt) => {{
Param::new(|$next : $Next| { Param::new($stage_n, move |$next : $Next| {
xfn_variant!(@BODY_LOOP $function ( $( ( $T $t ) )* ) $full) let $argc = $argc + 1;
let $stage_n = i(&format!("{}/{}", $name, $argc));
xfn_variant!(@BODY_LOOP $function $name $stage_n $argc ( $( ( $T $t ) )* ) $full)
}) })
}; }};
(@BODY_LOOP $function:ident () ( $( $t:ident )* )) => { (@BODY_LOOP $function:ident $name:ident $stage_n:ident $argc:ident (
Param::new(|last: TLast| $function ( $( $t , )* last ))
}; ) ( $( $t:ident )* )) => {{
Param::new($stage_n, |last: TLast| $function ( $( $t , )* last ))
}};
} }
xfn_variant!(1, () (2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)); xfn_variant!(1, () (2 3 4 5 6 7 8 9 10 11 12 13 14 15 16));

View File

@@ -1,18 +1,15 @@
use std::any::Any; use std::any::Any;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc;
use super::atom::{Atom, Atomic, AtomicResult}; use super::atom::{Atom, Atomic, AtomicResult, CallData, RunData};
use super::error::{ExternError, ExternResult}; use super::error::{ExternErrorObj, ExternResult};
use super::process::Unstable; use super::process::Unstable;
use super::to_clause::ToClause; use super::to_clause::ToClause;
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::Gen; use crate::gen::traits::Gen;
use crate::interpreter::apply::CallData;
use crate::interpreter::error::RunError; use crate::interpreter::error::RunError;
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort::{Clause, ClauseInst}; use crate::interpreter::nort::{Clause, Expr};
use crate::interpreter::run::RunData;
use crate::location::CodeLocation; use crate::location::CodeLocation;
use crate::utils::clonable_iter::Clonable; use crate::utils::clonable_iter::Clonable;
use crate::utils::ddispatch::Responder; use crate::utils::ddispatch::Responder;
@@ -40,7 +37,7 @@ impl<T: ToClause, U: ToClause> ToClause for Result<T, U> {
} }
} }
struct PendingError(Arc<dyn ExternError>); struct PendingError(ExternErrorObj);
impl Responder for PendingError {} impl Responder for PendingError {}
impl Debug for PendingError { impl Debug for PendingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -50,7 +47,7 @@ impl Debug for PendingError {
impl Atomic for PendingError { impl Atomic for PendingError {
fn as_any(self: Box<Self>) -> Box<dyn Any> { self } fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
fn as_any_ref(&self) -> &dyn Any { self } fn as_any_ref(&self) -> &dyn Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None } fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { fn run(self: Box<Self>, _: RunData) -> AtomicResult {
Err(RunError::Extern(self.0)) Err(RunError::Extern(self.0))
} }

View File

@@ -4,13 +4,13 @@ use std::ops::{Deref, DerefMut};
use ordered_float::NotNan; use ordered_float::NotNan;
use super::atom::{Atom, Atomic, AtomicResult, AtomicReturn, NotAFunction}; use super::atom::{
Atom, Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData,
};
use super::error::{ExternError, ExternResult}; use super::error::{ExternError, ExternResult};
use super::try_from_expr::TryFromExpr; use super::try_from_expr::TryFromExpr;
use crate::foreign::error::AssertionError; use crate::foreign::error::AssertionError;
use crate::interpreter::apply::CallData; use crate::interpreter::nort::{Clause, Expr};
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
use crate::libs::std::number::Numeric; use crate::libs::std::number::Numeric;
use crate::libs::std::string::OrcString; use crate::libs::std::string::OrcString;
use crate::utils::ddispatch::{Request, Responder}; use crate::utils::ddispatch::{Request, Responder};
@@ -72,9 +72,9 @@ impl<T: InertPayload> Atomic for Inert<T> {
fn as_any(self: Box<Self>) -> Box<dyn Any> { self } fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
fn as_any_ref(&self) -> &dyn Any { self } fn as_any_ref(&self) -> &dyn Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None } fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult { fn run(self: Box<Self>, _: RunData) -> AtomicResult {
AtomicReturn::inert(*self, run.ctx) AtomicReturn::inert(*self)
} }
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> { fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
Err(NotAFunction(self.clone().atom_expr(call.location)).rc()) Err(NotAFunction(self.clone().atom_expr(call.location)).rc())

View File

@@ -1,11 +1,9 @@
use std::fmt::Debug; use std::fmt::Debug;
use super::atom::{Atomic, AtomicReturn}; use super::atom::{Atomic, AtomicReturn, CallData, RunData};
use super::error::ExternResult; use super::error::ExternResult;
use super::to_clause::ToClause; use super::to_clause::ToClause;
use crate::interpreter::apply::CallData; use crate::interpreter::nort::{Clause, Expr};
use crate::interpreter::nort::{Clause, ClauseInst};
use crate::interpreter::run::RunData;
use crate::utils::ddispatch::Responder; use crate::utils::ddispatch::Responder;
/// An atom that immediately decays to the result of the function when /// An atom that immediately decays to the result of the function when
@@ -33,7 +31,7 @@ impl<F: FnOnce(RunData) -> R + Send + 'static, R: ToClause> Atomic
} }
fn run(self: Box<Self>, run: RunData) -> super::atom::AtomicResult { fn run(self: Box<Self>, run: RunData) -> super::atom::AtomicResult {
let clause = self.0(run.clone()).to_clause(run.location.clone()); let clause = self.0(run.clone()).to_clause(run.location.clone());
AtomicReturn::run(clause, run) Ok(AtomicReturn::Change(0, clause))
} }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None } fn redirect(&mut self) -> Option<&mut Expr> { None }
} }

View File

@@ -14,7 +14,7 @@ impl TryFromExpr for Expr {
} }
impl TryFromExpr for ClauseInst { impl TryFromExpr for ClauseInst {
fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(expr.clause.clone()) } fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(expr.clsi()) }
} }
/// Request a value of a particular type and also return its location for /// Request a value of a particular type and also return its location for

View File

@@ -4,11 +4,15 @@
use std::fmt::Debug; use std::fmt::Debug;
use dyn_clone::{clone_box, DynClone}; use dyn_clone::{clone_box, DynClone};
use intern_all::Tok;
use itertools::Itertools;
use substack::Substack;
use trait_set::trait_set; use trait_set::trait_set;
use super::tpl; use super::tpl;
use super::traits::{Gen, GenClause}; use super::traits::{Gen, GenClause};
use crate::foreign::atom::Atomic; use crate::foreign::atom::{AtomGenerator, Atomic};
use crate::foreign::fn_bridge::{xfn, Xfn};
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort::Expr; use crate::interpreter::nort::Expr;
use crate::location::CodeLocation; use crate::location::CodeLocation;
@@ -17,22 +21,46 @@ use crate::utils::combine::Combine;
trait_set! { trait_set! {
trait TreeLeaf = Gen<Expr, [Expr; 0]> + DynClone; trait TreeLeaf = Gen<Expr, [Expr; 0]> + DynClone;
trait XfnCB = FnOnce(Substack<Tok<String>>) -> AtomGenerator + DynClone;
}
enum GCKind {
Const(Box<dyn TreeLeaf>),
Xfn(usize, Box<dyn XfnCB>),
} }
/// A leaf in the [ConstTree] /// A leaf in the [ConstTree]
#[derive(Debug)] pub struct GenConst(GCKind);
pub struct GenConst(Box<dyn TreeLeaf>);
impl GenConst { impl GenConst {
fn new(data: impl GenClause + Clone + 'static) -> Self { fn c(data: impl GenClause + Clone + 'static) -> Self { Self(GCKind::Const(Box::new(data))) }
Self(Box::new(data)) fn f<const N: usize, Argv, Ret>(f: impl Xfn<N, Argv, Ret>) -> Self {
Self(GCKind::Xfn(N, Box::new(move |stck| {
AtomGenerator::cloner(xfn(&stck.unreverse().iter().join("::"), f))
})))
}
/// Instantiate as [crate::interpreter::nort]
pub fn gen_nort(self, stck: Substack<Tok<String>>, location: CodeLocation) -> Expr {
match self.0 {
GCKind::Const(c) => c.template(nort_gen(location), []),
GCKind::Xfn(_, cb) => tpl::AnyAtom(cb(stck)).template(nort_gen(location), []),
}
}
}
impl Debug for GenConst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
GCKind::Const(c) => write!(f, "{c:?}"),
GCKind::Xfn(n, _) => write!(f, "xfn/{n}"),
} }
/// Instantiate template as [crate::interpreter::nort]
pub fn gen_nort(&self, location: CodeLocation) -> Expr {
self.0.template(nort_gen(location), [])
} }
} }
impl Clone for GenConst { impl Clone for GenConst {
fn clone(&self) -> Self { Self(clone_box(&*self.0)) } fn clone(&self) -> Self {
match &self.0 {
GCKind::Const(c) => Self(GCKind::Const(clone_box(&**c))),
GCKind::Xfn(n, cb) => Self(GCKind::Xfn(*n, clone_box(&**cb))),
}
}
} }
/// Error condition when constant trees that define the the same constant are /// Error condition when constant trees that define the the same constant are
@@ -43,9 +71,7 @@ pub struct ConflictingConsts;
impl Combine for GenConst { impl Combine for GenConst {
type Error = ConflictingConsts; type Error = ConflictingConsts;
fn combine(self, _: Self) -> Result<Self, Self::Error> { fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingConsts) }
Err(ConflictingConsts)
}
} }
/// A lightweight module tree that can be built declaratively by hand to /// A lightweight module tree that can be built declaratively by hand to
@@ -56,16 +82,14 @@ pub type ConstTree = ModEntry<GenConst, (), ()>;
/// Describe a constant /// Describe a constant
#[must_use] #[must_use]
pub fn leaf(value: impl GenClause + Clone + 'static) -> ConstTree { pub fn leaf(value: impl GenClause + Clone + 'static) -> ConstTree {
ModEntry { x: (), member: ModMember::Item(GenConst::new(value)) } ModEntry::wrap(ModMember::Item(GenConst::c(value)))
} }
/// Describe an [Atomic] /// Describe an [Atomic]
#[must_use] #[must_use]
pub fn atom_leaf(atom: impl Atomic + Clone + 'static) -> ConstTree { pub fn atom_leaf(atom: impl Atomic + Clone + 'static) -> ConstTree { leaf(tpl::V(atom)) }
leaf(tpl::V(atom))
}
/// Describe an [Atomic] which appears as an entry in a [ConstTree#tree] /// Describe an [Atomic] which appears as an entry in a [ConstTree::tree]
/// ///
/// The unarray is used to trick rustfmt into breaking the atom into a block /// The unarray is used to trick rustfmt into breaking the atom into a block
/// without breaking this call into a block /// without breaking this call into a block
@@ -77,5 +101,22 @@ pub fn atom_ent<K: AsRef<str>>(
(key, atom_leaf(atom)) (key, atom_leaf(atom))
} }
/// Describe a function
pub fn xfn_leaf<const N: usize, Argv, Ret>(f: impl Xfn<N, Argv, Ret>) -> ConstTree {
ModEntry::wrap(ModMember::Item(GenConst::f(f)))
}
/// Describe a function which appears as an entry in a [ConstTree::tree]
///
/// The unarray is used to trick rustfmt into breaking the atom into a block
/// without breaking this call into a block
#[must_use]
pub fn xfn_ent<const N: usize, Argv, Ret, K: AsRef<str>>(
key: K,
[f]: [impl Xfn<N, Argv, Ret>; 1],
) -> (K, ConstTree) {
(key, xfn_leaf(f))
}
/// Errors produced duriung the merger of constant trees /// Errors produced duriung the merger of constant trees
pub type ConstCombineErr = TreeConflict<GenConst, (), ()>; pub type ConstCombineErr = TreeConflict<GenConst, (), ()>;

View File

@@ -64,16 +64,16 @@ fn parametric_fmt(
body: &Expr, body: &Expr,
wrap_right: bool, wrap_right: bool,
) -> std::fmt::Result { ) -> std::fmt::Result {
if wrap_right { // if wrap_right {
f.write_char('(')?; f.write_char('(')?;
} // }
f.write_str(prefix)?; f.write_str(prefix)?;
f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?; f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?;
f.write_str(".")?; f.write_str(".")?;
body.deep_fmt(f, depth + 1, Wrap(false, false))?; body.deep_fmt(f, depth + 1, Wrap(false, false))?;
if wrap_right { // if wrap_right {
f.write_char(')')?; f.write_char(')')?;
} // }
Ok(()) Ok(())
} }
@@ -92,18 +92,18 @@ impl Clause {
f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET)) f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET))
}, },
Self::Apply(func, x) => { Self::Apply(func, x) => {
if wl { // if wl {
f.write_char('(')?; f.write_char('(')?;
} // }
func.deep_fmt(f, depth, Wrap(false, true))?; func.deep_fmt(f, depth, Wrap(false, true))?;
f.write_char(' ')?; f.write_char(' ')?;
x.deep_fmt(f, depth, Wrap(true, wr && !wl))?; x.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
if wl { // if wl {
f.write_char(')')?; f.write_char(')')?;
} // }
Ok(()) Ok(())
}, },
Self::Constant(token) => write!(f, "{:?}", token), Self::Constant(token) => write!(f, "{token}"),
} }
} }
} }

View File

@@ -24,7 +24,13 @@ fn clause(cls: &ir::Clause, ctx: NortBuilder<(), usize>) -> nort::Clause {
pub fn ir_to_nort(expr: &ir::Expr) -> nort::Expr { pub fn ir_to_nort(expr: &ir::Expr) -> nort::Expr {
let c = NortBuilder::new(&|count| { let c = NortBuilder::new(&|count| {
let mut count: usize = *count; let mut count: usize = *count;
Box::new(move |()| count.checked_sub(1).map(|v| count = v).is_none()) Box::new(move |()| match count {
0 => true,
_ => {
count -= 1;
false
},
})
}); });
nort::ClauseInst::new(clause(&expr.value, c)).to_expr(expr.location.clone()) nort::ClauseInst::new(clause(&expr.value, c)).to_expr(expr.location.clone())
} }

View File

@@ -1,24 +1,10 @@
use std::collections::VecDeque;
use std::mem;
use never::Never; use never::Never;
use super::context::RunContext; use super::context::RunContext;
use super::error::RunError; use super::error::RunError;
use super::nort::{Clause, ClauseInst, Expr}; use super::nort::{Clause, ClauseInst, Expr};
use super::path_set::{PathSet, Step}; use super::path_set::{PathSet, Step};
use super::run::run; use crate::foreign::atom::CallData;
use crate::location::CodeLocation;
/// Information about a function call presented to an external function
pub struct CallData<'a> {
/// Location of the function expression
pub location: CodeLocation,
/// The argument the function was called on. Functions are curried
pub arg: Expr,
/// Information relating to this interpreter run
pub ctx: RunContext<'a>,
}
/// Process the clause at the end of the provided path. Note that paths always /// Process the clause at the end of the provided path. Note that paths always
/// point to at least one target. Note also that this is not cached as a /// point to at least one target. Note also that this is not cached as a
@@ -42,15 +28,12 @@ fn map_at<E>(
_ => (), _ => (),
} }
Ok(match (source, path.next()) { Ok(match (source, path.next()) {
(Clause::Lambda { .. } | Clause::Identity(_), _) => (Clause::Lambda { .. } | Clause::Identity(_), _) => unreachable!("Handled above"),
unreachable!("Handled above"),
// If the path ends and this isn't a lambda, process it // If the path ends and this isn't a lambda, process it
(val, None) => mapper(val)?, (val, None) => mapper(val)?,
// If it's an Apply, execute the next step in the path // If it's an Apply, execute the next step in the path
(Clause::Apply { f, x }, Some(head)) => { (Clause::Apply { f, x }, Some(head)) => {
let proc = |x: &Expr| { let proc = |x: &Expr| Ok(map_at(path, &x.cls(), mapper)?.to_expr(x.location()));
Ok(map_at(path, &x.clause.cls(), mapper)?.to_expr(x.location()))
};
match head { match head {
None => Clause::Apply { f: proc(f)?, x: x.clone() }, None => Clause::Apply { f: proc(f)?, x: x.clone() },
Some(n) => { Some(n) => {
@@ -69,7 +52,12 @@ fn map_at<E>(
/// with the value in the body. Note that a path may point to multiple /// with the value in the body. Note that a path may point to multiple
/// placeholders. /// placeholders.
#[must_use] #[must_use]
fn substitute(paths: &PathSet, value: ClauseInst, body: &Clause) -> Clause { pub fn substitute(
paths: &PathSet,
value: ClauseInst,
body: &Clause,
on_sub: &mut impl FnMut(),
) -> Clause {
let PathSet { steps, next } = paths; let PathSet { steps, next } = paths;
map_at(steps.iter().cloned(), body, &mut |chkpt| -> Result<Clause, Never> { map_at(steps.iter().cloned(), body, &mut |chkpt| -> Result<Clause, Never> {
match (chkpt, next) { match (chkpt, next) {
@@ -80,18 +68,20 @@ fn substitute(paths: &PathSet, value: ClauseInst, body: &Clause) -> Clause {
let mut argv = x.clone(); let mut argv = x.clone();
let f = match conts.get(&None) { let f = match conts.get(&None) {
None => f.clone(), None => f.clone(),
Some(sp) => substitute(sp, value.clone(), &f.clause.cls()) Some(sp) => substitute(sp, value.clone(), &f.cls(), on_sub).to_expr(f.location()),
.to_expr(f.location()),
}; };
for (i, old) in argv.iter_mut().rev().enumerate() { for (i, old) in argv.iter_mut().rev().enumerate() {
if let Some(sp) = conts.get(&Some(i)) { if let Some(sp) = conts.get(&Some(i)) {
let tmp = substitute(sp, value.clone(), &old.clause.cls()); let tmp = substitute(sp, value.clone(), &old.cls(), on_sub);
*old = tmp.to_expr(old.location()); *old = tmp.to_expr(old.location());
} }
} }
Ok(Clause::Apply { f, x: argv }) Ok(Clause::Apply { f, x: argv })
}, },
(Clause::LambdaArg, None) => Ok(Clause::Identity(value.clone())), (Clause::LambdaArg, None) => {
on_sub();
Ok(Clause::Identity(value.clone()))
},
(_, None) => panic!("Argument path must point to LambdaArg"), (_, None) => panic!("Argument path must point to LambdaArg"),
(_, Some(_)) => panic!("Argument path can only fork at Apply"), (_, Some(_)) => panic!("Argument path can only fork at Apply"),
} }
@@ -99,11 +89,7 @@ fn substitute(paths: &PathSet, value: ClauseInst, body: &Clause) -> Clause {
.unwrap_or_else(|e| match e {}) .unwrap_or_else(|e| match e {})
} }
pub(super) fn apply_as_atom( pub(super) fn apply_as_atom(f: Expr, arg: Expr, ctx: RunContext) -> Result<Clause, RunError> {
f: Expr,
arg: Expr,
ctx: RunContext,
) -> Result<Clause, RunError> {
let call = CallData { location: f.location(), arg, ctx }; let call = CallData { location: f.location(), arg, ctx };
match f.clause.try_unwrap() { match f.clause.try_unwrap() {
Ok(clause) => match clause { Ok(clause) => match clause {
@@ -116,73 +102,3 @@ pub(super) fn apply_as_atom(
}, },
} }
} }
/// Apply a function-like expression to a parameter.
pub(super) fn apply(
mut f: Expr,
mut argv: VecDeque<Expr>,
mut ctx: RunContext,
) -> Result<(Option<usize>, Clause), RunError> {
// allow looping but break on the main path so that `continue` functions as a
// trampoline
loop {
if argv.is_empty() {
return Ok((ctx.gas, f.clause.into_cls()));
} else if ctx.gas == Some(0) {
return Ok((Some(0), Clause::Apply { f, x: argv }));
}
let mut f_cls = f.clause.cls_mut();
match &mut *f_cls {
// apply an ExternFn or an internal function
Clause::Atom(_) => {
mem::drop(f_cls);
// take a step in expanding atom
let halt = run(f, ctx.clone())?;
ctx.gas = halt.gas;
if halt.inert && halt.state.clause.is_atom() {
let arg = argv.pop_front().expect("checked above");
let loc = halt.state.location();
f = apply_as_atom(halt.state, arg, ctx.clone())?.to_expr(loc)
} else {
f = halt.state
}
},
Clause::Lambda { args, body } => {
match args {
None => *f_cls = body.clause.clone().into_cls(),
Some(args) => {
let arg = argv.pop_front().expect("checked above").clause.clone();
let cls = substitute(args, arg, &body.clause.cls());
// cost of substitution
// XXX: should this be the number of occurrences instead?
ctx.use_gas(1);
mem::drop(f_cls);
f = cls.to_expr(f.location());
},
}
},
Clause::Constant(name) => {
let name = name.clone();
mem::drop(f_cls);
f = (ctx.symbols.get(&name).cloned())
.ok_or_else(|| RunError::MissingSymbol(name, f.location()))?;
ctx.use_gas(1);
},
Clause::Apply { f: fun, x } => {
for item in x.drain(..).rev() {
argv.push_front(item)
}
let tmp = fun.clone();
mem::drop(f_cls);
f = tmp;
},
Clause::Identity(f2) => {
let tmp = f2.clone();
mem::drop(f_cls);
f.clause = tmp
},
Clause::Bottom(bottom) => return Err(bottom.clone()),
Clause::LambdaArg => panic!("Leftover argument marker"),
}
}
}

View File

@@ -10,6 +10,8 @@ pub struct RunContext<'a> {
pub symbols: &'a HashMap<Sym, Expr>, pub symbols: &'a HashMap<Sym, Expr>,
/// The number of reduction steps the interpreter can take before returning /// The number of reduction steps the interpreter can take before returning
pub gas: Option<usize>, pub gas: Option<usize>,
/// The limit of recursion
pub stack_size: usize,
} }
impl<'a> RunContext<'a> { impl<'a> RunContext<'a> {
/// Consume some gas if it is being counted /// Consume some gas if it is being counted

View File

@@ -1,34 +1,66 @@
use std::fmt::{Debug, Display}; use std::fmt::{self, Debug, Display};
use std::sync::Arc;
use crate::foreign::error::ExternError; use itertools::Itertools;
use super::nort::Expr;
use super::run::Interrupted;
use crate::foreign::error::{ExternError, ExternErrorObj};
use crate::location::CodeLocation; use crate::location::CodeLocation;
use crate::name::Sym; use crate::name::Sym;
use super::run::Interrupted; /// Print a stack trace
pub fn strace(stack: &[Expr]) -> String {
stack.iter().rev().map(|x| format!("{x}\n at {}", x.location)).join("\n")
}
/// Problems in the process of execution /// Problems in the process of execution
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum RunError { pub enum RunError {
/// A Rust function encountered an error /// A Rust function encountered an error
Extern(Arc<dyn ExternError>), Extern(ExternErrorObj),
/// Symbol not in context
MissingSymbol(Sym, CodeLocation),
/// Ran out of gas /// Ran out of gas
Interrupted(Interrupted) Interrupted(Interrupted),
} }
impl From<Arc<dyn ExternError>> for RunError { impl<T: ExternError + 'static> From<T> for RunError {
fn from(value: Arc<dyn ExternError>) -> Self { Self::Extern(value) } fn from(value: T) -> Self { Self::Extern(value.rc()) }
}
impl From<ExternErrorObj> for RunError {
fn from(value: ExternErrorObj) -> Self { Self::Extern(value) }
} }
impl Display for RunError { impl Display for RunError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Extern(e) => write!(f, "Error in external function: {e}"), Self::Interrupted(i) => {
Self::MissingSymbol(sym, loc) => { write!(f, "Ran out of gas:\n{}", strace(&i.stack))
write!(f, "{sym}, called at {loc} is not loaded")
}, },
Self::Extern(e) => write!(f, "Program fault: {e}"),
} }
} }
} }
#[derive(Clone)]
pub(crate) struct StackOverflow {
pub stack: Vec<Expr>,
}
impl ExternError for StackOverflow {}
impl Display for StackOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let limit = self.stack.len() - 2; // 1 for failed call, 1 for current
write!(f, "Stack depth exceeded {limit}:\n{}", strace(&self.stack))
}
}
#[derive(Clone)]
pub(crate) struct MissingSymbol {
pub sym: Sym,
pub loc: CodeLocation,
}
impl ExternError for MissingSymbol {}
impl Display for MissingSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}, called at {} is not loaded", self.sym, self.loc)
}
}

View File

@@ -75,12 +75,13 @@ impl<'a> HandlerTable<'a> {
pub fn run_handler( pub fn run_handler(
mut state: Expr, mut state: Expr,
handlers: &mut HandlerTable, handlers: &mut HandlerTable,
RunContext { mut gas, symbols }: RunContext, mut ctx: RunContext,
) -> Result<Halt, RunError> { ) -> Result<Halt, RunError> {
loop { loop {
let inert; let halt = run(state, ctx.clone())?;
Halt { gas, inert, state } = run(state, RunContext { gas, symbols })?; state = halt.state;
let state_cls = state.clause.cls(); ctx.use_gas(halt.gas.unwrap_or(0));
let state_cls = state.cls();
if let Clause::Atom(Atom(a)) = &*state_cls { if let Clause::Atom(Atom(a)) = &*state_cls {
if let Some(res) = handlers.dispatch(a.as_ref(), state.location()) { if let Some(res) = handlers.dispatch(a.as_ref(), state.location()) {
drop(state_cls); drop(state_cls);
@@ -88,9 +89,9 @@ pub fn run_handler(
continue; continue;
} }
} }
if inert || gas == Some(0) { if halt.inert || ctx.no_gas() {
drop(state_cls); drop(state_cls);
break Ok(Halt { gas, inert, state }); break Ok(Halt { gas: ctx.gas, inert: halt.inert, state });
} }
} }
} }

View File

@@ -97,6 +97,28 @@ impl Expr {
| Clause::Bottom(_) => None, | Clause::Bottom(_) => None,
}) })
} }
/// Clone the refcounted [ClauseInst] out of the expression
#[must_use]
pub fn clsi(&self) -> ClauseInst { self.clause.clone() }
/// Readonly access to the [Clause]
///
/// # Panics
///
/// if the clause is already borrowed
#[must_use]
pub fn cls(&self) -> impl Deref<Target = Clause> + '_ { self.clause.cls() }
/// Read-Write access to the [Clause]
///
/// # Panics
///
/// if the clause is already borrowed
#[must_use]
pub fn cls_mut(&self) -> impl DerefMut<Target = Clause> + '_ {
self.clause.cls_mut()
}
} }
impl Debug for Expr { impl Debug for Expr {
@@ -160,15 +182,21 @@ impl ClauseInst {
/// Call a normalization function on the expression. The expr is /// Call a normalization function on the expression. The expr is
/// updated with the new clause which affects all copies of it /// updated with the new clause which affects all copies of it
/// across the tree. /// across the tree.
///
/// This function bypasses and collapses identities, but calling it in a plain
/// loop intermittently re-acquires the mutex, and looping inside of it breaks
/// identity collapsing. [ClauseInst::try_normalize_trampoline] solves these
/// problems.
pub fn try_normalize<T>( pub fn try_normalize<T>(
&self, &self,
mapper: impl FnOnce(Clause) -> Result<(Clause, T), RunError>, mapper: impl FnOnce(Clause) -> Result<(Clause, T), RunError>,
) -> Result<(ClauseInst, T), RunError> { ) -> Result<(Self, T), RunError> {
enum Report<T> { enum Report<T> {
Nested(ClauseInst, T), Nested(ClauseInst, T),
Plain(T), Plain(T),
} }
let ret = take_with_output(&mut *self.cls_mut(), |clause| match &clause { let ret = take_with_output(&mut *self.cls_mut(), |clause| match &clause {
// don't modify identities, instead update and return the nested clause
Clause::Identity(alt) => match alt.try_normalize(mapper) { Clause::Identity(alt) => match alt.try_normalize(mapper) {
Ok((nested, t)) => (clause, Ok(Report::Nested(nested, t))), Ok((nested, t)) => (clause, Ok(Report::Nested(nested, t))),
Err(e) => (Clause::Bottom(e.clone()), Err(e)), Err(e) => (Clause::Bottom(e.clone()), Err(e)),
@@ -184,6 +212,32 @@ impl ClauseInst {
}) })
} }
/// Repeatedly call a normalization function on the held clause, switching
/// [ClauseInst] values as needed to ensure that
pub fn try_normalize_trampoline<T>(
mut self,
mut mapper: impl FnMut(Clause) -> Result<(Clause, Option<T>), RunError>,
) -> Result<(Self, T), RunError> {
loop {
let (next, exit) = self.try_normalize(|mut cls| {
loop {
if matches!(cls, Clause::Identity(_)) {
break Ok((cls, None));
}
let (next, exit) = mapper(cls)?;
if let Some(exit) = exit {
break Ok((next, Some(exit)));
}
cls = next;
}
})?;
if let Some(exit) = exit {
break Ok((next, exit));
}
self = next
}
}
/// Call a predicate on the clause, returning whatever the /// Call a predicate on the clause, returning whatever the
/// predicate returns. This is a convenience function for reaching /// predicate returns. This is a convenience function for reaching
/// through the [Mutex]. The clause will never be [Clause::Identity]. /// through the [Mutex]. The clause will never be [Clause::Identity].
@@ -320,11 +374,11 @@ impl Display for Clause {
Clause::Apply { f: fun, x } => Clause::Apply { f: fun, x } =>
write!(f, "({fun} {})", x.iter().join(" ")), write!(f, "({fun} {})", x.iter().join(" ")),
Clause::Lambda { args, body } => match args { Clause::Lambda { args, body } => match args {
Some(path) => write!(f, "\\{path:?}.{body}"), Some(path) => write!(f, "[\\{path}.{body}]"),
None => write!(f, "\\_.{body}"), None => write!(f, "[\\_.{body}]"),
}, },
Clause::Constant(t) => write!(f, "{t}"), Clause::Constant(t) => write!(f, "{t}"),
Clause::Identity(other) => write!(f, "({other})"), Clause::Identity(other) => write!(f, "{{{other}}}"),
} }
} }
} }

View File

@@ -67,8 +67,7 @@ impl<'a, T: ?Sized, U: ?Sized> NortBuilder<'a, T, U> {
} }
fn non_app_step<V>(self, f: impl FnOnce(NortBuilder<T, U>) -> V) -> V { fn non_app_step<V>(self, f: impl FnOnce(NortBuilder<T, U>) -> V) -> V {
if let Some(IntGenData::Apply(_)) = self.stack.value() { if let Some(IntGenData::Apply(_)) = self.stack.value() {
let prev = self.pop(1); f(self.pop(1).push(IntGenData::AppF))
f(prev.push(IntGenData::AppF))
} else { } else {
f(self) f(self)
} }
@@ -80,14 +79,17 @@ impl<'a, T: ?Sized, U: ?Sized> NortBuilder<'a, T, U> {
pub fn arg_logic(self, name: &'a U) { pub fn arg_logic(self, name: &'a U) {
let mut lambda_chk = (self.lambda_picker)(name); let mut lambda_chk = (self.lambda_picker)(name);
self.non_app_step(|ctx| { self.non_app_step(|ctx| {
let opt = ctx.stack.rfold(None, |path, item| match item { let res = ctx.stack.iter().try_fold(vec![], |path, item| match item {
IntGenData::Apply(_) => panic!("This is removed after handling"), IntGenData::Apply(_) => panic!("This is removed after handling"),
IntGenData::Lambda(n, rc) => IntGenData::Lambda(n, rc) => match lambda_chk(n) {
lambda_chk(n).then(|| (vec![], *rc)).or(path), false => Ok(path),
IntGenData::AppArg(n) => path.map(|(p, rc)| (pushed(p, Some(*n)), rc)), true => Err((path, *rc))
IntGenData::AppF => path.map(|(p, rc)| (pushed(p, None), rc)), },
IntGenData::AppArg(n) => Ok(pushed(path, Some(*n))),
IntGenData::AppF => Ok(pushed(path, None)),
}); });
let (path, slot) = opt.expect("Argument not wrapped in matching lambda"); let (mut path, slot) = res.expect_err("Argument not wrapped in matching lambda");
path.reverse();
match &mut *slot.borrow_mut() { match &mut *slot.borrow_mut() {
slot @ None => *slot = Some(PathSet::end(path)), slot @ None => *slot = Some(PathSet::end(path)),
Some(slot) => take_mut::take(slot, |p| p.overlay(PathSet::end(path))), Some(slot) => take_mut::take(slot, |p| p.overlay(PathSet::end(path))),

View File

@@ -1,101 +1,129 @@
use std::collections::VecDeque; use std::mem;
use hashbrown::HashMap;
use super::apply::apply;
use super::context::{Halt, RunContext}; use super::context::{Halt, RunContext};
use super::error::RunError; use super::error::RunError;
use super::nort::{Clause, Expr}; use super::nort::{Clause, Expr};
use crate::foreign::atom::AtomicReturn; use crate::foreign::atom::{AtomicReturn, RunData};
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternError;
use crate::location::CodeLocation; use crate::interpreter::apply::{apply_as_atom, substitute};
use crate::name::Sym; use crate::interpreter::error::{strace, MissingSymbol, StackOverflow};
use crate::utils::pure_seq::pushed; use crate::utils::pure_seq::pushed;
/// Information about a normalization run presented to an atom /// Interpreter state when processing was interrupted
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct RunData<'a> {
/// Location of the atom
pub location: CodeLocation,
/// Information about the execution
pub ctx: RunContext<'a>,
}
#[derive(Debug)]
pub struct Interrupted { pub struct Interrupted {
stack: Vec<Expr>, /// Cached soft stack to save the interpreter having to rebuild it from the
/// bottom.
pub stack: Vec<Expr>,
} }
impl Interrupted { impl Interrupted {
pub fn resume(self, ctx: RunContext) -> Result<Halt, RunError> { /// Continue processing where it was interrupted
run_stack(self.stack, ctx) pub fn resume(self, ctx: RunContext) -> Result<Halt, RunError> { run_stack(self.stack, ctx) }
}
} }
/// Normalize an expression using beta reduction with memoization /// Normalize an expression using beta reduction with memoization
pub fn run(mut expr: Expr, mut ctx: RunContext) -> Result<Halt, RunError> { pub fn run(expr: Expr, ctx: RunContext) -> Result<Halt, RunError> {
run_stack(vec![expr], ctx) let mut v = Vec::with_capacity(1000);
v.push(expr);
run_stack(v, ctx)
} }
fn run_stack( fn run_stack(mut stack: Vec<Expr>, mut ctx: RunContext) -> Result<Halt, RunError> {
mut stack: Vec<Expr>,
mut ctx: RunContext,
) -> Result<Halt, RunError> {
let mut expr = stack.pop().expect("Empty stack"); let mut expr = stack.pop().expect("Empty stack");
let mut popped = false;
loop { loop {
// print!("Now running {expr}");
// let trace = strace(&stack);
// if trace.is_empty() {
// println!("\n")
// } else {
// println!("\n{trace}\n")
// };
if ctx.no_gas() { if ctx.no_gas() {
return Err(RunError::Interrupted(Interrupted { return Err(RunError::Interrupted(Interrupted { stack: pushed(stack, expr) }));
stack: pushed(stack, expr),
}));
} }
let (next_clsi, inert) = expr.clause.try_normalize(|mut cls| { ctx.use_gas(1);
loop { enum Res {
if ctx.no_gas() { Inert,
return Ok((cls, false)); Cont,
Push(Expr),
} }
match cls { let (next_clsi, res) = expr.clause.try_normalize(|cls| match cls {
cls @ Clause::Identity(_) => return Ok((cls, false)), Clause::Identity(_) => panic!("Passed by try_normalize"),
// TODO: Clause::LambdaArg => panic!("Unbound argument"),
// - unfuck nested loop Clause::Lambda { .. } => Ok((cls, Res::Inert)),
// - inline most of [apply] to eliminate recursion step Clause::Bottom(b) => Err(b),
Clause::Apply { f, x } => { Clause::Constant(n) => match ctx.symbols.get(&n) {
Some(expr) => Ok((Clause::Identity(expr.clsi()), Res::Cont)),
None => Err(RunError::Extern(MissingSymbol { sym: n.clone(), loc: expr.location() }.rc())),
},
Clause::Atom(mut a) => {
if !popped {
if let Some(delegate) = a.0.redirect() {
let next = delegate.clone();
return Ok((Clause::Atom(a), Res::Push(next)));
}
}
let rd = RunData { ctx: ctx.clone(), location: expr.location() };
match a.run(rd)? {
AtomicReturn::Inert(c) => Ok((c, Res::Inert)),
AtomicReturn::Change(gas, c) => {
ctx.use_gas(gas);
Ok((c, Res::Cont))
},
}
},
Clause::Apply { f, mut x } => {
if x.is_empty() { if x.is_empty() {
return Ok((f.clause.into_cls(), false)); return Ok((Clause::Identity(f.clsi()), Res::Cont));
} }
let (gas, clause) = apply(f, x, ctx.clone())?; match &*f.cls() {
if ctx.gas.is_some() { Clause::Identity(f2) =>
ctx.gas = gas; return Ok((Clause::Apply { f: f2.clone().to_expr(f.location()), x }, Res::Cont)),
Clause::Apply { f, x: x2 } => {
for item in x2.iter().rev() {
x.push_front(item.clone())
} }
cls = clause; return Ok((Clause::Apply { f: f.clone(), x }, Res::Cont));
}, },
Clause::Atom(data) => { _ => (),
let run = RunData { ctx: ctx.clone(), location: expr.location() };
let atomic_ret = data.run(run)?;
if ctx.gas.is_some() {
ctx.gas = atomic_ret.gas;
} }
if atomic_ret.inert { if !popped {
return Ok((atomic_ret.clause, true)); return Ok((Clause::Apply { f: f.clone(), x }, Res::Push(f)));
} }
cls = atomic_ret.clause; let f_cls = f.cls();
let arg = x.pop_front().expect("checked above");
let loc = f.location();
let f = match &*f_cls {
Clause::Atom(_) => {
mem::drop(f_cls);
apply_as_atom(f, arg, ctx.clone())?
}, },
Clause::Constant(c) => { Clause::Lambda { args, body } => match args {
let symval = (ctx.symbols.get(&c)).ok_or_else(|| { None => body.clsi().into_cls(),
RunError::MissingSymbol(c.clone(), expr.location()) Some(args) => substitute(args, arg.clsi(), &body.cls(), &mut || ctx.use_gas(1)),
})?;
ctx.gas = ctx.gas.map(|g| g - 1); // cost of lookup
cls = Clause::Identity(symval.clause.clone());
}, },
// non-reducible c => panic!("Run should never settle on {c}"),
c => return Ok((c, true)),
}; };
} Ok((Clause::Apply { f: f.to_expr(loc), x }, Res::Cont))
},
})?; })?;
expr.clause = next_clsi; expr.clause = next_clsi;
if inert { popped = matches!(res, Res::Inert);
match stack.pop() { match res {
Some(e) => expr = e, Res::Cont => continue,
None => return Ok(Halt { state: expr, gas: ctx.gas, inert }), Res::Inert => match stack.pop() {
None => return Ok(Halt { state: expr, gas: ctx.gas, inert: true }),
Some(prev) => expr = prev,
},
Res::Push(next) => {
if stack.len() == ctx.stack_size {
stack.extend([expr, next]);
return Err(RunError::Extern(StackOverflow { stack }.rc()));
} }
stack.push(expr);
expr = next;
},
} }
} }
} }

View File

@@ -20,11 +20,10 @@ use crate::facade::system::{IntoSystem, System};
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::cps_box::CPSBox; use crate::foreign::cps_box::CPSBox;
use crate::foreign::error::ExternError; use crate::foreign::error::ExternError;
use crate::foreign::fn_bridge::constructors::xfn_2ary;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::Gen; use crate::gen::traits::Gen;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::handler::HandlerTable; use crate::interpreter::handler::HandlerTable;
use crate::interpreter::nort::Expr; use crate::interpreter::nort::Expr;
@@ -81,9 +80,7 @@ impl Display for InfiniteBlock {
pub struct MessagePort(Sender<Box<dyn Any + Send>>); pub struct MessagePort(Sender<Box<dyn Any + Send>>);
impl MessagePort { impl MessagePort {
/// Send an event. Any type is accepted, handlers are dispatched by type ID /// Send an event. Any type is accepted, handlers are dispatched by type ID
pub fn send<T: Send + 'static>(&mut self, message: T) { pub fn send<T: Send + 'static>(&mut self, message: T) { let _ = self.0.send(Box::new(message)); }
let _ = self.0.send(Box::new(message));
}
} }
fn gen() -> CodeGenInfo { CodeGenInfo::no_details("asynch") } fn gen() -> CodeGenInfo { CodeGenInfo::no_details("asynch") }
@@ -124,17 +121,10 @@ impl<'a> AsynchSystem<'a> {
/// # Panics /// # Panics
/// ///
/// if the given type is already handled. /// if the given type is already handled.
pub fn register<T: 'static>( pub fn register<T: 'static>(&mut self, mut f: impl FnMut(Box<T>) -> Vec<Expr> + 'a) {
&mut self,
mut f: impl FnMut(Box<T>) -> Vec<Expr> + 'a,
) {
let cb = move |a: Box<dyn Any>| f(a.downcast().expect("keyed by TypeId")); let cb = move |a: Box<dyn Any>| f(a.downcast().expect("keyed by TypeId"));
let prev = self.handlers.insert(TypeId::of::<T>(), Box::new(cb)); let prev = self.handlers.insert(TypeId::of::<T>(), Box::new(cb));
assert!( assert!(prev.is_none(), "Duplicate handlers for async event {}", type_name::<T>())
prev.is_none(),
"Duplicate handlers for async event {}",
type_name::<T>()
)
} }
/// Obtain a message port for sending messages to the main thread. If an /// Obtain a message port for sending messages to the main thread. If an
@@ -189,16 +179,13 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
PollEvent::Recurring(expr) => return Ok(expr), PollEvent::Recurring(expr) => return Ok(expr),
PollEvent::Event(ev) => { PollEvent::Event(ev) => {
let handler = (handlers.get_mut(&ev.as_ref().type_id())) let handler = (handlers.get_mut(&ev.as_ref().type_id()))
.unwrap_or_else(|| { .unwrap_or_else(|| panic!("Unhandled messgae type: {:?}", (*ev).type_id()));
panic!("Unhandled messgae type: {:?}", (*ev).type_id())
});
let events = handler(ev); let events = handler(ev);
// we got new microtasks // we got new microtasks
if !events.is_empty() { if !events.is_empty() {
microtasks = VecDeque::from(events); microtasks = VecDeque::from(events);
// trampoline // trampoline
let loc = let loc = CodeLocation::Gen(CodeGenInfo::no_details("system::asynch"));
CodeLocation::Gen(CodeGenInfo::no_details("system::asynch"));
return Ok(Inert(Yield).atom_expr(loc)); return Ok(Inert(Yield).atom_expr(loc));
} }
}, },
@@ -211,8 +198,8 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
lexer_plugins: vec![], lexer_plugins: vec![],
line_parsers: vec![], line_parsers: vec![],
constants: ConstTree::ns("system::async", [ConstTree::tree([ constants: ConstTree::ns("system::async", [ConstTree::tree([
("set_timer", atom_leaf(xfn_2ary(set_timer))), xfn_ent("set_timer", [set_timer]),
("yield", atom_leaf(Inert(Yield))), atom_ent("yield", [Inert(Yield)]),
])]), ])]),
code: code(), code: code(),
prelude: Vec::new(), prelude: Vec::new(),

View File

@@ -7,13 +7,12 @@ use crate::facade::system::{IntoSystem, System};
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::cps_box::CPSBox; use crate::foreign::cps_box::CPSBox;
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::process::Unstable; use crate::foreign::process::Unstable;
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::Gen; use crate::gen::traits::Gen;
use crate::gen::tree::{atom_ent, atom_leaf, ConstTree}; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::handler::HandlerTable; use crate::interpreter::handler::HandlerTable;
use crate::interpreter::nort::{Clause, Expr}; use crate::interpreter::nort::{Clause, Expr};
@@ -80,10 +79,7 @@ fn read_dir(sched: &SeqScheduler, cmd: &CPSBox<ReadDirCmd>) -> Expr {
Ok(os_namev) => { Ok(os_namev) => {
let converted = (os_namev.into_iter()) let converted = (os_namev.into_iter())
.map(|(n, d)| { .map(|(n, d)| {
Ok(( Ok((Inert(n).atom_expr(succ.location()), Inert(d).atom_expr(succ.location())))
Inert(n).atom_expr(succ.location()),
Inert(d).atom_expr(succ.location()),
))
}) })
.collect::<Result<Vec<_>, Clause>>(); .collect::<Result<Vec<_>, Clause>>();
match converted { match converted {
@@ -124,13 +120,9 @@ fn write_file(sched: &SeqScheduler, cmd: &CPSBox<WriteFile>) -> Expr {
tpl.template(nort_gen(cont.location()), [cont]) tpl.template(nort_gen(cont.location()), [cont])
} }
fn open_file_read_cmd(name: OsString) -> CPSBox<ReadFileCmd> { fn open_file_read_cmd(name: OsString) -> CPSBox<ReadFileCmd> { CPSBox::new(3, ReadFileCmd(name)) }
CPSBox::new(3, ReadFileCmd(name))
}
fn read_dir_cmd(name: OsString) -> CPSBox<ReadDirCmd> { fn read_dir_cmd(name: OsString) -> CPSBox<ReadDirCmd> { CPSBox::new(3, ReadDirCmd(name)) }
CPSBox::new(3, ReadDirCmd(name))
}
fn open_file_write_cmd(name: OsString) -> CPSBox<WriteFile> { fn open_file_write_cmd(name: OsString) -> CPSBox<WriteFile> {
CPSBox::new(3, WriteFile { name, append: false }) CPSBox::new(3, WriteFile { name, append: false })
@@ -146,9 +138,7 @@ fn join_paths(root: OsString, sub: OsString) -> OsString {
path.into_os_string() path.into_os_string()
} }
fn pop_path( fn pop_path(path: Inert<OsString>) -> Option<(Inert<OsString>, Inert<OsString>)> {
path: Inert<OsString>,
) -> Option<(Inert<OsString>, Inert<OsString>)> {
let mut path = PathBuf::from(path.0); let mut path = PathBuf::from(path.0);
let sub = path.file_name()?.to_owned(); let sub = path.file_name()?.to_owned();
debug_assert!(path.pop(), "file_name above returned Some"); debug_assert!(path.pop(), "file_name above returned Some");
@@ -181,15 +171,15 @@ impl IntoSystem<'static> for DirectFS {
lexer_plugins: vec![], lexer_plugins: vec![],
line_parsers: vec![], line_parsers: vec![],
constants: ConstTree::ns("system::fs", [ConstTree::tree([ constants: ConstTree::ns("system::fs", [ConstTree::tree([
("read_file", atom_leaf(xfn_1ary(open_file_read_cmd))), xfn_ent("read_file", [open_file_read_cmd]),
("read_dir", atom_leaf(xfn_1ary(read_dir_cmd))), xfn_ent("read_dir", [read_dir_cmd]),
("write_file", atom_leaf(xfn_1ary(open_file_write_cmd))), xfn_ent("write_file", [open_file_write_cmd]),
("append_file", atom_leaf(xfn_1ary(open_file_append_cmd))), xfn_ent("append_file", [open_file_append_cmd]),
("join_paths", atom_leaf(xfn_2ary(join_paths))), xfn_ent("join_paths", [join_paths]),
("pop_path", atom_leaf(xfn_1ary(pop_path))), xfn_ent("pop_path", [pop_path]),
atom_ent("cwd", [Unstable::new(|_| -> ExternResult<_> { atom_ent("cwd", [Unstable::new(|_| -> ExternResult<_> {
let path = std::env::current_dir() let path =
.map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?; std::env::current_dir().map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?;
Ok(Inert(path.into_os_string())) Ok(Inert(path.into_os_string()))
})]), })]),
])]) ])])

View File

@@ -2,11 +2,10 @@ use std::ffi::OsString;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::foreign::try_from_expr::TryFromExpr; use crate::foreign::try_from_expr::TryFromExpr;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::nort::{Clause, Expr}; use crate::interpreter::nort::{Clause, Expr};
use crate::libs::std::string::OrcString; use crate::libs::std::string::OrcString;
use crate::location::CodeLocation; use crate::location::CodeLocation;
@@ -21,24 +20,20 @@ impl ToClause for OsString {
fn to_clause(self, _: CodeLocation) -> Clause { Inert(self).atom_cls() } fn to_clause(self, _: CodeLocation) -> Clause { Inert(self).atom_cls() }
} }
pub fn os_to_string( pub fn os_to_string(os: Inert<OsString>) -> Result<Inert<OrcString>, Inert<OsString>> {
os: Inert<OsString>,
) -> Result<Inert<OrcString>, Inert<OsString>> {
os.0.into_string().map(|s| Inert(s.into())).map_err(Inert) os.0.into_string().map(|s| Inert(s.into())).map_err(Inert)
} }
pub fn string_to_os(str: Inert<OrcString>) -> Inert<OsString> { pub fn string_to_os(str: Inert<OrcString>) -> Inert<OsString> { Inert(str.0.get_string().into()) }
Inert(str.0.get_string().into())
}
pub fn os_print(os: Inert<OsString>) -> Inert<OrcString> { pub fn os_print(os: Inert<OsString>) -> Inert<OrcString> {
Inert(os.0.to_string_lossy().to_string().into()) Inert(os.0.to_string_lossy().to_string().into())
} }
pub fn os_string_lib() -> ConstTree { pub fn os_string_lib() -> ConstTree {
ConstTree::tree([ ConstTree::ns("system::fs", [ConstTree::tree([
("os_to_string", atom_leaf(xfn_1ary(os_to_string))), xfn_ent("os_to_string", [os_to_string]),
("string_to_os", atom_leaf(xfn_1ary(string_to_os))), xfn_ent("string_to_os", [string_to_os]),
("os_print", atom_leaf(xfn_1ary(os_print))), xfn_ent("os_print", [os_print]),
]) ])])
} }

View File

@@ -3,9 +3,8 @@ use super::instances::{BRead, ReadCmd, SRead, WriteCmd};
use super::service::{Sink, Source}; use super::service::{Sink, Source};
use crate::foreign::cps_box::CPSBox; use crate::foreign::cps_box::CPSBox;
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
use crate::foreign::inert::Inert; use crate::foreign::inert::Inert;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::libs::scheduler::system::SharedHandle; use crate::libs::scheduler::system::SharedHandle;
use crate::libs::std::binary::Binary; use crate::libs::std::binary::Binary;
use crate::libs::std::runtime_error::RuntimeError; use crate::libs::std::runtime_error::RuntimeError;
@@ -45,35 +44,27 @@ pub fn read_until(
let cmd = ReadCmd::RBytes(BRead::Until(pattern)); let cmd = ReadCmd::RBytes(BRead::Until(pattern));
Ok(CPSBox::new(3, IOCmdHandlePack { handle, cmd })) Ok(CPSBox::new(3, IOCmdHandlePack { handle, cmd }))
} }
pub fn write_str( pub fn write_str(Inert(handle): WriteHandle, string: Inert<OrcString>) -> WriteCmdPack {
Inert(handle): WriteHandle,
string: Inert<OrcString>,
) -> WriteCmdPack {
let cmd = WriteCmd::WStr(string.0.get_string()); let cmd = WriteCmd::WStr(string.0.get_string());
CPSBox::new(3, IOCmdHandlePack { handle, cmd }) CPSBox::new(3, IOCmdHandlePack { handle, cmd })
} }
pub fn write_bin( pub fn write_bin(Inert(handle): WriteHandle, bytes: Inert<Binary>) -> WriteCmdPack {
Inert(handle): WriteHandle,
bytes: Inert<Binary>,
) -> WriteCmdPack {
CPSBox::new(3, IOCmdHandlePack { handle, cmd: WriteCmd::WBytes(bytes.0) }) CPSBox::new(3, IOCmdHandlePack { handle, cmd: WriteCmd::WBytes(bytes.0) })
} }
pub fn flush(Inert(handle): WriteHandle) -> WriteCmdPack { pub fn flush(Inert(handle): WriteHandle) -> WriteCmdPack {
CPSBox::new(3, IOCmdHandlePack { handle, cmd: WriteCmd::Flush }) CPSBox::new(3, IOCmdHandlePack { handle, cmd: WriteCmd::Flush })
} }
pub fn io_bindings<'a>( pub fn io_bindings<'a>(std_streams: impl IntoIterator<Item = (&'a str, ConstTree)>) -> ConstTree {
std_streams: impl IntoIterator<Item = (&'a str, ConstTree)>,
) -> ConstTree {
ConstTree::ns("system::io", [ConstTree::tree([ ConstTree::ns("system::io", [ConstTree::tree([
("read_string", atom_leaf(xfn_1ary(read_string))), xfn_ent("read_string", [read_string]),
("read_line", atom_leaf(xfn_1ary(read_line))), xfn_ent("read_line", [read_line]),
("read_bin", atom_leaf(xfn_1ary(read_bin))), xfn_ent("read_bin", [read_bin]),
("read_n_bytes", atom_leaf(xfn_2ary(read_bytes))), xfn_ent("read_n_bytes", [read_bytes]),
("read_until", atom_leaf(xfn_2ary(read_until))), xfn_ent("read_until", [read_until]),
("write_str", atom_leaf(xfn_2ary(write_str))), xfn_ent("write_str", [write_str]),
("write_bin", atom_leaf(xfn_2ary(write_bin))), xfn_ent("write_bin", [write_bin]),
("flush", atom_leaf(xfn_1ary(flush))), xfn_ent("flush", [flush]),
]) ])
.combine(ConstTree::tree(std_streams)) .combine(ConstTree::tree(std_streams))
.expect("std_stream name clashing with io functions")]) .expect("std_stream name clashing with io functions")])

View File

@@ -2,10 +2,10 @@
//! scheduling subsystem. Other systems also take clones as dependencies. //! scheduling subsystem. Other systems also take clones as dependencies.
//! //!
//! ``` //! ```
//! use orchidlang::facade::loader::Loader;
//! use orchidlang::libs::asynch::system::AsynchSystem; //! use orchidlang::libs::asynch::system::AsynchSystem;
//! use orchidlang::libs::scheduler::system::SeqScheduler; //! use orchidlang::libs::scheduler::system::SeqScheduler;
//! use orchidlang::libs::std::std_system::StdConfig; //! use orchidlang::libs::std::std_system::StdConfig;
//! use orchidlang::facade::loader::Loader;
//! //!
//! let mut asynch = AsynchSystem::new(); //! let mut asynch = AsynchSystem::new();
//! let scheduler = SeqScheduler::new(&mut asynch); //! let scheduler = SeqScheduler::new(&mut asynch);
@@ -30,9 +30,8 @@ use super::thread_pool::ThreadPool;
use crate::facade::system::{IntoSystem, System}; use crate::facade::system::{IntoSystem, System};
use crate::foreign::cps_box::CPSBox; use crate::foreign::cps_box::CPSBox;
use crate::foreign::error::{AssertionError, ExternResult}; use crate::foreign::error::{AssertionError, ExternResult};
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::handler::HandlerTable; use crate::interpreter::handler::HandlerTable;
use crate::interpreter::nort::Expr; use crate::interpreter::nort::Expr;
use crate::libs::asynch::system::{AsynchSystem, MessagePort}; use crate::libs::asynch::system::{AsynchSystem, MessagePort};
@@ -70,9 +69,7 @@ pub struct SharedHandle<T>(pub(super) Arc<Mutex<SharedResource<T>>>);
impl<T> SharedHandle<T> { impl<T> SharedHandle<T> {
/// Wrap a value to be accessible to a [SeqScheduler]. /// Wrap a value to be accessible to a [SeqScheduler].
pub fn wrap(t: T) -> Self { pub fn wrap(t: T) -> Self { Self(Arc::new(Mutex::new(SharedResource::Free(t)))) }
Self(Arc::new(Mutex::new(SharedResource::Free(t))))
}
/// Check the state of the handle /// Check the state of the handle
pub fn state(&self) -> SharedState { pub fn state(&self) -> SharedState {
@@ -151,9 +148,7 @@ fn take_and_drop(x: Expr) -> ExternResult<CPSBox<TakeCmd>> {
} }
} }
fn is_taken_error(x: Expr) -> Inert<bool> { fn is_taken_e(x: Expr) -> Inert<bool> { Inert(x.downcast::<Inert<SealedOrTaken>>().is_ok()) }
Inert(x.downcast::<Inert<SealedOrTaken>>().is_ok())
}
trait_set! { trait_set! {
/// The part of processing a blocking I/O task that cannot be done on a remote /// The part of processing a blocking I/O task that cannot be done on a remote
@@ -221,11 +216,9 @@ impl SeqScheduler {
|state| { |state| {
match state { match state {
SharedResource::Taken => (SharedResource::Taken, Err(SealedOrTaken)), SharedResource::Taken => (SharedResource::Taken, Err(SealedOrTaken)),
SharedResource::Busy(mut b) => { SharedResource::Busy(mut b) => match b.enqueue(operation, handler, early_cancel) {
match b.enqueue(operation, handler, early_cancel) {
Some(cancelled) => (SharedResource::Busy(b), Ok(cancelled)), Some(cancelled) => (SharedResource::Busy(b), Ok(cancelled)),
None => (SharedResource::Busy(b), Err(SealedOrTaken)), None => (SharedResource::Busy(b), Err(SealedOrTaken)),
}
}, },
SharedResource::Free(t) => { SharedResource::Free(t) => {
let cancelled = CancelFlag::new(); let cancelled = CancelFlag::new();
@@ -251,11 +244,9 @@ impl SeqScheduler {
) -> CancelFlag { ) -> CancelFlag {
let cancelled = CancelFlag::new(); let cancelled = CancelFlag::new();
let canc1 = cancelled.clone(); let canc1 = cancelled.clone();
let opid = self.0.pending.borrow_mut().insert(Box::new( let opid = self.0.pending.borrow_mut().insert(Box::new(|data: Box<dyn Any + Send>, _| {
|data: Box<dyn Any + Send>, _| {
handler(*data.downcast().expect("This is associated by ID"), canc1) handler(*data.downcast().expect("This is associated by ID"), canc1)
}, }));
));
let canc1 = cancelled.clone(); let canc1 = cancelled.clone();
let mut port = self.0.port.clone(); let mut port = self.0.port.clone();
self.0.pool.submit(Box::new(move || { self.0.pool.submit(Box::new(move || {
@@ -298,8 +289,7 @@ impl SeqScheduler {
let opid = self.0.pending.borrow_mut().insert(Box::new({ let opid = self.0.pending.borrow_mut().insert(Box::new({
let cancelled = cancelled.clone(); let cancelled = cancelled.clone();
move |data: Box<dyn Any + Send>, this: SeqScheduler| { move |data: Box<dyn Any + Send>, this: SeqScheduler| {
let (t, u): (T, Box<dyn Any + Send>) = let (t, u): (T, Box<dyn Any + Send>) = *data.downcast().expect("This is associated by ID");
*data.downcast().expect("This is associated by ID");
let handle2 = handle.clone(); let handle2 = handle.clone();
take_with_output(&mut *handle.0.lock().unwrap(), |state| { take_with_output(&mut *handle.0.lock().unwrap(), |state| {
let busy = unwrap_or! { state => SharedResource::Busy; let busy = unwrap_or! { state => SharedResource::Busy;
@@ -307,15 +297,9 @@ impl SeqScheduler {
}; };
let report = busy.rotate(t, u, cancelled); let report = busy.rotate(t, u, cancelled);
match report.kind { match report.kind {
NextItemReportKind::Free(t) => NextItemReportKind::Free(t) => (SharedResource::Free(t), report.events),
(SharedResource::Free(t), report.events),
NextItemReportKind::Taken => (SharedResource::Taken, report.events), NextItemReportKind::Taken => (SharedResource::Taken, report.events),
NextItemReportKind::Next { NextItemReportKind::Next { instance, cancelled, operation, rest } => {
instance,
cancelled,
operation,
rest,
} => {
this.submit(instance, handle2, cancelled, operation); this.submit(instance, handle2, cancelled, operation);
(SharedResource::Busy(rest), report.events) (SharedResource::Busy(rest), report.events)
}, },
@@ -352,8 +336,8 @@ impl IntoSystem<'static> for SeqScheduler {
lexer_plugins: vec![], lexer_plugins: vec![],
line_parsers: vec![], line_parsers: vec![],
constants: ConstTree::ns("system::scheduler", [ConstTree::tree([ constants: ConstTree::ns("system::scheduler", [ConstTree::tree([
("is_taken_error", atom_leaf(xfn_1ary(is_taken_error))), xfn_ent("is_taken_e", [is_taken_e]),
("take_and_drop", atom_leaf(xfn_1ary(take_and_drop))), xfn_ent("take_and_drop", [take_and_drop]),
])]), ])]),
} }
} }

View File

@@ -9,11 +9,8 @@ use itertools::Itertools;
use super::runtime_error::RuntimeError; use super::runtime_error::RuntimeError;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::{
xfn_1ary, xfn_2ary, xfn_3ary, xfn_4ary,
};
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::nort::Clause; use crate::interpreter::nort::Clause;
use crate::utils::iter_find::iter_find; use crate::utils::iter_find::iter_find;
use crate::utils::unwrap_or::unwrap_or; use crate::utils::unwrap_or::unwrap_or;
@@ -40,8 +37,7 @@ impl Debug for Binary {
let a = chunk.next().expect("Chunks cannot be empty"); let a = chunk.next().expect("Chunks cannot be empty");
let b = unwrap_or!(chunk.next(); return write!(f, "{a:02x}")); let b = unwrap_or!(chunk.next(); return write!(f, "{a:02x}"));
let c = unwrap_or!(chunk.next(); return write!(f, "{a:02x}{b:02x}")); let c = unwrap_or!(chunk.next(); return write!(f, "{a:02x}{b:02x}"));
let d = let d = unwrap_or!(chunk.next(); return write!(f, "{a:02x}{b:02x}{c:02x}"));
unwrap_or!(chunk.next(); return write!(f, "{a:02x}{b:02x}{c:02x}"));
write!(f, "{a:02x}{b:02x}{c:02x}{d:02x}")? write!(f, "{a:02x}{b:02x}{c:02x}{d:02x}")?
} }
if iter.next().is_some() { write!(f, "...") } else { Ok(()) } if iter.next().is_some() { write!(f, "...") } else { Ok(()) }
@@ -55,16 +51,9 @@ pub fn concatenate(a: Inert<Binary>, b: Inert<Binary>) -> Inert<Binary> {
} }
/// Extract a subsection of the binary data /// Extract a subsection of the binary data
pub fn slice( pub fn slice(s: Inert<Binary>, i: Inert<usize>, len: Inert<usize>) -> ExternResult<Inert<Binary>> {
s: Inert<Binary>,
i: Inert<usize>,
len: Inert<usize>,
) -> ExternResult<Inert<Binary>> {
if i.0 + len.0 < s.0.0.len() { if i.0 + len.0 < s.0.0.len() {
RuntimeError::fail( RuntimeError::fail("Byte index out of bounds".to_string(), "indexing binary")?
"Byte index out of bounds".to_string(),
"indexing binary",
)?
} }
Ok(Inert(Binary(Arc::new(s.0.0[i.0..i.0 + len.0].to_vec())))) Ok(Inert(Binary(Arc::new(s.0.0[i.0..i.0 + len.0].to_vec()))))
} }
@@ -76,21 +65,12 @@ pub fn find(haystack: Inert<Binary>, needle: Inert<Binary>) -> Option<Clause> {
} }
/// Split binary data block into two smaller blocks /// Split binary data block into two smaller blocks
pub fn split( pub fn split(bin: Inert<Binary>, i: Inert<usize>) -> ExternResult<(Inert<Binary>, Inert<Binary>)> {
bin: Inert<Binary>,
i: Inert<usize>,
) -> ExternResult<(Inert<Binary>, Inert<Binary>)> {
if bin.0.0.len() < i.0 { if bin.0.0.len() < i.0 {
RuntimeError::fail( RuntimeError::fail("Byte index out of bounds".to_string(), "splitting binary")?
"Byte index out of bounds".to_string(),
"splitting binary",
)?
} }
let (asl, bsl) = bin.0.0.split_at(i.0); let (asl, bsl) = bin.0.0.split_at(i.0);
Ok(( Ok((Inert(Binary(Arc::new(asl.to_vec()))), Inert(Binary(Arc::new(bsl.to_vec())))))
Inert(Binary(Arc::new(asl.to_vec()))),
Inert(Binary(Arc::new(bsl.to_vec()))),
))
} }
/// Read a number from a binary blob /// Read a number from a binary blob
@@ -101,10 +81,7 @@ pub fn get_num(
is_le: Inert<bool>, is_le: Inert<bool>,
) -> ExternResult<Inert<usize>> { ) -> ExternResult<Inert<usize>> {
if buf.0.0.len() < (loc.0 + size.0) { if buf.0.0.len() < (loc.0 + size.0) {
RuntimeError::fail( RuntimeError::fail("section out of range".to_string(), "reading number from binary data")?
"section out of range".to_string(),
"reading number from binary data",
)?
} }
if INT_BYTES < size.0 { if INT_BYTES < size.0 {
RuntimeError::fail( RuntimeError::fail(
@@ -148,13 +125,13 @@ pub fn size(b: Inert<Binary>) -> Inert<usize> { Inert(b.0.len()) }
pub(super) fn bin_lib() -> ConstTree { pub(super) fn bin_lib() -> ConstTree {
ConstTree::ns("std::binary", [ConstTree::tree([ ConstTree::ns("std::binary", [ConstTree::tree([
("concat", atom_leaf(xfn_2ary(concatenate))), xfn_ent("concat", [concatenate]),
("slice", atom_leaf(xfn_3ary(slice))), xfn_ent("slice", [slice]),
("find", atom_leaf(xfn_2ary(find))), xfn_ent("find", [find]),
("split", atom_leaf(xfn_2ary(split))), xfn_ent("split", [split]),
("get_num", atom_leaf(xfn_4ary(get_num))), xfn_ent("get_num", [get_num]),
("from_num", atom_leaf(xfn_3ary(from_num))), xfn_ent("from_num", [from_num]),
("size", atom_leaf(xfn_1ary(size))), xfn_ent("size", [size]),
("int_bytes", atom_leaf(Inert(INT_BYTES))), atom_ent("int_bytes", [Inert(INT_BYTES)]),
])]) ])])
} }

View File

@@ -1,4 +1,5 @@
import std::(pmatch, inspect) import std::(pmatch, inspect)
import std::known::(=)
export ::(!=, ==) export ::(!=, ==)
@@ -12,7 +13,7 @@ export macro if ...$cond then ...$true else ...$false:1 =0x1p84=> (
) )
( (
macro pmatch::request (== ...$other) macro pmatch::request (= ...$other)
=0x1p230=> pmatch::response ( =0x1p230=> pmatch::response (
if pmatch::value == (...$other) if pmatch::value == (...$other)
then pmatch::pass then pmatch::pass

View File

@@ -1,14 +1,12 @@
use super::number::Numeric; use super::number::Numeric;
use super::string::OrcString; use super::string::OrcString;
use crate::foreign::error::{AssertionError, ExternResult}; use crate::foreign::error::{AssertionError, ExternResult};
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
use crate::foreign::inert::Inert; use crate::foreign::inert::Inert;
use crate::foreign::try_from_expr::WithLoc; use crate::foreign::try_from_expr::WithLoc;
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::{Gen, GenClause}; use crate::gen::traits::{Gen, GenClause};
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort_builder::NortBuilder;
use crate::interpreter::nort::Expr; use crate::interpreter::nort::Expr;
const fn left() -> impl GenClause { tpl::L("l", tpl::L("_", tpl::P("l"))) } const fn left() -> impl GenClause { tpl::L("l", tpl::L("_", tpl::P("l"))) }
@@ -28,10 +26,7 @@ pub fn if_then_else(WithLoc(loc, b): WithLoc<Inert<bool>>) -> Expr {
/// - both are string, /// - both are string,
/// - both are bool, /// - both are bool,
/// - both are either uint or num /// - both are either uint or num
pub fn equals( pub fn equals(WithLoc(loc, a): WithLoc<Expr>, b: Expr) -> ExternResult<Inert<bool>> {
WithLoc(loc, a): WithLoc<Expr>,
b: Expr,
) -> ExternResult<Inert<bool>> {
Ok(Inert(if let Ok(l) = a.clone().downcast::<Inert<OrcString>>() { Ok(Inert(if let Ok(l) = a.clone().downcast::<Inert<OrcString>>() {
b.downcast::<Inert<OrcString>>().is_ok_and(|r| *l == *r) b.downcast::<Inert<OrcString>>().is_ok_and(|r| *l == *r)
} else if let Ok(l) = a.clone().downcast::<Inert<bool>>() { } else if let Ok(l) = a.clone().downcast::<Inert<bool>>() {
@@ -45,9 +40,9 @@ pub fn equals(
pub fn bool_lib() -> ConstTree { pub fn bool_lib() -> ConstTree {
ConstTree::ns("std::bool", [ConstTree::tree([ ConstTree::ns("std::bool", [ConstTree::tree([
("ifthenelse", atom_leaf(xfn_1ary(if_then_else))), xfn_ent("ifthenelse", [if_then_else]),
("equals", atom_leaf(xfn_2ary(equals))), xfn_ent("equals", [equals]),
("true", atom_leaf(Inert(true))), atom_ent("true", [Inert(true)]),
("false", atom_leaf(Inert(false))), atom_ent("false", [Inert(false)]),
])]) ])])
} }

View File

@@ -6,14 +6,12 @@ use super::protocol::{gen_resolv, Protocol};
use super::string::OrcString; use super::string::OrcString;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::error::{AssertionError, ExternResult}; use crate::foreign::error::{AssertionError, ExternResult};
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::inert::Inert; use crate::foreign::inert::Inert;
use crate::foreign::try_from_expr::WithLoc; use crate::foreign::try_from_expr::WithLoc;
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::Gen; use crate::gen::traits::Gen;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort_builder::NortBuilder;
use crate::interpreter::nort::{ClauseInst, Expr}; use crate::interpreter::nort::{ClauseInst, Expr};
use crate::parse::numeric::parse_num; use crate::parse::numeric::parse_num;
@@ -63,9 +61,9 @@ pub fn conv_lib() -> ConstTree {
ConstTree::ns("std", [ConstTree::tree([ ConstTree::ns("std", [ConstTree::tree([
TO_STRING.as_tree_ent([]), TO_STRING.as_tree_ent([]),
ConstTree::tree_ent("conv", [ ConstTree::tree_ent("conv", [
("to_float", atom_leaf(xfn_1ary(to_float))), xfn_ent("to_float", [to_float]),
("to_uint", atom_leaf(xfn_1ary(to_uint))), xfn_ent("to_uint", [to_uint]),
("to_string", atom_leaf(xfn_1ary(to_string))), xfn_ent("to_string", [to_string]),
]), ]),
])]) ])])
} }

View File

@@ -3,7 +3,7 @@ use std::iter;
use std::rc::Rc; use std::rc::Rc;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::fn_bridge::constructors::xfn_1ary; use crate::foreign::fn_bridge::xfn;
use crate::foreign::process::Unstable; use crate::foreign::process::Unstable;
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::foreign::try_from_expr::TryFromExpr; use crate::foreign::try_from_expr::TryFromExpr;
@@ -37,12 +37,11 @@ fn table_receiver_rec<
callback: impl DeferredRuntimeCallback<T, U, R>, callback: impl DeferredRuntimeCallback<T, U, R>,
) -> impl Atomic { ) -> impl Atomic {
let t = remaining_keys.pop_front().expect("empty handled elsewhere"); let t = remaining_keys.pop_front().expect("empty handled elsewhere");
xfn_1ary(move |u: U| { xfn("__table_receiver__", move |u: U| {
let results = pushed(results, (t, u)); let results = pushed(results, (t, u));
match remaining_keys.is_empty() { match remaining_keys.is_empty() {
true => callback(results).to_clause(CodeLocation::Source(range)), true => callback(results).to_clause(CodeLocation::Source(range)),
false => false => table_receiver_rec(range, results, remaining_keys, callback).atom_cls(),
table_receiver_rec(range, results, remaining_keys, callback).atom_cls(),
} }
}) })
} }
@@ -59,9 +58,7 @@ fn table_receiver<
if keys.is_empty() { if keys.is_empty() {
Unstable::new(move |_| callback(Vec::new())).ast_cls() Unstable::new(move |_| callback(Vec::new())).ast_cls()
} else { } else {
Unstable::new(move |_| { Unstable::new(move |_| table_receiver_rec(range, Vec::new(), keys, callback).atom_cls())
table_receiver_rec(range, Vec::new(), keys, callback).atom_cls()
})
.ast_cls() .ast_cls()
} }
} }
@@ -77,10 +74,8 @@ pub fn defer_to_runtime<
pairs: impl IntoIterator<Item = (T, Vec<parsed::Expr>)>, pairs: impl IntoIterator<Item = (T, Vec<parsed::Expr>)>,
callback: impl DeferredRuntimeCallback<T, U, R>, callback: impl DeferredRuntimeCallback<T, U, R>,
) -> parsed::Clause { ) -> parsed::Clause {
let (keys, ast_values) = let (keys, ast_values) = pairs.into_iter().unzip::<_, _, VecDeque<_>, Vec<_>>();
pairs.into_iter().unzip::<_, _, VecDeque<_>, Vec<_>>(); let items = iter::once(table_receiver(range.clone(), keys, callback))
let items = iter::once(table_receiver(range.clone(), keys, callback)).chain( .chain(ast_values.into_iter().map(|v| parsed::Clause::S(PType::Par, Rc::new(v))));
ast_values.into_iter().map(|v| parsed::Clause::S(PType::Par, Rc::new(v))),
);
parsed::Clause::s('(', items, range) parsed::Clause::s('(', items, range)
} }

View File

@@ -5,9 +5,8 @@
use std::process::ExitCode; use std::process::ExitCode;
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
/// An Orchid equivalent to Rust's binary exit status model /// An Orchid equivalent to Rust's binary exit status model
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -34,8 +33,8 @@ impl InertPayload for ExitStatus {
pub(super) fn exit_status_lib() -> ConstTree { pub(super) fn exit_status_lib() -> ConstTree {
let is_success = |es: Inert<ExitStatus>| Inert(es.0 == ExitStatus::Success); let is_success = |es: Inert<ExitStatus>| Inert(es.0 == ExitStatus::Success);
ConstTree::ns("std::exit_status", [ConstTree::tree([ ConstTree::ns("std::exit_status", [ConstTree::tree([
("success", atom_leaf(Inert(ExitStatus::Success))), atom_ent("success", [Inert(ExitStatus::Success)]),
("failure", atom_leaf(Inert(ExitStatus::Failure))), atom_ent("failure", [Inert(ExitStatus::Failure)]),
("is_success", atom_leaf(xfn_1ary(is_success))), xfn_ent("is_success", [is_success]),
])]) ])])
} }

View File

@@ -1,5 +1,6 @@
import super::known::* import super::known::*
import super::pmatch::* import super::pmatch
import super::pmatch::(match, =>)
import super::macro import super::macro
--[ Do nothing. Especially useful as a passive cps operation ]-- --[ Do nothing. Especially useful as a passive cps operation ]--
@@ -24,10 +25,11 @@ export macro ...$prefix $ ...$suffix:1 =0x1p38=> ...$prefix (...$suffix)
export macro ...$prefix |> $fn ..$suffix:1 =0x2p32=> $fn (...$prefix) ..$suffix export macro ...$prefix |> $fn ..$suffix:1 =0x2p32=> $fn (...$prefix) ..$suffix
( macro (..$argv) => ...$body ( macro (..$argv) => ...$body
=0x2p127=> lambda_walker macro::comma_list (..$argv) (...$body) =0x3p127=> lambda_walker macro::comma_list (..$argv) (...$body)
) )
( macro $_arg => ...$body ( macro $_arg => ...$body
=0x2p127=> \$_arg. ...$body) =0x2p127=> \$_arg. ...$body
)
( macro lambda_walker ( macro::list_item ($_argname) $tail ) $body ( macro lambda_walker ( macro::list_item ($_argname) $tail ) $body
=0x2p254=> \$_argname. lambda_walker $tail $body =0x2p254=> \$_argname. lambda_walker $tail $body
) )

View File

@@ -1,13 +1,10 @@
use std::fmt::Debug; use std::fmt::Debug;
use crate::foreign::atom::{Atomic, AtomicResult, AtomicReturn}; use crate::foreign::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::apply::CallData; use crate::interpreter::nort::{Clause, Expr};
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
use crate::utils::ddispatch::Responder; use crate::utils::ddispatch::Responder;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -16,24 +13,20 @@ impl Responder for Inspect {}
impl Atomic for Inspect { impl Atomic for Inspect {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self } fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self } fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None } fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult { fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
AtomicReturn::inert(*self, run.ctx)
}
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> { fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
eprintln!("{}", call.arg); eprintln!("{}", call.arg);
Ok(call.arg.to_clause(call.location)) Ok(call.arg.to_clause(call.location))
} }
} }
fn tee(x: Expr) -> Expr {
eprintln!("{x}");
x
}
pub fn inspect_lib() -> ConstTree { pub fn inspect_lib() -> ConstTree {
ConstTree::ns("std", [ConstTree::tree([ ConstTree::ns("std", [ConstTree::tree([
("inspect", atom_leaf(Inspect)), atom_ent("inspect", [Inspect]),
("tee", atom_leaf(xfn_1ary(tee))), xfn_ent("tee", [|x: Expr| {
eprintln!("{x}");
x
}]),
])]) ])])
} }

View File

@@ -94,6 +94,8 @@ export const enumerate := \list. (
cons t[n, head] $ r tail $ n + 1 cons t[n, head] $ r tail $ n + 1
) )
export const count := \list. fold list 0 \a. \n. a + 1
--[ --[
Turn a list of CPS commands into a sequence. This is achieved by calling every Turn a list of CPS commands into a sequence. This is achieved by calling every
element on the return value of the next element with the tail passed to it. element on the return value of the next element with the tail passed to it.
@@ -128,10 +130,10 @@ export ::(new)
(pmatch::take_binds $h_binds ( (pmatch::take_binds $h_binds (
(\pmatch::pass. (\pmatch::value. $t_expr) tail) (\pmatch::pass. (\pmatch::value. $t_expr) tail)
(pmatch::take_binds $t_binds ( (pmatch::take_binds $t_binds (
pmatch::give_binds pmatch::chain_binds $h_binds $t_binds pmatch::pass pmatch::give_binds (pmatch::chain_binds $h_binds $t_binds) pmatch::pass
)) ))
)) ))
) )
) )
(pmatch::chain_binds $h_binds $t_binds) ( (pmatch::chain_binds $h_binds $t_binds) )
) )

View File

@@ -84,11 +84,11 @@ export ::having
pmatch::take_binds $binds ( pmatch::take_binds $binds (
(\pmatch::pass. $t_expr) ( (\pmatch::pass. $t_expr) (
pmatch::take_binds $t_binds ( pmatch::take_binds $t_binds (
pmatch::give_binds pmatch::chain_binds $binds $t_binds pmatch::pass pmatch::give_binds (pmatch::chain_binds $binds $t_binds) pmatch::pass
) )
) )
) )
) )
) )
( pmatch::chain_binds $binds $t_binds ) ( (pmatch::chain_binds $binds $t_binds) )
) )

View File

@@ -16,3 +16,4 @@ mod state;
pub mod std_system; pub mod std_system;
pub mod string; pub mod string;
pub mod tuple; pub mod tuple;
mod tstring;

View File

@@ -5,11 +5,10 @@ use ordered_float::NotNan;
use super::arithmetic_error::ArithmeticError; use super::arithmetic_error::ArithmeticError;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::error::{AssertionError, ExternError, ExternResult}; use crate::foreign::error::{AssertionError, ExternError, ExternResult};
use crate::foreign::fn_bridge::constructors::xfn_2ary;
use crate::foreign::inert::Inert; use crate::foreign::inert::Inert;
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::foreign::try_from_expr::TryFromExpr; use crate::foreign::try_from_expr::TryFromExpr;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::nort::{Clause, Expr}; use crate::interpreter::nort::{Clause, Expr};
use crate::location::CodeLocation; use crate::location::CodeLocation;
@@ -140,11 +139,11 @@ pub fn less_than(a: Numeric, b: Numeric) -> Inert<bool> {
pub(super) fn num_lib() -> ConstTree { pub(super) fn num_lib() -> ConstTree {
ConstTree::ns("std::number", [ConstTree::tree([ ConstTree::ns("std::number", [ConstTree::tree([
("add", atom_leaf(xfn_2ary(add))), xfn_ent("add", [add]),
("subtract", atom_leaf(xfn_2ary(subtract))), xfn_ent("subtract", [subtract]),
("multiply", atom_leaf(xfn_2ary(multiply))), xfn_ent("multiply", [multiply]),
("divide", atom_leaf(xfn_2ary(divide))), xfn_ent("divide", [divide]),
("remainder", atom_leaf(xfn_2ary(remainder))), xfn_ent("remainder", [remainder]),
("less_than", atom_leaf(xfn_2ary(less_than))), xfn_ent("less_than", [less_than]),
])]) ])])
} }

View File

@@ -5,9 +5,8 @@ use never::Never;
use super::string::OrcString; use super::string::OrcString;
use crate::foreign::error::{ExternError, ExternResult}; use crate::foreign::error::{ExternError, ExternResult};
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::inert::Inert; use crate::foreign::inert::Inert;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_leaf, ConstTree};
/// An unrecoverable error in Orchid land. Because Orchid is lazy, this only /// An unrecoverable error in Orchid land. Because Orchid is lazy, this only
/// invalidates expressions that reference the one that generated it. /// invalidates expressions that reference the one that generated it.
@@ -28,6 +27,4 @@ pub fn orc_panic(msg: Inert<OrcString>) -> ExternResult<Never> {
Err(OrchidPanic(Arc::new(msg.0.get_string())).rc()) Err(OrchidPanic(Arc::new(msg.0.get_string())).rc())
} }
pub fn panic_lib() -> ConstTree { pub fn panic_lib() -> ConstTree { ConstTree::ns("std::panic", [xfn_leaf(orc_panic)]) }
ConstTree::ns("std::panic", [atom_leaf(xfn_1ary(orc_panic))])
}

View File

@@ -11,6 +11,8 @@ import std::panic
Response contains an expression and the list of names Response contains an expression and the list of names
]-- ]--
export ::(match, value, pass, fail, request, response, =>)
( (
macro ..$prefix:1 match ...$argument:0 { ..$body } ..$suffix:1 macro ..$prefix:1 match ...$argument:0 { ..$body } ..$suffix:1
=0x1p130=> ..$prefix ( =0x1p130=> ..$prefix (
@@ -33,21 +35,21 @@ macro request (( ..$pattern )) =0x1p254=> request ( ..$pattern )
export ::(no_binds, add_bind, chain_binds, give_binds, take_binds) export ::(no_binds, add_bind, chain_binds, give_binds, take_binds)
macro add_bind $_new no_binds =0x1p254=> ( binds_list $_new no_binds ) macro ( add_bind $_new no_binds ) =0x1p254=> ( binds_list $_new no_binds )
( macro add_bind $_new ( binds_list ...$tail ) ( macro ( add_bind $_new (binds_list ...$tail) )
=0x1p254=> ( binds_list $_new ( binds_list ...$tail ) ) =0x1p254=> ( binds_list $_new (binds_list ...$tail) )
) )
macro give_binds no_binds $cont =0x1p254=> $cont macro ( give_binds no_binds $cont ) =0x1p254=> $cont
( macro give_binds ( binds_list $_name $tail ) $cont ( macro ( give_binds ( binds_list $_name $tail ) $cont )
=0x1p254=> (give_binds $tail $cont $_name) =0x1p254=> (( give_binds $tail $cont ) $_name )
) )
macro take_binds no_binds $cont =0x1p254=> $cont macro ( take_binds no_binds $cont ) =0x1p254=> $cont
( macro take_binds ( binds_list $_name $tail ) $cont ( macro ( take_binds ( binds_list $_name $tail ) $cont )
=0x1p254=> \$_name. take_binds $tail $cont =0x1p254=> ( take_binds $tail \$_name. $cont )
) )
macro chain_binds no_binds $second =0x1p254=> $second macro ( chain_binds no_binds $second ) =0x1p254=> $second
( macro chain_binds ( binds_list $_head $tail ) $second ( macro ( chain_binds ( binds_list $_head $tail ) $second )
=0x1p254=> add_bind $_head chain_binds $tail $second =0x1p254=> ( add_bind $_head ( chain_binds $tail $second ))
) )
--[ primitive pattern ( _ ) ]-- --[ primitive pattern ( _ ) ]--
@@ -61,7 +63,7 @@ macro chain_binds no_binds $second =0x1p254=> $second
( (
macro request ( $_name ) macro request ( $_name )
=0x1p226=> response ( pass value ) ( add_bind $_name no_binds ) =0x1p226=> response ( pass value ) ( ( add_bind $_name no_binds ) )
) )
--[ primitive pattern ( and ) ]-- --[ primitive pattern ( and ) ]--
@@ -74,11 +76,11 @@ macro chain_binds no_binds $second =0x1p254=> $second
=0x1p254=> response ( =0x1p254=> response (
(\pass. $lh_expr) (take_binds $lh_binds ( (\pass. $lh_expr) (take_binds $lh_binds (
(\pass. $rh_expr) (take_binds $rh_binds ( (\pass. $rh_expr) (take_binds $rh_binds (
give_binds chain_binds $lh_binds $rh_binds pass give_binds (chain_binds $lh_binds $rh_binds) pass
)) ))
)) ))
) )
( chain_binds $lh_binds $rh_binds ) ( (chain_binds $lh_binds $rh_binds) )
) )
--[ primitive pattern ( or ) ]-- --[ primitive pattern ( or ) ]--
@@ -93,7 +95,7 @@ macro chain_binds no_binds $second =0x1p254=> $second
( -- for this to work, lh and rh must produce the same bindings ( -- for this to work, lh and rh must produce the same bindings
macro await_or_subpatterns ( response $lh_expr ( $lh_binds) ) ( response $rh_expr ( $rh_binds ) ) macro await_or_subpatterns ( response $lh_expr ( $lh_binds) ) ( response $rh_expr ( $rh_binds ) )
=0x1p254=> response ( =0x1p254=> response (
(\cancel. $lh_expr) -- lh works with pass directly because its bindings are reported up (\fail. $lh_expr) -- lh works with pass directly because its bindings are reported up
($rh_expr (take_binds $rh_binds -- rh runs if lh cancels ($rh_expr (take_binds $rh_binds -- rh runs if lh cancels
(give_binds $lh_binds pass) -- translate rh binds to lh binds (give_binds $lh_binds pass) -- translate rh binds to lh binds
)) ))
@@ -101,4 +103,3 @@ macro chain_binds no_binds $second =0x1p254=> $second
( $lh_binds ) -- report lh bindings ( $lh_binds ) -- report lh bindings
) )
export ::(match, cancel, argument, request, response, =>)

View File

@@ -22,3 +22,6 @@ export ::(loop_over, recursive, while)
import std::known::* import std::known::*
export ::[, _ ; . =] export ::[, _ ; . =]
import std
export ::(std)

View File

@@ -13,13 +13,12 @@ use super::runtime_error::RuntimeError;
use crate::error::ProjectResult; use crate::error::ProjectResult;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::xfn_2ary;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::process::Unstable; use crate::foreign::process::Unstable;
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::GenClause; use crate::gen::traits::GenClause;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{atom_leaf, xfn_ent, ConstTree};
use crate::interpreter::nort as int; use crate::interpreter::nort as int;
use crate::interpreter::nort::ClauseInst; use crate::interpreter::nort::ClauseInst;
use crate::libs::parse_custom_line::custom_line; use crate::libs::parse_custom_line::custom_line;
@@ -33,7 +32,6 @@ use crate::parse::parsed::{
SourceLineKind, SourceLineKind,
}; };
use crate::utils::ddispatch::Request; use crate::utils::ddispatch::Request;
use crate::utils::pure_seq::pushed;
pub struct TypeData { pub struct TypeData {
pub id: RefEqual, pub id: RefEqual,
@@ -439,9 +437,9 @@ pub const fn gen_resolv(name: &'static str) -> impl GenClause {
pub fn protocol_lib() -> ConstTree { pub fn protocol_lib() -> ConstTree {
ConstTree::ns("std::protocol", [ConstTree::tree([ ConstTree::ns("std::protocol", [ConstTree::tree([
("unwrap", atom_leaf(xfn_2ary(unwrap))), xfn_ent("unwrap", [unwrap]),
("wrap", atom_leaf(xfn_2ary(wrap))), xfn_ent("wrap", [wrap]),
("get_impl", atom_leaf(xfn_2ary(get_impl))), xfn_ent("get_impl", [get_impl]),
("resolve", atom_leaf(xfn_2ary(resolve))), xfn_ent("resolve", [resolve]),
])]) ])])
} }

View File

@@ -8,9 +8,8 @@ use std::sync::atomic::{self, AtomicUsize};
use intern_all::i; use intern_all::i;
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tree::{atom_ent, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::nort::Clause; use crate::interpreter::nort::Clause;
use crate::name::Sym; use crate::name::Sym;
@@ -57,9 +56,7 @@ impl Ord for RefEqual {
fn cmp(&self, other: &Self) -> cmp::Ordering { self.id().cmp(&other.id()) } fn cmp(&self, other: &Self) -> cmp::Ordering { self.id().cmp(&other.id()) }
} }
impl PartialOrd for RefEqual { impl PartialOrd for RefEqual {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
Some(self.cmp(other))
}
} }
impl Hash for RefEqual { impl Hash for RefEqual {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.id().hash(state) } fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.id().hash(state) }
@@ -67,11 +64,6 @@ impl Hash for RefEqual {
pub(super) fn reflect_lib() -> ConstTree { pub(super) fn reflect_lib() -> ConstTree {
ConstTree::ns("std::reflect", [ConstTree::tree([ ConstTree::ns("std::reflect", [ConstTree::tree([
atom_ent("auto_tuple_test", [xfn_1ary(|_: Inert<usize>| { xfn_ent("ref_equal", [|l: Inert<RefEqual>, r: Inert<RefEqual>| Inert(l.0.id() == r.0.id())]),
(Inert(1usize), Inert(2usize), Inert(3usize))
})]),
atom_ent("ref_equal", [xfn_2ary(
|l: Inert<RefEqual>, r: Inert<RefEqual>| Inert(l.0.id() == r.0.id()),
)]),
])]) ])])
} }

View File

@@ -2,9 +2,8 @@
//! for runtime errors such as missing files. //! for runtime errors such as missing files.
use std::fmt::Display; use std::fmt::Display;
use std::sync::Arc;
use crate::foreign::error::{ExternError, ExternResult}; use crate::foreign::error::{ExternError, ExternErrorObj, ExternResult};
/// Some external event prevented the operation from succeeding /// Some external event prevented the operation from succeeding
#[derive(Clone)] #[derive(Clone)]
@@ -21,7 +20,7 @@ impl RuntimeError {
} }
/// Construct and upcast to [ExternError] /// Construct and upcast to [ExternError]
pub fn ext(message: String, operation: &'static str) -> Arc<dyn ExternError> { pub fn ext(message: String, operation: &'static str) -> ExternErrorObj {
Self { message, operation }.rc() Self { message, operation }.rc()
} }
} }

View File

@@ -1,13 +1,11 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::foreign::fn_bridge::constructors::{xfn_2ary, xfn_3ary};
use crate::foreign::fn_bridge::Thunk; use crate::foreign::fn_bridge::Thunk;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tpl; use crate::gen::tpl;
use crate::gen::traits::Gen; use crate::gen::traits::Gen;
use crate::gen::tree::{atom_leaf, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::gen_nort::nort_gen; use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort_builder::NortBuilder;
use crate::interpreter::handler::HandlerTable; use crate::interpreter::handler::HandlerTable;
use crate::interpreter::nort::Expr; use crate::interpreter::nort::Expr;
@@ -77,8 +75,8 @@ pub fn state_handlers() -> HandlerTable<'static> {
pub fn state_lib() -> ConstTree { pub fn state_lib() -> ConstTree {
ConstTree::ns("std::state", [ConstTree::tree([ ConstTree::ns("std::state", [ConstTree::tree([
("new_state", atom_leaf(xfn_2ary(new_state))), xfn_ent("new_state", [new_state]),
("get_state", atom_leaf(xfn_2ary(get_state))), xfn_ent("get_state", [get_state]),
("set_state", atom_leaf(xfn_3ary(set_state))), xfn_ent("set_state", [set_state]),
])]) ])])
} }

View File

@@ -15,6 +15,7 @@ use super::protocol::{parsers, protocol_lib};
use super::reflect::reflect_lib; use super::reflect::reflect_lib;
use super::state::{state_handlers, state_lib}; use super::state::{state_handlers, state_lib};
use super::string::str_lib; use super::string::str_lib;
use super::tstring::TStringLexer;
use super::tuple::tuple_lib; use super::tuple::tuple_lib;
use crate::facade::system::{IntoSystem, System}; use crate::facade::system::{IntoSystem, System};
use crate::gen::tree::{ConstCombineErr, ConstTree}; use crate::gen::tree::{ConstCombineErr, ConstTree};
@@ -66,20 +67,19 @@ impl StdConfig {
impl IntoSystem<'static> for StdConfig { impl IntoSystem<'static> for StdConfig {
fn into_system(self) -> System<'static> { fn into_system(self) -> System<'static> {
let gen = CodeGenInfo::no_details("std");
System { System {
name: "stdlib", name: "stdlib",
constants: self.stdlib().expect("stdlib tree is malformed"), constants: self.stdlib().expect("stdlib tree is malformed"),
code: ModEntry::ns("std", [ModEntry::leaf( code: ModEntry::ns("std", [ModEntry::leaf(
EmbeddedFS::new::<StdEmbed>(".orc", gen.clone()).rc(), EmbeddedFS::new::<StdEmbed>(".orc", CodeGenInfo::no_details("std::fs")).rc(),
)]), )]),
prelude: vec![Prelude { prelude: vec![Prelude {
target: VName::literal("std::prelude"), target: VName::literal("std::prelude"),
exclude: VName::literal("std"), exclude: VName::literal("std"),
owner: gen.clone(), owner: CodeGenInfo::no_details("std::prelude"),
}], }],
handlers: state_handlers(), handlers: state_handlers(),
lexer_plugins: vec![], lexer_plugins: vec![Box::new(TStringLexer)],
line_parsers: parsers(), line_parsers: parsers(),
} }
} }

View File

@@ -11,11 +11,10 @@ use unicode_segmentation::UnicodeSegmentation;
use super::runtime_error::RuntimeError; use super::runtime_error::RuntimeError;
use crate::foreign::atom::Atomic; use crate::foreign::atom::Atomic;
use crate::foreign::error::ExternResult; use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary, xfn_3ary};
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::to_clause::ToClause; use crate::foreign::to_clause::ToClause;
use crate::foreign::try_from_expr::TryFromExpr; use crate::foreign::try_from_expr::TryFromExpr;
use crate::gen::tree::{atom_ent, ConstTree}; use crate::gen::tree::{xfn_ent, ConstTree};
use crate::interpreter::nort::{Clause, Expr}; use crate::interpreter::nort::{Clause, Expr};
use crate::location::CodeLocation; use crate::location::CodeLocation;
use crate::utils::iter_find::iter_find; use crate::utils::iter_find::iter_find;
@@ -50,8 +49,7 @@ impl OrcString {
pub fn get_string(self) -> String { pub fn get_string(self) -> String {
match self { match self {
Self::Interned(s) => s.as_str().to_owned(), Self::Interned(s) => s.as_str().to_owned(),
Self::Runtime(rc) => Self::Runtime(rc) => Arc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()),
Arc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()),
} }
} }
} }
@@ -68,9 +66,7 @@ impl Deref for OrcString {
} }
impl Hash for OrcString { impl Hash for OrcString {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.as_str().hash(state) }
self.as_str().hash(state)
}
} }
impl From<String> for OrcString { impl From<String> for OrcString {
@@ -96,9 +92,7 @@ impl InertPayload for OrcString {
} }
impl ToClause for String { impl ToClause for String {
fn to_clause(self, _: CodeLocation) -> Clause { fn to_clause(self, _: CodeLocation) -> Clause { Inert(OrcString::from(self)).atom_cls() }
Inert(OrcString::from(self)).atom_cls()
}
} }
impl TryFromExpr for String { impl TryFromExpr for String {
@@ -109,8 +103,7 @@ impl TryFromExpr for String {
pub(super) fn str_lib() -> ConstTree { pub(super) fn str_lib() -> ConstTree {
ConstTree::ns("std::string", [ConstTree::tree([ ConstTree::ns("std::string", [ConstTree::tree([
atom_ent("slice", [xfn_3ary( xfn_ent("slice", [|s: Inert<OrcString>, i: Inert<usize>, len: Inert<usize>| {
|s: Inert<OrcString>, i: Inert<usize>, len: Inert<usize>| {
let graphs = s.0.as_str().graphemes(true); let graphs = s.0.as_str().graphemes(true);
if i.0 == 0 { if i.0 == 0 {
return Ok(graphs.take(len.0).collect::<String>()); return Ok(graphs.take(len.0).collect::<String>());
@@ -132,39 +125,25 @@ pub(super) fn str_lib() -> ConstTree {
if count == len.0 { if count == len.0 {
Ok(ret) Ok(ret)
} else { } else {
RuntimeError::fail( RuntimeError::fail("Character index out of bounds".to_string(), "indexing string")
"Character index out of bounds".to_string(),
"indexing string",
)
} }
}, }]),
)]), xfn_ent("concat", [|a: String, b: Inert<OrcString>| a + b.0.as_str()]),
atom_ent("concat", [xfn_2ary(|a: String, b: Inert<OrcString>| { xfn_ent("find", [|haystack: Inert<OrcString>, needle: Inert<OrcString>| {
a + b.0.as_str()
})]),
atom_ent("find", [xfn_2ary(
|haystack: Inert<OrcString>, needle: Inert<OrcString>| {
let haystack_graphs = haystack.0.as_str().graphemes(true); let haystack_graphs = haystack.0.as_str().graphemes(true);
iter_find(haystack_graphs, needle.0.as_str().graphemes(true)).map(Inert) iter_find(haystack_graphs, needle.0.as_str().graphemes(true)).map(Inert)
}, }]),
)]), xfn_ent("split", [|s: String, i: Inert<usize>| -> (String, String) {
atom_ent("split", [xfn_2ary(
|s: String, i: Inert<usize>| -> (String, String) {
let mut graphs = s.as_str().graphemes(true); let mut graphs = s.as_str().graphemes(true);
(graphs.by_ref().take(i.0).collect(), graphs.collect()) (graphs.by_ref().take(i.0).collect(), graphs.collect())
}, }]),
)]), xfn_ent("len", [|s: Inert<OrcString>| Inert(s.0.graphemes(true).count())]),
atom_ent("len", [xfn_1ary(|s: Inert<OrcString>| { xfn_ent("size", [|s: Inert<OrcString>| Inert(s.0.as_bytes().len())]),
Inert(s.0.graphemes(true).count()) xfn_ent("intern", [|s: Inert<OrcString>| {
})]),
atom_ent("size", [xfn_1ary(|s: Inert<OrcString>| {
Inert(s.0.as_bytes().len())
})]),
atom_ent("intern", [xfn_1ary(|s: Inert<OrcString>| {
Inert(match s.0 { Inert(match s.0 {
OrcString::Runtime(s) => OrcString::Interned(i(&*s)), OrcString::Runtime(s) => OrcString::Interned(i(&*s)),
x => x, x => x,
}) })
})]), }]),
])]) ])])
} }

81
src/libs/std/tstring.rs Normal file
View File

@@ -0,0 +1,81 @@
use std::fmt::Write;
use intern_all::i;
use crate::error::ProjectResult;
use crate::foreign::atom::AtomGenerator;
use crate::foreign::inert::Inert;
use crate::libs::std::string::OrcString;
use crate::parse::errors::ParseErrorKind;
use crate::parse::lex_plugin::{LexPluginRecur, LexPluginReq, LexerPlugin};
use crate::parse::lexer::{Entry, LexRes, Lexeme};
use crate::parse::parsed::PType;
use crate::parse::string::parse_string;
pub struct TStringLexer;
impl LexerPlugin for TStringLexer {
fn lex<'a>(&self, req: &'_ dyn LexPluginReq<'a>) -> Option<ProjectResult<LexRes<'a>>> {
req.tail().strip_prefix("$\"").map(|mut txt| {
let ctx = req.ctx();
let mut parts = vec![Entry::new(ctx.range(0, txt), Lexeme::LP(PType::Par))];
let mut str = String::new();
let commit_str = |str: &mut String, tail: &str, parts: &mut Vec<Entry>| -> ProjectResult<_> {
let str_val = parse_string(str).map_err(|e| e.to_proj(ctx, ctx.pos(txt)))?;
let ag = AtomGenerator::cloner(Inert(OrcString::from(i(&str_val))));
parts.push(Entry::new(ctx.range(str.len(), tail), Lexeme::Atom(ag)));
*str = String::new();
Ok(())
};
loop {
if let Some(rest) = txt.strip_prefix('"') {
commit_str(&mut str, txt, &mut parts)?;
parts.push(Entry::new(ctx.range(0, rest), Lexeme::RP(PType::Par)));
return Ok(LexRes { tail: rest, tokens: parts });
}
if let Some(rest) = txt.strip_prefix("${") {
let mut depth = 0;
commit_str(&mut str, rest, &mut parts)?;
parts.extend(req.insert("++ std::conv::to_string (", ctx.source_range(0, rest)));
let res = req.recurse(LexPluginRecur {
tail: rest,
exit: &mut |c| {
match c.chars().next() {
None => return Err(UnclosedInterpolation.pack(ctx.source_range(2, rest))),
Some('{') => depth += 1,
Some('}') if depth == 0 => return Ok(true),
Some('}') => depth -= 1,
_ => (),
}
Ok(false)
},
})?;
txt = &res.tail[1..]; // account for final }
parts.extend(res.tokens);
parts.extend(req.insert(") ++", ctx.source_range(0, txt)));
} else {
let mut chars = txt.chars();
match chars.next() {
None => return Err(NoTStringEnd.pack(ctx.source_range(req.tail().len(), ""))),
Some('\\') => match chars.next() {
None => write!(str, "\\").expect("writing \\ into string"),
Some(next) => write!(str, "\\{next}").expect("writing \\ and char into string"),
},
Some(c) => write!(str, "{c}").expect("writing char into string"),
}
txt = chars.as_str();
}
}
})
}
}
pub struct UnclosedInterpolation;
impl ParseErrorKind for UnclosedInterpolation {
const DESCRIPTION: &'static str = "A ${ block within a $-string wasn't closed";
}
/// String literal never ends
pub(super) struct NoTStringEnd;
impl ParseErrorKind for NoTStringEnd {
const DESCRIPTION: &'static str = "A $-string literal was not closed with `\"`";
}

View File

@@ -37,41 +37,40 @@ export ::(t, size)
( macro::length macro::comma_list ( ..$items ) ) ( macro::length macro::comma_list ( ..$items ) )
( (
pattern_walker pattern_walker
(0) -- index of next item
macro::comma_list ( ..$items ) -- leftover items macro::comma_list ( ..$items ) -- leftover items
) )
) )
( macro tuple_pattern $length ( pattern_result $expr ( $binds ) ) ( macro tuple_pattern $length ( pattern_result $expr ( $binds ) )
=0x1p254=> pmatch::response ( =0x1p254=> pmatch::response (
if length pmatch::value == $length if length pmatch::value == $length
then $expr then ((\tuple_idx. $expr ) 0)
else pmatch::fail else pmatch::fail
) ( $binds ) ) ( $binds )
) )
( macro pattern_walker $length macro::list_end ( macro pattern_walker macro::list_end
=0x1p254=> pattern_result pmatch::pass ( pmatch::no_binds ) =0x1p254=> pattern_result pmatch::pass ( pmatch::no_binds )
) )
( macro pattern_walker (...$length) ( macro::list_item $next $tail ) ( macro pattern_walker ( macro::list_item $next $tail )
=0x1p254=> pattern_await =0x1p254=> pattern_await
(...$length)
( pmatch::request $next ) ( pmatch::request $next )
( pattern_walker (...$length + 1) $tail ) ( pattern_walker $tail )
) )
( macro pattern_await $length ( macro pattern_await
( pmatch::response $expr ( $binds ) ) ( pmatch::response $expr ( $binds ) )
( pattern_result $tail_expr ( $tail_binds ) ) ( pattern_result $tail_expr ( $tail_binds ) )
=0x1p254=> =0x1p254=>
pattern_result pattern_result
( (
(\pmatch::pass. (\pmatch::value. $expr) (pick pmatch::value $length)) ( (\pmatch::pass. (\pmatch::value. $expr) (pick pmatch::value tuple_idx)) (
pmatch::take_binds $binds ( pmatch::take_binds $binds (
(\pmatch::pass. $tail_expr) ( pmatch::take_binds $tail_binds ( (\pmatch::pass. (\tuple_idx. $tail_expr) (tuple_idx + 1))
( pmatch::take_binds $tail_binds (
pmatch::give_binds pmatch::give_binds
pmatch::chain_binds $binds $tail_binds (pmatch::chain_binds $binds $tail_binds)
pmatch::pass pmatch::pass
)) ))
) )
) )
) )
( pmatch::chain_binds $binds $tail_binds ) ( ( pmatch::chain_binds $binds $tail_binds ) )
) )

View File

@@ -9,24 +9,18 @@ use super::conv::TO_STRING;
use super::protocol::Tag; use super::protocol::Tag;
use super::reflect::refer; use super::reflect::refer;
use crate::foreign::error::{AssertionError, ExternResult}; use crate::foreign::error::{AssertionError, ExternResult};
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
use crate::foreign::fn_bridge::Thunk; use crate::foreign::fn_bridge::Thunk;
use crate::foreign::inert::{Inert, InertPayload}; use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::try_from_expr::WithLoc; use crate::foreign::try_from_expr::WithLoc;
use crate::gen::tpl; use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::gen::tree::{atom_leaf, leaf, ConstTree};
use crate::interpreter::nort::Expr; use crate::interpreter::nort::Expr;
use crate::location::{CodeGenInfo, CodeLocation}; use crate::location::{CodeGenInfo, CodeLocation};
use crate::utils::ddispatch::Request; use crate::utils::ddispatch::Request;
use crate::utils::pure_seq::pushed; use crate::utils::pure_seq::pushed;
static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| { static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| {
let location = let location = CodeLocation::Gen(CodeGenInfo::no_details("stdlib::tuple::tag"));
CodeLocation::Gen(CodeGenInfo::no_details("stdlib::tuple::tag")); Tag::new("tuple", [(TO_STRING.id(), refer("std::tuple::to_string_impl").to_expr(location))])
Tag::new("tuple", [(
TO_STRING.id(),
refer("std::tuple::to_string_impl").to_expr(location),
)])
}); });
/// A short contiquous random access sequence of Orchid values. /// A short contiquous random access sequence of Orchid values.
@@ -34,23 +28,18 @@ static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| {
pub struct Tuple(pub Arc<Vec<Expr>>); pub struct Tuple(pub Arc<Vec<Expr>>);
impl InertPayload for Tuple { impl InertPayload for Tuple {
const TYPE_STR: &'static str = "tuple"; const TYPE_STR: &'static str = "tuple";
fn respond(&self, mut request: Request) { fn respond(&self, mut request: Request) { request.serve_with(|| TUPLE_TAG.clone()) }
request.serve_with(|| TUPLE_TAG.clone())
}
} }
impl Debug for Tuple { impl Debug for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Tuple")?; write!(f, "Tuple")?;
f.debug_list().entries(self.0.iter()).finish() f.debug_list().entries(self.0.iter().map(|e| &e.clause)).finish()
} }
} }
fn length(tuple: Inert<Tuple>) -> Inert<usize> { Inert(tuple.0.0.len()) } fn length(tuple: Inert<Tuple>) -> Inert<usize> { Inert(tuple.0.0.len()) }
fn pick( fn pick(WithLoc(loc, tuple): WithLoc<Inert<Tuple>>, idx: Inert<usize>) -> ExternResult<Expr> {
WithLoc(loc, tuple): WithLoc<Inert<Tuple>>,
idx: Inert<usize>,
) -> ExternResult<Expr> {
(tuple.0.0.get(idx.0).cloned()).ok_or_else(|| { (tuple.0.0.get(idx.0).cloned()).ok_or_else(|| {
let msg = format!("{} <= {idx}", tuple.0.0.len()); let msg = format!("{} <= {idx}", tuple.0.0.len());
AssertionError::ext(loc, "Tuple index out of bounds", msg) AssertionError::ext(loc, "Tuple index out of bounds", msg)
@@ -64,9 +53,9 @@ fn push(Inert(tuple): Inert<Tuple>, item: Thunk) -> Inert<Tuple> {
pub(super) fn tuple_lib() -> ConstTree { pub(super) fn tuple_lib() -> ConstTree {
ConstTree::ns("std", [ConstTree::tree([TUPLE_TAG.as_tree_ent([ ConstTree::ns("std", [ConstTree::tree([TUPLE_TAG.as_tree_ent([
("empty", leaf(tpl::V(Inert(Tuple(Arc::new(Vec::new())))))), atom_ent("empty", [Inert(Tuple(Arc::new(Vec::new())))]),
("length", atom_leaf(xfn_1ary(length))), xfn_ent("length", [length]),
("pick", atom_leaf(xfn_2ary(pick))), xfn_ent("pick", [pick]),
("push", atom_leaf(xfn_2ary(push))), xfn_ent("push", [push]),
])])]) ])])])
} }

View File

@@ -280,7 +280,7 @@ impl Sym {
} }
impl Debug for Sym { impl Debug for Sym {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Sym{self})") write!(f, "Sym({self})")
} }
} }
impl Display for Sym { impl Display for Sym {

View File

@@ -39,7 +39,7 @@ pub trait ParseCtx {
} }
/// Create a contextful location for error reporting /// Create a contextful location for error reporting
#[must_use] #[must_use]
fn code_range(&self, len: usize, tl: &str) -> SourceRange { fn source_range(&self, len: usize, tl: &str) -> SourceRange {
self.range_loc(&self.range(len, tl)) self.range_loc(&self.range(len, tl))
} }
/// Create a contentful location from a range directly. /// Create a contentful location from a range directly.

View File

@@ -11,7 +11,7 @@ use crate::parse::sourcefile::{parse_line, split_lines};
/// Parse a file /// Parse a file
pub fn parse_file(ctx: &impl ParseCtx) -> ProjectResult<Vec<SourceLine>> { pub fn parse_file(ctx: &impl ParseCtx) -> ProjectResult<Vec<SourceLine>> {
let tokens = lex(vec![], ctx.source().as_str(), ctx, |_| false)?.tokens; let tokens = lex(vec![], ctx.source().as_str(), ctx, |_| Ok(false))?.tokens;
if tokens.is_empty() { if tokens.is_empty() {
Ok(Vec::new()) Ok(Vec::new())
} else { } else {
@@ -30,7 +30,7 @@ pub fn parse_entries(
range: SourceRange, range: SourceRange,
) -> Vec<SourceLine> { ) -> Vec<SourceLine> {
let ctx = FlatLocContext::new(ctx, &range); let ctx = FlatLocContext::new(ctx, &range);
let res = lex(vec![], text, &ctx, |_| false).expect("pre-specified source"); let res = lex(vec![], text, &ctx, |_| Ok(false)).expect("pre-specified source");
split_lines(Frag::from_slice(&res.tokens), &ctx) split_lines(Frag::from_slice(&res.tokens), &ctx)
.flat_map(|tokens| parse_line(tokens, &ctx).expect("pre-specified source")) .flat_map(|tokens| parse_line(tokens, &ctx).expect("pre-specified source"))
.map(|kind| kind.wrap(range.clone())) .map(|kind| kind.wrap(range.clone()))

View File

@@ -1,8 +1,9 @@
//! Abstractions for dynamic extensions to the lexer to parse custom literals //! Abstractions for dynamic extensions to the lexer to parse custom literals
use super::context::ParseCtx; use super::context::{FlatLocContext, ParseCtx};
use super::lexer::{lex, LexRes}; use super::lexer::{lex, Entry, LexRes};
use crate::error::ProjectResult; use crate::error::ProjectResult;
use crate::location::SourceRange;
/// Data passed to the recursive sub-lexer /// Data passed to the recursive sub-lexer
pub struct LexPluginRecur<'a, 'b> { pub struct LexPluginRecur<'a, 'b> {
@@ -11,7 +12,7 @@ pub struct LexPluginRecur<'a, 'b> {
/// Callback that will be called between lexemes on the leftover text. /// Callback that will be called between lexemes on the leftover text.
/// When it returns true, the lexer exits and leaves the remaining text for /// When it returns true, the lexer exits and leaves the remaining text for
/// you. /// you.
pub exit: &'b dyn for<'c> Fn(&'c str) -> bool, pub exit: &'b mut dyn for<'c> FnMut(&'c str) -> ProjectResult<bool>,
} }
/// Data and actions available to a lexer plugin /// Data and actions available to a lexer plugin
@@ -25,16 +26,20 @@ pub trait LexPluginReq<'a> {
/// expressions in your literals like the template strings of most languages /// expressions in your literals like the template strings of most languages
/// other than Rust. /// other than Rust.
fn recurse(&self, req: LexPluginRecur<'a, '_>) -> ProjectResult<LexRes<'a>>; fn recurse(&self, req: LexPluginRecur<'a, '_>) -> ProjectResult<LexRes<'a>>;
/// Lex an inserted piece of text, especially when translating custom syntax
/// into multiple lexemes.
///
/// # Panics
///
/// If tokenization fails
fn insert(&self, data: &str, range: SourceRange) -> Vec<Entry>;
} }
/// External plugin that parses a literal into recognized Orchid lexemes, most /// External plugin that parses a literal into recognized Orchid lexemes, most
/// likely atoms. /// likely atoms.
pub trait LexerPlugin: Send + Sync { pub trait LexerPlugin: Send + Sync {
/// Run the lexer /// Run the lexer
fn lex<'a>( fn lex<'a>(&self, req: &'_ dyn LexPluginReq<'a>) -> Option<ProjectResult<LexRes<'a>>>;
&self,
req: &'_ dyn LexPluginReq<'a>,
) -> Option<ProjectResult<LexRes<'a>>>;
} }
pub(super) struct LexPlugReqImpl<'a, 'b, TCtx: ParseCtx> { pub(super) struct LexPlugReqImpl<'a, 'b, TCtx: ParseCtx> {
@@ -47,4 +52,10 @@ impl<'a, 'b, TCtx: ParseCtx> LexPluginReq<'a> for LexPlugReqImpl<'a, 'b, TCtx> {
fn recurse(&self, req: LexPluginRecur<'a, '_>) -> ProjectResult<LexRes<'a>> { fn recurse(&self, req: LexPluginRecur<'a, '_>) -> ProjectResult<LexRes<'a>> {
lex(Vec::new(), req.tail, self.ctx, |s| (req.exit)(s)) lex(Vec::new(), req.tail, self.ctx, |s| (req.exit)(s))
} }
fn insert(&self, data: &str, range: SourceRange) -> Vec<Entry> {
let ctx = FlatLocContext::new(self.ctx as &dyn ParseCtx, &range);
lex(Vec::new(), data, &ctx, |_| Ok(false))
.expect("Insert failed to lex")
.tokens
}
} }

View File

@@ -40,7 +40,9 @@ impl Entry {
matches!(self.lexeme, Lexeme::Comment(_) | Lexeme::BR) matches!(self.lexeme, Lexeme::Comment(_) | Lexeme::BR)
} }
fn new(range: Range<usize>, lexeme: Lexeme) -> Self { Self { lexeme, range } } /// Create a new entry
#[must_use]
pub fn new(range: Range<usize>, lexeme: Lexeme) -> Self { Self { lexeme, range } }
} }
impl Display for Entry { impl Display for Entry {
@@ -139,7 +141,7 @@ pub fn namechar(c: char) -> bool { c.is_alphanumeric() | (c == '_') }
pub fn namestart(c: char) -> bool { c.is_alphabetic() | (c == '_') } pub fn namestart(c: char) -> bool { c.is_alphabetic() | (c == '_') }
/// Character filter that can appear in operators. /// Character filter that can appear in operators.
pub fn opchar(c: char) -> bool { pub fn opchar(c: char) -> bool {
!namestart(c) && !numstart(c) && !c.is_whitespace() && !"()[]{},".contains(c) !namestart(c) && !numstart(c) && !c.is_whitespace() && !"()[]{},'\"\\".contains(c)
} }
/// Split off all characters from the beginning that match a filter /// Split off all characters from the beginning that match a filter
@@ -176,18 +178,18 @@ pub fn lex<'a>(
mut tokens: Vec<Entry>, mut tokens: Vec<Entry>,
mut data: &'a str, mut data: &'a str,
ctx: &'_ impl ParseCtx, ctx: &'_ impl ParseCtx,
bail: impl Fn(&str) -> bool, mut bail: impl FnMut(&str) -> ProjectResult<bool>,
) -> ProjectResult<LexRes<'a>> { ) -> ProjectResult<LexRes<'a>> {
let mut prev_len = data.len() + 1; let mut prev_len = data.len() + 1;
'tail: loop { 'tail: loop {
if bail(data) {
return Ok(LexRes { tokens, tail: data });
}
if prev_len == data.len() { if prev_len == data.len() {
panic!("got stuck at {data:?}, parsed {:?}", tokens.last().unwrap()); panic!("got stuck at {data:?}, parsed {:?}", tokens.last().unwrap());
} }
prev_len = data.len(); prev_len = data.len();
data = data.trim_start_matches(|c: char| c.is_whitespace() && c != '\n'); data = data.trim_start_matches(|c: char| c.is_whitespace() && c != '\n');
if bail(data)? {
return Ok(LexRes { tokens, tail: data });
}
let mut chars = data.chars(); let mut chars = data.chars();
let head = match chars.next() { let head = match chars.next() {
None => return Ok(LexRes { tokens, tail: data }), None => return Ok(LexRes { tokens, tail: data }),
@@ -221,7 +223,7 @@ pub fn lex<'a>(
} }
if let Some(tail) = data.strip_prefix("--[") { if let Some(tail) = data.strip_prefix("--[") {
let (note, tail) = (tail.split_once("]--")) let (note, tail) = (tail.split_once("]--"))
.ok_or_else(|| NoCommentEnd.pack(ctx.code_range(tail.len(), "")))?; .ok_or_else(|| NoCommentEnd.pack(ctx.source_range(tail.len(), "")))?;
let lexeme = Lexeme::Comment(Arc::new(note.to_string())); let lexeme = Lexeme::Comment(Arc::new(note.to_string()));
tokens.push(Entry::new(ctx.range(note.len() + 3, tail), lexeme)); tokens.push(Entry::new(ctx.range(note.len() + 3, tail), lexeme));
data = tail; data = tail;
@@ -276,7 +278,7 @@ pub fn lex<'a>(
.and_then(|num| match num { .and_then(|num| match num {
Numeric::Uint(usize) => Ok(usize), Numeric::Uint(usize) => Ok(usize),
Numeric::Float(_) => Err( Numeric::Float(_) => Err(
FloatPlacehPrio.pack(ctx.code_range(num_str.len(), tail)), FloatPlacehPrio.pack(ctx.source_range(num_str.len(), tail)),
), ),
}) })
.map(|p| (p, num_str.len() + 1, tail)) .map(|p| (p, num_str.len() + 1, tail))

View File

@@ -3,13 +3,12 @@
use intern_all::i; use intern_all::i;
use itertools::Itertools; use itertools::Itertools;
use super::errors::{ use super::context::ParseCtx;
BadCodePoint, BadEscapeSequence, NoStringEnd, NotHex, ParseErrorKind, use super::errors::{BadCodePoint, BadEscapeSequence, NoStringEnd, NotHex, ParseErrorKind};
};
#[allow(unused)] // for doc #[allow(unused)] // for doc
use super::lex_plugin::LexerPlugin; use super::lex_plugin::LexerPlugin;
use super::lexer::{Entry, LexRes, Lexeme}; use super::lexer::{Entry, LexRes, Lexeme};
use crate::error::ProjectResult; use crate::error::{ProjectErrorObj, ProjectResult};
use crate::foreign::atom::AtomGenerator; use crate::foreign::atom::AtomGenerator;
use crate::foreign::inert::Inert; use crate::foreign::inert::Inert;
use crate::libs::std::string::OrcString; use crate::libs::std::string::OrcString;
@@ -32,6 +31,19 @@ pub struct StringError {
kind: StringErrorKind, kind: StringErrorKind,
} }
impl StringError {
/// Convert into project error for reporting
pub fn to_proj(self, ctx: &dyn ParseCtx, pos: usize) -> ProjectErrorObj {
let start = pos + self.pos;
let location = ctx.range_loc(&(start..start + 1));
match self.kind {
StringErrorKind::NotHex => NotHex.pack(location),
StringErrorKind::BadCodePoint => BadCodePoint.pack(location),
StringErrorKind::BadEscSeq => BadEscapeSequence.pack(location),
}
}
}
/// Process escape sequences in a string literal /// Process escape sequences in a string literal
pub fn parse_string(str: &str) -> Result<String, StringError> { pub fn parse_string(str: &str) -> Result<String, StringError> {
let mut target = String::new(); let mut target = String::new();
@@ -61,18 +73,14 @@ pub fn parse_string(str: &str) -> Result<String, StringError> {
'u' => { 'u' => {
let acc = ((0..4).rev()) let acc = ((0..4).rev())
.map(|radical| { .map(|radical| {
let (j, c) = (iter.next()) let (j, c) = (iter.next()).ok_or(StringError { pos, kind: StringErrorKind::NotHex })?;
.ok_or(StringError { pos, kind: StringErrorKind::NotHex })?;
pos = j; pos = j;
let b = let b = u32::from_str_radix(&String::from(c), 16)
u32::from_str_radix(&String::from(c), 16).map_err(|_| { .map_err(|_| StringError { pos, kind: StringErrorKind::NotHex })?;
StringError { pos, kind: StringErrorKind::NotHex }
})?;
Ok(16u32.pow(radical) + b) Ok(16u32.pow(radical) + b)
}) })
.fold_ok(0, u32::wrapping_add)?; .fold_ok(0, u32::wrapping_add)?;
char::from_u32(acc) char::from_u32(acc).ok_or(StringError { pos, kind: StringErrorKind::BadCodePoint })?
.ok_or(StringError { pos, kind: StringErrorKind::BadCodePoint })?
}, },
_ => return Err(StringError { pos, kind: StringErrorKind::BadEscSeq }), _ => return Err(StringError { pos, kind: StringErrorKind::BadEscSeq }),
}; };
@@ -91,26 +99,15 @@ impl LexerPlugin for StringLexer {
req.tail().strip_prefix('"').map(|data| { req.tail().strip_prefix('"').map(|data| {
let mut leftover = data; let mut leftover = data;
return loop { return loop {
let (inside, outside) = let (inside, outside) = (leftover.split_once('"'))
(leftover.split_once('"')).ok_or_else(|| { .ok_or_else(|| NoStringEnd.pack(req.ctx().source_range(data.len(), "")))?;
NoStringEnd.pack(req.ctx().code_range(data.len(), "")) let backslashes = inside.chars().rev().take_while(|c| *c == '\\').count();
})?;
let backslashes =
inside.chars().rev().take_while(|c| *c == '\\').count();
if backslashes % 2 == 0 { if backslashes % 2 == 0 {
// cut form tail to recoup what string_content doesn't have // cut form tail to recoup what string_content doesn't have
let (string_data, tail) = let (string_data, tail) = data.split_at(data.len() - outside.len() - 1);
data.split_at(data.len() - outside.len() - 1);
let tail = &tail[1..]; // push the tail past the end quote let tail = &tail[1..]; // push the tail past the end quote
let string = parse_string(string_data).map_err(|e| { let string =
let start = req.ctx().pos(data) + e.pos; parse_string(string_data).map_err(|e| e.to_proj(req.ctx(), req.ctx().pos(data)))?;
let location = req.ctx().range_loc(&(start..start + 1));
match e.kind {
StringErrorKind::NotHex => NotHex.pack(location),
StringErrorKind::BadCodePoint => BadCodePoint.pack(location),
StringErrorKind::BadEscSeq => BadEscapeSequence.pack(location),
}
})?;
let output = Inert(OrcString::from(i(&string))); let output = Inert(OrcString::from(i(&string)));
let ag = AtomGenerator::cloner(output); let ag = AtomGenerator::cloner(output);
let range = req.ctx().range(string_data.len(), tail); let range = req.ctx().range(string_data.len(), tail);
@@ -144,8 +141,7 @@ mod test {
[Entry { lexeme: Lexeme::Atom(ag), .. }] => ag, [Entry { lexeme: Lexeme::Atom(ag), .. }] => ag,
_ => panic!("Expected a single atom"), _ => panic!("Expected a single atom"),
}; };
let atom = let atom = ag.run().try_downcast::<Inert<OrcString>>().expect("Lexed to inert");
ag.run().try_downcast::<Inert<OrcString>>().expect("Lexed to inert");
assert_eq!(atom.0.as_str(), "hello world!"); assert_eq!(atom.0.as_str(), "hello world!");
assert_eq!(res.tail, " - says the programmer"); assert_eq!(res.tail, " - says the programmer");
} }

View File

@@ -10,9 +10,7 @@ use intern_all::{sweep_t, Tok};
use super::dealias::resolve_aliases::resolve_aliases; use super::dealias::resolve_aliases::resolve_aliases;
use super::process_source::{process_ns, resolve_globs, GlobImports}; use super::process_source::{process_ns, resolve_globs, GlobImports};
use super::project::{ItemKind, ProjItem, ProjXEnt, ProjectMod, ProjectTree}; use super::project::{ItemKind, ProjItem, ProjXEnt, ProjectMod, ProjectTree};
use crate::error::{ use crate::error::{bundle_location, ErrorPosition, ProjectError, ProjectResult};
bundle_location, ErrorPosition, ProjectError, ProjectResult,
};
use crate::location::{CodeGenInfo, CodeLocation, SourceCode, SourceRange}; use crate::location::{CodeGenInfo, CodeLocation, SourceCode, SourceRange};
use crate::name::{PathSlice, Sym, VName, VPath}; use crate::name::{PathSlice, Sym, VName, VPath};
use crate::parse::context::ParseCtxImpl; use crate::parse::context::ParseCtxImpl;
@@ -42,10 +40,7 @@ fn split_max_prefix<'a, T>(
path: &'a [T], path: &'a [T],
is_valid: &impl Fn(&[T]) -> bool, is_valid: &impl Fn(&[T]) -> bool,
) -> Option<(&'a [T], &'a [T])> { ) -> Option<(&'a [T], &'a [T])> {
(0..=path.len()) (0..=path.len()).rev().map(|i| path.split_at(i)).find(|(file, _)| is_valid(file))
.rev()
.map(|i| path.split_at(i))
.find(|(file, _)| is_valid(file))
} }
/// Represents a prelude / implicit import requested by a library. /// Represents a prelude / implicit import requested by a library.
@@ -94,10 +89,8 @@ pub fn load_solution(
) -> ProjectResult<ProjectTree> { ) -> ProjectResult<ProjectTree> {
let mut target_queue = VecDeque::<(Sym, CodeLocation)>::new(); let mut target_queue = VecDeque::<(Sym, CodeLocation)>::new();
target_queue.extend(targets.into_iter()); target_queue.extend(targets.into_iter());
target_queue.extend( target_queue
(ctx.preludes.iter()) .extend((ctx.preludes.iter()).map(|p| (p.target.to_sym(), CodeLocation::Gen(p.owner.clone()))));
.map(|p| (p.target.to_sym(), CodeLocation::Gen(p.owner.clone()))),
);
let mut known_files = HashSet::new(); let mut known_files = HashSet::new();
let mut tree_acc: ProjectMod = Module::wrap([]); let mut tree_acc: ProjectMod = Module::wrap([]);
let mut glob_acc: GlobImports = Module::wrap([]); let mut glob_acc: GlobImports = Module::wrap([]);
@@ -111,21 +104,16 @@ pub fn load_solution(
} }
known_files.insert(filename.to_vec()); known_files.insert(filename.to_vec());
let path = VPath(filename.to_vec()); let path = VPath(filename.to_vec());
let loaded = fs let loaded = fs.read(PathSlice(filename)).map_err(|e| bundle_location(&referrer, &*e))?;
.read(PathSlice(filename))
.map_err(|e| bundle_location(&referrer, &*e))?;
let code = match loaded { let code = match loaded {
Loaded::Collection(_) => Loaded::Collection(_) => return Err(UnexpectedDirectory { path }.pack()),
return Err(UnexpectedDirectory { path }.pack()),
Loaded::Code(source) => SourceCode { source, path: Arc::new(path) }, Loaded::Code(source) => SourceCode { source, path: Arc::new(path) },
}; };
let full_range = let full_range = SourceRange { range: 0..code.source.len(), code: code.clone() };
SourceRange { range: 0..code.source.len(), code: code.clone() };
let lines = parse_file(&ctx.parsing(code.clone()))?; let lines = parse_file(&ctx.parsing(code.clone()))?;
let report = process_ns(code.path, lines, full_range)?; let report = process_ns(code.path, lines, full_range)?;
target_queue.extend( target_queue.extend(
(report.external_references.into_iter()) (report.external_references.into_iter()).map(|(k, v)| (k, CodeLocation::Source(v))),
.map(|(k, v)| (k, CodeLocation::Source(v))),
); );
if !report.comments.is_empty() && filename.is_empty() { if !report.comments.is_empty() && filename.is_empty() {
todo!("panic - module op comments on root are lost") todo!("panic - module op comments on root are lost")
@@ -137,23 +125,22 @@ pub fn load_solution(
// i over valid indices of filename // i over valid indices of filename
let key = filename[i].clone(); // last segment let key = filename[i].clone(); // last segment
let comments = comments.take().into_iter().flatten().collect(); let comments = comments.take().into_iter().flatten().collect();
glob = glob = Module::wrap([(key.clone(), ModEntry::wrap(ModMember::Sub(glob)))]);
Module::wrap([(key.clone(), ModEntry::wrap(ModMember::Sub(glob)))]);
module = Module::wrap([(key, ModEntry { module = Module::wrap([(key, ModEntry {
member: ModMember::Sub(module), member: ModMember::Sub(module),
x: ProjXEnt { comments, ..Default::default() }, x: ProjXEnt { comments, ..Default::default() },
})]); })]);
} }
glob_acc = (glob_acc.combine(glob)) glob_acc = (glob_acc.combine(glob)).expect("source code loaded for two nested paths");
.expect("source code loaded for two nested paths"); tree_acc = (tree_acc.combine(module)).expect("source code loaded for two nested paths");
tree_acc = (tree_acc.combine(module))
.expect("source code loaded for two nested paths");
} else { } else {
known_files.insert(target[..].to_vec()); known_files.insert(target[..].to_vec());
// If the path is not within a file, load it as directory // If the path is not within a file, load it as directory
match fs.read(target.as_path_slice()) { match fs.read(target.as_path_slice()) {
Ok(Loaded::Collection(c)) => target_queue Ok(Loaded::Collection(c)) => target_queue.extend(c.iter().map(|e| {
.extend(c.iter().map(|e| (Sym::parse(e).unwrap(), referrer.clone()))), let name = VPath::new(target.iter()).as_prefix_of(e.clone()).to_sym();
(name, referrer.clone())
})),
Ok(Loaded::Code(_)) => unreachable!("Should have split to self and []"), Ok(Loaded::Code(_)) => unreachable!("Should have split to self and []"),
// Ignore error if the path is walkable in the const tree // Ignore error if the path is walkable in the const tree
Err(_) if env.walk1_ref(&[], &target[..], |_| true).is_ok() => (), Err(_) if env.walk1_ref(&[], &target[..], |_| true).is_ok() => (),
@@ -172,12 +159,10 @@ pub fn load_solution(
)?; )?;
let ret = resolve_aliases(tree_acc, env)?; let ret = resolve_aliases(tree_acc, env)?;
for ((glob, original), locations) in contention { for ((glob, original), locations) in contention {
let (glob_val, _) = ret let (glob_val, _) =
.walk1_ref(&[], &glob[..], |_| true) ret.walk1_ref(&[], &glob[..], |_| true).expect("Should've emerged in dealias");
.expect("Should've emerged in dealias"); let (original_val, _) =
let (original_val, _) = ret ret.walk1_ref(&[], &original[..], |_| true).expect("Should've emerged in dealias");
.walk1_ref(&[], &original[..], |_| true)
.expect("Should've emerged in dealias");
let glob_real = match &glob_val.member { let glob_real = match &glob_val.member {
ModMember::Item(ProjItem { kind: ItemKind::Alias(glob_tgt) }) => glob_tgt, ModMember::Item(ProjItem { kind: ItemKind::Alias(glob_tgt) }) => glob_tgt,
_ => &glob, _ => &glob,
@@ -208,9 +193,7 @@ struct UnexpectedDirectory {
impl ProjectError for UnexpectedDirectory { impl ProjectError for UnexpectedDirectory {
const DESCRIPTION: &'static str = "A stage that deals specifically with code \ const DESCRIPTION: &'static str = "A stage that deals specifically with code \
encountered a path that refers to a directory"; encountered a path that refers to a directory";
fn message(&self) -> String { fn message(&self) -> String { format!("{} was expected to be a file", self.path) }
format!("{} was expected to be a file", self.path)
}
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { [] } fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { [] }
} }
@@ -223,8 +206,7 @@ struct ConflictingGlobs {
locations: Vec<CodeLocation>, locations: Vec<CodeLocation>,
} }
impl ProjectError for ConflictingGlobs { impl ProjectError for ConflictingGlobs {
const DESCRIPTION: &'static str = const DESCRIPTION: &'static str = "A symbol from a glob import conflicts with an existing name";
"A symbol from a glob import conflicts with an existing name";
fn message(&self) -> String { fn message(&self) -> String {
let Self { glob, glob_real, original, real, .. } = self; let Self { glob, glob_real, original, real, .. } = self;
format!( format!(
@@ -233,7 +215,6 @@ impl ProjectError for ConflictingGlobs {
) )
} }
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
(self.locations.iter()) (self.locations.iter()).map(|l| ErrorPosition { location: l.clone(), message: None })
.map(|l| ErrorPosition { location: l.clone(), message: None })
} }
} }

View File

@@ -9,12 +9,9 @@ use never::Never;
use super::load_solution::Prelude; use super::load_solution::Prelude;
use super::path::absolute_path; use super::path::absolute_path;
use super::project::{ use super::project::{
ItemKind, ProjItem, ProjRule, ProjXEnt, ProjXMod, ProjectEntry, ProjectMod, ItemKind, ProjItem, ProjRule, ProjXEnt, ProjXMod, ProjectEntry, ProjectMod, SourceModule,
SourceModule,
};
use crate::error::{
ErrorPosition, ProjectError, ProjectErrorObj, ProjectResult,
}; };
use crate::error::{ErrorPosition, ProjectError, ProjectErrorObj, ProjectResult};
use crate::location::{CodeLocation, SourceRange}; use crate::location::{CodeLocation, SourceRange};
use crate::name::{Sym, VName, VPath}; use crate::name::{Sym, VName, VPath};
use crate::parse::parsed::{ use crate::parse::parsed::{
@@ -112,8 +109,7 @@ pub(super) fn process_ns(
external_references.insert(abs.to_sym(), import.range.clone()); external_references.insert(abs.to_sym(), import.range.clone());
} }
match import.name { match import.name {
None => (glob_imports.x.0) None => (glob_imports.x.0).push(GlobImpReport { target: abs, location: import.range }),
.push(GlobImpReport { target: abs, location: import.range }),
Some(key) => { Some(key) => {
let entry = get_or_make(&mut entries, &key, default_entry); let entry = get_or_make(&mut entries, &key, default_entry);
entry.x.locations.push(CodeLocation::Source(import.range)); entry.x.locations.push(CodeLocation::Source(import.range));
@@ -151,8 +147,7 @@ pub(super) fn process_ns(
entry.x.comments.append(&mut new_comments); entry.x.comments.append(&mut new_comments);
}, },
MemberKind::Rule(Rule { pattern, prio, template }) => { MemberKind::Rule(Rule { pattern, prio, template }) => {
let prule = let prule = ProjRule { pattern, prio, template, comments: new_comments };
ProjRule { pattern, prio, template, comments: new_comments };
new_comments = Vec::new(); new_comments = Vec::new();
for name in prule.collect_root_names() { for name in prule.collect_root_names() {
let entry = get_or_make(&mut entries, &name, default_entry); let entry = get_or_make(&mut entries, &name, default_entry);
@@ -171,10 +166,7 @@ pub(super) fn process_ns(
MemberKind::Module(ModuleBlock { name, body }) => { MemberKind::Module(ModuleBlock { name, body }) => {
let entry = get_or_make(&mut entries, &name, default_entry); let entry = get_or_make(&mut entries, &name, default_entry);
entry.x.locations.push(CodeLocation::Source(range.clone())); entry.x.locations.push(CodeLocation::Source(range.clone()));
if !matches!( if !matches!(entry.member, ModMember::Item(ProjItem { kind: ItemKind::None })) {
entry.member,
ModMember::Item(ProjItem { kind: ItemKind::None })
) {
let err = MultipleDefinitions { let err = MultipleDefinitions {
path: (*path).clone().as_prefix_of(name).to_sym(), path: (*path).clone().as_prefix_of(name).to_sym(),
locations: entry.x.locations.clone(), locations: entry.x.locations.clone(),
@@ -196,14 +188,12 @@ pub(super) fn process_ns(
entry.member = ModMember::Sub(report.module); entry.member = ModMember::Sub(report.module);
// record new external references // record new external references
external_references.extend( external_references.extend(
(report.external_references.into_iter()) (report.external_references.into_iter()).filter(|(r, _)| !r[..].starts_with(&path.0)),
.filter(|(r, _)| !r[..].starts_with(&path.0)),
); );
// add glob_imports subtree to own tree // add glob_imports subtree to own tree
glob_imports.entries.insert(name, ModEntry { glob_imports
x: (), .entries
member: ModMember::Sub(report.glob_imports), .insert(name, ModEntry { x: (), member: ModMember::Sub(report.glob_imports) });
});
}, },
}, },
} }
@@ -219,15 +209,10 @@ pub(super) fn process_ns(
}) })
} }
fn walk_at_path( fn walk_at_path(e: WalkError, root: &ProjectMod, path: &[Tok<String>]) -> ProjectErrorObj {
e: WalkError, let submod =
root: &ProjectMod, (root.walk_ref(&[], path, |_| true)).expect("Invalid source path in walk error populator");
path: &[Tok<String>], let src = submod.x.src.as_ref().expect("Import cannot appear in implied module");
) -> ProjectErrorObj {
let submod = (root.walk_ref(&[], path, |_| true))
.expect("Invalid source path in walk error populator");
let src =
submod.x.src.as_ref().expect("Import cannot appear in implied module");
e.at(&CodeLocation::Source(src.range.clone())) e.at(&CodeLocation::Source(src.range.clone()))
} }
@@ -244,15 +229,12 @@ pub fn resolve_globs(
contention: &mut HashMap<(Sym, Sym), Vec<CodeLocation>>, contention: &mut HashMap<(Sym, Sym), Vec<CodeLocation>>,
) -> ProjectResult<()> { ) -> ProjectResult<()> {
// All glob imports in this module // All glob imports in this module
let all = (globtree.x.0.into_iter()) let all =
.map(|gir| (gir.target, CodeLocation::Source(gir.location))) (globtree.x.0.into_iter()).map(|gir| (gir.target, CodeLocation::Source(gir.location))).chain(
.chain(
preludes preludes
.iter() .iter()
.filter(|&pre| !globtree_prefix.0.starts_with(&pre.exclude[..])) .filter(|&pre| !globtree_prefix.0.starts_with(&pre.exclude[..]))
.map(|Prelude { target, owner, .. }| { .map(|Prelude { target, owner, .. }| (target.clone(), CodeLocation::Gen(owner.clone()))),
(target.clone(), CodeLocation::Gen(owner.clone()))
}),
); );
for (target, location) in all { for (target, location) in all {
let (tgt, parent) = project_root let (tgt, parent) = project_root
@@ -272,8 +254,7 @@ pub fn resolve_globs(
.chain(module.keys(|e| e.x.exported)) .chain(module.keys(|e| e.x.exported))
.collect_vec(); .collect_vec();
// Reference to the module to be modified // Reference to the module to be modified
let mut_mod = let mut_mod = globtree_prefix.0.iter().fold(&mut *project_root, |m, k| {
globtree_prefix.0.iter().fold(&mut *project_root, |m, k| {
let entry = m.entries.get_mut(k).expect("this is a source path"); let entry = m.entries.get_mut(k).expect("this is a source path");
unwrap_or!(&mut entry.member => ModMember::Sub; { unwrap_or!(&mut entry.member => ModMember::Sub; {
panic!("This is also a source path") panic!("This is also a source path")
@@ -286,23 +267,12 @@ pub fn resolve_globs(
let entry = get_or_make(&mut mut_mod.entries, &key, default_entry); let entry = get_or_make(&mut mut_mod.entries, &key, default_entry);
entry.x.locations.push(location.clone()); entry.x.locations.push(location.clone());
let alias_tgt = target.clone().suffix([key.clone()]).to_sym(); let alias_tgt = target.clone().suffix([key.clone()]).to_sym();
match &mut entry.member { if let ModMember::Item(ProjItem { kind: kref @ ItemKind::None }) = &mut entry.member {
ModMember::Item(ProjItem { kind: kref @ ItemKind::None }) => *kref = ItemKind::Alias(alias_tgt)
*kref = ItemKind::Alias(alias_tgt), } else {
ModMember::Item(ProjItem { kind: ItemKind::Alias(prev_alias) }) => let local_name = globtree_prefix.clone().as_prefix_of(key.clone()).to_sym();
if prev_alias != &alias_tgt {
let local_name =
globtree_prefix.clone().as_prefix_of(key.clone()).to_sym();
let locs = pushed_ref(&entry.x.locations, location.clone()); let locs = pushed_ref(&entry.x.locations, location.clone());
contention.insert((alias_tgt, local_name), locs); contention.insert((alias_tgt, local_name), locs);
},
_ => {
let err = MultipleDefinitions {
locations: entry.x.locations.clone(),
path: globtree_prefix.as_prefix_of(key).to_sym(),
};
return Err(err.pack());
},
} }
} }
}, },
@@ -313,14 +283,7 @@ pub fn resolve_globs(
ModMember::Item(n) => match n {}, ModMember::Item(n) => match n {},
ModMember::Sub(module) => { ModMember::Sub(module) => {
let subpath = VPath(pushed_ref(&globtree_prefix.0, key)); let subpath = VPath(pushed_ref(&globtree_prefix.0, key));
resolve_globs( resolve_globs(subpath, module, preludes.clone(), project_root, env, contention)?;
subpath,
module,
preludes.clone(),
project_root,
env,
contention,
)?;
}, },
} }
} }
@@ -333,12 +296,9 @@ struct MultipleExports {
} }
impl ProjectError for MultipleExports { impl ProjectError for MultipleExports {
const DESCRIPTION: &'static str = "A symbol was exported in multiple places"; const DESCRIPTION: &'static str = "A symbol was exported in multiple places";
fn message(&self) -> String { fn message(&self) -> String { format!("{} exported multiple times", self.path) }
format!("{} exported multiple times", self.path)
}
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
(self.locations.iter()) (self.locations.iter()).map(|l| ErrorPosition { location: l.clone(), message: None })
.map(|l| ErrorPosition { location: l.clone(), message: None })
} }
} }
@@ -348,11 +308,8 @@ pub(super) struct MultipleDefinitions {
} }
impl ProjectError for MultipleDefinitions { impl ProjectError for MultipleDefinitions {
const DESCRIPTION: &'static str = "Symbol defined twice"; const DESCRIPTION: &'static str = "Symbol defined twice";
fn message(&self) -> String { fn message(&self) -> String { format!("{} refers to multiple conflicting items", self.path) }
format!("{} refers to multiple conflicting items", self.path)
}
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
(self.locations.iter()) (self.locations.iter()).map(|l| ErrorPosition { location: l.clone(), message: None })
.map(|l| ErrorPosition { location: l.clone(), message: None })
} }
} }

View File

@@ -198,6 +198,7 @@ impl Display for ProjXMod {
pub type ProjectEntry = ModEntry<ProjItem, ProjXMod, ProjXEnt>; pub type ProjectEntry = ModEntry<ProjItem, ProjXMod, ProjXEnt>;
/// A node in the tree describing the project /// A node in the tree describing the project
pub type ProjectMod = Module<ProjItem, ProjXMod, ProjXEnt>; pub type ProjectMod = Module<ProjItem, ProjXMod, ProjXEnt>;
/// A reference to an item or module in the project
pub type ProjectMemberRef<'a> = ModMemberRef<'a, ProjItem, ProjXMod, ProjXEnt>; pub type ProjectMemberRef<'a> = ModMemberRef<'a, ProjItem, ProjXMod, ProjXEnt>;
fn collect_rules_rec(bag: &mut Vec<ProjRule>, module: &ProjectMod) { fn collect_rules_rec(bag: &mut Vec<ProjRule>, module: &ProjectMod) {
@@ -267,5 +268,6 @@ pub struct ConstReport {
pub comments: Vec<Arc<String>>, pub comments: Vec<Arc<String>>,
/// Value assigned to the constant /// Value assigned to the constant
pub value: Expr, pub value: Expr,
/// Source location this constant was parsed from
pub range: SourceRange, pub range: SourceRange,
} }

View File

@@ -1,14 +1,18 @@
// Construction: //! Optimized form of macro pattern that can be quickly tested against the AST.
// convert pattern into hierarchy of plain, scan, middle //!
// - plain: accept any sequence or any non-empty sequence //! # Construction
// - scan: a single scalar pattern moves LTR or RTL, submatchers on either //!
// side //! convert pattern into hierarchy of plain, scan, middle
// - middle: two scalar patterns walk over all permutations of matches //! - plain: accept any sequence or any non-empty sequence
// while getting progressively closer to each other //! - scan: a single scalar pattern moves LTR or RTL, submatchers on either
// //! side
// Application: //! - middle: two scalar patterns walk over all permutations of matches
// walk over the current matcher's valid options and poll the submatchers //! while getting progressively closer to each other
// for each of them //!
//! # Application
//!
//! walk over the current matcher's valid options and poll the submatchers
//! for each of them
mod any_match; mod any_match;
mod build; mod build;

View File

@@ -1,3 +1,5 @@
//! Datastructures for cached pattern
use std::fmt::{Display, Write}; use std::fmt::{Display, Write};
use std::rc::Rc; use std::rc::Rc;

View File

@@ -7,7 +7,6 @@ use crate::error::{ErrorPosition, ProjectError, ProjectErrorObj};
use crate::location::{CodeLocation, SourceRange}; use crate::location::{CodeLocation, SourceRange};
use crate::parse::parsed::{search_all_slcs, Clause, PHClass, Placeholder}; use crate::parse::parsed::{search_all_slcs, Clause, PHClass, Placeholder};
use crate::pipeline::project::ProjRule; use crate::pipeline::project::ProjRule;
use crate::utils::boxed_iter::BoxedIter;
/// Various reasons why a substitution rule may be invalid /// Various reasons why a substitution rule may be invalid
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View File

@@ -1,3 +1,5 @@
//! Join hashmaps with a callback for merging or failing on conflicting keys.
use std::hash::Hash; use std::hash::Hash;
use hashbrown::HashMap; use hashbrown::HashMap;
@@ -10,8 +12,7 @@ pub fn join_maps<K: Eq + Hash, V>(
right: HashMap<K, V>, right: HashMap<K, V>,
mut merge: impl FnMut(&K, V, V) -> V, mut merge: impl FnMut(&K, V, V) -> V,
) -> HashMap<K, V> { ) -> HashMap<K, V> {
try_join_maps(left, right, |k, l, r| Ok(merge(k, l, r))) try_join_maps(left, right, |k, l, r| Ok(merge(k, l, r))).unwrap_or_else(|e: Never| match e {})
.unwrap_or_else(|e: Never| match e {})
} }
/// Combine two hashmaps via a fallible value merger. See also [join_maps] /// Combine two hashmaps via a fallible value merger. See also [join_maps]
@@ -28,6 +29,6 @@ pub fn try_join_maps<K: Eq + Hash, V, E>(
}; };
mixed.insert(key, val); mixed.insert(key, val);
} }
mixed.extend(right.into_iter()); mixed.extend(right);
Ok(mixed) Ok(mixed)
} }

View File

@@ -1,7 +1,8 @@
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use intern_all::Tok; use intern_all::{i, Tok};
use itertools::Itertools;
use crate::error::{ErrorSansLocation, ErrorSansLocationObj}; use crate::error::{ErrorSansLocation, ErrorSansLocationObj};
use crate::name::{PathSlice, VPath}; use crate::name::{PathSlice, VPath};
@@ -31,6 +32,11 @@ pub type FSResult = Result<Loaded, ErrorSansLocationObj>;
/// Distinguished error for missing code /// Distinguished error for missing code
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub struct CodeNotFound(pub VPath); pub struct CodeNotFound(pub VPath);
impl CodeNotFound {
pub fn new(path: VPath) -> Self {
Self(path)
}
}
impl ErrorSansLocation for CodeNotFound { impl ErrorSansLocation for CodeNotFound {
const DESCRIPTION: &'static str = "No source code for path"; const DESCRIPTION: &'static str = "No source code for path";
fn message(&self) -> String { format!("{} not found", self.0) } fn message(&self) -> String { format!("{} not found", self.0) }
@@ -60,7 +66,5 @@ impl VirtFS for &dyn VirtFS {
fn get(&self, path: &[Tok<String>], full_path: PathSlice) -> FSResult { fn get(&self, path: &[Tok<String>], full_path: PathSlice) -> FSResult {
(*self).get(path, full_path) (*self).get(path, full_path)
} }
fn display(&self, path: &[Tok<String>]) -> Option<String> { fn display(&self, path: &[Tok<String>]) -> Option<String> { (*self).display(path) }
(*self).display(path)
}
} }

View File

@@ -30,7 +30,7 @@ impl VirtFS for DeclTree {
ModMember::Sub(module) => match path.split_first() { ModMember::Sub(module) => match path.split_first() {
None => Ok(Loaded::collection(module.keys(|_| true))), None => Ok(Loaded::collection(module.keys(|_| true))),
Some((head, tail)) => (module.entries.get(head)) Some((head, tail)) => (module.entries.get(head))
.ok_or_else(|| CodeNotFound(full_path.to_vpath()).pack()) .ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
.and_then(|ent| ent.get(tail, full_path)), .and_then(|ent| ent.get(tail, full_path)),
}, },
} }

View File

@@ -89,7 +89,7 @@ impl DirNode {
let mut file = File::open(&fpath).map_err(|file_e| { let mut file = File::open(&fpath).map_err(|file_e| {
match (dir_e.kind(), file_e.kind()) { match (dir_e.kind(), file_e.kind()) {
(ErrorKind::NotFound, ErrorKind::NotFound) => (ErrorKind::NotFound, ErrorKind::NotFound) =>
CodeNotFound(orig_path.to_vpath()).pack(), CodeNotFound::new(orig_path.to_vpath()).pack(),
_ => OpenError::wrap(file_e, dir_e), _ => OpenError::wrap(file_e, dir_e),
} }
})?; })?;

View File

@@ -61,7 +61,7 @@ impl VirtFS for EmbeddedFS {
return Ok(Loaded::collection(self.tree.keys(|_| true))); return Ok(Loaded::collection(self.tree.keys(|_| true)));
} }
let entry = (self.tree.walk1_ref(&[], path, |_| true)) let entry = (self.tree.walk1_ref(&[], path, |_| true))
.map_err(|_| CodeNotFound(full_path.to_vpath()).pack())?; .map_err(|_| CodeNotFound::new(full_path.to_vpath()).pack())?;
Ok(match &entry.0.member { Ok(match &entry.0.member {
ModMember::Item(text) => Loaded::Code(text.clone()), ModMember::Item(text) => Loaded::Code(text.clone()),
ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)), ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)),

View File

@@ -33,7 +33,7 @@ impl PrefixFS {
impl VirtFS for PrefixFS { impl VirtFS for PrefixFS {
fn get(&self, path: &[Tok<String>], full_path: PathSlice) -> super::FSResult { fn get(&self, path: &[Tok<String>], full_path: PathSlice) -> super::FSResult {
let path = (self.proc_path(path)) let path = (self.proc_path(path))
.ok_or_else(|| CodeNotFound(full_path.to_vpath()).pack())?; .ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())?;
self.wrapped.get(&path, full_path) self.wrapped.get(&path, full_path)
} }
fn display(&self, path: &[Tok<String>]) -> Option<String> { fn display(&self, path: &[Tok<String>]) -> Option<String> {