Transfer commit

This commit is contained in:
2023-03-21 19:36:40 +00:00
parent 180ebb56fa
commit f3ce910f66
63 changed files with 1410 additions and 1023 deletions

View File

@@ -3,10 +3,10 @@ mod boolean;
mod ifthenelse;
pub use boolean::Boolean;
use crate::project::{Loader, fnlib_loader};
use crate::project::{Loader, extlib_loader};
pub fn bool() -> impl Loader {
fnlib_loader(vec![
extlib_loader(vec![
("ifthenelse", Box::new(ifthenelse::IfThenElse1)),
("equals", Box::new(equals::Equals2))
])

View File

@@ -1,11 +1,11 @@
use crate::project::{fnlib_loader, Loader};
use crate::project::{extlib_loader, Loader};
mod to_string;
mod parse_float;
mod parse_uint;
pub fn conv() -> impl Loader {
fnlib_loader(vec![
extlib_loader(vec![
("parse_float", Box::new(parse_float::ParseFloat1)),
("parse_uint", Box::new(parse_uint::ParseUint1)),
("to_string", Box::new(to_string::ToString1))

View File

@@ -1,10 +1,10 @@
use crate::project::{Loader, fnlib_loader};
use crate::project::{Loader, extlib_loader};
mod print;
mod readline;
pub fn cpsio() -> impl Loader {
fnlib_loader(vec![
extlib_loader(vec![
("print", Box::new(print::Print2)),
("readline", Box::new(readline::Readln2))
])

View File

@@ -2,10 +2,10 @@ mod numeric;
pub mod operators;
pub use numeric::Numeric;
use crate::project::{fnlib_loader, Loader};
use crate::project::{extlib_loader, Loader};
pub fn num() -> impl Loader {
fnlib_loader(vec![
extlib_loader(vec![
("add", Box::new(operators::add::Add2)),
("subtract", Box::new(operators::subtract::Subtract2)),
("multiply", Box::new(operators::multiply::Multiply2)),

View File

@@ -14,15 +14,29 @@ pub enum Numeric {
Num(NotNan<f64>)
}
impl Numeric {
/// Wrap a f64 in a Numeric
///
/// # Panics
///
/// if the value is NaN or Infinity.try_into()
fn num<T>(value: T) -> Self where T: Into<f64> {
let f = value.into();
assert!(f.is_finite(), "unrepresentable number");
NotNan::try_from(f).map(Self::Num).expect("not a number")
}
}
impl Add for Numeric {
type Output = Numeric;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a + b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a + b),
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() + b)
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a + b),
(Numeric::Uint(a), Numeric::Num(b)) |
(Numeric::Num(b), Numeric::Uint(a))
=> Numeric::num::<f64>(a as f64 + *b)
}
}
}
@@ -34,10 +48,10 @@ impl Sub for Numeric {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) if b < a => Numeric::Uint(a - b),
(Numeric::Uint(a), Numeric::Uint(b))
=> Numeric::Num(NotNan::new(a as f64 - b as f64).unwrap()),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a - b),
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() - b)
=> Numeric::num(a as f64 - b as f64),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a - b),
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 - *b),
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a - b as f64)
}
}
}
@@ -48,8 +62,9 @@ impl Mul for Numeric {
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a * b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a * b),
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a * b),
(Numeric::Uint(a), Numeric::Num(b)) |
(Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() * b)
}
}
@@ -59,9 +74,9 @@ impl Div for Numeric {
type Output = Numeric;
fn div(self, rhs: Self) -> Self::Output {
let a = match self { Numeric::Uint(i) => i as f64, Numeric::Num(f) => *f };
let b = match rhs { Numeric::Uint(i) => i as f64, Numeric::Num(f) => *f };
Numeric::Num(NotNan::new(a / b).unwrap())
let a: f64 = self.into();
let b: f64 = rhs.into();
Numeric::num(a / b)
}
}
@@ -71,9 +86,9 @@ impl Rem for Numeric {
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a % b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a % b),
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() % b)
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a % b),
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 % *b),
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a % b as f64)
}
}
}
@@ -108,4 +123,13 @@ impl From<Numeric> for String {
Numeric::Num(n) => n.to_string()
}
}
}
impl Into<f64> for Numeric {
fn into(self) -> f64 {
match self {
Numeric::Num(n) => *n,
Numeric::Uint(i) => i as f64
}
}
}

View File

@@ -2,10 +2,10 @@ mod concatenate;
mod cls2str;
mod char_at;
pub use cls2str::cls2str;
use crate::project::{Loader, fnlib_loader};
use crate::project::{Loader, extlib_loader};
pub fn str() -> impl Loader {
fnlib_loader(vec![
extlib_loader(vec![
("concatenate", Box::new(concatenate::Concatenate2))
])
}

View File

@@ -5,20 +5,26 @@ use std::rc::Rc;
use dyn_clone::DynClone;
use crate::representations::interpreted::{Clause, RuntimeError, InternalError};
use crate::representations::interpreted::{
Clause, RuntimeError, InternalError
};
pub trait ExternError: Display {
fn into_extern(self) -> Rc<dyn ExternError> where Self: 'static + Sized {
fn into_extern(self) -> Rc<dyn ExternError>
where Self: 'static + Sized {
Rc::new(self)
}
}
/// Represents an externally defined function from the perspective of the executor
/// Since Orchid lacks basic numerical operations, these are also external functions.
/// Represents an externally defined function from the perspective of
/// the executor. Since Orchid lacks basic numerical operations,
/// these are also external functions.
pub trait ExternFn: DynClone {
fn name(&self) -> &str;
fn apply(&self, arg: Clause) -> Result<Clause, Rc<dyn ExternError>>;
fn hash(&self, state: &mut dyn std::hash::Hasher) { state.write_str(self.name()) }
fn hash(&self, state: &mut dyn std::hash::Hasher) {
state.write_str(self.name())
}
}
impl Eq for dyn ExternFn {}
@@ -26,7 +32,9 @@ impl PartialEq for dyn ExternFn {
fn eq(&self, other: &Self) -> bool { self.name() == other.name() }
}
impl Hash for dyn ExternFn {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.name().hash(state) }
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name().hash(state)
}
}
impl Debug for dyn ExternFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -41,27 +49,31 @@ pub trait Atomic: Any + Debug + DynClone where Self: 'static {
fn run_once(&self) -> Result<Clause, InternalError>;
fn run_n_times(&self, n: usize) -> Result<(Clause, usize), RuntimeError>;
fn run_to_completion(&self) -> Result<Clause, RuntimeError>;
fn typestr(&self) -> &str { "clause" }
}
/// Represents a black box unit of code with its own normalization steps. Typically [ExternFn]
/// will produce an [Atom] when applied to a [Clause], this [Atom] will then forward `run_*` calls
/// to the argument until it yields [InternalError::NonReducible] at which point the [Atom] will
/// validate and process the argument, returning a different [Atom] intended for processing by
/// external code, a new [ExternFn] to capture an additional argument, or an Orchid expression
/// Represents a black box unit of code with its own normalization steps.
/// Typically [ExternFn] will produce an [Atom] when applied to a [Clause],
/// this [Atom] will then forward `run_*` calls to the argument until it
/// yields [InternalError::NonReducible] at which point the [Atom] will
/// validate and process the argument, returning a different [Atom]
/// intended for processing by external code, a new [ExternFn] to capture
/// an additional argument, or an Orchid expression
/// to pass control back to the interpreter.
pub struct Atom(pub Box<dyn Atomic>);
impl Atom {
pub fn new<T: 'static + Atomic>(data: T) -> Self {
Self(Box::new(data) as Box<dyn Atomic>)
}
pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic }
pub fn data(&self) -> &dyn Atomic {
self.0.as_ref() as &dyn Atomic
}
pub fn try_cast<T: Atomic>(&self) -> Result<&T, ()> {
self.data().as_any().downcast_ref().ok_or(())
}
pub fn is<T: 'static>(&self) -> bool { self.data().as_any().is::<T>() }
pub fn cast<T: 'static>(&self) -> &T {
self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast")
self.data().as_any().downcast_ref()
.expect("Type mismatch on Atom::cast")
}
}
@@ -78,7 +90,7 @@ impl Hash for Atom {
}
impl Debug for Atom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.data().typestr())
write!(f, "##ATOM[{:?}]##", self.data())
}
}
impl Eq for Atom {}

View File

@@ -1,5 +1,4 @@
#![feature(specialization)]
#![feature(core_intrinsics)]
#![feature(adt_const_params)]
#![feature(generic_const_exprs)]
#![feature(generators, generator_trait)]
@@ -9,7 +8,6 @@
#![feature(hasher_prefixfree_extras)]
#![feature(closure_lifetime_binder)]
#![feature(generic_arg_infer)]
use std::{env::current_dir, collections::HashMap};
// mod executor;
@@ -22,6 +20,7 @@ mod scheduler;
pub(crate) mod foreign;
mod external;
mod foreign_macros;
use lasso::Rodeo;
pub use representations::ast;
use ast::{Expr, Clause};
// use representations::typed as t;
@@ -54,13 +53,13 @@ export (...$a - ...$b:1) =1001=> (subtract (...$a) (...$b))
export (...$a * ...$b) =1000=> (multiply (...$a) (...$b))
export (...$a % ...$b:1) =1000=> (remainder (...$a) (...$b))
export (...$a / ...$b:1) =1000=> (divide (...$a) (...$b))
export (...$a = ...$b) =1002=> (equals (...$a) (...$b))
export (...$a == ...$b) =1002=> (equals (...$a) (...$b))
export (...$a ++ ...$b) =1003=> (concatenate (...$a) (...$b))
export do { ...$statement ; ...$rest:1 } =10_001=> (
statement (...$statement) do { ...$rest }
)
export do { ...$statement } =10_000=> (...$statement)
export do { ...$return } =10_000=> (...$return)
export statement (let $_name = ...$value) ...$next =10_000=> (
(\$_name. ...$next) (...$value)
@@ -86,11 +85,15 @@ fn initial_tree() -> Mrc<[Expr]> {
#[allow(unused)]
fn load_project() {
let collect_rules = rule_collector(map_loader(HashMap::from([
("std", std().boxed()),
("prelude", string_loader(PRELUDE).boxed()),
("mod", file_loader(current_dir().expect("Missing CWD!")).boxed())
])));
let mut rodeo = Rodeo::default();
let collect_rules = rule_collector(
rodeo,
map_loader(HashMap::from([
("std", std().boxed()),
("prelude", string_loader(PRELUDE).boxed()),
("mod", file_loader(current_dir().expect("Missing CWD!")).boxed())
]))
);
let rules = match collect_rules.try_find(&literal(&["mod", "main"])) {
Ok(rules) => rules,
Err(err) => if let ModuleError::Syntax(pe) = err {
@@ -124,11 +127,5 @@ fn load_project() {
}
fn main() {
// lambda_notation_debug();
load_project();
// let mut std = std();
// match std.load(&["parse_float"]) {
// Ok(_) => println!("wtf"),
// Err(e) => panic!("{:?}", e)
// }
}

View File

@@ -1,9 +1,10 @@
use std::rc::Rc;
use chumsky::{self, prelude::*, Parser};
use mappable_rc::Mrc;
use lasso::Spur;
use crate::enum_parser;
use crate::representations::Primitive;
use crate::representations::{Literal, ast::{Clause, Expr}};
use crate::utils::to_mrc_slice;
use super::lexer::Lexeme;
@@ -12,18 +13,22 @@ fn sexpr_parser<P>(
expr: P
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, to_mrc_slice(b)))
Lexeme::paren_parser(expr.repeated())
.map(|(del, b)| Clause::S(del, Rc::new(b)))
}
/// Parses `\name.body` or `\name:type.body` where name is any valid name and type and body are
/// both expressions. Comments are allowed and ignored everywhere in between the tokens
fn lambda_parser<P>(
expr: P
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
/// Parses `\name.body` or `\name:type.body` where name is any valid name
/// and type and body are both expressions. Comments are allowed
/// and ignored everywhere in between the tokens
fn lambda_parser<'a, P, F>(
expr: P, intern: &'a F
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
where
P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone + 'a,
F: Fn(&str) -> Spur + 'a {
just(Lexeme::BS)
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
.ignore_then(enum_parser!(Lexeme::Name))
.ignore_then(namelike_parser(intern))
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
.then(
just(Lexeme::Type)
@@ -35,20 +40,21 @@ where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
.then_ignore(just(Lexeme::name(".")))
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
.then(expr.repeated().at_least(1))
.map(|((name, typ), body): ((String, Vec<Expr>), Vec<Expr>)| {
// for ent in &mut body { ent.bind_parameter(&name) };
Clause::Lambda(name, to_mrc_slice(typ), to_mrc_slice(body))
.map(|((name, typ), body): ((Clause, Vec<Expr>), Vec<Expr>)| {
Clause::Lambda(Rc::new(name), Rc::new(typ), Rc::new(body))
})
}
/// see [lambda_parser] but `@` instead of `\` and the name is optional
fn auto_parser<P>(
expr: P
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
fn auto_parser<'a, P, F>(
expr: P, intern: &'a F
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
where
P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone + 'a,
F: Fn(&str) -> Spur + 'a {
just(Lexeme::At)
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
.ignore_then(enum_parser!(Lexeme::Name).or_not())
.ignore_then(namelike_parser(intern).or_not())
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
.then(
just(Lexeme::Type)
@@ -60,23 +66,27 @@ where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
.then_ignore(just(Lexeme::name(".")))
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
.then(expr.repeated().at_least(1))
.try_map(|((name, typ), body): ((Option<String>, Vec<Expr>), Vec<Expr>), s| {
.try_map(|((name, typ), body): ((Option<Clause>, Vec<Expr>), Vec<Expr>), s| {
if name.is_none() && typ.is_empty() {
Err(Simple::custom(s, "Auto without name or type has no effect"))
} else {
Ok(Clause::Auto(name, to_mrc_slice(typ), to_mrc_slice(body)))
} else {
Ok(Clause::Auto(name.map(Rc::new), Rc::new(typ), Rc::new(body)))
}
})
}
/// Parses a sequence of names separated by :: <br/>
/// Comments are allowed and ignored in between
fn name_parser() -> impl Parser<Lexeme, Vec<String>, Error = Simple<Lexeme>> + Clone {
enum_parser!(Lexeme::Name).separated_by(
enum_parser!(Lexeme::Comment).repeated()
.then(just(Lexeme::NS))
.then(enum_parser!(Lexeme::Comment).repeated())
).at_least(1)
pub fn ns_name_parser<'a, F>(intern: &'a F)
-> impl Parser<Lexeme, Vec<Spur>, Error = Simple<Lexeme>> + Clone + 'a
where F: Fn(&str) -> Spur + 'a {
enum_parser!(Lexeme::Name)
.map(|s| intern(&s))
.separated_by(
enum_parser!(Lexeme::Comment).repeated()
.then(just(Lexeme::NS))
.then(enum_parser!(Lexeme::Comment).repeated())
).at_least(1)
}
/// Parse any legal argument name starting with a `$`
@@ -87,42 +97,59 @@ fn placeholder_parser() -> impl Parser<Lexeme, String, Error = Simple<Lexeme>> +
})
}
pub fn namelike_parser<'a, F>(intern: &'a F)
-> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
where F: Fn(&str) -> Spur + 'a {
choice((
just(Lexeme::name("...")).to(true)
.or(just(Lexeme::name("..")).to(false))
.then(placeholder_parser())
.then(
just(Lexeme::Type)
.ignore_then(enum_parser!(Lexeme::Uint))
.or_not().map(Option::unwrap_or_default)
)
.map(|((nonzero, key), prio)| Clause::Placeh{key, vec: Some((
prio.try_into().unwrap(),
nonzero
))}),
ns_name_parser(intern)
.map(|qualified| Clause::Name(Rc::new(qualified))),
))
}
pub fn clause_parser<'a, P, F>(
expr: P, intern: &'a F
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
where
P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone + 'a,
F: Fn(&str) -> Spur + 'a {
enum_parser!(Lexeme::Comment).repeated()
.ignore_then(choice((
enum_parser!(Lexeme >> Literal; Uint, Num, Char, Str)
.map(Primitive::Literal).map(Clause::P),
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
namelike_parser(intern),
sexpr_parser(expr.clone()),
lambda_parser(expr.clone(), intern),
auto_parser(expr.clone(), intern),
just(Lexeme::At).ignore_then(expr.clone()).map(|arg| {
Clause::Explicit(Rc::new(arg))
})
))).then_ignore(enum_parser!(Lexeme::Comment).repeated())
}
/// Parse an expression
pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
pub fn xpr_parser<'a, F>(intern: &'a F)
-> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> + 'a
where F: Fn(&str) -> Spur + 'a {
recursive(|expr| {
let clause =
enum_parser!(Lexeme::Comment).repeated()
.ignore_then(choice((
enum_parser!(Lexeme >> Literal; Uint, Num, Char, Str).map(Primitive::Literal).map(Clause::P),
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
just(Lexeme::name("...")).to(true)
.or(just(Lexeme::name("..")).to(false))
.then(placeholder_parser())
.then(
just(Lexeme::Type)
.ignore_then(enum_parser!(Lexeme::Uint))
.or_not().map(Option::unwrap_or_default)
)
.map(|((nonzero, key), prio)| Clause::Placeh{key, vec: Some((
prio.try_into().unwrap(),
nonzero
))}),
name_parser().map(|qualified| Clause::Name {
local: if qualified.len() == 1 {Some(qualified[0].clone())} else {None},
qualified: to_mrc_slice(qualified)
}),
sexpr_parser(expr.clone()),
lambda_parser(expr.clone()),
auto_parser(expr.clone()),
just(Lexeme::At).ignore_then(expr.clone()).map(|arg| {
Clause::Explicit(Mrc::new(arg))
})
))).then_ignore(enum_parser!(Lexeme::Comment).repeated());
let clause = clause_parser(expr, intern);
clause.clone().then(
just(Lexeme::Type)
.ignore_then(clause.clone())
.repeated()
)
.map(|(val, typ)| Expr(val, to_mrc_slice(typ)))
.map(|(val, typ)| Expr(val, Rc::new(typ)))
}).labelled("Expression")
}

