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:
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -48,6 +48,7 @@ enum Command {
|
||||
},
|
||||
#[command(arg_required_else_help = true)]
|
||||
MacroDebug {
|
||||
#[arg(long, short)]
|
||||
symbol: String,
|
||||
},
|
||||
ListMacros,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, (), ()>;
|
||||
|
||||
@@ -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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}}}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))),
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()))
|
||||
})]),
|
||||
])])
|
||||
|
||||
@@ -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]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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")])
|
||||
|
||||
@@ -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]),
|
||||
])]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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]),
|
||||
]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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) )
|
||||
)
|
||||
|
||||
@@ -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) )
|
||||
)
|
||||
|
||||
@@ -16,3 +16,4 @@ mod state;
|
||||
pub mod std_system;
|
||||
pub mod string;
|
||||
pub mod tuple;
|
||||
mod tstring;
|
||||
|
||||
@@ -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]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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)]) }
|
||||
|
||||
@@ -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, =>)
|
||||
|
||||
@@ -22,3 +22,6 @@ export ::(loop_over, recursive, while)
|
||||
|
||||
import std::known::*
|
||||
export ::[, _ ; . =]
|
||||
|
||||
import std
|
||||
export ::(std)
|
||||
|
||||
@@ -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]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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())]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]),
|
||||
])])
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
81
src/libs/std/tstring.rs
Normal 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 `\"`";
|
||||
}
|
||||
@@ -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 ) )
|
||||
)
|
||||
|
||||
@@ -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]),
|
||||
])])])
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Datastructures for cached pattern
|
||||
|
||||
use std::fmt::{Display, Write};
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
})?;
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user