Transfer commit
This commit is contained in:
4
src/external/bool/mod.rs
vendored
4
src/external/bool/mod.rs
vendored
@@ -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))
|
||||
])
|
||||
|
||||
4
src/external/conv/mod.rs
vendored
4
src/external/conv/mod.rs
vendored
@@ -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))
|
||||
|
||||
4
src/external/cpsio/mod.rs
vendored
4
src/external/cpsio/mod.rs
vendored
@@ -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))
|
||||
])
|
||||
|
||||
4
src/external/num/mod.rs
vendored
4
src/external/num/mod.rs
vendored
@@ -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)),
|
||||
|
||||
54
src/external/num/numeric.rs
vendored
54
src/external/num/numeric.rs
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/external/str/mod.rs
vendored
4
src/external/str/mod.rs
vendored
@@ -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))
|
||||
])
|
||||
}
|
||||
@@ -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 {}
|
||||
|
||||
27
src/main.rs
27
src/main.rs
@@ -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)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/project/loading/extlib_loader.rs
Normal file
34
src/project/loading/extlib_loader.rs
Normal 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)
|
||||
}
|
||||
@@ -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) }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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("::"))}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),+))
|
||||
};
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)]
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
76
src/representations/sourcefile.rs
Normal file
76
src/representations/sourcefile.rs
Normal 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()
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)?)))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
19
src/utils/interned_display.rs
Normal file
19
src/utils/interned_display.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
// }
|
||||
@@ -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())
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -8,7 +8,9 @@ use super::Side;
|
||||
pub enum Product2<T> {
|
||||
Left,
|
||||
Right,
|
||||
#[allow(unused)]
|
||||
Either,
|
||||
#[allow(unused)]
|
||||
New(T)
|
||||
}
|
||||
impl<T> Product2<T> {
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#[macro_export]
|
||||
macro_rules! unless_let {
|
||||
($m:pat_param = $expr:tt) => {
|
||||
if let $m = $expr {} else
|
||||
}
|
||||
}
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
}
|
||||
@@ -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) => {
|
||||
Reference in New Issue
Block a user