View File

@@ -1,34 +1,33 @@
use std::rc::Rc;
use chumsky::{Parser, prelude::*};
use itertools::Itertools;
use mappable_rc::Mrc;
use lasso::Spur;
use crate::representations::sourcefile::Import;
use crate::utils::iter::{box_once, box_flatten, into_boxed_iter, BoxedIterIter};
use crate::utils::{to_mrc_slice, mrc_derive};
use crate::{enum_parser, box_chain};
use super::lexer::Lexeme;
#[derive(Debug, Clone)]
pub struct Import {
pub path: Mrc<[String]>,
/// If name is None, this is a wildcard import
pub name: Option<String>
}
/// initialize a BoxedIter<BoxedIter<String>> with a single element.
fn init_table(name: String) -> BoxedIterIter<'static, String> {
fn init_table(name: Spur) -> BoxedIterIter<'static, Spur> {
// I'm not at all confident that this is a good approach.
box_once(box_once(name))
}
/// Parse an import command
/// Syntax is same as Rust's `use` except the verb is import, no trailing semi
/// and the delimiters are plain parentheses. Namespaces should preferably contain
/// crossplatform filename-legal characters but the symbols are explicitly allowed
/// to go wild. There's a blacklist in [name]
pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme>> {
// TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad.
recursive(|expr: Recursive<Lexeme, BoxedIterIter<String>, Simple<Lexeme>>| {
enum_parser!(Lexeme::Name)
/// Syntax is same as Rust's `use` except the verb is import, no trailing
/// semi and the delimiters are plain parentheses. Namespaces should
/// preferably contain crossplatform filename-legal characters but the
/// symbols are explicitly allowed to go wild.
/// There's a blacklist in [name]
pub fn import_parser<'a, F>(intern: &'a F)
-> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme>> + 'a
where F: Fn(&str) -> Spur + 'a {
let globstar = intern("*");
// TODO: this algorithm isn't cache friendly and copies a lot
recursive(move |expr:Recursive<Lexeme, BoxedIterIter<Spur>, Simple<Lexeme>>| {
enum_parser!(Lexeme::Name).map(|s| intern(s.as_str()))
.separated_by(just(Lexeme::NS))
.then(
just(Lexeme::NS)
@@ -39,15 +38,17 @@ pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
.map(|v| box_flatten(v.into_iter()))
.labelled("import group"),
// Each expr returns a list of imports, flatten those into a common list
just(Lexeme::name("*")).map(|_| init_table("*".to_string()))
// Each expr returns a list of imports, flatten into common list
just(Lexeme::name("*")).map(move |_| init_table(globstar))
.labelled("wildcard import"), // Just a *, wrapped
enum_parser!(Lexeme::Name).map(init_table)
enum_parser!(Lexeme::Name)
.map(|s| init_table(intern(s.as_str())))
.labelled("import terminal") // Just a name, wrapped
))
).or_not()
)
.map(|(name, opt_post): (Vec<String>, Option<BoxedIterIter<String>>)| -> BoxedIterIter<String> {
.map(|(name, opt_post): (Vec<Spur>, Option<BoxedIterIter<Spur>>)|
-> BoxedIterIter<Spur> {
if let Some(post) = opt_post {
Box::new(post.map(move |el| {
box_chain!(name.clone().into_iter(), el)
@@ -56,14 +57,17 @@ pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme
box_once(into_boxed_iter(name))
}
})
}).map(|paths| {
}).map(move |paths| {
paths.filter_map(|namespaces| {
let path = to_mrc_slice(namespaces.collect_vec());
let path_prefix = mrc_derive(&path, |p| &p[..p.len() - 1]);
match path.last()?.as_str() {
"*" => Some(Import { path: path_prefix, name: None }),
name => Some(Import { path: path_prefix, name: Some(name.to_owned()) })
}
let mut path = namespaces.collect_vec();
let name = path.pop()?;
Some(Import {
path: Rc::new(path),
name: {
if name == globstar { None }
else { Some(name.to_owned()) }
}
})
}).collect()
}).labelled("import")
}

View File

@@ -9,12 +9,8 @@ mod import;
mod enum_parser;
mod parse;
pub use sourcefile::FileEntry;
pub use sourcefile::line_parser;
pub use sourcefile::imports;
pub use sourcefile::exported_names;
pub use lexer::{lexer, Lexeme, Entry as LexerEntry};
pub use name::is_op;
pub use parse::{parse, reparse, ParseError};
pub use import::Import;
pub use number::{float_parser, int_parser};

View File

@@ -2,11 +2,12 @@ use std::{ops::Range, fmt::Debug};
use chumsky::{prelude::{Simple, end}, Stream, Parser};
use itertools::Itertools;
use lasso::Spur;
use thiserror::Error;
use crate::{ast::Rule, parse::{lexer::LexedText, sourcefile::split_lines}};
use crate::{ast::Rule, parse::{lexer::LexedText, sourcefile::split_lines}, representations::sourcefile::FileEntry};
use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry};
use super::{Lexeme, lexer, line_parser, LexerEntry};
#[derive(Error, Debug, Clone)]
@@ -17,14 +18,19 @@ pub enum ParseError {
Ast(Vec<Simple<Lexeme>>)
}
pub fn parse<'a, Op>(ops: &[Op], data: &str) -> Result<Vec<FileEntry>, ParseError>
where Op: 'a + AsRef<str> + Clone {
pub fn parse<'a, Op, F>(
ops: &[Op], data: &str, intern: &F
) -> Result<Vec<FileEntry>, ParseError>
where
Op: 'a + AsRef<str> + Clone,
F: Fn(&str) -> Spur
{
let lexie = lexer(ops);
let token_batchv = split_lines(data).map(|line| {
lexie.parse(line).map_err(ParseError::Lex)
}).collect::<Result<Vec<_>, _>>()?;
println!("Lexed:\n{:?}", LexedText(token_batchv.clone()));
let parsr = line_parser().then_ignore(end());
let parsr = line_parser(intern).then_ignore(end());
let (parsed_lines, errors_per_line) = token_batchv.into_iter().filter(|v| {
!v.is_empty()
}).map(|v| {
@@ -47,10 +53,15 @@ where Op: 'a + AsRef<str> + Clone {
else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) }
}
pub fn reparse<'a, Op>(ops: &[Op], data: &str, pre: &[FileEntry])
pub fn reparse<'a, Op, F>(
ops: &[Op], data: &str, pre: &[FileEntry], intern: &F
)
-> Result<Vec<FileEntry>, ParseError>
where Op: 'a + AsRef<str> + Clone {
let result = parse(ops, data)?;
where
Op: 'a + AsRef<str> + Clone,
F: Fn(&str) -> Spur
{
let result = parse(ops, data, intern)?;
Ok(result.into_iter().zip(pre.iter()).map(|(mut output, donor)| {
if let FileEntry::Rule(Rule{source, ..}, _) = &mut output {
if let FileEntry::Rule(Rule{source: s2, ..}, _) = donor {

View File

@@ -1,164 +1,64 @@
use std::collections::HashSet;
use std::iter;
use std::rc::Rc;
use crate::{enum_parser, box_chain};
use crate::ast::{Expr, Clause, Rule};
use crate::utils::{to_mrc_slice, one_mrc_slice};
use crate::utils::Stackframe;
use crate::utils::iter::box_empty;
use crate::representations::sourcefile::FileEntry;
use crate::enum_parser;
use crate::ast::{Expr, Rule};
use super::expression::xpr_parser;
use super::import::{self, Import};
use super::expression::{xpr_parser, ns_name_parser};
use super::import::import_parser;
use super::lexer::Lexeme;
use chumsky::{Parser, prelude::*};
use lasso::Spur;
use ordered_float::NotNan;
use lazy_static::lazy_static;
/// Anything we might encounter in a file
#[derive(Debug, Clone)]
pub enum FileEntry {
Import(Vec<import::Import>),
Comment(String),
/// The bool indicates whether the rule is exported - whether tokens uniquely defined inside it
/// should be exported
Rule(Rule, bool),
Export(Vec<Vec<String>>)
}
fn visit_all_names_clause_recur<'a, F>(
clause: &'a Clause,
binds: Stackframe<String>,
cb: &mut F
) where F: FnMut(&'a [String]) {
match clause {
Clause::Auto(name, typ, body) => {
for x in typ.iter() {
visit_all_names_expr_recur(x, binds.clone(), cb)
}
let binds_dup = binds.clone();
let new_binds = if let Some(n) = name {
binds_dup.push(n.to_owned())
} else {
binds
};
for x in body.iter() {
visit_all_names_expr_recur(x, new_binds.clone(), cb)
}
},
Clause::Lambda(name, typ, body) => {
for x in typ.iter() {
visit_all_names_expr_recur(x, binds.clone(), cb)
}
for x in body.iter() {
visit_all_names_expr_recur(x, binds.push(name.to_owned()), cb)
}
},
Clause::S(_, body) => for x in body.iter() {
visit_all_names_expr_recur(x, binds.clone(), cb)
},
Clause::Name{ local: Some(name), qualified } => {
if binds.iter().all(|x| x != name) {
cb(qualified)
}
}
_ => (),
}
}
/// Recursively iterate through all "names" in an expression. It also finds a lot of things that
/// aren't names, such as all bound parameters. Generally speaking, this is not a very
/// sophisticated search.
///
/// TODO: find a way to exclude parameters
fn visit_all_names_expr_recur<'a, F>(
expr: &'a Expr,
binds: Stackframe<String>,
cb: &mut F
) where F: FnMut(&'a [String]) {
let Expr(val, typ) = expr;
visit_all_names_clause_recur(val, binds.clone(), cb);
for typ in typ.as_ref() {
visit_all_names_clause_recur(typ, binds.clone(), cb);
}
}
/// Collect all names that occur in an expression
fn find_all_names(expr: &Expr) -> HashSet<&[String]> {
let mut ret = HashSet::new();
visit_all_names_expr_recur(expr, Stackframe::new(String::new()), &mut |n| {
if !n.last().unwrap().starts_with('$') {
ret.insert(n);
}
});
ret
}
fn rule_parser() -> impl Parser<Lexeme, (Vec<Expr>, NotNan<f64>, Vec<Expr>), Error = Simple<Lexeme>> {
xpr_parser().repeated()
fn rule_parser<'a, F>(intern: &'a F) -> impl Parser<Lexeme, (
Vec<Expr>, NotNan<f64>, Vec<Expr>
), Error = Simple<Lexeme>> + 'a
where F: Fn(&str) -> Spur + 'a {
xpr_parser(intern).repeated()
.then(enum_parser!(Lexeme::Rule))
.then(xpr_parser().repeated())
// .map(|((lhs, prio), rhs)| )
.then(xpr_parser(intern).repeated())
.map(|((a, b), c)| (a, b, c))
.labelled("Rule")
}
pub fn line_parser() -> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> {
pub fn line_parser<'a, F>(intern: &'a F)
-> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> + 'a
where F: Fn(&str) -> Spur + 'a {
choice((
// In case the usercode wants to parse doc
enum_parser!(Lexeme >> FileEntry; Comment),
just(Lexeme::Import)
.ignore_then(import_parser().map(FileEntry::Import))
.ignore_then(import_parser(intern).map(FileEntry::Import))
.then_ignore(enum_parser!(Lexeme::Comment).or_not()),
just(Lexeme::Export).map_err_with_span(|e, s| {
println!("{:?} could not yield an export", s); e
}).ignore_then(
just(Lexeme::NS).ignore_then(
enum_parser!(Lexeme::Name).map(|n| vec![n])
ns_name_parser(intern).map(Rc::new)
.separated_by(just(Lexeme::name(",")))
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
).map(FileEntry::Export)
.or(rule_parser().map(|(source, prio, target)| {
.or(rule_parser(intern).map(|(source, prio, target)| {
FileEntry::Rule(Rule {
source: to_mrc_slice(source),
source: Rc::new(source),
prio,
target: to_mrc_slice(target)
target: Rc::new(target)
}, true)
}))
),
// This could match almost anything so it has to go last
rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{
source: to_mrc_slice(source),
prio,
target: to_mrc_slice(target)
}, false)),
rule_parser(intern).map(|(source, prio, target)| {
FileEntry::Rule(Rule{
source: Rc::new(source),
prio,
target: Rc::new(target)
}, false)
}),
))
}
/// Collect all exported names (and a lot of other words) from a file
pub fn exported_names(src: &[FileEntry]) -> HashSet<&[String]> {
src.iter().flat_map(|ent| match ent {
FileEntry::Rule(Rule{source, target, ..}, true) =>
box_chain!(source.iter(), target.iter()),
_ => box_empty()
}).flat_map(find_all_names).chain(
src.iter().filter_map(|ent| {
if let FileEntry::Export(names) = ent {Some(names.iter())} else {None}
}).flatten().map(Vec::as_slice)
).collect()
}
/// Summarize all imports from a file in a single list of qualified names
pub fn imports<'a, 'b, I>(
src: I
) -> impl Iterator<Item = &'b import::Import> + 'a
where I: Iterator<Item = &'b FileEntry> + 'a {
src.filter_map(|ent| match ent {
FileEntry::Import(impv) => Some(impv.iter()),
_ => None
}).flatten()
}
pub fn split_lines(data: &str) -> impl Iterator<Item = &str> {
let mut source = data.char_indices();
let mut last_slice = 0;

View File

@@ -1,7 +1,33 @@
use crate::parse::FileEntry;
use lasso::Spur;
use super::{Loader, Loaded};
use crate::representations::sourcefile::FileEntry;
pub fn ext_loader(data: Vec<FileEntry>) -> impl Loader {
move |_: &[&str]| Ok(Loaded::External(data.clone()))
use super::{Loader, Loaded, LoadingError};
pub fn ext_loader<'a, T, F>(
data: Vec<FileEntry>,
mut submods: Vec<(&'static str, T)>,
intern: &'a F
) -> impl Loader + 'a
where
T: Loader + 'a,
F: Fn(&str) -> Spur {
move |path: &[&str]| {
let (step, rest) = match path.split_first() {
None => return Ok(Loaded::AST(
data.iter().cloned().chain(
submods.iter().map(|(s, _)| FileEntry::LazyModule(intern(s)))
).collect()
)),
Some(t) => t
};
if let Some((_, l)) = submods.iter_mut().find(|(s, l)| s == step) {
l.load(rest)
} else {
let errtyp = if rest.is_empty() {
LoadingError::UnknownNode
} else {LoadingError::Missing};
Err(errtyp(step.to_string()))
}
}
}

View File

@@ -0,0 +1,34 @@
use std::rc::Rc;
use lasso::Spur;
use ordered_float::NotNan;
use crate::representations::Primitive;
use crate::representations::sourcefile::FileEntry;
use crate::foreign::ExternFn;
use crate::ast::{Rule, Clause};
use super::{Loader, ext_loader};
pub fn extlib_loader<'a, T, F>(
fns: Vec<(&'static str, Box<dyn ExternFn>)>,
submods: Vec<(&'static str, T)>,
intern: &'a F
) -> impl Loader + 'a
where
T: Loader + 'a,
F: Fn(&str) -> Spur + 'a
{
let entries = (
fns.into_iter().map(|(name, xfn)| FileEntry::Rule(Rule {
source: Rc::new(vec![
Clause::Name(Rc::new(vec![intern(name)])).into_expr(),
]),
prio: NotNan::try_from(0.0f64).unwrap(),
target: Rc::new(vec![
Clause::P(Primitive::ExternFn(xfn)).into_expr(),
])
}, true))
).collect();
ext_loader(entries, submods, intern)
}

View File

@@ -1,22 +1,34 @@
use std::fs::read_to_string;
use std::path::PathBuf;
use lasso::Spur;
use crate::representations::sourcefile::FileEntry;
use super::{Loaded, Loader, LoadingError};
pub fn file_loader(proj: PathBuf) -> impl Loader + 'static {
pub fn file_loader<'a, F>(
proj: PathBuf,
intern: &'a F
) -> impl Loader + 'a
where F: Fn(&str) -> Spur + 'a {
move |path: &[&str]| {
let dirpath = proj.join(path.join("/"));
if dirpath.is_dir() || dirpath.is_symlink() {
return Ok(Loaded::Namespace(
return Ok(Loaded::AST(
dirpath.read_dir()?
.filter_map(|entr| {
let ent = entr.ok()?;
let typ = ent.file_type().ok()?;
let path = ent.path();
if typ.is_dir() || typ.is_symlink() {
Some(ent.file_name().to_string_lossy().into_owned())
let name = ent.file_name();
let spur = intern(name.to_string_lossy().as_ref());
Some(FileEntry::LazyModule(spur))
} else if typ.is_file() && path.extension()? == "orc" {
Some(path.file_stem()?.to_string_lossy().into_owned())
let name = path.file_stem().expect("extension tested above");
let spur = intern(name.to_string_lossy().as_ref());
Some(FileEntry::LazyModule(spur))
} else { None }
})
.collect()
@@ -24,7 +36,7 @@ pub fn file_loader(proj: PathBuf) -> impl Loader + 'static {
}
let orcfile = dirpath.with_extension("orc");
if orcfile.is_file() {
read_to_string(orcfile).map(Loaded::Module).map_err(LoadingError::from)
read_to_string(orcfile).map(Loaded::Source).map_err(LoadingError::from)
} else {
let pathstr = dirpath.to_string_lossy().into_owned();
Err(if dirpath.exists() { LoadingError::UnknownNode(pathstr) }

View File

@@ -1,23 +0,0 @@
use itertools::Itertools;
use ordered_float::NotNan;
use crate::parse::FileEntry;
use crate::representations::Primitive;
use crate::utils::{one_mrc_slice, mrc_empty_slice};
use crate::foreign::ExternFn;
use crate::ast::{Rule, Expr, Clause};
use super::{Loader, ext_loader};
pub fn fnlib_loader(src: Vec<(&'static str, Box<dyn ExternFn>)>) -> impl Loader {
let entries = src.into_iter().map(|(name, xfn)| FileEntry::Rule(Rule {
source: one_mrc_slice(Expr(Clause::Name{
local: Some(name.to_string()),
qualified: one_mrc_slice(name.to_string())
}, mrc_empty_slice())),
prio: NotNan::try_from(0.0f64).unwrap(),
target: one_mrc_slice(Expr(Clause::P(Primitive::ExternFn(xfn)), mrc_empty_slice()))
}, true))
.collect_vec();
ext_loader(entries)
}

View File

@@ -5,7 +5,7 @@ use super::{Loader, LoadingError, Loaded};
pub fn map_loader<'a, T: Loader + 'a>(mut map: HashMap<&'a str, T>) -> impl Loader + 'a {
move |path: &[&str]| {
let (key, subpath) = if let Some(sf) = path.split_first() {sf}
else {return Ok(Loaded::Module(map.keys().cloned().collect()))};
else {return Ok(Loaded::Source(map.keys().cloned().collect()))};
let sub = if let Some(sub) = map.get_mut(key.to_string().as_str()) {sub}
else {return Err(
if subpath.len() == 0 {LoadingError::UnknownNode(path.join("::"))}

View File

@@ -2,21 +2,19 @@ mod file_loader;
mod ext_loader;
mod string_loader;
mod map_loader;
mod fnlib_loader;
mod overlay_loader;
mod extlib_loader;
mod prefix_loader;
pub use file_loader::file_loader;
pub use ext_loader::ext_loader;
pub use fnlib_loader::fnlib_loader;
pub use extlib_loader::extlib_loader;
pub use string_loader::string_loader;
pub use map_loader::map_loader;
pub use overlay_loader::overlay_loader;
pub use prefix_loader::prefix_loader;
use std::{rc::Rc, io};
use crate::parse::FileEntry;
use crate::representations::sourcefile::FileEntry;
#[derive(Clone, Debug)]
pub enum LoadingError {
@@ -34,11 +32,10 @@ impl From<io::Error> for LoadingError {
}
}
#[derive(Debug, Clone)]
#[derive(Clone)]
pub enum Loaded {
Module(String),
Namespace(Vec<String>),
External(Vec<FileEntry>)
Source(String),
AST(Vec<FileEntry>)
}
pub trait Loader {

View File

@@ -1,19 +0,0 @@
use super::{Loader, LoadingError};
pub fn overlay_loader(mut base: impl Loader, mut overlay: impl Loader) -> impl Loader {
move |path: &[&str]| match overlay.load(path) {
ok@Ok(_) => ok,
e@Err(LoadingError::IOErr(_)) => e,
Err(_) => base.load(path)
}
}
#[macro_export]
macro_rules! overlay_loader {
($left:expr, $right:expr) => {
overlay_loader($left, $right)
};
($left:expr, $mid:expr, $($rest:expr),+) => {
overlay_loader($left, overlay_loader!($mid, $($rest),+))
};
}

View File

@@ -1,5 +1,5 @@
use super::{Loader, Loaded};
pub fn string_loader<'a>(data: &'a str) -> impl Loader + 'a {
move |_: &[&str]| Ok(Loaded::Module(data.to_string()))
move |_: &[&str]| Ok(Loaded::Source(data.to_string()))
}

View File

@@ -8,7 +8,7 @@ pub use module_error::ModuleError;
pub use rule_collector::rule_collector;
pub use loading::{
Loader, Loaded, LoadingError,
ext_loader, file_loader, string_loader, map_loader, fnlib_loader,
overlay_loader, prefix_loader
ext_loader, file_loader, string_loader, map_loader, extlib_loader,
prefix_loader
};
use crate::ast::Rule;

View File

@@ -1,73 +1,80 @@
use std::collections::HashMap;
use mappable_rc::Mrc;
use std::rc::Rc;
use itertools::Itertools;
use lasso::Spur;
use thiserror::Error;
use crate::utils::{Stackframe, to_mrc_slice};
use crate::utils::Stackframe;
use crate::ast::{Expr, Clause};
type ImportMap = HashMap<String, Mrc<[String]>>;
type ImportMap = HashMap<Spur, Rc<Vec<Spur>>>;
#[derive(Debug, Clone, Error)]
pub enum ResolutionError<Err> {
#[error("Reference cycle at {0:?}")]
Cycle(Vec<Mrc<[String]>>),
Cycle(Vec<Rc<Vec<Spur>>>),
#[error("No module provides {0:?}")]
NoModule(Mrc<[String]>),
NoModule(Rc<Vec<Spur>>),
#[error(transparent)]
Delegate(#[from] Err)
}
type ResolutionResult<E> = Result<Mrc<[String]>, ResolutionError<E>>;
type ResolutionResult<E> = Result<Rc<Vec<Spur>>, ResolutionError<E>>;
/// Recursively resolves symbols to their original names in expressions while caching every
/// resolution. This makes the resolution process lightning fast and invalidation completely
/// impossible since the intermediate steps of a resolution aren't stored.
/// Recursively resolves symbols to their original names in expressions
/// while caching every resolution. This makes the resolution process
/// lightning fast and invalidation completely impossible since
/// the intermediate steps of a resolution aren't stored.
pub struct NameResolver<FSplit, FImps, E> {
cache: HashMap<Mrc<[String]>, ResolutionResult<E>>,
get_modname: FSplit,
cache: HashMap<Rc<Vec<Spur>>, ResolutionResult<E>>,
split: FSplit,
get_imports: FImps
}
impl<FSplit, FImps, E> NameResolver<FSplit, FImps, E>
where
FSplit: FnMut(Mrc<[String]>) -> Option<Mrc<[String]>>,
FImps: FnMut(Mrc<[String]>) -> Result<ImportMap, E>,
FSplit: FnMut(Rc<Vec<Spur>>) -> Option<(Rc<Vec<Spur>>, Rc<Vec<Spur>>)>,
FImps: FnMut(Rc<Vec<Spur>>) -> Result<ImportMap, E>,
E: Clone
{
pub fn new(get_modname: FSplit, get_imports: FImps) -> Self {
pub fn new(split: FSplit, get_imports: FImps) -> Self {
Self {
cache: HashMap::new(),
get_modname,
split,
get_imports
}
}
fn split(&self, symbol: Rc<Vec<Spur>>)
-> Result<(Rc<Vec<Spur>>, Rc<Vec<Spur>>), ResolutionError<E>> {
let (path, name) = (self.split)(symbol.clone())
.ok_or_else(|| ResolutionError::NoModule(symbol.clone()))?;
if name.is_empty() {
panic!("get_modname matched all to module and nothing to name")
}
Ok((path, name))
}
/// Obtains a symbol's originnal name
/// Uses a substack to detect loops
fn find_origin_rec(
&mut self,
symbol: Mrc<[String]>,
import_path: Stackframe<Mrc<[String]>>
) -> Result<Mrc<[String]>, ResolutionError<E>> {
symbol: Rc<Vec<Spur>>,
import_path: Stackframe<Rc<Vec<Spur>>>
) -> Result<Rc<Vec<Spur>>, ResolutionError<E>> {
if let Some(cached) = self.cache.get(&symbol) {
return cached.as_ref().map_err(|e| e.clone()).map(Mrc::clone)
return cached.clone()
}
// The imports and path of the referenced file and the local name
let path = (self.get_modname)(Mrc::clone(&symbol)).ok_or_else(|| {
ResolutionError::NoModule(Mrc::clone(&symbol))
})?;
let name = &symbol[path.len()..];
if name.is_empty() {
panic!("get_modname matched all to module and nothing to name in {:?}", import_path)
}
let imports = (self.get_imports)(Mrc::clone(&path))?;
let (path, name) = self.split(symbol)?;
let imports = (self.get_imports)(path.clone())?;
let result = if let Some(source) = imports.get(&name[0]) {
let new_sym: Vec<String> = source.iter().chain(name.iter()).cloned().collect();
let new_sym = source.iter().chain(name.iter()).cloned().collect_vec();
if import_path.iter().any(|el| el.as_ref() == new_sym.as_slice()) {
Err(ResolutionError::Cycle(import_path.iter().map(Mrc::clone).collect()))
Err(ResolutionError::Cycle(import_path.iter().cloned().collect()))
} else {
self.find_origin_rec(to_mrc_slice(new_sym), import_path.push(Mrc::clone(&symbol)))
self.find_origin_rec(Rc::new(new_sym), import_path.push(symbol.clone()))
}
} else {
Ok(symbol.clone()) // If not imported, it must be locally defined
@@ -81,30 +88,27 @@ where
}
fn process_exprmrcopt_rec(&mut self,
exbo: &Option<Mrc<Expr>>
) -> Result<Option<Mrc<Expr>>, ResolutionError<E>> {
exbo.iter().map(|exb| Ok(Mrc::new(self.process_expression_rec(exb.as_ref())?)))
exbo: &Option<Rc<Expr>>
) -> Result<Option<Rc<Expr>>, ResolutionError<E>> {
exbo.iter().map(|exb| Ok(Rc::new(self.process_expression_rec(exb)?)))
.next().transpose()
}
fn process_clause_rec(&mut self, tok: &Clause) -> Result<Clause, ResolutionError<E>> {
Ok(match tok {
Clause::S(c, exv) => Clause::S(*c, to_mrc_slice(
exv.as_ref().iter().map(|e| self.process_expression_rec(e))
.collect::<Result<Vec<Expr>, ResolutionError<E>>>()?
Clause::S(c, exv) => Clause::S(*c, Rc::new(
exv.iter().map(|e| self.process_expression_rec(e))
.collect::<Result<_, _>>()?
)),
Clause::Lambda(name, typ, body) => Clause::Lambda(name.clone(),
to_mrc_slice(self.process_exprv_rec(typ.as_ref())?),
to_mrc_slice(self.process_exprv_rec(body.as_ref())?)
Rc::new(self.process_exprv_rec(&typ)?),
Rc::new(self.process_exprv_rec(&body)?)
),
Clause::Auto(name, typ, body) => Clause::Auto(name.clone(),
to_mrc_slice(self.process_exprv_rec(typ.as_ref())?),
to_mrc_slice(self.process_exprv_rec(body.as_ref())?)
Rc::new(self.process_exprv_rec(&typ)?),
Rc::new(self.process_exprv_rec(&body)?)
),
Clause::Name{local, qualified} => Clause::Name{
local: local.clone(),
qualified: self.find_origin(Mrc::clone(qualified))?
},
Clause::Name(name) => Clause::Name(self.find_origin(name.clone())?),
x => x.clone()
})
}
@@ -112,12 +116,14 @@ where
fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result<Expr, ResolutionError<E>> {
Ok(Expr(
self.process_clause_rec(token)?,
typ.iter().map(|t| self.process_clause_rec(t)).collect::<Result<_, _>>()?
Rc::new(typ.iter().map(|t| {
self.process_clause_rec(t)
}).collect::<Result<_, _>>()?)
))
}
pub fn find_origin(&mut self, symbol: Mrc<[String]>) -> Result<Mrc<[String]>, ResolutionError<E>> {
self.find_origin_rec(Mrc::clone(&symbol), Stackframe::new(symbol))
pub fn find_origin(&mut self, symbol: Rc<Vec<Spur>>) -> Result<Rc<Vec<Spur>>, ResolutionError<E>> {
self.find_origin_rec(symbol.clone(), Stackframe::new(symbol))
}
#[allow(dead_code)]

View File

@@ -1,6 +1,8 @@
use mappable_rc::Mrc;
use std::rc::Rc;
use crate::{ast::{Expr, Clause}, utils::{collect_to_mrc, to_mrc_slice}};
use lasso::Spur;
use crate::ast::{Expr, Clause};
/// Replaces the first element of a name with the matching prefix from a prefix map
@@ -8,34 +10,33 @@ use crate::{ast::{Expr, Clause}, utils::{collect_to_mrc, to_mrc_slice}};
/// Called by [#prefix] which handles Typed.
fn prefix_clause(
expr: &Clause,
namespace: Mrc<[String]>
namespace: &[Spur]
) -> Clause {
match expr {
Clause::S(c, v) => Clause::S(*c,
collect_to_mrc(v.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace))))
),
Clause::S(c, v) => Clause::S(*c, Rc::new(v.iter().map(|e| {
prefix_expr(e, namespace)
}).collect())),
Clause::Auto(name, typ, body) => Clause::Auto(
name.clone(),
collect_to_mrc(typ.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
collect_to_mrc(body.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
Rc::new(typ.iter().map(|e| prefix_expr(e, namespace)).collect()),
Rc::new(body.iter().map(|e| prefix_expr(e, namespace)).collect()),
),
Clause::Lambda(name, typ, body) => Clause::Lambda(
name.clone(),
collect_to_mrc(typ.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
collect_to_mrc(body.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
Rc::new(typ.iter().map(|e| prefix_expr(e, namespace)).collect()),
Rc::new(body.iter().map(|e| prefix_expr(e, namespace)).collect()),
),
Clause::Name(name) => Clause::Name(
Rc::new(namespace.iter().chain(name.iter()).cloned().collect())
),
Clause::Name{local, qualified} => Clause::Name{
local: local.clone(),
qualified: collect_to_mrc(namespace.iter().chain(qualified.iter()).cloned())
},
x => x.clone()
}
}
/// Produce an Expr object for any value of Expr
pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: Mrc<[String]>) -> Expr {
pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: &[Spur]) -> Expr {
Expr(
prefix_clause(clause, Mrc::clone(&namespace)),
to_mrc_slice(typ.iter().map(|e| prefix_clause(e, Mrc::clone(&namespace))).collect())
prefix_clause(clause, namespace),
Rc::new(typ.iter().map(|e| prefix_clause(e, namespace)).collect())
)
}

View File

@@ -1,85 +1,92 @@
use std::cell::RefCell;
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use std::rc::Rc;
use itertools::Itertools;
use mappable_rc::Mrc;
use lasso::Spur;
use crate::ast::Rule;
use crate::parse::{self, FileEntry};
use crate::utils::{Cache, mrc_derive, to_mrc_slice, one_mrc_slice};
use crate::parse;
use crate::representations::sourcefile::{FileEntry, exported_names, imports};
use crate::utils::Cache;
use super::name_resolver::NameResolver;
use super::module_error::ModuleError;
use super::prefix::prefix_expr;
use super::loading::{Loaded, Loader, LoadingError};
use crate::parse::Import;
type ParseResult<T> = Result<T, ModuleError<LoadingError>>;
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct Module {
pub rules: Vec<Rule>,
pub exports: Vec<String>,
pub references: HashSet<Mrc<[String]>>
pub exports: Vec<Spur>,
pub references: HashSet<Rc<Vec<Spur>>>
}
pub type RuleCollectionResult = Result<Vec<super::Rule>, ModuleError<LoadingError>>;
pub fn rule_collector<F: 'static>(
pub fn rule_collector<'a, F: 'a, G: 'a, H: 'a>(
intern: &'a G, deintern: &'a H,
load_mod: F
) -> Cache<'static, Mrc<[String]>, RuleCollectionResult>
where F: Loader
) -> Cache<'static, Rc<Vec<Spur>>, RuleCollectionResult>
where F: Loader, G: Fn(&str) -> Spur, H: Fn(Spur) -> &'a str
{
let load_mod_rc = RefCell::new(load_mod);
// Map paths to a namespace with name list (folder) or module with source text (file)
let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _| -> ParseResult<Loaded> {
load_mod_rc.borrow_mut().load(&path.iter().map(|s| s.as_str()).collect_vec()).map_err(ModuleError::Load)
}));
let loaded = Cache::rc(move |path: Rc<Vec<Spur>>, _| -> ParseResult<Rc<Loaded>> {
let load_mod = load_mod_rc.borrow_mut();
let spath = path.iter().cloned().map(deintern).collect_vec();
load_mod.load(&spath).map(Rc::new).map_err(ModuleError::Load)
});
// Map names to the longest prefix that points to a valid module
// At least one segment must be in the prefix, and the prefix must not be the whole name
let modname = Rc::new(Cache::new({
let modname = Cache::rc({
let loaded = loaded.clone();
move |symbol: Mrc<[String]>, _| -> Result<Mrc<[String]>, Vec<ModuleError<LoadingError>>> {
move |symbol: Rc<Vec<Spur>>, _| -> Result<Rc<Vec<Spur>>, Rc<Vec<ModuleError<LoadingError>>>> {
let mut errv: Vec<ModuleError<LoadingError>> = Vec::new();
let reg_err = |e, errv: &mut Vec<ModuleError<LoadingError>>| {
errv.push(e);
if symbol.len() == errv.len() { Err(errv.clone()) }
if symbol.len() == errv.len() { Err(Rc::new(errv.clone())) }
else { Ok(()) }
};
loop {
let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len() - 1]);
match loaded.try_find(&path) {
// TODO: this should not live on the heap
let path = Rc::new(symbol.iter()
.take(symbol.len() - errv.len() - 1)
.cloned()
.collect_vec());
match loaded.find(&path).as_ref() {
Ok(imports) => match imports.as_ref() {
Loaded::Module(_) | Loaded::External(_) => break Ok(path),
Loaded::Namespace(_) => reg_err(ModuleError::None, &mut errv)?
Loaded::Source(_) | Loaded::AST(_) => break Ok(path),
},
Err(err) => reg_err(err, &mut errv)?
Err(err) => reg_err(err.clone(), &mut errv)?
}
}
}
}));
});
// Preliminarily parse a file, substitution rules and imports are valid
let prelude_path = one_mrc_slice("prelude".to_string());
let preparsed = Rc::new(Cache::new({
// let prelude_path = vec!["prelude".to_string()];
// let interned_prelude_path = Rc::new(
// prelude_path.iter()
// .map(|s| intern(s.as_str()))
// .collect_vec()
// );
let loaded = loaded.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>> {
let loaded = loaded.try_find(&path)?;
move |path: Rc<Vec<Spur>>, _| -> ParseResult<Vec<FileEntry>> {
let loaded = loaded.find(&path)?;
match loaded.as_ref() {
Loaded::Module(source) => {
let mut entv = parse::parse(&[] as &[&str], source.as_str())?;
if !entv.iter().any(|ent| if let FileEntry::Import(imps) = ent {
imps.iter().any(|imp| imp.path.starts_with(&prelude_path))
} else {false}) && path != prelude_path {
entv.push(FileEntry::Import(vec![Import{
name: None, path: Mrc::clone(&prelude_path)
}]))
}
Loaded::Source(source) => {
let mut entv = parse::parse(&[] as &[&str], source.as_str(), intern)?;
// if path != interned_prelude_path {
// entv.push(FileEntry::Import(vec![Import{
// name: None, path: prelude_path
// }]))
// }
Ok(entv)
}
Loaded::External(ast) => Ok(ast.clone()),
Loaded::Namespace(_) => Err(ModuleError::None),
Loaded::AST(ast) => Ok(ast.clone()),
}
}
}));
@@ -87,13 +94,10 @@ where F: Loader
let exports = Rc::new(Cache::new({
let loaded = loaded.clone();
let preparsed = preparsed.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<String>> {
let loaded = loaded.try_find(&path)?;
if let Loaded::Namespace(names) = loaded.as_ref() {
return Ok(names.clone());
}
let preparsed = preparsed.try_find(&path)?;
Ok(parse::exported_names(&preparsed)
move |path: Rc<Vec<Spur>>, _| -> ParseResult<Vec<Spur>> {
let loaded = loaded.find(&path)?;
let preparsed = preparsed.find(&path)?;
Ok(exported_names(&preparsed)
.into_iter()
.map(|n| n[0].clone())
.collect())
@@ -103,24 +107,26 @@ where F: Loader
let imports = Rc::new(Cache::new({
let preparsed = preparsed.clone();
let exports = exports.clone();
move |path: Mrc<[String]>, _| -> ParseResult<HashMap<String, Mrc<[String]>>> {
let entv = preparsed.try_find(&path)?;
let import_entries = parse::imports(entv.iter());
let mut imported_symbols: HashMap<String, Mrc<[String]>> = HashMap::new();
move |path: Rc<Vec<Spur>>, _| -> ParseResult<Rc<HashMap<Spur, Rc<Vec<Spur>>>>> {
let entv = preparsed.find(&path)?;
let import_entries = imports(entv.iter());
let mut imported_symbols = HashMap::<Spur, Rc<Vec<Spur>>>::new();
for imp in import_entries {
let export = exports.try_find(&imp.path)?;
let export_list = exports.find(&path)?;
if let Some(ref name) = imp.name {
if export.contains(name) {
imported_symbols.insert(name.clone(), Mrc::clone(&imp.path));
} else {panic!("{:?} doesn't export {}", imp.path, name)}
if export_list.contains(name) {
imported_symbols.insert(name.clone(), imp.path.clone());
} else {
panic!("{:?} doesn't export {}", imp.path, deintern(*name))
}
} else {
for exp in export.as_ref() {
imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path));
for exp in export_list {
imported_symbols.insert(exp, imp.path.clone());
}
}
}
println!("Imports for {:?} are {:?}", path.as_ref(), imported_symbols);
Ok(imported_symbols)
// println!("Imports for {:?} are {:?}", path.as_ref(), imported_symbols);
Ok(Rc::new(imported_symbols))
}
}));
// Final parse, operators are correctly separated
@@ -128,69 +134,73 @@ where F: Loader
let preparsed = preparsed.clone();
let imports = imports.clone();
let loaded = loaded.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>> {
move |path: Rc<Vec<Spur>>, _| -> ParseResult<Vec<FileEntry>> {
let imported_ops: Vec<String> =
imports.try_find(&path)?
.keys()
.filter(|s| parse::is_op(s))
.cloned()
.collect();
// let parser = file_parser(&prelude, &imported_ops);
let pre = preparsed.try_find(&path)?;
match loaded.try_find(&path)?.as_ref() {
Loaded::Module(source) => Ok(parse::reparse(&imported_ops, source.as_str(), &pre)?),
Loaded::External(ast) => Ok(ast.clone()),
Loaded::Namespace(_) => Err(ModuleError::None)
imports.find(&path)?
.keys()
.map(|s| deintern(*s).to_string())
.filter(|s| parse::is_op(s))
.collect();
let pre = preparsed.find(&path)?;
match loaded.find(&path)?.as_ref() {
Loaded::Source(source) => Ok(parse::reparse(
&imported_ops, source.as_str(), &pre, intern
)?),
Loaded::AST(ast) => Ok(ast.clone()),
}
}
}));
let name_resolver_rc = RefCell::new(NameResolver::new({
let name_resolver = NameResolver::new({
let modname = modname.clone();
move |path| {
Some(modname.try_find(&path).ok()?.as_ref().clone())
let modname = modname.find(&path).ok()?;
let symname = Rc::new(path[modname.len()..].to_vec());
Some((modname, symname))
}
}, {
let imports = imports.clone();
move |path| {
imports.try_find(&path).map(|f| f.as_ref().clone())
imports.find(&path).map(|f| f.as_ref().clone())
}
}));
});
// Turn parsed files into a bag of rules and a list of toplevel export names
let resolved = Rc::new(Cache::new({
let parsed = parsed.clone();
let exports = exports.clone();
let imports = imports.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Module> {
let mut name_resolver = name_resolver_rc.borrow_mut();
move |path: Rc<Vec<Spur>>, _| -> ParseResult<Module> {
let module = Module {
rules: parsed.try_find(&path)?
rules: parsed.find(&path)?
.iter()
.filter_map(|ent| {
if let FileEntry::Rule(Rule{source, prio, target}, _) = ent {
Some(Rule {
source: source.iter()
.map(|ex| {
prefix_expr(ex, Mrc::clone(&path))
}).collect(),
target: target.iter().map(|ex| {
prefix_expr(ex, Mrc::clone(&path))
}).collect(),
source: Rc::new(
source.iter()
.map(|ex| prefix_expr(ex, &path))
.collect_vec()
),
target: Rc::new(
target.iter()
.map(|ex| prefix_expr(ex, &path))
.collect_vec()
),
prio: *prio,
})
} else { None }
})
.map(|Rule{ source, target, prio }| Ok(super::Rule {
source: to_mrc_slice(source.iter()
source: Rc::new(source.iter()
.map(|ex| name_resolver.process_expression(ex))
.collect::<Result<Vec<_>, _>>()?),
target: to_mrc_slice(target.iter()
target: Rc::new(target.iter()
.map(|ex| name_resolver.process_expression(ex))
.collect::<Result<Vec<_>, _>>()?),
prio
}))
.collect::<ParseResult<Vec<super::Rule>>>()?,
exports: exports.try_find(&path)?.as_ref().clone(),
references: imports.try_find(&path)?
exports: exports.find(&path)?.clone(),
references: imports.find(&path)?
.values().cloned().collect()
};
Ok(module)
@@ -198,14 +208,14 @@ where F: Loader
}));
Cache::new({
let resolved = resolved.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<super::Rule>> {
move |path: Rc<Vec<Spur>>, _| -> ParseResult<Vec<super::Rule>> {
// Breadth-first search
let mut processed: HashSet<Mrc<[String]>> = HashSet::new();
let mut processed: HashSet<Rc<Vec<Spur>>> = HashSet::new();
let mut rules: Vec<super::Rule> = Vec::new();
let mut pending: VecDeque<Mrc<[String]>> = VecDeque::new();
let mut pending: VecDeque<Rc<Vec<Spur>>> = VecDeque::new();
pending.push_back(path);
while let Some(el) = pending.pop_front() {
let resolved = resolved.try_find(&el)?;
let resolved = resolved.find(&el)?;
processed.insert(el.clone());
pending.extend(
resolved.references.iter()

View File

@@ -1,133 +1,193 @@
use mappable_rc::Mrc;
use lasso::RodeoResolver;
use lasso::Spur;
use itertools::Itertools;
use ordered_float::NotNan;
use std::{hash::Hash, intrinsics::likely};
use std::fmt::Debug;
use crate::utils::mrc_empty_slice;
use crate::utils::one_mrc_slice;
use std::hash::Hash;
use std::rc::Rc;
use crate::utils::InternedDisplay;
use crate::utils::Stackframe;
use super::primitive::Primitive;
/// An S-expression with a type
#[derive(PartialEq, Eq, Hash)]
pub struct Expr(pub Clause, pub Mrc<[Clause]>);
pub struct Expr(pub Clause, pub Rc<Vec<Clause>>);
impl Expr {
pub fn into_clause(self) -> Clause {
if likely(self.1.len() == 0) { self.0 }
else { Clause::S('(', one_mrc_slice(self)) }
if self.1.len() == 0 { self.0 }
else { Clause::S('(', Rc::new(vec![self])) }
}
pub fn visit_names<F>(&self,
binds: Stackframe<Rc<Vec<Spur>>>,
cb: &mut F
) where F: FnMut(Rc<Vec<Spur>>) {
let Expr(val, typ) = self;
val.visit_names(binds.clone(), cb);
for typ in typ.as_ref() {
typ.visit_names(binds.clone(), cb);
}
}
}
impl Clone for Expr {
fn clone(&self) -> Self {
Self(self.0.clone(), Mrc::clone(&self.1))
Self(self.0.clone(), self.1.clone())
}
}
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl InternedDisplay for Expr {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result {
let Expr(val, typ) = self;
write!(f, "{:?}", val)?;
val.fmt(f, rr)?;
for typ in typ.as_ref() {
write!(f, ":{:?}", typ)?
write!(f, ":")?;
typ.fmt(f, rr)?;
}
Ok(())
}
}
/// An S-expression as read from a source file
#[derive(PartialEq, Eq, Hash)]
#[derive(PartialEq, Eq, Hash, Clone)]
pub enum Clause {
P(Primitive),
/// A c-style name or an operator, eg. `+`, `i`, `foo::bar`
Name{
local: Option<String>,
qualified: Mrc<[String]>
},
/// A parenthesized expression, eg. `(print out "hello")`, `[1, 2, 3]`, `{Some(t) => t}`
S(char, Mrc<[Expr]>),
/// An explicit expression associated with the leftmost, outermost [Clause::Auto], eg. `read @Uint`
Explicit(Mrc<Expr>),
Name(Rc<Vec<Spur>>),
/// A parenthesized exmrc_empty_slice()pression
/// eg. `(print out "hello")`, `[1, 2, 3]`, `{Some(t) => t}`
S(char, Rc<Vec<Expr>>),
/// An explicit expression associated with the leftmost, outermost
/// [Clause::Auto], eg. `read @Uint`
Explicit(Rc<Expr>),
/// A function expression, eg. `\x. x + 1`
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),
Lambda(Rc<Clause>, Rc<Vec<Expr>>, Rc<Vec<Expr>>),
/// A parameterized expression with type inference, eg. `@T. T -> T`
Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>),
Auto(Option<Rc<Clause>>, Rc<Vec<Expr>>, Rc<Vec<Expr>>),
/// A placeholder for macros, eg. `$name`, `...$body`, `...$lhs:1`
Placeh{
key: String,
/// None => matches one token
/// Some((prio, nonzero)) =>
/// prio is the sizing priority for the vectorial (higher prio grows first)
/// prio is the sizing priority for the vectorial
/// (higher prio grows first)
/// nonzero is whether the vectorial matches 1..n or 0..n tokens
vec: Option<(usize, bool)>
},
}
impl Clause {
pub fn body(&self) -> Option<Mrc<[Expr]>> {
pub fn body(&self) -> Option<Rc<Vec<Expr>>> {
match self {
Self::Auto(_, _, body) |
Self::Lambda(_, _, body) |
Self::S(_, body) => Some(Mrc::clone(body)),
Self::S(_, body) => Some(body.clone()),
_ => None
}
}
pub fn typ(&self) -> Option<Mrc<[Expr]>> {
pub fn typ(&self) -> Option<Rc<Vec<Expr>>> {
match self {
Self::Auto(_, typ, _) | Self::Lambda(_, typ, _) => Some(Mrc::clone(typ)),
Self::Auto(_, typ, _) | Self::Lambda(_, typ, _) => Some(typ.clone()),
_ => None
}
}
pub fn into_expr(self) -> Expr {
if let Self::S('(', body) = &self {
if body.len() == 1 { body[0].clone() }
else { Expr(self, mrc_empty_slice()) }
} else { Expr(self, mrc_empty_slice()) }
else { Expr(self, Rc::default()) }
} else { Expr(self, Rc::default()) }
}
pub fn from_exprv(exprv: Mrc<[Expr]>) -> Option<Clause> {
pub fn from_exprv(exprv: &[Expr]) -> Option<Clause> {
if exprv.len() == 0 { None }
else if exprv.len() == 1 { Some(exprv[0].clone().into_clause()) }
else { Some(Self::S('(', exprv)) }
else { Some(Self::S('(', Rc::new(exprv.to_vec()))) }
}
}
impl Clone for Clause {
fn clone(&self) -> Self {
/// Recursively iterate through all "names" in an expression.
/// It also finds a lot of things that aren't names, such as all
/// bound parameters. Generally speaking, this is not a very
/// sophisticated search.
pub fn visit_names<F>(&self,
binds: Stackframe<Rc<Vec<Spur>>>,
cb: &mut F
) where F: FnMut(Rc<Vec<Spur>>) {
match self {
Self::S(c, b) => Self::S(*c, Mrc::clone(b)),
Self::Auto(n, t, b) => Self::Auto(
n.clone(), Mrc::clone(t), Mrc::clone(b)
),
Self::Name { local: l, qualified: q } => Self::Name {
local: l.clone(), qualified: Mrc::clone(q)
Clause::Auto(name, typ, body) => {
for x in typ.iter() {
x.visit_names(binds.clone(), cb)
}
let binds_dup = binds.clone();
let new_binds = if let Some(rc) = name {
if let Clause::Name(name) = rc.as_ref() {
binds_dup.push(name.clone())
} else { binds }
} else { binds };
for x in body.iter() {
x.visit_names(new_binds.clone(), cb)
}
},
Self::Lambda(n, t, b) => Self::Lambda(
n.clone(), Mrc::clone(t), Mrc::clone(b)
),
Self::Placeh{key, vec} => Self::Placeh{key: key.clone(), vec: *vec},
Self::P(p) => Self::P(p.clone()),
Self::Explicit(expr) => Self::Explicit(Mrc::clone(expr))
Clause::Lambda(name, typ, body) => {
for x in typ.iter() {
x.visit_names(binds.clone(), cb)
}
for x in body.iter() {
let new_binds = if let Clause::Name(name) = name.as_ref() {
binds.push(name.clone())
} else { binds };
x.visit_names(new_binds, cb)
}
},
Clause::S(_, body) => for x in body.iter() {
x.visit_names(binds.clone(), cb)
},
Clause::Name(name) => {
if binds.iter().all(|x| x != name) {
cb(name.clone())
}
}
_ => (),
}
}
}
fn fmt_expr_seq(it: &mut dyn Iterator<Item = &Expr>, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_expr_seq(
it: &mut dyn Iterator<Item = &Expr>,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result {
for item in Itertools::intersperse(it.map(Some), None) { match item {
Some(expr) => write!(f, "{:?}", expr),
Some(expr) => expr.fmt(f, rr),
None => f.write_str(" "),
}? }
Ok(())
}
impl Debug for Clause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub fn fmt_name(
name: &Rc<Vec<Spur>>, f: &mut std::fmt::Formatter, rr: RodeoResolver
) -> std::fmt::Result {
for el in itertools::intersperse(
name.iter().map(|s| rr.resolve(s)),
"::"
) {
write!(f, "{}", el)?
}
Ok(())
}
impl InternedDisplay for Clause {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result {
match self {
Self::P(p) => write!(f, "{:?}", p),
Self::Name{local, qualified} =>
if let Some(local) = local {write!(f, "{}`{}`", qualified.join("::"), local)}
else {write!(f, "{}", qualified.join("::"))},
Self::Name(name) => fmt_name(name, f, rr),
Self::S(del, items) => {
f.write_str(&del.to_string())?;
fmt_expr_seq(&mut items.iter(), f)?;
fmt_expr_seq(&mut items.iter(), f, rr)?;
f.write_str(match del {
'(' => ")", '[' => "]", '{' => "}",
_ => "CLOSING_DELIM"
@@ -135,44 +195,49 @@ impl Debug for Clause {
},
Self::Lambda(name, argtyp, body) => {
f.write_str("\\")?;
f.write_str(name)?;
f.write_str(":")?; fmt_expr_seq(&mut argtyp.iter(), f)?; f.write_str(".")?;
fmt_expr_seq(&mut body.iter(), f)
name.fmt(f, rr)?;
f.write_str(":")?;
fmt_expr_seq(&mut argtyp.iter(), f, rr)?;
f.write_str(".")?;
fmt_expr_seq(&mut body.iter(), f, rr)
},
Self::Auto(name, argtyp, body) => {
Self::Auto(name_opt, argtyp, body) => {
f.write_str("@")?;
f.write_str(&name.clone().unwrap_or_default())?;
f.write_str(":")?; fmt_expr_seq(&mut argtyp.iter(), f)?; f.write_str(".")?;
fmt_expr_seq(&mut body.iter(), f)
if let Some(name) = name_opt { name.fmt(f, rr)? }
f.write_str(":")?;
fmt_expr_seq(&mut argtyp.iter(), f, rr)?;
f.write_str(".")?;
fmt_expr_seq(&mut body.iter(), f, rr)
},
Self::Placeh{key, vec: None} => write!(f, "${key}"),
Self::Placeh{key, vec: Some((prio, true))} => write!(f, "...${key}:{prio}"),
Self::Placeh{key, vec: Some((prio, false))} => write!(f, "..${key}:{prio}"),
Self::Explicit(expr) => write!(f, "@{:?}", expr.as_ref())
Self::Placeh{key, vec: Some((prio, true))} =>
write!(f, "...${key}:{prio}"),
Self::Placeh{key, vec: Some((prio, false))} =>
write!(f, "..${key}:{prio}"),
Self::Explicit(expr) => {
write!(f, "@")?;
expr.fmt(f, rr)
}
}
}
}
/// A substitution rule as read from the source
#[derive(PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Rule {
pub source: Mrc<[Expr]>,
pub source: Rc<Vec<Expr>>,
pub prio: NotNan<f64>,
pub target: Mrc<[Expr]>
pub target: Rc<Vec<Expr>>
}
impl Clone for Rule {
fn clone(&self) -> Self {
Self {
source: Mrc::clone(&self.source),
prio: self.prio,
target: Mrc::clone(&self.target)
}
}
}
impl Debug for Rule {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} ={}=> {:?}", self.source, self.prio, self.target)
impl InternedDisplay for Rule {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result {
for e in self.source.iter() { e.fmt(f, rr)?; write!(f, " ")?; }
write!(f, "={}=>", self.prio)?;
for e in self.target.iter() { write!(f, " ")?; e.fmt(f, rr)?; }
Ok(())
}
}

View File

@@ -1,5 +1,7 @@
use std::{rc::Rc, fmt::Display};
use lasso::{Spur, RodeoResolver};
use crate::utils::Stackframe;
use super::{ast, postmacro};
@@ -8,27 +10,30 @@ use super::{ast, postmacro};
pub enum Error {
/// `()` as a clause is meaningless in lambda calculus
EmptyS,
/// Only `(...)` may be converted to typed lambdas. `[...]` and `{...}` left in the code are
/// signs of incomplete macro execution
/// Only `(...)` may be converted to typed lambdas. `[...]` and `{...}`
/// left in the code are signs of incomplete macro execution
BadGroup(char),
/// `foo:bar:baz` will be parsed as `(foo:bar):baz`. Explicitly specifying `foo:(bar:baz)`
/// is forbidden and it's also meaningless since `baz` can only ever be the kind of types
/// `foo:bar:baz` will be parsed as `(foo:bar):baz`. Explicitly
/// specifying `foo:(bar:baz)` is forbidden and it's also meaningless
/// since `baz` can only ever be the kind of types
ExplicitKindOfType,
/// Name never bound in an enclosing scope - indicates incomplete macro substitution
Unbound(String),
/// Namespaced names can never occur in the code, these are signs of incomplete macro execution
Symbol,
/// Placeholders shouldn't even occur in the code during macro execution. Something is clearly
/// terribly wrong
/// Name never bound in an enclosing scope - indicates incomplete
/// macro substitution
Unbound(Vec<String>),
/// Placeholders shouldn't even occur in the code during macro execution.
/// Something is clearly terribly wrong
Placeholder,
/// It's possible to try and transform the clause `(foo:bar)` into a typed clause,
/// however the correct value of this ast clause is a typed expression (included in the error)
/// It's possible to try and transform the clause `(foo:bar)` into a
/// typed clause, however the correct value of this ast clause is a
/// typed expression (included in the error)
///
/// [expr] handles this case, so it's only really possible to get this
/// error if you're calling [clause] directly
ExprToClause(postmacro::Expr),
/// @ tokens only ever occur between a function and a parameter
NonInfixAt
NonInfixAt,
/// Arguments can be either [ast::Clause::Name] or [ast::Clause::Placeh]
InvalidArg
}
impl Display for Error {
@@ -37,44 +42,64 @@ impl Display for Error {
Error::EmptyS => write!(f, "`()` as a clause is meaningless in lambda calculus"),
Error::BadGroup(c) => write!(f, "Only `(...)` may be converted to typed lambdas. `[...]` and `{{...}}` left in the code are signs of incomplete macro execution"),
Error::ExplicitKindOfType => write!(f, "`foo:bar:baz` will be parsed as `(foo:bar):baz`. Explicitly specifying `foo:(bar:baz)` is forbidden and meaningless since `baz` can only ever be the kind of types"),
Error::Unbound(name) => write!(f, "Name \"{name}\" never bound in an enclosing scope. This indicates incomplete macro substitution"),
Error::Symbol => write!(f, "Namespaced names not matching any macros found in the code."),
Error::Unbound(name) => {
write!(f, "Name \"");
for el in itertools::intersperse(
name.iter().map(String::as_str),
"::"
) { write!(f, "{}", el)? }
write!(f, "\" never bound in an enclosing scope. This indicates incomplete macro substitution")
}
Error::Placeholder => write!(f, "Placeholders shouldn't even occur in the code during macro execution, this is likely a compiler bug"),
Error::ExprToClause(expr) => write!(f, "Attempted to transform the clause (foo:bar) into a typed clause. This is likely a compiler bug"),
Error::NonInfixAt => write!(f, "@ as a token can only ever occur between a generic and a type parameter.")
Error::NonInfixAt => write!(f, "@ as a token can only ever occur between a generic and a type parameter."),
Error::InvalidArg => write!(f, "Arguments can be either Name or Placeholder nodes")
}
}
}
#[derive(Clone, Copy)]
struct Init<'a>(&'a RodeoResolver);
/// Try to convert an expression from AST format to typed lambda
pub fn expr(expr: &ast::Expr) -> Result<postmacro::Expr, Error> {
expr_rec(expr, Context::default())
pub fn expr(expr: &ast::Expr, i: Init) -> Result<postmacro::Expr, Error> {
expr_rec(expr, Context::new(i))
}
/// Try and convert a single clause from AST format to typed lambda
pub fn clause(clause: &ast::Clause) -> Result<postmacro::Clause, Error> {
clause_rec(clause, Context::default())
pub fn clause(
clause: &ast::Clause, i: Init
) -> Result<postmacro::Clause, Error> {
clause_rec(clause, Context::new(i))
}
/// Try and convert a sequence of expressions from AST format to typed lambda
pub fn exprv(exprv: &[ast::Expr]) -> Result<postmacro::Expr, Error> {
exprv_rec(exprv, Context::default())
/// Try and convert a sequence of expressions from AST format to
/// typed lambda
pub fn exprv(
exprv: &[ast::Expr], i: Init
) -> Result<postmacro::Expr, Error> {
exprv_rec(exprv, Context::new(i))
}
#[derive(Clone, Copy)]
struct Context<'a> {
names: Stackframe<'a, (&'a str, bool)>
names: Stackframe<'a, (&'a [Spur], bool)>,
rr: &'a RodeoResolver
}
impl<'a> Context<'a> {
fn w_name<'b>(&'b self, name: &'b str, is_auto: bool) -> Context<'b> where 'a: 'b {
Context { names: self.names.push((name, is_auto)) }
fn w_name<'b>(&'b self,
name: &'b [Spur],
is_auto: bool
) -> Context<'b> where 'a: 'b {
Context {
names: self.names.push((name, is_auto)),
rr: self.rr
}
}
}
impl Default for Context<'static> {
fn default() -> Self {
Self { names: Stackframe::new(("", false)) }
fn new(i: Init) -> Context<'static> {
Context { names: Stackframe::new((&[], false)), rr: i.0 }
}
}
@@ -138,8 +163,12 @@ fn clause_rec<'a>(
if t.len() > 0 {return Err(Error::ExplicitKindOfType)}
else {Rc::new(vec![c])}
};
let body_ctx = if let Some(name) = no {
ctx.w_name(&&**name, true)
let body_ctx = if let Some(rc) = no {
match rc.as_ref() {
ast::Clause::Name(name) => ctx.w_name(&&**name, true),
ast::Clause::Placeh { .. } => return Err(Error::Placeholder),
_ => return Err(Error::InvalidArg)
}
} else {ctx};
let body = exprv_rec(b.as_ref(), body_ctx)?;
Ok(postmacro::Clause::Auto(typ, Rc::new(body)))
@@ -150,14 +179,22 @@ fn clause_rec<'a>(
if t.len() > 0 {return Err(Error::ExplicitKindOfType)}
else {Rc::new(vec![c])}
};
let body_ctx = ctx.w_name(&&**n, false);
let body_ctx = match n.as_ref() {
ast::Clause::Name(name) => ctx.w_name(&&**name, true),
ast::Clause::Placeh { .. } => return Err(Error::Placeholder),
_ => return Err(Error::InvalidArg)
};
let body = exprv_rec(b.as_ref(), body_ctx)?;
Ok(postmacro::Clause::Lambda(typ, Rc::new(body)))
}
ast::Clause::Name { local: Some(arg), .. } => {
let (level, (_, is_auto)) = ctx.names.iter().enumerate().find(|(_, (n, _))| n == arg)
.ok_or_else(|| Error::Unbound(arg.clone()))?;
let label = if *is_auto {postmacro::Clause::AutoArg} else {postmacro::Clause::LambdaArg};
ast::Clause::Name(name) => {
let (level, (_, is_auto)) = ctx.names.iter().enumerate()
.find(|(_, (n, _))| n == &name.as_slice())
.ok_or_else(|| Error::Unbound(
name.iter().map(|s| ctx.rr.resolve(s).to_string()).collect()
))?;
let label = if *is_auto {postmacro::Clause::AutoArg}
else {postmacro::Clause::LambdaArg};
Ok(label(level))
}
ast::Clause::S(paren, entries) => {
@@ -166,7 +203,6 @@ fn clause_rec<'a>(
if typ.len() == 0 {Ok(val)}
else {Err(Error::ExprToClause(postmacro::Expr(val, typ)))}
},
ast::Clause::Name { local: None, .. } => Err(Error::Symbol),
ast::Clause::Placeh { .. } => Err(Error::Placeholder),
ast::Clause::Explicit(..) => Err(Error::NonInfixAt)
}

View File

@@ -1,10 +0,0 @@
use std::sync::atomic::AtomicU64;
use lazy_static::lazy_static;
lazy_static! {
static ref NEXT_NAME: AtomicU64 = AtomicU64::new(0);
}
pub fn get_name() -> u64 {
NEXT_NAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}

View File

@@ -2,11 +2,11 @@ pub mod ast;
// pub mod typed;
pub mod literal;
pub mod ast_to_postmacro;
pub mod get_name;
pub(crate) mod interpreted;
mod postmacro;
mod primitive;
mod path_set;
pub mod sourcefile;
pub use path_set::PathSet;
pub use primitive::Primitive;
pub mod postmacro_to_interpreted;

View File

@@ -1,8 +1,6 @@
use crate::utils::string_from_charset;
use super::primitive::Primitive;
use super::ast_to_postmacro;
use super::ast;
use std::fmt::{Debug, Write};
use std::rc::Rc;
@@ -103,18 +101,4 @@ impl Debug for Clause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.deep_fmt(f, 0, Wrap(false, false))
}
}
impl TryFrom<&ast::Expr> for Expr {
type Error = ast_to_postmacro::Error;
fn try_from(value: &ast::Expr) -> Result<Self, Self::Error> {
ast_to_postmacro::expr(value)
}
}
impl TryFrom<&ast::Clause> for Clause {
type Error = ast_to_postmacro::Error;
fn try_from(value: &ast::Clause) -> Result<Self, Self::Error> {
ast_to_postmacro::clause(value)
}
}

View File

@@ -0,0 +1,76 @@
use std::rc::Rc;
use std::collections::HashSet;
use lasso::Spur;
use crate::box_chain;
use crate::utils::{Stackframe, iter::box_empty};
use crate::ast::{Rule, Expr};
#[derive(Debug, Clone)]
pub struct Import {
pub path: Rc<Vec<Spur>>,
/// If name is None, this is a wildcard import
pub name: Option<Spur>
}
/// Anything we might encounter in a file
#[derive(Clone)]
pub enum FileEntry {
Import(Vec<Import>),
Comment(String),
/// The bool indicates whether the rule is exported, that is,
/// whether tokens uniquely defined inside it should be exported
Rule(Rule, bool),
Export(Vec<Rc<Vec<Spur>>>),
LazyModule(Spur)
}
/// Collect all names that occur in an expression
fn find_all_names_expr(
expr: &Expr
) -> HashSet<Rc<Vec<Spur>>> {
let mut ret = HashSet::new();
expr.visit_names(
Stackframe::new(Rc::default()),
&mut |n| { ret.insert(n); }
);
ret
}
/// Collect all exported names (and a lot of other words) from a file
pub fn exported_names(
src: &[FileEntry]
) -> HashSet<Rc<Vec<Spur>>> {
src.iter().flat_map(|ent| match ent {
FileEntry::Rule(Rule{source, target, ..}, true) =>
box_chain!(source.iter(), target.iter()),
_ => box_empty()
}).flat_map(|e| find_all_names_expr(e))
.chain(
src.iter().filter_map(|ent| {
if let FileEntry::Export(names) = ent {
Some(names.iter())
} else {None}
}).flatten().cloned()
).chain(
src.iter().filter_map(|ent| {
if let FileEntry::LazyModule(lm) = ent {
Some(Rc::new(vec![*lm]))
} else {None}
})
).collect()
}
/// Summarize all imports from a file in a single list of qualified names
pub fn imports<'a, 'b, I>(
src: I
) -> impl Iterator<Item = &'b Import> + 'a
where I: Iterator<Item = &'b FileEntry> + 'a {
src.filter_map(|ent| match ent {
FileEntry::Import(impv) => Some(impv.iter()),
_ => None
}).flatten()
}

View File

@@ -1,10 +1,11 @@
use std::iter;
use std::rc::Rc;
use hashbrown::HashMap;
use mappable_rc::Mrc;
use crate::unwrap_or;
use crate::utils::{to_mrc_slice, one_mrc_slice, mrc_empty_slice, replace_first};
use crate::utils::{to_mrc_slice, one_mrc_slice, mrc_empty_slice};
use crate::utils::iter::{box_once, into_boxed_iter};
use crate::ast::{Expr, Clause};
use super::slice_matcher::SliceMatcherDnC;
@@ -14,7 +15,7 @@ use super::update_first_seq_rec;
fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
-> Result<(), String> {
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| -> Result<(), String> {
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| {
match clause {
Clause::Placeh{key, vec} => {
if let Some(known) = is_vec.get(key) {
@@ -23,16 +24,24 @@ fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
is_vec.insert(key.clone(), vec.is_some());
}
}
Clause::Auto(name, typ, body) => {
if let Some(key) = name.as_ref().and_then(|key| key.strip_prefix('$')) {
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
Clause::Auto(name_opt, typ, body) => {
if let Some(name) = name_opt.as_ref() {
if let Clause::Placeh { key, vec } = name.as_ref() {
if vec.is_some() || is_vec.get(key) == Some(&true) {
return Err(key.to_string())
}
is_vec.insert(key.to_owned(), false);
}
}
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
}
Clause::Lambda(name, typ, body) => {
if let Some(key) = name.strip_prefix('$') {
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
if let Clause::Placeh { key, vec } = name.as_ref() {
if vec.is_some() || is_vec.get(key) == Some(&true) {
return Err(key.to_string())
}
is_vec.insert(key.to_owned(), false);
}
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
@@ -52,33 +61,56 @@ fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
Ok(())
}
fn slice_to_vec(src: &mut Mrc<[Expr]>, tgt: &mut Mrc<[Expr]>) {
let prefix_expr = Expr(Clause::Placeh{key: "::prefix".to_string(), vec: Some((0, false))}, to_mrc_slice(vec![]));
let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, to_mrc_slice(vec![]));
/// Ensure that src starts and ends with a vectorial placeholder without
/// modifying the meaning of the substitution rule
fn slice_to_vec(src: &mut Rc<Vec<Expr>>, tgt: &mut Rc<Vec<Expr>>) {
let prefix_expr = Expr(Clause::Placeh{
key: "::prefix".to_string(),
vec: Some((0, false))
}, Rc::default());
let postfix_expr = Expr(Clause::Placeh{
key: "::postfix".to_string(),
vec: Some((0, false))
}, Rc::default());
// Prefix or postfix to match the full vector
let head_multi = matches!(src.first().expect("Src can never be empty!").0, Clause::Placeh{vec: Some(_), ..});
let tail_multi = matches!(src.last().expect("Impossible branch!").0, Clause::Placeh{vec: Some(_), ..});
let head_multi = matches!(
src.first().expect("Src can never be empty!").0,
Clause::Placeh{vec: Some(_), ..}
);
let tail_multi = matches!(
src.last().expect("Impossible branch!").0,
Clause::Placeh{vec: Some(_), ..}
);
let prefix_vec = if head_multi {vec![]} else {vec![prefix_expr]};
let postfix_vec = if tail_multi {vec![]} else {vec![postfix_expr]};
*src = to_mrc_slice(prefix_vec.iter().chain(src.iter()).chain(postfix_vec.iter()).cloned().collect());
*tgt = to_mrc_slice(prefix_vec.iter().chain(tgt.iter()).chain(postfix_vec.iter()).cloned().collect());
*src = Rc::new(
prefix_vec.iter()
.chain(src.iter())
.chain(postfix_vec.iter())
.cloned().collect()
);
*tgt = Rc::new(
prefix_vec.iter()
.chain(tgt.iter())
.chain(postfix_vec.iter())
.cloned().collect()
);
}
/// keep re-probing the input with pred until it stops matching
fn update_all_seqs<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
fn update_all_seqs<F>(input: Rc<Vec<Expr>>, pred: &mut F)
-> Option<Rc<Vec<Expr>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
let mut tmp = update_first_seq_rec::exprv(input, pred);
while let Some(xv) = tmp {
tmp = update_first_seq_rec::exprv(Mrc::clone(&xv), pred);
tmp = update_first_seq_rec::exprv(xv.clone(), pred);
if tmp.is_none() {return Some(xv)}
}
None
}
// fn write_clause_rec(state: &State, clause: &Clause) ->
fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box<dyn Iterator<Item = Expr>> {
fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr)
-> Box<dyn Iterator<Item = Expr>> {
let out_typ = tpl_typ.iter()
.flat_map(|c| write_expr_rec(state, &c.clone().into_expr()))
.map(Expr::into_clause)
@@ -86,6 +118,11 @@ fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box<dyn It
match tpl_clause {
Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto(
name_opt.as_ref().and_then(|name| {
if let Clause::Placeh { key, .. } = name {
match &state[key] {
Entry::NameOpt(name) => name.as_ref().map(|s| s.as_ref().to_owned())
}
}
if let Some(state_key) = name.strip_prefix('$') {
match &state[state_key] {
Entry::NameOpt(name) => name.as_ref().map(|s| s.as_ref().to_owned()),

View File

@@ -11,14 +11,17 @@ use super::State;
use super::split_at_max_vec::split_at_max_vec;
/// Tuple with custom cloning logic
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct CacheEntry<'a>(Mrc<[Expr]>, &'a SliceMatcherDnC);
impl<'a> Clone for CacheEntry<'a> {
fn clone(&self) -> Self {
let CacheEntry(mrc, matcher) = self;
CacheEntry(Mrc::clone(mrc), matcher)
}
}
// #[derive(Debug, Eq, PartialEq, Hash)]
// pub struct CacheEntry<'a>(Mrc<[Expr]>, &'a SliceMatcherDnC);
// impl<'a> Clone for CacheEntry<'a> {
// fn clone(&self) -> Self {
// let CacheEntry(mrc, matcher) = self;
// CacheEntry(Mrc::clone(mrc), matcher)
// }
// }
// ^^^^
// This has been removed because the slice-based version needs no custom
// cloning logic. In the next iteration, remove the this altogether.
/// Matcher that applies a pattern to a slice via divide-and-conquer
@@ -66,8 +69,8 @@ impl SliceMatcherDnC {
matches!(self.clause.as_ref(), Clause::Placeh{vec: Some(..), ..})
}
/// If clause is a name, the qualified name this can match
pub fn clause_qual_name(&self) -> Option<Mrc<[String]>> {
if let Clause::Name { qualified, .. } = self.clause.as_ref() {Some(Mrc::clone(qualified))} else {None}
pub fn clause_qual_name(&self) -> Option<Rc<Vec<Spur>>> {
if let Clause::Name(name) = self.clause.as_ref() {Some(name.clone())} else {None}
}
/// If clause is a Placeh, the key in the state the match will be stored at
pub fn state_key(&self) -> Option<&String> {
@@ -89,8 +92,8 @@ impl SliceMatcherDnC {
/// Enumerate all valid subdivisions based on the reported size constraints of self and
/// the two subranges
pub fn valid_subdivisions(&self,
range: Mrc<[Expr]>
pub fn valid_subdivisions<'a>(&'a self,
range: &'a [Expr]
) -> impl Iterator<Item = (Mrc<[Expr]>, Mrc<[Expr]>, Mrc<[Expr]>)> {
let own_max = unwrap_or!(self.own_max_size(range.len()); return box_empty());
let own_min = self.own_min_size();
@@ -196,7 +199,7 @@ impl SliceMatcherDnC {
if !range.is_empty() {None}
else {Some(State::new())}
},
Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned())
Some(m) => cache.find(&CacheEntry(range, m))
}
}

View File

@@ -1,15 +1,16 @@
use std::{ops::{Add, Index}, rc::Rc, fmt::Debug};
use hashbrown::HashMap;
use lasso::Spur;
use crate::ast::Expr;
#[derive(Debug, PartialEq, Eq)]
#[derive(PartialEq, Eq)]
pub enum Entry {
Vec(Rc<Vec<Expr>>),
Scalar(Rc<Expr>),
Name(Rc<String>),
NameOpt(Option<Rc<String>>)
Name(Rc<Vec<Spur>>),
NameOpt(Option<Rc<Vec<Spur>>>)
}
/// A bucket of indexed expression fragments. Addition may fail if there's a conflict.
@@ -55,33 +56,32 @@ impl State {
}
Some(self)
}
pub fn insert_name<S1, S2>(mut self, k: &S1, v: &S2) -> Option<Self>
pub fn insert_name<S1>(mut self, k: &S1, v: &[Spur]) -> Option<Self>
where
S1: AsRef<str> + ToString + ?Sized,
S2: AsRef<str> + ToString + ?Sized
S1: AsRef<str> + ToString + ?Sized
{
if let Some(old) = self.0.get(k.as_ref()) {
if let Entry::Name(val) = old {
if val.as_str() != v.as_ref() {return None}
if val.as_ref() != v.as_ref() {return None}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::Name(Rc::new(v.to_string())));
self.0.insert(k.to_string(), Entry::Name(Rc::new(v.to_vec())));
}
Some(self)
}
pub fn insert_name_opt<S1, S2>(mut self, k: &S1, v: Option<&S2>) -> Option<Self>
where
S1: AsRef<str> + ToString + ?Sized,
S2: AsRef<str> + ToString + ?Sized
pub fn insert_name_opt<S1, S2>(mut self, k: &S1, v: Option<&[Spur]>)
-> Option<Self>
where S1: AsRef<str> + ToString + ?Sized
{
if let Some(old) = self.0.get(k.as_ref()) {
if let Entry::NameOpt(val) = old {
if val.as_ref().map(|s| s.as_ref().as_str()) != v.map(|s| s.as_ref()) {
if val.as_ref().map(|s| s.as_ref().as_slice()) != v {
return None
}
} else {return None}
} else {
self.0.insert(k.to_string(), Entry::NameOpt(v.map(|s| Rc::new(s.to_string()))));
let data = v.map(|s| Rc::new(s.to_vec()));
self.0.insert(k.to_string(), Entry::NameOpt(data));
}
Some(self)
}
@@ -138,10 +138,4 @@ impl IntoIterator for State {
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Debug for State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}

View File

@@ -1,53 +1,54 @@
use mappable_rc::Mrc;
use std::rc::Rc;
use crate::{ast::{Expr, Clause}, utils::{replace_first, to_mrc_slice}};
use crate::utils::replace_first;
use crate::ast::{Expr, Clause};
/// Traverse the tree, calling pred on every sibling list until it returns some vec
/// then replace the sibling list with that vec and return true
/// Traverse the tree, calling pred on every sibling list until it returns
/// some vec then replace the sibling list with that vec and return true
/// return false if pred never returned some
pub fn exprv<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
if let o@Some(_) = pred(Mrc::clone(&input)) {return o}
pub fn exprv<F>(input: Rc<Vec<Expr>>, pred: &mut F) -> Option<Rc<Vec<Expr>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
if let o@Some(_) = pred(input.clone()) {return o}
replace_first(input.as_ref(), |ex| expr(ex, pred))
.map(|i| to_mrc_slice(i.collect()))
.map(|i| Rc::new(i.collect()))
}
pub fn expr<F>(Expr(cls, typ): &Expr, pred: &mut F) -> Option<Expr>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
if let Some(t) = clausev(Mrc::clone(typ), pred) {return Some(Expr(cls.clone(), t))}
if let Some(c) = clause(cls, pred) {return Some(Expr(c, Mrc::clone(typ)))}
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
if let Some(t) = clausev(typ.clone(), pred) {return Some(Expr(cls.clone(), t))}
if let Some(c) = clause(cls, pred) {return Some(Expr(c, typ.clone()))}
None
}
pub fn clausev<F>(input: Mrc<[Clause]>, pred: &mut F) -> Option<Mrc<[Clause]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
pub fn clausev<F>(input: Rc<Vec<Clause>>, pred: &mut F) -> Option<Rc<Vec<Clause>>>
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
replace_first(input.as_ref(), |c| clause(c, pred))
.map(|i| to_mrc_slice(i.collect()))
.map(|i| Rc::new(i.collect()))
}
pub fn clause<F>(c: &Clause, pred: &mut F) -> Option<Clause>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
where F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>> {
match c {
Clause::P(_) | Clause::Placeh {..} | Clause::Name {..} => None,
Clause::Lambda(n, typ, body) => {
if let Some(b) = exprv(Mrc::clone(body), pred) {
return Some(Clause::Lambda(n.clone(), Mrc::clone(typ), b))
if let Some(b) = exprv(body.clone(), pred) {
return Some(Clause::Lambda(n.clone(), typ.clone(), b))
}
if let Some(t) = exprv(Mrc::clone(typ), pred) {
return Some(Clause::Lambda(n.clone(), t, Mrc::clone(body)))
if let Some(t) = exprv(typ.clone(), pred) {
return Some(Clause::Lambda(n.clone(), t, body.clone()))
}
None
}
Clause::Auto(n, typ, body) => {
if let Some(b) = exprv(Mrc::clone(body), pred) {
return Some(Clause::Auto(n.clone(), Mrc::clone(typ), b))
if let Some(b) = exprv(body.clone(), pred) {
return Some(Clause::Auto(n.clone(), typ.clone(), b))
}
if let Some(t) = exprv(Mrc::clone(typ), pred) {
return Some(Clause::Auto(n.clone(), t, Mrc::clone(body)))
if let Some(t) = exprv(typ.clone(), pred) {
return Some(Clause::Auto(n.clone(), t, body.clone()))
}
None
}
Clause::S(c, body) => Some(Clause::S(*c, exprv(Mrc::clone(body), pred)?)),
Clause::Explicit(t) => Some(Clause::Explicit(Mrc::new(expr(t, pred)?)))
Clause::S(c, body) => Some(Clause::S(*c, exprv(body.clone(), pred)?)),
Clause::Explicit(t) => Some(Clause::Explicit(Rc::new(expr(t, pred)?)))
}
}

View File

@@ -5,6 +5,8 @@ use std::hash::Hash;
use crate::unwrap_or;
use crate::utils::BoxedIter;
// TODO: move to own crate
/// Two-stage breadth-first search;
/// Instead of enumerating neighbors before returning a node, it puts visited but not yet
/// enumerated nodes in a separate queue and only enumerates them to refill the queue of children

View File

@@ -1,96 +1,47 @@
use std::{hash::Hash, cell::RefCell, rc::Rc};
use hashbrown::HashMap;
use mappable_rc::Mrc;
/// Convenience trait for overriding Mrc's strange cloning logic
pub trait MyClone {
fn my_clone(&self) -> Self;
}
impl<T> MyClone for T where T: Clone {
default fn my_clone(&self) -> Self { self.clone() }
}
impl<T: ?Sized> MyClone for Rc<T> {
fn my_clone(&self) -> Self { Rc::clone(self) }
}
impl<T: ?Sized> MyClone for Mrc<T> {
fn my_clone(&self) -> Self { Mrc::clone(self) }
}
// TODO: make this a crate
/// Cache the return values of an effectless closure in a hashmap
/// Inspired by the closure_cacher crate.
pub struct Cache<'a, I, O: 'static> {
store: RefCell<HashMap<I, Mrc<O>>>,
closure: Box<dyn Fn (I, &Self) -> Mrc<O> + 'a>
store: RefCell<HashMap<I, O>>,
closure: Box<dyn Fn (I, &Self) -> O + 'a>
}
impl<'a, I, O> Cache<'a, I, O> where
I: Eq + Hash + MyClone
I: Eq + Hash + Clone, O: Clone
{
pub fn new<F: 'a>(closure: F) -> Self where F: Fn(I, &Self) -> O {
Self::new_raw(move |o, s| Mrc::new(closure(o, s)))
}
/// Take an Mrc<O> closure rather than an O closure
/// Used internally to derive caches from other systems working with Mrc-s
pub fn new_raw<F: 'a>(closure: F) -> Self where F: Fn(I, &Self) -> Mrc<O> {
Self {
store: RefCell::new(HashMap::new()),
closure: Box::new(closure)
}
}
pub fn rc<F: 'a>(closure: F) -> Rc<Self> where F: Fn(I, &Self) -> O {
Rc::new(Self::new(closure))
}
/// Produce and cache a result by cloning I if necessary
pub fn find(&self, i: &I) -> Mrc<O> {
pub fn find(&self, i: &I) -> O {
let closure = &self.closure;
if let Some(v) = self.store.borrow().get(i) {
return Mrc::clone(v)
return v.clone()
}
// In the moment of invocation the refcell is on immutable
// this is important for recursive calculations
let result = closure(i.my_clone(), self);
let result = closure(i.clone(), self);
let mut store = self.store.borrow_mut();
Mrc::clone(store.raw_entry_mut().from_key(i)
.or_insert_with(|| (i.my_clone(), result)).1)
store.raw_entry_mut().from_key(i)
.or_insert_with(|| (i.clone(), result)).1.clone()
}
#[allow(dead_code)]
/// Return the result if it has already been computed
pub fn known(&self, i: &I) -> Option<Mrc<O>> {
pub fn known(&self, i: &I) -> Option<O> {
let store = self.store.borrow();
store.get(i).map(Mrc::clone)
}
#[allow(dead_code)]
/// Forget the output for the given input
pub fn drop(&self, i: &I) -> bool {
self.store.borrow_mut().remove(i).is_some()
store.get(i).cloned()
}
}
impl<'a, I, O, E> Cache<'a, I, Result<O, E>> where
I: Eq + Hash + MyClone,
// O: Clone,
E: Clone
{
/// Sink the ref from a Result into the Ok value, such that cloning only occurs on the sad path
/// but the return value can be short-circuited
pub fn try_find(&self, i: &I) -> Result<Mrc<O>, E> {
let ent = self.find(i);
Mrc::try_map(ent, |t| t.as_ref().ok())
.map_err(|res| Result::as_ref(&res).err().unwrap().to_owned())
}
}
impl<'a, I, O> Cache<'a, I, Option<O>> where
I: Eq + Hash + MyClone,
// O: Clone
{
#[allow(dead_code)]
/// Sink the ref from an Option into the Some value such that the return value can be
/// short-circuited
pub fn try_find(&self, i: &I) -> Option<Mrc<O>> where I: Clone {
let ent = self.find(i);
Mrc::try_map(ent, |o| o.as_ref()).ok()
}
}

View File

@@ -0,0 +1,19 @@
use std::fmt::Display;
use lasso::RodeoResolver;
pub trait InternedDisplay {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result;
}
impl<T> InternedDisplay for T where T: Display {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result {
<Self as Display>::fmt(&self, f)
}
}

View File

@@ -1,27 +0,0 @@
// use std::{collections::HashSet, hash::Hash};
// use hashbrown::HashMap;
// #[derive(Copy, Clone)]
// pub struct Interned<'a, T> {
// interner: &'a Interner<T>,
// data: &'a T,
// }
// impl<'a, T: Eq> Eq for Interned<'a, T> {}
// impl<'a, T: PartialEq> PartialEq for Interned<'a, T> {
// fn eq(&self, other: &Self) -> bool {
// if (self.interner as *const _) == (other.interner as *const _) {
// (self.data as *const _) == (other.data as *const _)
// } else {self.data == other.data}
// }
// }
// pub struct Interner<T> {
// data: HashSet<T>,
// hash_cache: HashMap<>
// }
// impl Interner<T> {
// }

View File

@@ -1,6 +1,6 @@
/// Utility functions to get rid of explicit casts to BoxedIter which are tedious
use std::iter;
use std::{iter, mem};
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
pub type BoxedIterIter<'a, T> = BoxedIter<'a, BoxedIter<'a, T>>;
@@ -30,6 +30,7 @@ where
{
Box::new(i.flatten())
}
pub fn into_boxed_iter<'a, T: 'a>(t: T) -> BoxedIter<'a, <T as IntoIterator>::Item>
where T: IntoIterator {
Box::new(t.into_iter())

View File

@@ -1,27 +0,0 @@
use std::mem;
// use itertools::Itertools;
/// Merge two sorted iterators into a sorted iterator.
pub fn merge_sorted<T, I, J, F, O>(mut i: I, mut j: J, mut f: F) -> impl Iterator<Item = T>
where
I: Iterator<Item = T>, J: Iterator<Item = T>,
F: FnMut(&T) -> O, O: Ord,
{
let mut i_item: Option<T> = None;
let mut j_item: Option<T> = None;
std::iter::from_fn(move || {
match (&mut i_item, &mut j_item) {
(&mut None, &mut None) => None,
(&mut None, j_item @ &mut Some(_)) => Some((j_item, None)),
(i_item @ &mut Some(_), &mut None) => Some((i_item, i.next())),
(Some(i_val), Some(j_val)) => Some(
if f(i_val) < f(j_val) {
(&mut i_item, i.next())
} else {
(&mut j_item, j.next())
}
)
}.and_then(|(dest, value)| mem::replace(dest, value))
})
}

View File

@@ -1,7 +1,8 @@
mod cache;
pub mod translate;
mod replace_first;
mod interner;
mod interned_display;
pub use interned_display::InternedDisplay;
// mod visitor;
pub use replace_first::replace_first;
pub use cache::Cache;
@@ -9,16 +10,13 @@ mod substack;
pub use substack::Stackframe;
mod side;
pub use side::Side;
mod merge_sorted;
pub use merge_sorted::merge_sorted;
mod unwrap_or;
pub mod iter;
pub use iter::BoxedIter;
mod bfs;
mod unless_let;
mod string_from_charset;
pub use string_from_charset::string_from_charset;
mod for_loop;
mod xloop;
mod protomap;
pub use protomap::ProtoMap;
mod product2;

View File

@@ -8,7 +8,9 @@ use super::Side;
pub enum Product2<T> {
Left,
Right,
#[allow(unused)]
Either,
#[allow(unused)]
New(T)
}
impl<T> Product2<T> {

View File

@@ -2,16 +2,18 @@ use std::{iter, ops::{Index, Add}, borrow::Borrow};
use smallvec::SmallVec;
const INLINE_ENTRIES: usize = 2;
// TODO: make this a crate alongside substack
/// Linked-array-list of key-value pairs.
/// Lookup and modification is O(n + cachemiss * n / m)
/// Can be extended by reference in O(m) < O(n)
/// - Lookup and modification is O(n + cachemiss * n / m)
/// - Can be extended by reference in O(m) < O(n)
///
/// The number of elements stored inline in a stackframe is 2 by default, which is enough for most
/// recursive algorithms. The cost of overruns is a heap allocation and subsequent heap indirections,
/// plus wasted stack space which is likely wasted L1 as well. The cost of underruns is wasted stack
/// space.
/// The number of elements stored inline in a stackframe is 2 by default,
/// which is enough for most recursive algorithms.
/// - The cost of overruns is a heap allocation and subsequent
/// heap indirections, plus wasted stack space which is likely wasted L1
/// as well.
/// - The cost of underruns is wasted stack space.
pub struct ProtoMap<'a, K, V, const STACK_COUNT: usize = 2> {
entries: SmallVec<[(K, Option<V>); STACK_COUNT]>,
prototype: Option<&'a ProtoMap<'a, K, V, STACK_COUNT>>

View File

@@ -1,6 +1,9 @@
use std::iter;
pub fn replace_first<'a, T, F>(slice: &'a [T], mut f: F) -> Option<impl Iterator<Item = T> + 'a>
/// Iterate over a sequence with the first element the function returns
/// Some() for updated, but only if there is such an element.
pub fn replace_first<'a, T, F>(slice: &'a [T], mut f: F)
-> Option<impl Iterator<Item = T> + 'a>
where T: Clone, F: FnMut(&T) -> Option<T> {
for i in 0..slice.len() {
if let Some(new) = f(&slice[i]) {

View File

@@ -1,5 +1,7 @@
use std::fmt::Display;
/// A primitive for encoding the two sides Left and Right. While booleans
/// are technically usable for this purpose, they're less descriptive.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Side {Left, Right}
@@ -32,8 +34,11 @@ impl Side {
pub fn crop<'a, T>(&self, margin: usize, slice: &'a [T]) -> &'a [T] {
self.opposite().slice(slice.len() - margin, slice)
}
/// ignore N elements from this end and M elements from the other end of a slice
pub fn crop_both<'a, T>(&self, margin: usize, opposite: usize, slice: &'a [T]) -> &'a [T] {
/// ignore N elements from this end and M elements from the other end
/// of a slice
pub fn crop_both<'a, T>(&self,
margin: usize, opposite: usize, slice: &'a [T]
) -> &'a [T] {
self.crop(margin, self.opposite().crop(opposite, slice))
}
/// Pick this side from a pair of things

View File

@@ -9,6 +9,8 @@ fn string_from_charset_rec(val: u64, digits: &str) -> String {
prefix
}
/// Generate alphabetized names from numbers using a set of permitted
/// characters
pub fn string_from_charset(val: u64, digits: &str) -> String {
string_from_charset_rec(val + 1, digits)
}

View File

@@ -1,8 +1,10 @@
use std::fmt::Debug;
/// Implement a FILO stack that lives on the regular call stack as a linked list.
/// Mainly useful to detect loops in recursive algorithms where the recursion isn't
/// deep enough to warrant a heap-allocated set
// TODO: extract to crate
/// A FILO stack that lives on the regular call stack as a linked list.
/// Mainly useful to detect loops in recursive algorithms where
/// the recursion isn't deep enough to warrant a heap-allocated set.
#[derive(Clone, Copy)]
pub struct Stackframe<'a, T> {
pub item: T,
@@ -33,6 +35,7 @@ impl<'a, T: 'a> Stackframe<'a, T> {
len: self.len + 1
}
}
#[allow(unused)]
pub fn opush(prev: Option<&'a Self>, item: T) -> Self {
Self {
item,
@@ -40,15 +43,19 @@ impl<'a, T: 'a> Stackframe<'a, T> {
len: prev.map_or(1, |s| s.len)
}
}
#[allow(unused)]
pub fn len(&self) -> usize { self.len }
#[allow(unused)]
pub fn pop(&self, count: usize) -> Option<&Self> {
if count == 0 {Some(self)}
else {self.prev.expect("Index out of range").pop(count - 1)}
}
#[allow(unused)]
pub fn opop(cur: Option<&Self>, count: usize) -> Option<&Self> {
if count == 0 {cur}
else {Self::opop(cur.expect("Index out of range").prev, count - 1)}
}
#[allow(unused)]
pub fn o_into_iter(curr: Option<&Self>) -> StackframeIterator<T> {
StackframeIterator { curr }
}
@@ -66,7 +73,9 @@ pub struct StackframeIterator<'a, T> {
}
impl<'a, T> StackframeIterator<'a, T> {
pub fn first_some<U, F: Fn(&T) -> Option<U>>(&mut self, f: F) -> Option<U> {
#[allow(unused)]
pub fn first_some<U, F>(&mut self, f: F) -> Option<U>
where F: Fn(&T) -> Option<U> {
while let Some(x) = self.next() {
if let Some(result) = f(x) {
return Some(result)

View File

@@ -1,5 +1,10 @@
use std::mem;
// TODO: extract to crate
#[allow(unused)]
/// Map over a `&mut` with a mapper function that takes ownership of
/// the value
pub fn translate<T, F: FnOnce(T) -> T>(data: &mut T, f: F) {
unsafe {
let mut acc = mem::MaybeUninit::<T>::uninit().assume_init();
@@ -10,6 +15,8 @@ pub fn translate<T, F: FnOnce(T) -> T>(data: &mut T, f: F) {
}
}
/// Map over a `&mut` with a mapper function that takes ownership of
/// the value and also produces some unrelated data.
pub fn process<T, U, F: FnOnce(T) -> (T, U)>(data: &mut T, f: F) -> U {
unsafe {
let mut acc = mem::MaybeUninit::<T>::uninit().assume_init();

View File

@@ -1,6 +0,0 @@
#[macro_export]
macro_rules! unless_let {
($m:pat_param = $expr:tt) => {
if let $m = $expr {} else
}
}

View File

@@ -1,3 +1,6 @@
/// A macro version of [Option::unwrap_or_else] which supports
/// flow control statements such as `return` and `break` in the "else"
/// branch.
#[macro_export]
macro_rules! unwrap_or {
($m:expr; $fail:expr) => {

View File

@@ -1,18 +0,0 @@
pub trait Visit<T> {
type Return;
fn visit(&self, target: T) -> Return;
}
pub trait ImpureVisit<T> {
type Shard;
type Return;
fn impure_visit(&self, target: T) -> (Shard, Return);
fn merge(&mut self, s: Shard);
}
pub struct OverlayVisitor<VBase, VOver>(VBase, VOver);
impl<VBase, VOver, T, R> Visitor<T> for OverlayVisitor<VBase, VOver>
where VBase: Visitor<T, Return = Option<R>>, VOver: Visitor<T, Return = Option<R>> {
}

View File

@@ -48,7 +48,8 @@
/// to these as well just like the others. In all cases the exit expression is optional, its
/// default value is `()`.
///
/// **todo** find a valid use case for While let for a demo
/// TODO: find a valid use case for While let for a demo
/// TODO: break out into crate
#[macro_export]
macro_rules! xloop {
(for $p:pat in $it:expr; $body:stmt) => {