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

View File

@@ -19,12 +19,14 @@
"editor.lineNumbers": "off",
"editor.glyphMargin": false,
"editor.rulers": [],
"editor.guides.indentation": false,
"editor.guides.indentation": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
},
"[rust]": {
"editor.rulers": [80],
"editor.rulers": [
100
],
},
"rust-analyzer.showUnlinkedFileNotification": false,
"rust-analyzer.checkOnSave": true,

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,35 +5,27 @@ use std::sync::{Arc, Mutex};
use never::Never;
use super::error::{ExternError, ExternResult};
use crate::interpreter::apply::CallData;
use crate::interpreter::context::RunContext;
use crate::interpreter::error::RunError;
use crate::interpreter::nort;
use crate::interpreter::run::RunData;
use crate::location::{CodeLocation, SourceRange};
use crate::name::NameLike;
use crate::parse::parsed;
use crate::utils::ddispatch::{request, Request, Responder};
/// Information returned by [Atomic::run]. This mirrors
/// [crate::interpreter::Return] but with a clause instead of an Expr.
pub struct AtomicReturn {
/// The next form of the expression
pub clause: nort::Clause,
/// Remaining gas
pub gas: Option<usize>,
/// Whether further normalization is possible by repeated calls to
/// [Atomic::run]
pub inert: bool,
/// Information returned by [Atomic::run].
pub enum AtomicReturn {
/// No work was done. If the atom takes an argument, it can be provided now
Inert(nort::Clause),
/// Work was done, returns new clause and consumed gas. 1 gas is already
/// consumed by the virtual call, so nonzero values indicate expensive
/// operations.
Change(usize, nort::Clause),
}
impl AtomicReturn {
/// Report indicating that the value is inert
pub fn inert<T: Atomic, E>(this: T, ctx: RunContext) -> Result<Self, E> {
Ok(Self { clause: this.atom_cls(), gas: ctx.gas, inert: true })
}
/// 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 })
pub fn inert<T: Atomic, E>(this: T) -> Result<Self, E> {
Ok(Self::Inert(this.atom_cls()))
}
}
@@ -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
///
/// # Lifecycle methods
@@ -91,7 +102,7 @@ where Self: 'static
/// Returns a reference to a possible expression held inside the atom which
/// 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
/// inert. If it wraps a computation, it should execute one logical step of
@@ -172,7 +183,7 @@ impl AtomGenerator {
}
impl Debug for AtomGenerator {
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 {
fn as_any(self: Box<Self>) -> Box<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 apply_ref(&self, _: CallData) -> ExternResult<nort::Clause> {
match *self {}

View File

@@ -4,11 +4,11 @@ use std::fmt::Debug;
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 crate::interpreter::apply::CallData;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
use crate::interpreter::nort::{Clause, Expr};
use crate::location::CodeLocation;
use crate::utils::ddispatch::{Request, Responder};
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_ref(&self) -> &dyn std::any::Any { self }
fn parser_eq(&self, _: &dyn std::any::Any) -> bool { false }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
AtomicReturn::inert(*self, run.ctx)
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
AtomicReturn::inert(*self)
}
fn apply(mut self: Box<Self>, call: CallData) -> ExternResult<Clause> {
self.assert_applicable(&call.location)?;

View File

@@ -10,7 +10,7 @@ use crate::location::CodeLocation;
pub trait ExternError: Display + Send + Sync + DynClone {
/// Convert into trait object
#[must_use]
fn rc(self) -> Arc<dyn ExternError>
fn rc(self) -> ExternErrorObj
where Self: 'static + Sized {
Arc::new(self)
}
@@ -25,7 +25,10 @@ impl Debug for dyn ExternError {
impl Error for dyn ExternError {}
/// 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
/// hold.
@@ -52,7 +55,7 @@ impl AssertionError {
location: CodeLocation,
message: &'static str,
details: String,
) -> Arc<dyn ExternError> {
) -> ExternErrorObj {
Self { location, message, details }.rc()
}
}

View File

@@ -1,15 +1,14 @@
use std::any::{Any, TypeId};
use std::fmt::Debug;
use std::fmt::{Debug, Display};
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::to_clause::ToClause;
use super::try_from_expr::TryFromExpr;
use crate::interpreter::apply::CallData;
use crate::interpreter::context::Halt;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::{run, RunData};
use crate::interpreter::nort::{Clause, Expr};
use crate::utils::ddispatch::Responder;
/// 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.
pub struct Param<T, U, F> {
data: F,
name: Tok<String>,
_t: PhantomData<T>,
_u: PhantomData<U>,
}
unsafe impl<T, U, F: Send> Send for Param<T, U, F> {}
impl<T, U, F> Param<T, U, F> {
/// 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 {
Self { data: f, _t: PhantomData, _u: PhantomData }
Self { name, data: f, _t: PhantomData, _u: PhantomData }
}
/// Take out the function
pub fn get(self) -> F { self.data }
}
impl<T, U, F: Clone> Clone for Param<T, U, F> {
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> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FnMiddleStage")
.field("argument", &self.arg)
.finish_non_exhaustive()
write!(f, "FnMiddleStage({} {})", self.f, self.arg)
}
}
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_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
(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 {
let Self { arg, f: Param { data: f, .. } } = *self;
let clause = 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")
Ok(AtomicReturn::Change(0, f(arg.downcast()?).to_clause(r.location)))
}
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> Debug for Param<T, U, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Param")
}
}
impl<
T: 'static + TryFromExpr + Clone,
@@ -108,10 +106,8 @@ impl<
{
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, r: RunData) -> AtomicResult {
AtomicReturn::inert(*self, r.ctx)
}
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
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
/// 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
/// function can always return another call to `xfn_`N`ary` to consume more
/// arguments.
pub mod constructors {
pub mod xfn_impls {
use intern_all::{i, Tok};
use super::super::atom::Atomic;
use super::super::try_from_expr::TryFromExpr;
#[allow(unused)] // for doc
use super::Thunk;
use super::{Param, ToClause};
use super::{Param, ToClause, Xfn};
macro_rules! xfn_variant {
(
@@ -139,42 +160,40 @@ pub mod constructors {
($($alt:expr)*)
) => {
paste::paste!{
#[doc = "Convert a function of " $number " argument(s) into a curried"
" 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 >] <
impl<
$( $t : TryFromExpr + Clone + Send + 'static, )*
TLast: TryFromExpr + Clone + 'static,
TReturn: ToClause + Send + 'static,
TFunction: FnOnce( $( $t , )* TLast )
-> TReturn + Clone + Send + 'static
>(function: TFunction) -> impl Atomic + Clone {
xfn_variant!(@BODY_LOOP function
( $( ( $t [< $t:lower >] ) )* )
( $( [< $t:lower >] )* )
)
> Xfn<$number, ($($t,)* TLast,), TReturn> for TFunction {
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:lower >] )* )
)
}
}
}
};
(@BODY_LOOP $function:ident (
(@BODY_LOOP $function:ident $name:ident $stage_n:ident $argc:ident (
( $Next:ident $next:ident )
$( ( $T:ident $t:ident ) )*
) $full:tt) => {
Param::new(|$next : $Next| {
xfn_variant!(@BODY_LOOP $function ( $( ( $T $t ) )* ) $full)
) $full:tt) => {{
Param::new($stage_n, move |$next : $Next| {
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 )* )) => {
Param::new(|last: TLast| $function ( $( $t , )* last ))
};
}};
(@BODY_LOOP $function:ident $name:ident $stage_n:ident $argc:ident (
) ( $( $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));

View File

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

View File

@@ -4,13 +4,13 @@ use std::ops::{Deref, DerefMut};
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::try_from_expr::TryFromExpr;
use crate::foreign::error::AssertionError;
use crate::interpreter::apply::CallData;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
use crate::interpreter::nort::{Clause, Expr};
use crate::libs::std::number::Numeric;
use crate::libs::std::string::OrcString;
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_ref(&self) -> &dyn Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
AtomicReturn::inert(*self, run.ctx)
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
AtomicReturn::inert(*self)
}
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
Err(NotAFunction(self.clone().atom_expr(call.location)).rc())

View File

@@ -1,11 +1,9 @@
use std::fmt::Debug;
use super::atom::{Atomic, AtomicReturn};
use super::atom::{Atomic, AtomicReturn, CallData, RunData};
use super::error::ExternResult;
use super::to_clause::ToClause;
use crate::interpreter::apply::CallData;
use crate::interpreter::nort::{Clause, ClauseInst};
use crate::interpreter::run::RunData;
use crate::interpreter::nort::{Clause, Expr};
use crate::utils::ddispatch::Responder;
/// 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 {
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 {
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

View File

@@ -4,11 +4,15 @@
use std::fmt::Debug;
use dyn_clone::{clone_box, DynClone};
use intern_all::Tok;
use itertools::Itertools;
use substack::Substack;
use trait_set::trait_set;
use super::tpl;
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::nort::Expr;
use crate::location::CodeLocation;
@@ -17,22 +21,46 @@ use crate::utils::combine::Combine;
trait_set! {
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]
#[derive(Debug)]
pub struct GenConst(Box<dyn TreeLeaf>);
pub struct GenConst(GCKind);
impl GenConst {
fn new(data: impl GenClause + Clone + 'static) -> Self {
Self(Box::new(data))
fn c(data: impl GenClause + Clone + 'static) -> Self { Self(GCKind::Const(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 template as [crate::interpreter::nort]
pub fn gen_nort(&self, location: CodeLocation) -> Expr {
self.0.template(nort_gen(location), [])
/// 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}"),
}
}
}
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
@@ -43,9 +71,7 @@ pub struct ConflictingConsts;
impl Combine for GenConst {
type Error = ConflictingConsts;
fn combine(self, _: Self) -> Result<Self, Self::Error> {
Err(ConflictingConsts)
}
fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingConsts) }
}
/// A lightweight module tree that can be built declaratively by hand to
@@ -56,16 +82,14 @@ pub type ConstTree = ModEntry<GenConst, (), ()>;
/// Describe a constant
#[must_use]
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]
#[must_use]
pub fn atom_leaf(atom: impl Atomic + Clone + 'static) -> ConstTree {
leaf(tpl::V(atom))
}
pub fn atom_leaf(atom: impl Atomic + Clone + 'static) -> ConstTree { 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
/// without breaking this call into a block
@@ -77,5 +101,22 @@ pub fn atom_ent<K: AsRef<str>>(
(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
pub type ConstCombineErr = TreeConflict<GenConst, (), ()>;

View File

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

View File

@@ -1,24 +1,10 @@
use std::collections::VecDeque;
use std::mem;
use never::Never;
use super::context::RunContext;
use super::error::RunError;
use super::nort::{Clause, ClauseInst, Expr};
use super::path_set::{PathSet, Step};
use super::run::run;
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>,
}
use crate::foreign::atom::CallData;
/// 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
@@ -42,15 +28,12 @@ fn map_at<E>(
_ => (),
}
Ok(match (source, path.next()) {
(Clause::Lambda { .. } | Clause::Identity(_), _) =>
unreachable!("Handled above"),
(Clause::Lambda { .. } | Clause::Identity(_), _) => unreachable!("Handled above"),
// If the path ends and this isn't a lambda, process it
(val, None) => mapper(val)?,
// If it's an Apply, execute the next step in the path
(Clause::Apply { f, x }, Some(head)) => {
let proc = |x: &Expr| {
Ok(map_at(path, &x.clause.cls(), mapper)?.to_expr(x.location()))
};
let proc = |x: &Expr| Ok(map_at(path, &x.cls(), mapper)?.to_expr(x.location()));
match head {
None => Clause::Apply { f: proc(f)?, x: x.clone() },
Some(n) => {
@@ -69,7 +52,12 @@ fn map_at<E>(
/// with the value in the body. Note that a path may point to multiple
/// placeholders.
#[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;
map_at(steps.iter().cloned(), body, &mut |chkpt| -> Result<Clause, Never> {
match (chkpt, next) {
@@ -80,18 +68,20 @@ fn substitute(paths: &PathSet, value: ClauseInst, body: &Clause) -> Clause {
let mut argv = x.clone();
let f = match conts.get(&None) {
None => f.clone(),
Some(sp) => substitute(sp, value.clone(), &f.clause.cls())
.to_expr(f.location()),
Some(sp) => substitute(sp, value.clone(), &f.cls(), on_sub).to_expr(f.location()),
};
for (i, old) in argv.iter_mut().rev().enumerate() {
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());
}
}
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"),
(_, 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 {})
}
pub(super) fn apply_as_atom(
f: Expr,
arg: Expr,
ctx: RunContext,
) -> Result<Clause, RunError> {
pub(super) fn apply_as_atom(f: Expr, arg: Expr, ctx: RunContext) -> Result<Clause, RunError> {
let call = CallData { location: f.location(), arg, ctx };
match f.clause.try_unwrap() {
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>,
/// The number of reduction steps the interpreter can take before returning
pub gas: Option<usize>,
/// The limit of recursion
pub stack_size: usize,
}
impl<'a> RunContext<'a> {
/// Consume some gas if it is being counted

View File

@@ -1,34 +1,66 @@
use std::fmt::{Debug, Display};
use std::sync::Arc;
use std::fmt::{self, Debug, Display};
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::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
#[derive(Debug, Clone)]
pub enum RunError {
/// A Rust function encountered an error
Extern(Arc<dyn ExternError>),
/// Symbol not in context
MissingSymbol(Sym, CodeLocation),
Extern(ExternErrorObj),
/// Ran out of gas
Interrupted(Interrupted)
Interrupted(Interrupted),
}
impl From<Arc<dyn ExternError>> for RunError {
fn from(value: Arc<dyn ExternError>) -> Self { Self::Extern(value) }
impl<T: ExternError + 'static> From<T> for RunError {
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 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Extern(e) => write!(f, "Error in external function: {e}"),
Self::MissingSymbol(sym, loc) => {
write!(f, "{sym}, called at {loc} is not loaded")
Self::Interrupted(i) => {
write!(f, "Ran out of gas:\n{}", strace(&i.stack))
},
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(
mut state: Expr,
handlers: &mut HandlerTable,
RunContext { mut gas, symbols }: RunContext,
mut ctx: RunContext,
) -> Result<Halt, RunError> {
loop {
let inert;
Halt { gas, inert, state } = run(state, RunContext { gas, symbols })?;
let state_cls = state.clause.cls();
let halt = run(state, ctx.clone())?;
state = halt.state;
ctx.use_gas(halt.gas.unwrap_or(0));
let state_cls = state.cls();
if let Clause::Atom(Atom(a)) = &*state_cls {
if let Some(res) = handlers.dispatch(a.as_ref(), state.location()) {
drop(state_cls);
@@ -88,9 +89,9 @@ pub fn run_handler(
continue;
}
}
if inert || gas == Some(0) {
if halt.inert || ctx.no_gas() {
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,
})
}
/// 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 {
@@ -160,15 +182,21 @@ impl ClauseInst {
/// Call a normalization function on the expression. The expr is
/// updated with the new clause which affects all copies of it
/// 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>(
&self,
mapper: impl FnOnce(Clause) -> Result<(Clause, T), RunError>,
) -> Result<(ClauseInst, T), RunError> {
) -> Result<(Self, T), RunError> {
enum Report<T> {
Nested(ClauseInst, T),
Plain(T),
}
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) {
Ok((nested, t)) => (clause, Ok(Report::Nested(nested, t))),
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
/// predicate returns. This is a convenience function for reaching
/// through the [Mutex]. The clause will never be [Clause::Identity].
@@ -320,11 +374,11 @@ impl Display for Clause {
Clause::Apply { f: fun, x } =>
write!(f, "({fun} {})", x.iter().join(" ")),
Clause::Lambda { args, body } => match args {
Some(path) => write!(f, "\\{path:?}.{body}"),
None => write!(f, "\\_.{body}"),
Some(path) => write!(f, "[\\{path}.{body}]"),
None => write!(f, "[\\_.{body}]"),
},
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 {
if let Some(IntGenData::Apply(_)) = self.stack.value() {
let prev = self.pop(1);
f(prev.push(IntGenData::AppF))
f(self.pop(1).push(IntGenData::AppF))
} else {
f(self)
}
@@ -80,14 +79,17 @@ impl<'a, T: ?Sized, U: ?Sized> NortBuilder<'a, T, U> {
pub fn arg_logic(self, name: &'a U) {
let mut lambda_chk = (self.lambda_picker)(name);
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::Lambda(n, rc) =>
lambda_chk(n).then(|| (vec![], *rc)).or(path),
IntGenData::AppArg(n) => path.map(|(p, rc)| (pushed(p, Some(*n)), rc)),
IntGenData::AppF => path.map(|(p, rc)| (pushed(p, None), rc)),
IntGenData::Lambda(n, rc) => match lambda_chk(n) {
false => Ok(path),
true => Err((path, *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() {
slot @ None => *slot = Some(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::error::RunError;
use super::nort::{Clause, Expr};
use crate::foreign::atom::AtomicReturn;
use crate::foreign::error::ExternResult;
use crate::location::CodeLocation;
use crate::name::Sym;
use crate::foreign::atom::{AtomicReturn, RunData};
use crate::foreign::error::ExternError;
use crate::interpreter::apply::{apply_as_atom, substitute};
use crate::interpreter::error::{strace, MissingSymbol, StackOverflow};
use crate::utils::pure_seq::pushed;
/// 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>,
}
#[derive(Debug)]
/// Interpreter state when processing was interrupted
#[derive(Debug, Clone)]
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 {
pub fn resume(self, ctx: RunContext) -> Result<Halt, RunError> {
run_stack(self.stack, ctx)
}
/// Continue processing where it was interrupted
pub fn resume(self, ctx: RunContext) -> Result<Halt, RunError> { run_stack(self.stack, ctx) }
}
/// Normalize an expression using beta reduction with memoization
pub fn run(mut expr: Expr, mut ctx: RunContext) -> Result<Halt, RunError> {
run_stack(vec![expr], ctx)
pub fn run(expr: Expr, ctx: RunContext) -> Result<Halt, RunError> {
let mut v = Vec::with_capacity(1000);
v.push(expr);
run_stack(v, ctx)
}
fn run_stack(
mut stack: Vec<Expr>,
mut ctx: RunContext,
) -> Result<Halt, RunError> {
fn run_stack(mut stack: Vec<Expr>, mut ctx: RunContext) -> Result<Halt, RunError> {
let mut expr = stack.pop().expect("Empty stack");
let mut popped = false;
loop {
// print!("Now running {expr}");
// let trace = strace(&stack);
// if trace.is_empty() {
// println!("\n")
// } else {
// println!("\n{trace}\n")
// };
if ctx.no_gas() {
return Err(RunError::Interrupted(Interrupted {
stack: pushed(stack, expr),
}));
return Err(RunError::Interrupted(Interrupted { stack: pushed(stack, expr) }));
}
let (next_clsi, inert) = expr.clause.try_normalize(|mut cls| {
loop {
if ctx.no_gas() {
return Ok((cls, false));
ctx.use_gas(1);
enum Res {
Inert,
Cont,
Push(Expr),
}
let (next_clsi, res) = expr.clause.try_normalize(|cls| match cls {
Clause::Identity(_) => panic!("Passed by try_normalize"),
Clause::LambdaArg => panic!("Unbound argument"),
Clause::Lambda { .. } => Ok((cls, Res::Inert)),
Clause::Bottom(b) => Err(b),
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)));
}
}
match cls {
cls @ Clause::Identity(_) => return Ok((cls, false)),
// TODO:
// - unfuck nested loop
// - inline most of [apply] to eliminate recursion step
Clause::Apply { f, x } => {
if x.is_empty() {
return Ok((f.clause.into_cls(), false));
}
let (gas, clause) = apply(f, x, ctx.clone())?;
if ctx.gas.is_some() {
ctx.gas = gas;
}
cls = clause;
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::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;
}
},
Clause::Apply { f, mut x } => {
if x.is_empty() {
return Ok((Clause::Identity(f.clsi()), Res::Cont));
}
match &*f.cls() {
Clause::Identity(f2) =>
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())
}
if atomic_ret.inert {
return Ok((atomic_ret.clause, true));
}
cls = atomic_ret.clause;
return Ok((Clause::Apply { f: f.clone(), x }, Res::Cont));
},
Clause::Constant(c) => {
let symval = (ctx.symbols.get(&c)).ok_or_else(|| {
RunError::MissingSymbol(c.clone(), expr.location())
})?;
ctx.gas = ctx.gas.map(|g| g - 1); // cost of lookup
cls = Clause::Identity(symval.clause.clone());
_ => (),
}
if !popped {
return Ok((Clause::Apply { f: f.clone(), x }, Res::Push(f)));
}
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())?
},
// non-reducible
c => return Ok((c, true)),
Clause::Lambda { args, body } => match args {
None => body.clsi().into_cls(),
Some(args) => substitute(args, arg.clsi(), &body.cls(), &mut || ctx.use_gas(1)),
},
c => panic!("Run should never settle on {c}"),
};
}
Ok((Clause::Apply { f: f.to_expr(loc), x }, Res::Cont))
},
})?;
expr.clause = next_clsi;
if inert {
match stack.pop() {
Some(e) => expr = e,
None => return Ok(Halt { state: expr, gas: ctx.gas, inert }),
}
popped = matches!(res, Res::Inert);
match res {
Res::Cont => continue,
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::cps_box::CPSBox;
use crate::foreign::error::ExternError;
use crate::foreign::fn_bridge::constructors::xfn_2ary;
use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tpl;
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::handler::HandlerTable;
use crate::interpreter::nort::Expr;
@@ -81,9 +80,7 @@ impl Display for InfiniteBlock {
pub struct MessagePort(Sender<Box<dyn Any + Send>>);
impl MessagePort {
/// Send an event. Any type is accepted, handlers are dispatched by type ID
pub fn send<T: Send + 'static>(&mut self, message: T) {
let _ = self.0.send(Box::new(message));
}
pub fn send<T: Send + 'static>(&mut self, message: T) { let _ = self.0.send(Box::new(message)); }
}
fn gen() -> CodeGenInfo { CodeGenInfo::no_details("asynch") }
@@ -124,17 +121,10 @@ impl<'a> AsynchSystem<'a> {
/// # Panics
///
/// if the given type is already handled.
pub fn register<T: 'static>(
&mut self,
mut f: impl FnMut(Box<T>) -> Vec<Expr> + 'a,
) {
pub fn register<T: 'static>(&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 prev = self.handlers.insert(TypeId::of::<T>(), Box::new(cb));
assert!(
prev.is_none(),
"Duplicate handlers for async event {}",
type_name::<T>()
)
assert!(prev.is_none(), "Duplicate handlers for async event {}", type_name::<T>())
}
/// 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::Event(ev) => {
let handler = (handlers.get_mut(&ev.as_ref().type_id()))
.unwrap_or_else(|| {
panic!("Unhandled messgae type: {:?}", (*ev).type_id())
});
.unwrap_or_else(|| panic!("Unhandled messgae type: {:?}", (*ev).type_id()));
let events = handler(ev);
// we got new microtasks
if !events.is_empty() {
microtasks = VecDeque::from(events);
// trampoline
let loc =
CodeLocation::Gen(CodeGenInfo::no_details("system::asynch"));
let loc = CodeLocation::Gen(CodeGenInfo::no_details("system::asynch"));
return Ok(Inert(Yield).atom_expr(loc));
}
},
@@ -211,8 +198,8 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
lexer_plugins: vec![],
line_parsers: vec![],
constants: ConstTree::ns("system::async", [ConstTree::tree([
("set_timer", atom_leaf(xfn_2ary(set_timer))),
("yield", atom_leaf(Inert(Yield))),
xfn_ent("set_timer", [set_timer]),
atom_ent("yield", [Inert(Yield)]),
])]),
code: code(),
prelude: Vec::new(),

View File

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

View File

@@ -2,11 +2,10 @@ use std::ffi::OsString;
use crate::foreign::atom::Atomic;
use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::xfn_1ary;
use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::to_clause::ToClause;
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::libs::std::string::OrcString;
use crate::location::CodeLocation;
@@ -21,24 +20,20 @@ impl ToClause for OsString {
fn to_clause(self, _: CodeLocation) -> Clause { Inert(self).atom_cls() }
}
pub fn os_to_string(
os: Inert<OsString>,
) -> Result<Inert<OrcString>, Inert<OsString>> {
pub fn os_to_string(os: Inert<OsString>) -> Result<Inert<OrcString>, Inert<OsString>> {
os.0.into_string().map(|s| Inert(s.into())).map_err(Inert)
}
pub fn string_to_os(str: Inert<OrcString>) -> Inert<OsString> {
Inert(str.0.get_string().into())
}
pub fn string_to_os(str: Inert<OrcString>) -> Inert<OsString> { Inert(str.0.get_string().into()) }
pub fn os_print(os: Inert<OsString>) -> Inert<OrcString> {
Inert(os.0.to_string_lossy().to_string().into())
}
pub fn os_string_lib() -> ConstTree {
ConstTree::tree([
("os_to_string", atom_leaf(xfn_1ary(os_to_string))),
("string_to_os", atom_leaf(xfn_1ary(string_to_os))),
("os_print", atom_leaf(xfn_1ary(os_print))),
])
ConstTree::ns("system::fs", [ConstTree::tree([
xfn_ent("os_to_string", [os_to_string]),
xfn_ent("string_to_os", [string_to_os]),
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 crate::foreign::cps_box::CPSBox;
use crate::foreign::error::ExternResult;
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
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::std::binary::Binary;
use crate::libs::std::runtime_error::RuntimeError;
@@ -45,35 +44,27 @@ pub fn read_until(
let cmd = ReadCmd::RBytes(BRead::Until(pattern));
Ok(CPSBox::new(3, IOCmdHandlePack { handle, cmd }))
}
pub fn write_str(
Inert(handle): WriteHandle,
string: Inert<OrcString>,
) -> WriteCmdPack {
pub fn write_str(Inert(handle): WriteHandle, string: Inert<OrcString>) -> WriteCmdPack {
let cmd = WriteCmd::WStr(string.0.get_string());
CPSBox::new(3, IOCmdHandlePack { handle, cmd })
}
pub fn write_bin(
Inert(handle): WriteHandle,
bytes: Inert<Binary>,
) -> WriteCmdPack {
pub fn write_bin(Inert(handle): WriteHandle, bytes: Inert<Binary>) -> WriteCmdPack {
CPSBox::new(3, IOCmdHandlePack { handle, cmd: WriteCmd::WBytes(bytes.0) })
}
pub fn flush(Inert(handle): WriteHandle) -> WriteCmdPack {
CPSBox::new(3, IOCmdHandlePack { handle, cmd: WriteCmd::Flush })
}
pub fn io_bindings<'a>(
std_streams: impl IntoIterator<Item = (&'a str, ConstTree)>,
) -> ConstTree {
pub fn io_bindings<'a>(std_streams: impl IntoIterator<Item = (&'a str, ConstTree)>) -> ConstTree {
ConstTree::ns("system::io", [ConstTree::tree([
("read_string", atom_leaf(xfn_1ary(read_string))),
("read_line", atom_leaf(xfn_1ary(read_line))),
("read_bin", atom_leaf(xfn_1ary(read_bin))),
("read_n_bytes", atom_leaf(xfn_2ary(read_bytes))),
("read_until", atom_leaf(xfn_2ary(read_until))),
("write_str", atom_leaf(xfn_2ary(write_str))),
("write_bin", atom_leaf(xfn_2ary(write_bin))),
("flush", atom_leaf(xfn_1ary(flush))),
xfn_ent("read_string", [read_string]),
xfn_ent("read_line", [read_line]),
xfn_ent("read_bin", [read_bin]),
xfn_ent("read_n_bytes", [read_bytes]),
xfn_ent("read_until", [read_until]),
xfn_ent("write_str", [write_str]),
xfn_ent("write_bin", [write_bin]),
xfn_ent("flush", [flush]),
])
.combine(ConstTree::tree(std_streams))
.expect("std_stream name clashing with io functions")])

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import std::(pmatch, inspect)
import std::known::(=)
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 (
if pmatch::value == (...$other)
then pmatch::pass

View File

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

View File

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

View File

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

View File

@@ -5,9 +5,8 @@
use std::process::ExitCode;
use crate::foreign::fn_bridge::constructors::xfn_1ary;
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
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -34,8 +33,8 @@ impl InertPayload for ExitStatus {
pub(super) fn exit_status_lib() -> ConstTree {
let is_success = |es: Inert<ExitStatus>| Inert(es.0 == ExitStatus::Success);
ConstTree::ns("std::exit_status", [ConstTree::tree([
("success", atom_leaf(Inert(ExitStatus::Success))),
("failure", atom_leaf(Inert(ExitStatus::Failure))),
("is_success", atom_leaf(xfn_1ary(is_success))),
atom_ent("success", [Inert(ExitStatus::Success)]),
atom_ent("failure", [Inert(ExitStatus::Failure)]),
xfn_ent("is_success", [is_success]),
])])
}

View File

@@ -1,5 +1,6 @@
import super::known::*
import super::pmatch::*
import super::pmatch
import super::pmatch::(match, =>)
import super::macro
--[ 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
( macro (..$argv) => ...$body
=0x2p127=> lambda_walker macro::comma_list (..$argv) (...$body)
=0x3p127=> lambda_walker macro::comma_list (..$argv) (...$body)
)
( macro $_arg => ...$body
=0x2p127=> \$_arg. ...$body)
=0x2p127=> \$_arg. ...$body
)
( macro lambda_walker ( macro::list_item ($_argname) $tail ) $body
=0x2p254=> \$_argname. lambda_walker $tail $body
)

View File

@@ -1,39 +1,32 @@
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::fn_bridge::constructors::xfn_1ary;
use crate::foreign::to_clause::ToClause;
use crate::gen::tree::{atom_leaf, ConstTree};
use crate::interpreter::apply::CallData;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::nort::{Clause, Expr};
use crate::utils::ddispatch::Responder;
#[derive(Debug, Clone)]
struct Inspect;
impl Responder for Inspect {}
impl Responder for Inspect {}
impl Atomic for Inspect {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
AtomicReturn::inert(*self, run.ctx)
}
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
eprintln!("{}", call.arg);
Ok(call.arg.to_clause(call.location))
}
}
fn tee(x: Expr) -> Expr {
eprintln!("{x}");
x
}
}
pub fn inspect_lib() -> ConstTree {
ConstTree::ns("std", [ConstTree::tree([
("inspect", atom_leaf(Inspect)),
("tee", atom_leaf(xfn_1ary(tee))),
atom_ent("inspect", [Inspect]),
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
)
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
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::pass. (\pmatch::value. $t_expr) tail)
(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::pass. $t_expr) (
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 string;
pub mod tuple;
mod tstring;

View File

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

View File

@@ -5,9 +5,8 @@ use never::Never;
use super::string::OrcString;
use crate::foreign::error::{ExternError, ExternResult};
use crate::foreign::fn_bridge::constructors::xfn_1ary;
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
/// 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())
}
pub fn panic_lib() -> ConstTree {
ConstTree::ns("std::panic", [atom_leaf(xfn_1ary(orc_panic))])
}
pub fn panic_lib() -> ConstTree { ConstTree::ns("std::panic", [xfn_leaf(orc_panic)]) }

View File

@@ -11,6 +11,8 @@ import std::panic
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
=0x1p130=> ..$prefix (
@@ -33,21 +35,21 @@ macro request (( ..$pattern )) =0x1p254=> request ( ..$pattern )
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 ( binds_list ...$tail )
=0x1p254=> ( binds_list $_new ( binds_list ...$tail ) )
macro ( add_bind $_new no_binds ) =0x1p254=> ( binds_list $_new no_binds )
( macro ( add_bind $_new (binds_list ...$tail) )
=0x1p254=> ( binds_list $_new (binds_list ...$tail) )
)
macro give_binds no_binds $cont =0x1p254=> $cont
( macro give_binds ( binds_list $_name $tail ) $cont
=0x1p254=> (give_binds $tail $cont $_name)
macro ( give_binds no_binds $cont ) =0x1p254=> $cont
( macro ( give_binds ( binds_list $_name $tail ) $cont )
=0x1p254=> (( give_binds $tail $cont ) $_name )
)
macro take_binds no_binds $cont =0x1p254=> $cont
( macro take_binds ( binds_list $_name $tail ) $cont
=0x1p254=> \$_name. take_binds $tail $cont
macro ( take_binds no_binds $cont ) =0x1p254=> $cont
( macro ( take_binds ( binds_list $_name $tail ) $cont )
=0x1p254=> ( take_binds $tail \$_name. $cont )
)
macro chain_binds no_binds $second =0x1p254=> $second
( macro chain_binds ( binds_list $_head $tail ) $second
=0x1p254=> add_bind $_head chain_binds $tail $second
macro ( chain_binds no_binds $second ) =0x1p254=> $second
( macro ( chain_binds ( binds_list $_head $tail ) $second )
=0x1p254=> ( add_bind $_head ( chain_binds $tail $second ))
)
--[ primitive pattern ( _ ) ]--
@@ -61,7 +63,7 @@ macro chain_binds no_binds $second =0x1p254=> $second
(
macro request ( $_name )
=0x1p226=> response ( pass value ) ( add_bind $_name no_binds )
=0x1p226=> response ( pass value ) ( ( add_bind $_name no_binds ) )
)
--[ primitive pattern ( and ) ]--
@@ -74,11 +76,11 @@ macro chain_binds no_binds $second =0x1p254=> $second
=0x1p254=> response (
(\pass. $lh_expr) (take_binds $lh_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 ) ]--
@@ -93,7 +95,7 @@ macro chain_binds no_binds $second =0x1p254=> $second
( -- 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 ) )
=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
(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
)
export ::(match, cancel, argument, request, response, =>)

View File

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

View File

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

View File

@@ -8,9 +8,8 @@ use std::sync::atomic::{self, AtomicUsize};
use intern_all::i;
use crate::foreign::fn_bridge::constructors::{xfn_1ary, xfn_2ary};
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::name::Sym;
@@ -57,9 +56,7 @@ impl Ord for RefEqual {
fn cmp(&self, other: &Self) -> cmp::Ordering { self.id().cmp(&other.id()) }
}
impl PartialOrd for RefEqual {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
}
impl Hash for RefEqual {
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 {
ConstTree::ns("std::reflect", [ConstTree::tree([
atom_ent("auto_tuple_test", [xfn_1ary(|_: Inert<usize>| {
(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()),
)]),
xfn_ent("ref_equal", [|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.
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
#[derive(Clone)]
@@ -21,7 +20,7 @@ impl RuntimeError {
}
/// 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()
}
}

View File

@@ -1,13 +1,11 @@
use std::sync::{Arc, Mutex};
use crate::foreign::fn_bridge::constructors::{xfn_2ary, xfn_3ary};
use crate::foreign::fn_bridge::Thunk;
use crate::foreign::inert::{Inert, InertPayload};
use crate::gen::tpl;
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::nort_builder::NortBuilder;
use crate::interpreter::handler::HandlerTable;
use crate::interpreter::nort::Expr;
@@ -77,8 +75,8 @@ pub fn state_handlers() -> HandlerTable<'static> {
pub fn state_lib() -> ConstTree {
ConstTree::ns("std::state", [ConstTree::tree([
("new_state", atom_leaf(xfn_2ary(new_state))),
("get_state", atom_leaf(xfn_2ary(get_state))),
("set_state", atom_leaf(xfn_3ary(set_state))),
xfn_ent("new_state", [new_state]),
xfn_ent("get_state", [get_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::state::{state_handlers, state_lib};
use super::string::str_lib;
use super::tstring::TStringLexer;
use super::tuple::tuple_lib;
use crate::facade::system::{IntoSystem, System};
use crate::gen::tree::{ConstCombineErr, ConstTree};
@@ -66,20 +67,19 @@ impl StdConfig {
impl IntoSystem<'static> for StdConfig {
fn into_system(self) -> System<'static> {
let gen = CodeGenInfo::no_details("std");
System {
name: "stdlib",
constants: self.stdlib().expect("stdlib tree is malformed"),
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 {
target: VName::literal("std::prelude"),
exclude: VName::literal("std"),
owner: gen.clone(),
owner: CodeGenInfo::no_details("std::prelude"),
}],
handlers: state_handlers(),
lexer_plugins: vec![],
lexer_plugins: vec![Box::new(TStringLexer)],
line_parsers: parsers(),
}
}

View File

@@ -11,11 +11,10 @@ use unicode_segmentation::UnicodeSegmentation;
use super::runtime_error::RuntimeError;
use crate::foreign::atom::Atomic;
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::to_clause::ToClause;
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::location::CodeLocation;
use crate::utils::iter_find::iter_find;
@@ -50,8 +49,7 @@ impl OrcString {
pub fn get_string(self) -> String {
match self {
Self::Interned(s) => s.as_str().to_owned(),
Self::Runtime(rc) =>
Arc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()),
Self::Runtime(rc) => Arc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()),
}
}
}
@@ -68,9 +66,7 @@ impl Deref for OrcString {
}
impl Hash for OrcString {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.as_str().hash(state) }
}
impl From<String> for OrcString {
@@ -96,9 +92,7 @@ impl InertPayload for OrcString {
}
impl ToClause for String {
fn to_clause(self, _: CodeLocation) -> Clause {
Inert(OrcString::from(self)).atom_cls()
}
fn to_clause(self, _: CodeLocation) -> Clause { Inert(OrcString::from(self)).atom_cls() }
}
impl TryFromExpr for String {
@@ -109,62 +103,47 @@ impl TryFromExpr for String {
pub(super) fn str_lib() -> ConstTree {
ConstTree::ns("std::string", [ConstTree::tree([
atom_ent("slice", [xfn_3ary(
|s: Inert<OrcString>, i: Inert<usize>, len: Inert<usize>| {
let graphs = s.0.as_str().graphemes(true);
if i.0 == 0 {
return Ok(graphs.take(len.0).collect::<String>());
}
let mut prefix = graphs.skip(i.0 - 1);
if prefix.next().is_none() {
return Err(RuntimeError::ext(
"Character index out of bounds".to_string(),
"indexing string",
));
}
let mut count = 0;
let ret = (prefix.take(len.0))
.map(|x| {
count += 1;
x
})
.collect::<String>();
if count == len.0 {
Ok(ret)
} else {
RuntimeError::fail(
"Character index out of bounds".to_string(),
"indexing string",
)
}
},
)]),
atom_ent("concat", [xfn_2ary(|a: String, b: 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);
iter_find(haystack_graphs, needle.0.as_str().graphemes(true)).map(Inert)
},
)]),
atom_ent("split", [xfn_2ary(
|s: String, i: Inert<usize>| -> (String, String) {
let mut graphs = s.as_str().graphemes(true);
(graphs.by_ref().take(i.0).collect(), graphs.collect())
},
)]),
atom_ent("len", [xfn_1ary(|s: Inert<OrcString>| {
Inert(s.0.graphemes(true).count())
})]),
atom_ent("size", [xfn_1ary(|s: Inert<OrcString>| {
Inert(s.0.as_bytes().len())
})]),
atom_ent("intern", [xfn_1ary(|s: Inert<OrcString>| {
xfn_ent("slice", [|s: Inert<OrcString>, i: Inert<usize>, len: Inert<usize>| {
let graphs = s.0.as_str().graphemes(true);
if i.0 == 0 {
return Ok(graphs.take(len.0).collect::<String>());
}
let mut prefix = graphs.skip(i.0 - 1);
if prefix.next().is_none() {
return Err(RuntimeError::ext(
"Character index out of bounds".to_string(),
"indexing string",
));
}
let mut count = 0;
let ret = (prefix.take(len.0))
.map(|x| {
count += 1;
x
})
.collect::<String>();
if count == len.0 {
Ok(ret)
} else {
RuntimeError::fail("Character index out of bounds".to_string(), "indexing string")
}
}]),
xfn_ent("concat", [|a: String, b: Inert<OrcString>| a + b.0.as_str()]),
xfn_ent("find", [|haystack: Inert<OrcString>, needle: Inert<OrcString>| {
let haystack_graphs = haystack.0.as_str().graphemes(true);
iter_find(haystack_graphs, needle.0.as_str().graphemes(true)).map(Inert)
}]),
xfn_ent("split", [|s: String, i: Inert<usize>| -> (String, String) {
let mut graphs = s.as_str().graphemes(true);
(graphs.by_ref().take(i.0).collect(), graphs.collect())
}]),
xfn_ent("len", [|s: Inert<OrcString>| Inert(s.0.graphemes(true).count())]),
xfn_ent("size", [|s: Inert<OrcString>| Inert(s.0.as_bytes().len())]),
xfn_ent("intern", [|s: Inert<OrcString>| {
Inert(match s.0 {
OrcString::Runtime(s) => OrcString::Interned(i(&*s)),
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 ) )
(
pattern_walker
(0) -- index of next item
macro::comma_list ( ..$items ) -- leftover items
)
)
( macro tuple_pattern $length ( pattern_result $expr ( $binds ) )
=0x1p254=> pmatch::response (
if length pmatch::value == $length
then $expr
then ((\tuple_idx. $expr ) 0)
else pmatch::fail
) ( $binds )
)
( macro pattern_walker $length macro::list_end
( macro pattern_walker macro::list_end
=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
(...$length)
( pmatch::request $next )
( pattern_walker (...$length + 1) $tail )
( pattern_walker $tail )
)
( macro pattern_await $length
( macro pattern_await
( pmatch::response $expr ( $binds ) )
( pattern_result $tail_expr ( $tail_binds ) )
=0x1p254=>
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::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::chain_binds $binds $tail_binds
(pmatch::chain_binds $binds $tail_binds)
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::reflect::refer;
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::inert::{Inert, InertPayload};
use crate::foreign::try_from_expr::WithLoc;
use crate::gen::tpl;
use crate::gen::tree::{atom_leaf, leaf, ConstTree};
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::nort::Expr;
use crate::location::{CodeGenInfo, CodeLocation};
use crate::utils::ddispatch::Request;
use crate::utils::pure_seq::pushed;
static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| {
let location =
CodeLocation::Gen(CodeGenInfo::no_details("stdlib::tuple::tag"));
Tag::new("tuple", [(
TO_STRING.id(),
refer("std::tuple::to_string_impl").to_expr(location),
)])
let location = CodeLocation::Gen(CodeGenInfo::no_details("stdlib::tuple::tag"));
Tag::new("tuple", [(TO_STRING.id(), refer("std::tuple::to_string_impl").to_expr(location))])
});
/// A short contiquous random access sequence of Orchid values.
@@ -34,27 +28,22 @@ static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| {
pub struct Tuple(pub Arc<Vec<Expr>>);
impl InertPayload for Tuple {
const TYPE_STR: &'static str = "tuple";
fn respond(&self, mut request: Request) {
request.serve_with(|| TUPLE_TAG.clone())
}
fn respond(&self, mut request: Request) { request.serve_with(|| TUPLE_TAG.clone()) }
}
impl Debug for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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 pick(
WithLoc(loc, tuple): WithLoc<Inert<Tuple>>,
idx: Inert<usize>,
) -> ExternResult<Expr> {
fn pick(WithLoc(loc, tuple): WithLoc<Inert<Tuple>>, idx: Inert<usize>) -> ExternResult<Expr> {
(tuple.0.0.get(idx.0).cloned()).ok_or_else(|| {
let msg = format!("{} <= {idx}", tuple.0.0.len());
AssertionError::ext(loc, "Tuple index out of bounds", msg)
})
})
}
fn push(Inert(tuple): Inert<Tuple>, item: Thunk) -> Inert<Tuple> {
@@ -64,9 +53,9 @@ fn push(Inert(tuple): Inert<Tuple>, item: Thunk) -> Inert<Tuple> {
pub(super) fn tuple_lib() -> ConstTree {
ConstTree::ns("std", [ConstTree::tree([TUPLE_TAG.as_tree_ent([
("empty", leaf(tpl::V(Inert(Tuple(Arc::new(Vec::new())))))),
("length", atom_leaf(xfn_1ary(length))),
("pick", atom_leaf(xfn_2ary(pick))),
("push", atom_leaf(xfn_2ary(push))),
atom_ent("empty", [Inert(Tuple(Arc::new(Vec::new())))]),
xfn_ent("length", [length]),
xfn_ent("pick", [pick]),
xfn_ent("push", [push]),
])])])
}

View File

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

View File

@@ -39,7 +39,7 @@ pub trait ParseCtx {
}
/// Create a contextful location for error reporting
#[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))
}
/// 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
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() {
Ok(Vec::new())
} else {
@@ -30,7 +30,7 @@ pub fn parse_entries(
range: SourceRange,
) -> Vec<SourceLine> {
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)
.flat_map(|tokens| parse_line(tokens, &ctx).expect("pre-specified source"))
.map(|kind| kind.wrap(range.clone()))

View File

@@ -1,8 +1,9 @@
//! Abstractions for dynamic extensions to the lexer to parse custom literals
use super::context::ParseCtx;
use super::lexer::{lex, LexRes};
use super::context::{FlatLocContext, ParseCtx};
use super::lexer::{lex, Entry, LexRes};
use crate::error::ProjectResult;
use crate::location::SourceRange;
/// Data passed to the recursive sub-lexer
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.
/// When it returns true, the lexer exits and leaves the remaining text for
/// 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
@@ -25,16 +26,20 @@ pub trait LexPluginReq<'a> {
/// expressions in your literals like the template strings of most languages
/// other than Rust.
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
/// likely atoms.
pub trait LexerPlugin: Send + Sync {
/// Run the lexer
fn lex<'a>(
&self,
req: &'_ dyn LexPluginReq<'a>,
) -> Option<ProjectResult<LexRes<'a>>>;
fn lex<'a>(&self, req: &'_ dyn LexPluginReq<'a>) -> Option<ProjectResult<LexRes<'a>>>;
}
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>> {
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)
}
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 {
@@ -139,7 +141,7 @@ pub fn namechar(c: char) -> bool { c.is_alphanumeric() | (c == '_') }
pub fn namestart(c: char) -> bool { c.is_alphabetic() | (c == '_') }
/// Character filter that can appear in operators.
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
@@ -176,18 +178,18 @@ pub fn lex<'a>(
mut tokens: Vec<Entry>,
mut data: &'a str,
ctx: &'_ impl ParseCtx,
bail: impl Fn(&str) -> bool,
mut bail: impl FnMut(&str) -> ProjectResult<bool>,
) -> ProjectResult<LexRes<'a>> {
let mut prev_len = data.len() + 1;
'tail: loop {
if bail(data) {
return Ok(LexRes { tokens, tail: data });
}
if prev_len == data.len() {
panic!("got stuck at {data:?}, parsed {:?}", tokens.last().unwrap());
}
prev_len = data.len();
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 head = match chars.next() {
None => return Ok(LexRes { tokens, tail: data }),
@@ -221,7 +223,7 @@ pub fn lex<'a>(
}
if let Some(tail) = data.strip_prefix("--[") {
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()));
tokens.push(Entry::new(ctx.range(note.len() + 3, tail), lexeme));
data = tail;
@@ -276,7 +278,7 @@ pub fn lex<'a>(
.and_then(|num| match num {
Numeric::Uint(usize) => Ok(usize),
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))

View File

@@ -3,13 +3,12 @@
use intern_all::i;
use itertools::Itertools;
use super::errors::{
BadCodePoint, BadEscapeSequence, NoStringEnd, NotHex, ParseErrorKind,
};
use super::context::ParseCtx;
use super::errors::{BadCodePoint, BadEscapeSequence, NoStringEnd, NotHex, ParseErrorKind};
#[allow(unused)] // for doc
use super::lex_plugin::LexerPlugin;
use super::lexer::{Entry, LexRes, Lexeme};
use crate::error::ProjectResult;
use crate::error::{ProjectErrorObj, ProjectResult};
use crate::foreign::atom::AtomGenerator;
use crate::foreign::inert::Inert;
use crate::libs::std::string::OrcString;
@@ -32,6 +31,19 @@ pub struct StringError {
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
pub fn parse_string(str: &str) -> Result<String, StringError> {
let mut target = String::new();
@@ -61,18 +73,14 @@ pub fn parse_string(str: &str) -> Result<String, StringError> {
'u' => {
let acc = ((0..4).rev())
.map(|radical| {
let (j, c) = (iter.next())
.ok_or(StringError { pos, kind: StringErrorKind::NotHex })?;
let (j, c) = (iter.next()).ok_or(StringError { pos, kind: StringErrorKind::NotHex })?;
pos = j;
let b =
u32::from_str_radix(&String::from(c), 16).map_err(|_| {
StringError { pos, kind: StringErrorKind::NotHex }
})?;
let b = u32::from_str_radix(&String::from(c), 16)
.map_err(|_| StringError { pos, kind: StringErrorKind::NotHex })?;
Ok(16u32.pow(radical) + b)
})
.fold_ok(0, u32::wrapping_add)?;
char::from_u32(acc)
.ok_or(StringError { pos, kind: StringErrorKind::BadCodePoint })?
char::from_u32(acc).ok_or(StringError { pos, kind: StringErrorKind::BadCodePoint })?
},
_ => return Err(StringError { pos, kind: StringErrorKind::BadEscSeq }),
};
@@ -91,26 +99,15 @@ impl LexerPlugin for StringLexer {
req.tail().strip_prefix('"').map(|data| {
let mut leftover = data;
return loop {
let (inside, outside) =
(leftover.split_once('"')).ok_or_else(|| {
NoStringEnd.pack(req.ctx().code_range(data.len(), ""))
})?;
let backslashes =
inside.chars().rev().take_while(|c| *c == '\\').count();
let (inside, outside) = (leftover.split_once('"'))
.ok_or_else(|| NoStringEnd.pack(req.ctx().source_range(data.len(), "")))?;
let backslashes = inside.chars().rev().take_while(|c| *c == '\\').count();
if backslashes % 2 == 0 {
// cut form tail to recoup what string_content doesn't have
let (string_data, tail) =
data.split_at(data.len() - outside.len() - 1);
let (string_data, tail) = data.split_at(data.len() - outside.len() - 1);
let tail = &tail[1..]; // push the tail past the end quote
let string = parse_string(string_data).map_err(|e| {
let start = req.ctx().pos(data) + e.pos;
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 string =
parse_string(string_data).map_err(|e| e.to_proj(req.ctx(), req.ctx().pos(data)))?;
let output = Inert(OrcString::from(i(&string)));
let ag = AtomGenerator::cloner(output);
let range = req.ctx().range(string_data.len(), tail);
@@ -144,8 +141,7 @@ mod test {
[Entry { lexeme: Lexeme::Atom(ag), .. }] => ag,
_ => panic!("Expected a single atom"),
};
let atom =
ag.run().try_downcast::<Inert<OrcString>>().expect("Lexed to inert");
let atom = ag.run().try_downcast::<Inert<OrcString>>().expect("Lexed to inert");
assert_eq!(atom.0.as_str(), "hello world!");
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::process_source::{process_ns, resolve_globs, GlobImports};
use super::project::{ItemKind, ProjItem, ProjXEnt, ProjectMod, ProjectTree};
use crate::error::{
bundle_location, ErrorPosition, ProjectError, ProjectResult,
};
use crate::error::{bundle_location, ErrorPosition, ProjectError, ProjectResult};
use crate::location::{CodeGenInfo, CodeLocation, SourceCode, SourceRange};
use crate::name::{PathSlice, Sym, VName, VPath};
use crate::parse::context::ParseCtxImpl;
@@ -42,10 +40,7 @@ fn split_max_prefix<'a, T>(
path: &'a [T],
is_valid: &impl Fn(&[T]) -> bool,
) -> Option<(&'a [T], &'a [T])> {
(0..=path.len())
.rev()
.map(|i| path.split_at(i))
.find(|(file, _)| is_valid(file))
(0..=path.len()).rev().map(|i| path.split_at(i)).find(|(file, _)| is_valid(file))
}
/// Represents a prelude / implicit import requested by a library.
@@ -94,10 +89,8 @@ pub fn load_solution(
) -> ProjectResult<ProjectTree> {
let mut target_queue = VecDeque::<(Sym, CodeLocation)>::new();
target_queue.extend(targets.into_iter());
target_queue.extend(
(ctx.preludes.iter())
.map(|p| (p.target.to_sym(), CodeLocation::Gen(p.owner.clone()))),
);
target_queue
.extend((ctx.preludes.iter()).map(|p| (p.target.to_sym(), CodeLocation::Gen(p.owner.clone()))));
let mut known_files = HashSet::new();
let mut tree_acc: ProjectMod = Module::wrap([]);
let mut glob_acc: GlobImports = Module::wrap([]);
@@ -111,21 +104,16 @@ pub fn load_solution(
}
known_files.insert(filename.to_vec());
let path = VPath(filename.to_vec());
let loaded = fs
.read(PathSlice(filename))
.map_err(|e| bundle_location(&referrer, &*e))?;
let loaded = fs.read(PathSlice(filename)).map_err(|e| bundle_location(&referrer, &*e))?;
let code = match loaded {
Loaded::Collection(_) =>
return Err(UnexpectedDirectory { path }.pack()),
Loaded::Collection(_) => return Err(UnexpectedDirectory { path }.pack()),
Loaded::Code(source) => SourceCode { source, path: Arc::new(path) },
};
let full_range =
SourceRange { range: 0..code.source.len(), code: code.clone() };
let full_range = SourceRange { range: 0..code.source.len(), code: code.clone() };
let lines = parse_file(&ctx.parsing(code.clone()))?;
let report = process_ns(code.path, lines, full_range)?;
target_queue.extend(
(report.external_references.into_iter())
.map(|(k, v)| (k, CodeLocation::Source(v))),
(report.external_references.into_iter()).map(|(k, v)| (k, CodeLocation::Source(v))),
);
if !report.comments.is_empty() && filename.is_empty() {
todo!("panic - module op comments on root are lost")
@@ -137,23 +125,22 @@ pub fn load_solution(
// i over valid indices of filename
let key = filename[i].clone(); // last segment
let comments = comments.take().into_iter().flatten().collect();
glob =
Module::wrap([(key.clone(), ModEntry::wrap(ModMember::Sub(glob)))]);
glob = Module::wrap([(key.clone(), ModEntry::wrap(ModMember::Sub(glob)))]);
module = Module::wrap([(key, ModEntry {
member: ModMember::Sub(module),
x: ProjXEnt { comments, ..Default::default() },
})]);
}
glob_acc = (glob_acc.combine(glob))
.expect("source code loaded for two nested paths");
tree_acc = (tree_acc.combine(module))
.expect("source code loaded for two nested paths");
glob_acc = (glob_acc.combine(glob)).expect("source code loaded for two nested paths");
tree_acc = (tree_acc.combine(module)).expect("source code loaded for two nested paths");
} else {
known_files.insert(target[..].to_vec());
// If the path is not within a file, load it as directory
match fs.read(target.as_path_slice()) {
Ok(Loaded::Collection(c)) => target_queue
.extend(c.iter().map(|e| (Sym::parse(e).unwrap(), referrer.clone()))),
Ok(Loaded::Collection(c)) => target_queue.extend(c.iter().map(|e| {
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 []"),
// Ignore error if the path is walkable in the const tree
Err(_) if env.walk1_ref(&[], &target[..], |_| true).is_ok() => (),
@@ -172,12 +159,10 @@ pub fn load_solution(
)?;
let ret = resolve_aliases(tree_acc, env)?;
for ((glob, original), locations) in contention {
let (glob_val, _) = ret
.walk1_ref(&[], &glob[..], |_| true)
.expect("Should've emerged in dealias");
let (original_val, _) = ret
.walk1_ref(&[], &original[..], |_| true)
.expect("Should've emerged in dealias");
let (glob_val, _) =
ret.walk1_ref(&[], &glob[..], |_| true).expect("Should've emerged in dealias");
let (original_val, _) =
ret.walk1_ref(&[], &original[..], |_| true).expect("Should've emerged in dealias");
let glob_real = match &glob_val.member {
ModMember::Item(ProjItem { kind: ItemKind::Alias(glob_tgt) }) => glob_tgt,
_ => &glob,
@@ -208,9 +193,7 @@ struct UnexpectedDirectory {
impl ProjectError for UnexpectedDirectory {
const DESCRIPTION: &'static str = "A stage that deals specifically with code \
encountered a path that refers to a directory";
fn message(&self) -> String {
format!("{} was expected to be a file", self.path)
}
fn message(&self) -> String { format!("{} was expected to be a file", self.path) }
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { [] }
}
@@ -223,8 +206,7 @@ struct ConflictingGlobs {
locations: Vec<CodeLocation>,
}
impl ProjectError for ConflictingGlobs {
const DESCRIPTION: &'static str =
"A symbol from a glob import conflicts with an existing name";
const DESCRIPTION: &'static str = "A symbol from a glob import conflicts with an existing name";
fn message(&self) -> String {
let Self { glob, glob_real, original, real, .. } = self;
format!(
@@ -233,7 +215,6 @@ impl ProjectError for ConflictingGlobs {
)
}
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
(self.locations.iter())
.map(|l| ErrorPosition { location: l.clone(), message: None })
(self.locations.iter()).map(|l| ErrorPosition { location: l.clone(), message: None })
}
}

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,6 @@ use crate::error::{ErrorPosition, ProjectError, ProjectErrorObj};
use crate::location::{CodeLocation, SourceRange};
use crate::parse::parsed::{search_all_slcs, Clause, PHClass, Placeholder};
use crate::pipeline::project::ProjRule;
use crate::utils::boxed_iter::BoxedIter;
/// Various reasons why a substitution rule may be invalid
#[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 hashbrown::HashMap;
@@ -10,8 +12,7 @@ pub fn join_maps<K: Eq + Hash, V>(
right: HashMap<K, V>,
mut merge: impl FnMut(&K, V, V) -> V,
) -> HashMap<K, V> {
try_join_maps(left, right, |k, l, r| Ok(merge(k, l, r)))
.unwrap_or_else(|e: Never| match e {})
try_join_maps(left, right, |k, l, r| Ok(merge(k, l, r))).unwrap_or_else(|e: Never| match e {})
}
/// Combine two hashmaps via a fallible value merger. See also [join_maps]
@@ -23,11 +24,11 @@ pub fn try_join_maps<K: Eq + Hash, V, E>(
let mut mixed = HashMap::with_capacity(left.len() + right.len());
for (key, lval) in left {
let val = match right.remove(&key) {
None => lval,
None => lval,
Some(rval) => merge(&key, lval, rval)?,
};
mixed.insert(key, val);
}
mixed.extend(right.into_iter());
mixed.extend(right);
Ok(mixed)
}

View File

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

View File

@@ -30,7 +30,7 @@ impl VirtFS for DeclTree {
ModMember::Sub(module) => match path.split_first() {
None => Ok(Loaded::collection(module.keys(|_| true))),
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)),
},
}

View File

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

View File

@@ -61,7 +61,7 @@ impl VirtFS for EmbeddedFS {
return Ok(Loaded::collection(self.tree.keys(|_| 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 {
ModMember::Item(text) => Loaded::Code(text.clone()),
ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)),

View File

@@ -33,7 +33,7 @@ impl PrefixFS {
impl VirtFS for PrefixFS {
fn get(&self, path: &[Tok<String>], full_path: PathSlice) -> super::FSResult {
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)
}
fn display(&self, path: &[Tok<String>]) -> Option<String> {