From 891d78c1129a4c627912a0e3d902b43ae148d6ce Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Fri, 19 Aug 2022 12:55:02 +0200 Subject: [PATCH] Rule execution now runs, no tests tho --- README.md | 46 ++-- examples/dummy_project/main.orc | 10 +- examples/rule_demo/main.orc | 1 + src/expression.rs | 86 ++++-- src/main.rs | 46 +++- src/parse/expression.rs | 42 +-- src/parse/import.rs | 37 +-- src/parse/lexer.rs | 25 +- src/parse/name.rs | 6 +- src/parse/number.rs | 10 +- src/parse/parse.rs | 19 +- src/parse/sourcefile.rs | 56 ++-- src/parse/string.rs | 4 +- src/project/file_loader.rs | 6 +- src/project/name_resolver.rs | 83 +++--- src/project/prefix.rs | 26 +- src/project/rule_collector.rs | 85 +++--- src/rule/executor/execute.rs | 151 +++++++++++ src/rule/executor/mod.rs | 6 +- src/rule/executor/slice_matcher.rs | 363 ++++++++++++-------------- src/rule/executor/split_at_max_vec.rs | 33 +++ src/rule/executor/state.rs | 97 +++++-- src/rule/mod.rs | 5 +- src/rule/name.rs | 3 - src/rule/repository.rs | 45 ++++ src/rule/rule.rs | 50 ---- src/utils/cache.rs | 47 +++- src/utils/iter.rs | 36 +++ src/utils/mod.rs | 26 +- src/utils/sorted_pairs.rs | 35 --- 30 files changed, 925 insertions(+), 560 deletions(-) create mode 100644 examples/rule_demo/main.orc create mode 100644 src/rule/executor/execute.rs create mode 100644 src/rule/executor/split_at_max_vec.rs delete mode 100644 src/rule/name.rs create mode 100644 src/rule/repository.rs delete mode 100644 src/rule/rule.rs create mode 100644 src/utils/iter.rs delete mode 100644 src/utils/sorted_pairs.rs diff --git a/README.md b/README.md index 29fa170..4bf3819 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ Hello World in Orchid ```orchid import std::io::(println, out) -main == println out "Hello World!" +main := println out "Hello World!" ``` Basic command line calculator ```orchid import std::io::(readln, printf, in, out) -main == ( +main := ( readln in >>= int |> \a. readln in >>= \op. readln in >>= int |> \b. @@ -31,7 +31,7 @@ Grep ```orchid import std::io::(readln, println, in, out, getarg) -main == loop \r. ( +main := loop \r. ( readln in >>= \line. if (substring (getarg 1) line) then (println out ln >>= r) @@ -41,7 +41,7 @@ main == loop \r. ( Filter through an arbitrary collection ```orchid -filter == @C:Type -> Type. @:Map C. @T. \f:T -> Bool. \coll:C T. ( +filter := @C:Type -> Type. @:Map C. @T. \f:T -> Bool. \coll:C T. ( coll >> \el. if (f el) then (Some el) else Nil ):(C T) ``` @@ -122,9 +122,9 @@ types whose defaults have implmentations based on your defaults. For a demonstration, here's a sample implementation of the Option monad. ```orchid --[[ The definition of Monad ]]-- -Bind == \M:Type -> Type. @T -> @U -> (T -> M U) -> M T -> M U -Return == \M:Type -> Type. @T -> T -> M T -Monad == \M:Type -> Type. ( +Bind := \M:Type -> Type. @T -> @U -> (T -> M U) -> M T -> M U +Return := \M:Type -> Type. @T -> T -> M T +Monad := \M:Type -> Type. ( @:Bind M. @:Return M. 0 --[ Note that empty expressions are forbidden so those that exist @@ -134,19 +134,19 @@ Monad == \M:Type -> Type. ( ) --[[ The definition of Option ]]-- -export Option == \T:Type. @U -> U -> (T -> U) -> U +export Option := \T:Type. @U -> U -> (T -> U) -> U --[ Constructors ]-- -export Some == @T. \data:T. ( \default. \map. map data ):(Option T) -export None == @T. ( \default. \map. default ):(Option T) +export Some := @T. \data:T. ( \default. \map. map data ):(Option T) +export None := @T. ( \default. \map. default ):(Option T) --[ Implement Monad ]-- -default returnOption == Some:(Return Option) -default bindOption == ( @T:Type. @U:Type. +default returnOption := Some:(Return Option) +default bindOption := ( @T:Type. @U:Type. \f:T -> U. \opt:Option T. opt None f ):(Bind Option) --[ Sample function that works on unknown monad to demonstrate HKTs. Turns (Option (M T)) into (M (Option T)), "raising" the unknown monad out of the Option ]-- -export raise == @M:Type -> Type. @T:Type. @:Monad M. \opt:Option (M T). ( +export raise := @M:Type -> Type. @T:Type. @:Monad M. \opt:Option (M T). ( opt (return None) (\m. bind m (\x. Some x)) ):(M (Option T)) ``` @@ -162,7 +162,7 @@ Add has three arguments, two are the types of the operands and one is the result: ```orchid -default concatListAdd replacing applicativeAdd == @T. ( +default concatListAdd replacing elementwiseAdd := @T. ( ... ):(Add (List T) (List T) (List T)) ``` @@ -170,7 +170,7 @@ default concatListAdd replacing applicativeAdd == @T. ( For completeness' sake, the original definition might look like this: ```orchid -default elementwiseAdd == @C:Type -> Type. @T. @U. @V. @:(Applicative C). @:(Add T U V). ( +default elementwiseAdd := @C:Type -> Type. @T. @U. @V. @:(Applicative C). @:(Add T U V). ( ... ):(Add (C T) (C U) (C V)) ``` @@ -179,7 +179,7 @@ With the use of autos, here's what the recursive multiplication implementation looks like: ```orchid -default iterativeMultiply == @T. @:(Add T T T). ( +default iterativeMultiply := @T. @:(Add T T T). ( \a:int.\b:T. loop \r. (\i. ifthenelse (ieq i 0) b @@ -191,7 +191,7 @@ default iterativeMultiply == @T. @:(Add T T T). ( This could then be applied to any type that's closed over addition ```orchid -aroundTheWorldLyrics == ( +aroundTheWorldLyrics := ( mult 18 (add (mult 4 "Around the World\n") "\n") ) ``` @@ -218,20 +218,20 @@ are searched back-to-front. If order is still a problem, you can always parenthesize subexpressions at the callsite. ```orchid -(...$pre:(seq 2) if $1 then $2 else $3 ...$post:(seq 1)) =2=> ( - ...$pre +(..$pre:2 if $1 then $2 else $3 ..$post:1) =2=> ( + ..$pre (ifthenelse $1 $2 $3) ...$post ) $a + $b =10=> (add $a $b) -$a == $b =5=> (eq $a $b) +$a = $b =5=> (eq $a $b) $a - $b =10=> (sub $a $b) ``` The recursive addition function now looks like this ```orchid -default iterativeMultiply == @T. @:(Add T T T). ( +default iterativeMultiply := @T. @:(Add T T T). ( \a:int.\b:T. loop \r. (\i. if (i = 0) then b else (b + (r (i - 1))) @@ -249,7 +249,7 @@ environment and can carry structured data payloads. Here's an example of a carriage being used to turn a square-bracketed list expression into a lambda expression that matches a conslist. Notice how the square brackets pair up, as all three variants of brackets -group nodes. +are considered branches in the S-tree rather than individual tokens. ``` -- Initial step, eliminates entry condition (square brackets) and constructs @@ -303,4 +303,4 @@ ideas. - [ ] reactive calculation of values that are deemed to be read more often than written - [ ] automatic profiling based on performance metrics generated by debug - builds \ No newline at end of file + builds diff --git a/examples/dummy_project/main.orc b/examples/dummy_project/main.orc index 05c820e..b3ae120 100644 --- a/examples/dummy_project/main.orc +++ b/examples/dummy_project/main.orc @@ -9,15 +9,15 @@ export ;> $a =200=> (greet $a) reeee := \$a.b -- single-word exported rule -export main == ( +export main := ( print "What is your name?" >> readln >>= \name. greet name ) -export < $a ...$rest /> == (createElement (tok_to_str $a) [(props_carriage ...$rest)]) -export (props_carriage $key = $value) == (tok_to_str $key) => $value +export < $a ...$rest /> := (createElement (tok_to_str $a) [(props_carriage ...$rest)]) +export (props_carriage $key = $value) := (tok_to_str $key) => $value -- The broadest trait definition in existence -Foo == (Bar Baz) --- default anyFoo = @T. @impl:(T (Bar Baz)). impl:(T Foo) \ No newline at end of file +Foo := (Bar Baz) +-- default anyFoo = @T. @impl:(T (Bar Baz)). impl:(T Foo) diff --git a/examples/rule_demo/main.orc b/examples/rule_demo/main.orc new file mode 100644 index 0000000..b26a0ca --- /dev/null +++ b/examples/rule_demo/main.orc @@ -0,0 +1 @@ +expor main := foo bar baz diff --git a/src/expression.rs b/src/expression.rs index bbf2f12..6531d95 100644 --- a/src/expression.rs +++ b/src/expression.rs @@ -1,6 +1,7 @@ +use mappable_rc::Mrc; use itertools::Itertools; use ordered_float::NotNan; -use std::{fmt::Debug}; +use std::fmt::Debug; /// An exact value #[derive(Clone, PartialEq, Eq, Hash)] @@ -23,12 +24,17 @@ impl Debug for Literal { } /// An S-expression with a type -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct Expr(pub Clause, pub Option>); +#[derive(PartialEq, Eq, Hash)] +pub struct Expr(pub Clause, pub Option>); + +impl Clone for Expr { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1.as_ref().map(Mrc::clone)) + } +} impl Debug for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // f.debug_tuple("Expr").field(&self.0).field(&self.1).finish() let Expr(val, typ) = self; write!(f, "{:?}", val)?; if let Some(typ) = typ { write!(f, "{:?}", typ) } @@ -37,38 +43,62 @@ impl Debug for Expr { } /// An S-expression as read from a source file -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash)] pub enum Clause { Literal(Literal), Name{ local: Option, - qualified: Vec + qualified: Mrc<[String]> }, - S(char, Vec), - Lambda(String, Vec, Vec), - Auto(Option, Vec, Vec), + S(char, Mrc<[Expr]>), + Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>), + Auto(Option, Mrc<[Expr]>, Mrc<[Expr]>), /// Second parameter: /// None => matches one token - /// Some(prio) => prio is the sizing priority for the vectorial (higher prio grows first) - Placeh(String, Option), + /// Some((prio, nonzero)) => + /// prio is the sizing priority for the vectorial (higher prio grows first) + /// nonzero is whether the vectorial matches 1..n or 0..n tokens + Placeh{ + key: String, + vec: Option<(usize, bool)> + }, } impl Clause { - pub fn body(&self) -> Option<&Vec> { + pub fn body(&self) -> Option> { match self { Clause::Auto(_, _, body) | Clause::Lambda(_, _, body) | - Clause::S(_, body) => Some(body), + Clause::S(_, body) => Some(Mrc::clone(body)), _ => None } } - pub fn typ(&self) -> Option<&Vec> { + pub fn typ(&self) -> Option> { match self { - Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(typ), + Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(Mrc::clone(typ)), _ => None } } } +impl Clone for Clause { + fn clone(&self) -> Self { + match self { + Clause::S(c, b) => Clause::S(*c, Mrc::clone(b)), + Clause::Auto(n, t, b) => Clause::Auto( + n.clone(), Mrc::clone(t), Mrc::clone(b) + ), + Clause::Name { local: l, qualified: q } => Clause::Name { + local: l.clone(), qualified: Mrc::clone(q) + }, + Clause::Lambda(n, t, b) => Clause::Lambda( + n.clone(), Mrc::clone(t), Mrc::clone(b) + ), + Clause::Placeh{key, vec} => Clause::Placeh{key: key.clone(), vec: *vec}, + Clause::Literal(l) => Clause::Literal(l.clone()) + } + } +} + fn fmt_expr_seq(it: &mut dyn Iterator, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for item in Itertools::intersperse(it.map(Some), None) { match item { Some(expr) => write!(f, "{:?}", expr), @@ -82,7 +112,7 @@ impl Debug for Clause { match self { Self::Literal(arg0) => write!(f, "{:?}", arg0), Self::Name{local, qualified} => - if let Some(local) = local {write!(f, "{}<{}>", qualified.join("::"), local)} + if let Some(local) = local {write!(f, "{}`{}`", qualified.join("::"), local)} else {write!(f, "{}", qualified.join("::"))}, Self::S(del, items) => { f.write_str(&del.to_string())?; @@ -104,23 +134,33 @@ impl Debug for Clause { f.write_str(":")?; fmt_expr_seq(&mut argtyp.iter(), f)?; f.write_str(".")?; fmt_expr_seq(&mut body.iter(), f) }, - // Self::Parameter(name) => write!(f, "`{}", name), - Self::Placeh(name, None) => write!(f, "${}", name), - Self::Placeh(name, Some(prio)) => write!(f, "...${}:{}", name, prio) + 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}") } } } /// A substitution rule as read from the source -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash)] pub struct Rule { - pub source: Vec, + pub source: Mrc<[Expr]>, pub prio: NotNan, - pub target: Vec + pub target: Mrc<[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) } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index d3f0163..3e30493 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,23 @@ +#![feature(specialization)] -use std::env::current_dir; +use std::{env::current_dir, process::exit}; mod parse; mod project; mod utils; mod expression; mod rule; +use expression::{Expr, Clause}; +use mappable_rc::Mrc; use project::{rule_collector, Loaded, file_loader}; +use rule::Repository; +use utils::to_mrc_slice; -fn literal(orig: &[&str]) -> Vec { +fn literal(orig: &[&str]) -> Mrc<[String]> { + to_mrc_slice(vliteral(orig)) +} + +fn vliteral(orig: &[&str]) -> Vec { orig.iter().map(|&s| s.to_owned()).collect() } @@ -21,15 +30,40 @@ export (match_sequence $lhs) >> (match_sequence $rhs) =100=> (bind ($lhs) (\_. $ export (match_sequence $lhs) >>= (match_sequence $rhs) =100=> (bind ($lhs) ($rhs)) "#; + +fn initial_tree() -> Mrc<[Expr]> { + to_mrc_slice(vec![Expr(Clause::Name { + local: None, + qualified: to_mrc_slice(vec!["main".to_string(), "main".to_string()]) + }, None)]) +} + fn main() { let cwd = current_dir().unwrap(); let collect_rules = rule_collector(move |n| { - if n == vec!["prelude"] { Ok(Loaded::Module(PRELUDE.to_string())) } + if n == literal(&["prelude"]) { Ok(Loaded::Module(PRELUDE.to_string())) } else { file_loader(cwd.clone())(n) } - }, literal(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"])); + }, vliteral(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"])); match collect_rules.try_find(&literal(&["main"])) { - Ok(rules) => for rule in rules.iter() { - println!("{rule:?}") + Ok(rules) => { + let mut tree = initial_tree(); + println!("Start processing {tree:?}"); + let repo = Repository::new(rules.as_ref().to_owned()); + println!("Ruleset: {repo:?}"); + let mut i = 0; loop { + if 10 <= i {break} else {i += 1} + match repo.step(Mrc::clone(&tree)) { + Ok(Some(phase)) => { + tree = phase; + println!("Step {i}: {tree:?}") + }, + Ok(None) => exit(0), + Err(e) => { + eprintln!("Rule error: {e:?}"); + exit(0) + } + } + } } Err(err) => println!("{:#?}", err) } diff --git a/src/parse/expression.rs b/src/parse/expression.rs index 1644554..aa08a5a 100644 --- a/src/parse/expression.rs +++ b/src/parse/expression.rs @@ -1,13 +1,16 @@ use chumsky::{self, prelude::*, Parser}; -use crate::{enum_parser, expression::{Clause, Expr, Literal}}; +use mappable_rc::Mrc; +use crate::enum_parser; +use crate::expression::{Clause, Expr, Literal}; +use crate::utils::to_mrc_slice; -use super::{lexer::Lexeme}; +use super::lexer::Lexeme; fn sexpr_parser

( expr: P ) -> impl Parser> + Clone where P: Parser> + Clone { - Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, b)) + Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, to_mrc_slice(b))) } fn lambda_parser

( @@ -28,9 +31,9 @@ where P: Parser> + Clone { .then_ignore(just(Lexeme::name("."))) .then_ignore(enum_parser!(Lexeme::Comment).repeated()) .then(expr.repeated().at_least(1)) - .map(|((name, typ), mut body): ((String, Vec), Vec)| { + .map(|((name, typ), body): ((String, Vec), Vec)| { // for ent in &mut body { ent.bind_parameter(&name) }; - Clause::Lambda(name, typ, body) + Clause::Lambda(name, to_mrc_slice(typ), to_mrc_slice(body)) }) } @@ -51,13 +54,10 @@ where P: Parser> + Clone { .then_ignore(just(Lexeme::name("."))) .then_ignore(enum_parser!(Lexeme::Comment).repeated()) .then(expr.repeated().at_least(1)) - .try_map(|((name, typ), mut body), s| if name == None && typ.is_empty() { + .try_map(|((name, typ), body), s| if name.is_none() && typ.is_empty() { Err(Simple::custom(s, "Auto without name or type has no effect")) } else { - // if let Some(n) = &name { - // for ent in &mut body { ent.bind_parameter(n) } - // } - Ok(Clause::Auto(name, typ, body)) + Ok(Clause::Auto(name, to_mrc_slice(typ), to_mrc_slice(body))) }) } @@ -71,8 +71,8 @@ fn name_parser() -> impl Parser, Error = Simple> + C fn placeholder_parser() -> impl Parser> + Clone { enum_parser!(Lexeme::Name).try_map(|name, span| { - name.strip_prefix("$").map(&str::to_string) - .ok_or(Simple::custom(span, "Not a placeholder")) + name.strip_prefix('$').map(&str::to_string) + .ok_or_else(|| Simple::custom(span, "Not a placeholder")) }) } @@ -83,18 +83,22 @@ pub fn xpr_parser() -> impl Parser> { enum_parser!(Lexeme::Comment).repeated() .ignore_then(choice(( enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal), - placeholder_parser().map(|n| Clause::Placeh(n, None)), - just(Lexeme::name("...")) - .ignore_then(placeholder_parser()) + 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::Int)) .or_not().map(Option::unwrap_or_default) ) - .map(|(name, prio)| Clause::Placeh(name, Some(prio.try_into().unwrap()))), + .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 + qualified: to_mrc_slice(qualified) }), sexpr_parser(expr.clone()), lambda_parser(expr.clone()), @@ -104,6 +108,6 @@ pub fn xpr_parser() -> impl Parser> { just(Lexeme::Type) .ignore_then(expr.clone()).or_not() ) - .map(|(val, typ)| Expr(val, typ.map(Box::new))) + .map(|(val, typ)| Expr(val, typ.map(Mrc::new))) }).labelled("Expression") -} \ No newline at end of file +} diff --git a/src/parse/import.rs b/src/parse/import.rs index 670410d..0b093b2 100644 --- a/src/parse/import.rs +++ b/src/parse/import.rs @@ -1,20 +1,22 @@ -use std::iter; - use chumsky::{Parser, prelude::*}; -use crate::{enum_parser, utils::BoxedIter}; +use itertools::Itertools; +use mappable_rc::Mrc; +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: Vec, + pub path: Mrc<[String]>, pub name: Option } /// initialize a BoxedIter> with a single element. -fn init_table(name: String) -> BoxedIter<'static, BoxedIter<'static, String>> { +fn init_table(name: String) -> BoxedIterIter<'static, String> { // I'm not at all confident that this is a good approach. - Box::new(iter::once(Box::new(iter::once(name)) as BoxedIter)) + box_once(box_once(name)) } /// Parse an import command @@ -24,7 +26,7 @@ fn init_table(name: String) -> BoxedIter<'static, BoxedIter<'static, String>> { /// to go wild. There's a blacklist in [name] pub fn import_parser() -> impl Parser, Error = Simple> { // TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad. - recursive(|expr: Recursive>, Simple>| { + recursive(|expr: Recursive, Simple>| { enum_parser!(Lexeme::Name) .separated_by(just(Lexeme::NS)) .then( @@ -34,7 +36,7 @@ pub fn import_parser() -> impl Parser, Error = Simple>) + .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())) @@ -44,22 +46,23 @@ pub fn import_parser() -> impl Parser, Error = Simple, Option>>)| -> BoxedIter> { + .map(|(name, opt_post): (Vec, Option>)| -> BoxedIterIter { if let Some(post) = opt_post { Box::new(post.map(move |el| { - Box::new(name.clone().into_iter().chain(el)) as BoxedIter - })) as BoxedIter> + box_chain!(name.clone().into_iter(), el) + })) } else { - Box::new(iter::once(Box::new(name.into_iter()) as BoxedIter)) + box_once(into_boxed_iter(name)) } }) }).map(|paths| { paths.filter_map(|namespaces| { - let mut path: Vec = namespaces.collect(); - match path.pop()?.as_str() { - "*" => Some(Import { path, name: None }), - name => Some(Import { path, name: Some(name.to_owned()) }) + 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()) }) } }).collect() }).labelled("import") -} \ No newline at end of file +} diff --git a/src/parse/lexer.rs b/src/parse/lexer.rs index 92450e2..b59577b 100644 --- a/src/parse/lexer.rs +++ b/src/parse/lexer.rs @@ -2,7 +2,7 @@ use std::{ops::Range, iter, fmt}; use ordered_float::NotNan; use chumsky::{Parser, prelude::*}; use std::fmt::Debug; -use crate::utils::BoxedIter; +use crate::{utils::{BoxedIter, iter::{box_once, box_flatten}}, box_chain}; use super::{number, string, name, comment}; @@ -14,9 +14,10 @@ impl Debug for Entry { // f.debug_tuple("Entry").field(&self.0).field(&self.1).finish() } } -impl Into<(Lexeme, Range)> for Entry { - fn into(self) -> (Lexeme, Range) { - (self.0, self.1) + +impl From for (Lexeme, Range) { + fn from(ent: Entry) -> Self { + (ent.0, ent.1) } } @@ -107,13 +108,13 @@ fn paren_parser<'a>( lp: char, rp: char ) -> impl Parser, Error=Simple> + 'a { expr.padded().repeated() - .map(|x| Box::new(x.into_iter().flatten()) as LexSubres) + .map(|x| box_flatten(x.into_iter())) .delimited_by(just(lp), just(rp)).map_with_span(move |b, s| { - Box::new( - iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1)) - .chain(b) - .chain(iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end))) - ) as LexSubres + box_chain!( + iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1)), + b, + iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end)) + ) }) } @@ -127,7 +128,7 @@ where T: AsRef + Clone { paren_parser(recurse.clone(), '[', ']'), paren_parser(recurse.clone(), '{', '}'), choice(( - just("==").padded().to(Lexeme::rule(0f64)), + just(":=").padded().to(Lexeme::rule(0f64)), just("=").ignore_then(number::float_parser()).then_ignore(just("=>")).map(Lexeme::rule), comment::comment_parser().map(Lexeme::Comment), just("::").padded().to(Lexeme::NS), @@ -139,7 +140,7 @@ where T: AsRef + Clone { string::char_parser().map(Lexeme::Char), string::str_parser().map(Lexeme::Str), name::name_parser(&all_ops).map(Lexeme::Name), // includes namespacing - )).map_with_span(|lx, span| Box::new(iter::once(Entry(lx, span))) as LexSubres) + )).map_with_span(|lx, span| box_once(Entry(lx, span)) as LexSubres) )) }).separated_by(one_of("\t ").repeated()) .flatten().collect() diff --git a/src/parse/name.rs b/src/parse/name.rs index 6e33917..755d496 100644 --- a/src/parse/name.rs +++ b/src/parse/name.rs @@ -3,11 +3,11 @@ use chumsky::{self, prelude::*, Parser}; /// Matches any one of the passed operators, longest-first fn op_parser<'a, T: AsRef + Clone>(ops: &[T]) -> BoxedParser<'a, char, String, Simple> { let mut sorted_ops: Vec = ops.iter().map(|t| t.as_ref().to_string()).collect(); - sorted_ops.sort_by(|a, b| b.len().cmp(&a.len())); + sorted_ops.sort_by_key(|op| -(op.len() as i64)); sorted_ops.into_iter() .map(|op| just(op).boxed()) .reduce(|a, b| a.or(b).boxed()) - .unwrap_or(empty().map(|()| panic!("Empty isn't meant to match")).boxed()) + .unwrap_or_else(|| empty().map(|()| panic!("Empty isn't meant to match")).boxed()) .labelled("operator").boxed() } @@ -56,4 +56,4 @@ pub fn is_op>(s: T) -> bool { Some(x) => !x.is_alphanumeric(), None => false } -} \ No newline at end of file +} diff --git a/src/parse/number.rs b/src/parse/number.rs index 331a196..579260c 100644 --- a/src/parse/number.rs +++ b/src/parse/number.rs @@ -14,7 +14,7 @@ fn separated_digits_parser(base: u32) -> impl Parser impl Parser> { /// parse exponent notation, or return 0 as the default exponent. /// The exponent is always in decimal. fn pow_parser() -> impl Parser> { - return choice(( + choice(( just('p') .ignore_then(text::int(10)) .map(|s: String| s.parse().unwrap()), @@ -45,15 +45,15 @@ fn pow_parser() -> impl Parser> { /// /// TODO it panics if it finds a negative exponent fn nat2u(base: u64) -> impl Fn((u64, i32),) -> u64 { - return move |(val, exp)| { + move |(val, exp)| { if exp == 0 {val} else {val * base.checked_pow(exp.try_into().unwrap()).unwrap()} - }; + } } /// returns a mapper that converts a mantissa and an exponent into a float fn nat2f(base: u64) -> impl Fn((NotNan, i32),) -> NotNan { - return move |(val, exp)| { + move |(val, exp)| { if exp == 0 {val} else {val * (base as f64).powf(exp.try_into().unwrap())} } diff --git a/src/parse/parse.rs b/src/parse/parse.rs index 46976d5..3edf305 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -4,7 +4,7 @@ use chumsky::{prelude::{Simple, end}, Stream, Parser}; use itertools::Itertools; use thiserror::Error; -use crate::expression::Rule; +use crate::{expression::Rule, parse::lexer::LexedText}; use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry}; @@ -24,14 +24,17 @@ where S: Into, Iter>> { let lexed = lexer(ops).parse(stream).map_err(ParseError::Lex)?; println!("Lexed:\n{:?}", lexed); + let LexedText(token_batchv) = lexed; let parsr = line_parser().then_ignore(end()); - let (parsed_lines, errors_per_line) = lexed.0.into_iter().filter_map(|v| { + let (parsed_lines, errors_per_line) = token_batchv.into_iter().filter(|v| { + !v.is_empty() + }).map(|v| { // Find the first invalid position for Stream::for_iter let LexerEntry(_, Range{ end, .. }) = v.last().unwrap().clone(); // Stream expects tuples, lexer outputs structs let tuples = v.into_iter().map_into::<(Lexeme, Range)>(); - Some(parsr.parse(Stream::from_iter(end..end+1, tuples))) - // ^^^^^^^^^^ + parsr.parse(Stream::from_iter(end..end+1, tuples)) + // ^^^^^^^^^^ // I haven't the foggiest idea why this is needed, parsers are supposed to be lazy so the // end of input should make little difference }).map(|res| match res { @@ -39,13 +42,13 @@ where Err(e) => (None, e) }).unzip::<_, _, Vec<_>, Vec<_>>(); let total_err = errors_per_line.into_iter() - .map(Vec::into_iter).flatten() + .flat_map(Vec::into_iter) .collect::>(); - if total_err.len() > 0 { Err(ParseError::Ast(total_err)) } + if !total_err.is_empty() { Err(ParseError::Ast(total_err)) } else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) } } -pub fn reparse<'a, Iter, S, Op>(ops: &[Op], stream: S, pre: &Vec) +pub fn reparse<'a, Iter, S, Op>(ops: &[Op], stream: S, pre: &[FileEntry]) -> Result, ParseError> where Op: 'a + AsRef + Clone, @@ -62,4 +65,4 @@ where } output }).collect()) -} \ No newline at end of file +} diff --git a/src/parse/sourcefile.rs b/src/parse/sourcefile.rs index f3fb5c7..c7383a7 100644 --- a/src/parse/sourcefile.rs +++ b/src/parse/sourcefile.rs @@ -1,10 +1,10 @@ use std::collections::HashSet; -use std::iter; -use crate::enum_parser; +use crate::{enum_parser, box_chain}; use crate::expression::{Expr, Clause, Rule}; -use crate::utils::BoxedIter; +use crate::utils::to_mrc_slice; use crate::utils::Stackframe; +use crate::utils::iter::box_empty; use super::expression::xpr_parser; use super::import; @@ -24,12 +24,12 @@ pub enum FileEntry { fn visit_all_names_clause_recur<'a, F>( clause: &'a Clause, binds: Stackframe, - mut cb: &mut F -) where F: FnMut(&'a Vec) { + 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(), &mut cb) + visit_all_names_expr_recur(x, binds.clone(), cb) } let binds_dup = binds.clone(); let new_binds = if let Some(n) = name { @@ -38,25 +38,23 @@ fn visit_all_names_clause_recur<'a, F>( binds }; for x in body.iter() { - visit_all_names_expr_recur(x, new_binds.clone(), &mut cb) + 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(), &mut cb) + 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()), &mut cb) + 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(), &mut cb) + visit_all_names_expr_recur(x, binds.clone(), cb) }, - Clause::Name{ local, qualified } => { - if let Some(name) = local { - if binds.iter().all(|x| x != name) { - cb(qualified) - } + Clause::Name{ local: Some(name), qualified } => { + if binds.iter().all(|x| x != name) { + cb(qualified) } } _ => (), @@ -72,7 +70,7 @@ fn visit_all_names_expr_recur<'a, F>( expr: &'a Expr, binds: Stackframe, cb: &mut F -) where F: FnMut(&'a Vec) { +) where F: FnMut(&'a [String]) { let Expr(val, typ) = expr; visit_all_names_clause_recur(val, binds.clone(), cb); if let Some(t) = typ { @@ -81,10 +79,10 @@ fn visit_all_names_expr_recur<'a, F>( } /// Collect all names that occur in an expression -fn find_all_names(expr: &Expr) -> HashSet<&Vec> { +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("$") { + if !n.last().unwrap().starts_with('$') { ret.insert(n); } }); @@ -111,19 +109,27 @@ pub fn line_parser() -> impl Parser> { println!("{:?} could not yield an export", s); e }) .ignore_then(rule_parser()) - .map(|(source, prio, target)| FileEntry::Rule(Rule{source, prio, target}, true)), + .map(|(source, prio, target)| FileEntry::Rule(Rule { + source: to_mrc_slice(source), + prio, + target: to_mrc_slice(target) + }, true)), // This could match almost anything so it has to go last - rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{source, prio, target}, false)), + rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{ + source: to_mrc_slice(source), + prio, + target: to_mrc_slice(target) + }, false)), )) } /// Collect all exported names (and a lot of other words) from a file -pub fn exported_names(src: &Vec) -> HashSet<&Vec> { +pub fn exported_names(src: &[FileEntry]) -> HashSet<&[String]> { src.iter().flat_map(|ent| match ent { FileEntry::Rule(Rule{source, target, ..}, true) => - Box::new(source.iter().chain(target.iter())) as BoxedIter<&Expr>, - _ => Box::new(iter::empty()) - }).map(find_all_names).flatten().collect() + box_chain!(source.iter(), target.iter()), + _ => box_empty() + }).flat_map(find_all_names).collect() } /// Summarize all imports from a file in a single list of qualified names @@ -135,4 +141,4 @@ where I: Iterator + 'a { FileEntry::Import(impv) => Some(impv.iter()), _ => None }).flatten() -} \ No newline at end of file +} diff --git a/src/parse/string.rs b/src/parse/string.rs index 3b66150..d86367d 100644 --- a/src/parse/string.rs +++ b/src/parse/string.rs @@ -13,7 +13,7 @@ fn text_parser(delim: char) -> impl Parser> { .or(just('r').to('\r')) .or(just('t').to('\t')) .or(just('u').ignore_then( - filter(|c: &char| c.is_digit(16)) + filter(|c: &char| c.is_ascii_hexdigit()) .repeated() .exactly(4) .collect::() @@ -43,4 +43,4 @@ pub fn str_parser() -> impl Parser> { .repeated() ).then_ignore(just('"')) .flatten().collect() -} \ No newline at end of file +} diff --git a/src/project/file_loader.rs b/src/project/file_loader.rs index 81f87b9..b412308 100644 --- a/src/project/file_loader.rs +++ b/src/project/file_loader.rs @@ -3,6 +3,8 @@ use std::rc::Rc; use std::fs::read_to_string; use std::path::PathBuf; +use mappable_rc::Mrc; + use super::loaded::Loaded; #[derive(Clone, Debug)] @@ -18,7 +20,7 @@ impl From for LoadingError { } } -pub fn file_loader(proj: PathBuf) -> impl FnMut(Vec) -> Result + 'static { +pub fn file_loader(proj: PathBuf) -> impl FnMut(Mrc<[String]>) -> Result + 'static { move |path| { let dirpath = proj.join(path.join("/")); if dirpath.is_dir() || dirpath.is_symlink() { @@ -46,4 +48,4 @@ pub fn file_loader(proj: PathBuf) -> impl FnMut(Vec) -> Result>; +type ImportMap = HashMap>; #[derive(Debug, Clone, Error)] pub enum ResolutionError { #[error("Reference cycle at {0:?}")] - Cycle(Vec>), + Cycle(Vec>), #[error("No module provides {0:?}")] - NoModule(Vec), + NoModule(Mrc<[String]>), #[error(transparent)] Delegate(#[from] Err) } +type ResolutionResult = Result, ResolutionError>; + /// 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 { - cache: HashMap, Result, ResolutionError>>, + cache: HashMap, ResolutionResult>, get_modname: FSplit, get_imports: FImps } - impl NameResolver where - FSplit: FnMut(&Vec) -> Option>, - FImps: FnMut(&Vec) -> Result, + FSplit: FnMut(Mrc<[String]>) -> Option>, + FImps: FnMut(Mrc<[String]>) -> Result, E: Clone { pub fn new(get_modname: FSplit, get_imports: FImps) -> Self { @@ -43,58 +45,63 @@ where /// Obtains a symbol's originnal name /// Uses a substack to detect loops - fn find_origin_rec<'a>( + fn find_origin_rec( &mut self, - symbol: &'a Vec, - import_path: Stackframe<'a, &'a Vec> - ) -> Result, ResolutionError> { - if let Some(cached) = self.cache.get(symbol) { return cached.clone() } + symbol: Mrc<[String]>, + import_path: Stackframe> + ) -> Result, ResolutionError> { + if let Some(cached) = self.cache.get(&symbol) { return cached.as_ref().map_err(|e| e.clone()).map(Mrc::clone) } // The imports and path of the referenced file and the local name - let path = (self.get_modname)(symbol).ok_or(ResolutionError::NoModule(symbol.clone()))?; - let (_, name) = symbol.split_at(path.len()); - let imports = (self.get_imports)(&path)?; + 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!("Something's really broken\n{:?}", import_path) + } + let imports = (self.get_imports)(Mrc::clone(&path))?; let result = if let Some(source) = imports.get(&name[0]) { let new_sym: Vec = source.iter().chain(name.iter()).cloned().collect(); - if import_path.iter().any(|el| el == &&new_sym) { - Err(ResolutionError::Cycle(import_path.iter().cloned().cloned().collect())) + if import_path.iter().any(|el| el.as_ref() == new_sym.as_slice()) { + Err(ResolutionError::Cycle(import_path.iter().map(Mrc::clone).collect())) } else { - self.find_origin_rec(&new_sym, import_path.push(symbol)) + self.find_origin_rec(to_mrc_slice(new_sym), import_path.push(Mrc::clone(&symbol))) } } else { Ok(symbol.clone()) // If not imported, it must be locally defined }; - self.cache.insert(symbol.clone(), result.clone()); - return result + self.cache.insert(symbol, result.clone()); + result } fn process_exprv_rec(&mut self, exv: &[Expr]) -> Result, ResolutionError> { exv.iter().map(|ex| self.process_expression_rec(ex)).collect() } - fn process_exprboxopt_rec(&mut self, - exbo: &Option> - ) -> Result>, ResolutionError> { - exbo.iter().map(|exb| Ok(Box::new(self.process_expression_rec(exb.as_ref())?))) + fn process_exprmrcopt_rec(&mut self, + exbo: &Option> + ) -> Result>, ResolutionError> { + exbo.iter().map(|exb| Ok(Mrc::new(self.process_expression_rec(exb.as_ref())?))) .next().transpose() } fn process_clause_rec(&mut self, tok: &Clause) -> Result> { Ok(match tok { - Clause::S(c, exv) => Clause::S(*c, - exv.iter().map(|e| self.process_expression_rec(e)) + Clause::S(c, exv) => Clause::S(*c, to_mrc_slice( + exv.as_ref().iter().map(|e| self.process_expression_rec(e)) .collect::, ResolutionError>>()? - ), + )), Clause::Lambda(name, typ, body) => Clause::Lambda(name.clone(), - self.process_exprv_rec(typ)?, - self.process_exprv_rec(body)? + to_mrc_slice(self.process_exprv_rec(typ.as_ref())?), + to_mrc_slice(self.process_exprv_rec(body.as_ref())?) ), Clause::Auto(name, typ, body) => Clause::Auto(name.clone(), - self.process_exprv_rec(typ)?, - self.process_exprv_rec(body)? + to_mrc_slice(self.process_exprv_rec(typ.as_ref())?), + to_mrc_slice(self.process_exprv_rec(body.as_ref())?) ), Clause::Name{local, qualified} => Clause::Name{ local: local.clone(), - qualified: self.find_origin(qualified)? + qualified: self.find_origin(Mrc::clone(qualified))? }, x => x.clone() }) @@ -103,12 +110,12 @@ where fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result> { Ok(Expr( self.process_clause_rec(token)?, - self.process_exprboxopt_rec(typ)? + self.process_exprmrcopt_rec(typ)? )) } - pub fn find_origin(&mut self, symbol: &Vec) -> Result, ResolutionError> { - self.find_origin_rec(symbol, Stackframe::new(symbol)) + pub fn find_origin(&mut self, symbol: Mrc<[String]>) -> Result, ResolutionError> { + self.find_origin_rec(Mrc::clone(&symbol), Stackframe::new(symbol)) } #[allow(dead_code)] @@ -119,4 +126,4 @@ where pub fn process_expression(&mut self, ex: &Expr) -> Result> { self.process_expression_rec(ex) } -} \ No newline at end of file +} diff --git a/src/project/prefix.rs b/src/project/prefix.rs index 46c2566..6185055 100644 --- a/src/project/prefix.rs +++ b/src/project/prefix.rs @@ -1,4 +1,6 @@ -use crate::expression::{Expr, Clause}; +use mappable_rc::Mrc; + +use crate::{expression::{Expr, Clause}, utils::collect_to_mrc}; /// Replaces the first element of a name with the matching prefix from a prefix map @@ -6,32 +8,34 @@ use crate::expression::{Expr, Clause}; /// Called by [#prefix] which handles Typed. fn prefix_clause( expr: &Clause, - namespace: &Vec + namespace: Mrc<[String]> ) -> Clause { match expr { - Clause::S(c, v) => Clause::S(*c, v.iter().map(|e| prefix_expr(e, namespace)).collect()), + Clause::S(c, v) => Clause::S(*c, + collect_to_mrc(v.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))) + ), Clause::Auto(name, typ, body) => Clause::Auto( name.clone(), - typ.iter().map(|e| prefix_expr(e, namespace)).collect(), - body.iter().map(|e| prefix_expr(e, namespace)).collect(), + 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)))), ), Clause::Lambda(name, typ, body) => Clause::Lambda( name.clone(), - typ.iter().map(|e| prefix_expr(e, namespace)).collect(), - body.iter().map(|e| prefix_expr(e, namespace)).collect(), + 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)))), ), Clause::Name{local, qualified} => Clause::Name{ local: local.clone(), - qualified: namespace.iter().chain(qualified.iter()).cloned().collect() + 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: &Vec) -> Expr { +pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: Mrc<[String]>) -> Expr { Expr( - prefix_clause(clause, namespace), - typ.as_ref().map(|e| Box::new(prefix_expr(e, namespace))) + prefix_clause(clause, Mrc::clone(&namespace)), + typ.as_ref().map(|e| Mrc::new(prefix_expr(e, namespace))) ) } diff --git a/src/project/rule_collector.rs b/src/project/rule_collector.rs index 8124637..1a8902e 100644 --- a/src/project/rule_collector.rs +++ b/src/project/rule_collector.rs @@ -1,10 +1,13 @@ +use std::cell::RefCell; use std::collections::{HashMap, HashSet, VecDeque}; use std::fmt::Debug; use std::rc::Rc; +use mappable_rc::Mrc; + use crate::expression::Rule; use crate::parse::{self, FileEntry}; -use crate::utils::Cache; +use crate::utils::{Cache, mrc_derive, to_mrc_slice}; use super::name_resolver::NameResolver; use super::module_error::ModuleError; @@ -17,27 +20,29 @@ type ParseResult = Result>; pub struct Module { pub rules: Vec, pub exports: Vec, - pub references: Vec> + pub references: Vec> } +pub type RuleCollectionResult = Result, ModuleError>; + pub fn rule_collector( mut load_mod: F, prelude: Vec -// ) -> impl FnMut(Vec) -> Result<&'a Vec, ParseError> + 'a -) -> Cache<'static, Vec, Result, ModuleError>> +) -> Cache<'static, Mrc<[String]>, RuleCollectionResult> where - F: FnMut(Vec) -> Result, + F: FnMut(Mrc<[String]>) -> Result, ELoad: Clone + Debug { + 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: Vec, _| + let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _| -> ParseResult { - load_mod(path).map_err(ModuleError::Load) + (load_mod_rc.borrow_mut())(path).map_err(ModuleError::Load) })); // Map names to the longest prefix that points to a valid module let modname = Rc::new(Cache::new({ let loaded = Rc::clone(&loaded); - move |symbol: Vec, _| -> Result, Vec>> { + move |symbol: Mrc<[String]>, _| -> Result, Vec>> { let mut errv: Vec> = Vec::new(); let reg_err = |e, errv: &mut Vec>| { errv.push(e); @@ -45,11 +50,10 @@ where else { Ok(()) } }; loop { - let (path, _) = symbol.split_at(symbol.len() - errv.len()); - let pathv = path.to_vec(); - match loaded.try_find(&pathv) { + let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len()]); + match loaded.try_find(&path) { Ok(imports) => match imports.as_ref() { - Loaded::Module(_) => break Ok(pathv.clone()), + Loaded::Module(_) => break Ok(path), _ => reg_err(ModuleError::None, &mut errv)? }, Err(err) => reg_err(err, &mut errv)? @@ -61,7 +65,7 @@ where let preparsed = Rc::new(Cache::new({ let loaded = Rc::clone(&loaded); let prelude2 = prelude.clone(); - move |path: Vec, _| -> ParseResult, ELoad> { + move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { let loaded = loaded.try_find(&path)?; if let Loaded::Module(source) = loaded.as_ref() { Ok(parse::parse(&prelude2, source.as_str())?) @@ -72,7 +76,7 @@ where let exports = Rc::new(Cache::new({ let loaded = Rc::clone(&loaded); let preparsed = Rc::clone(&preparsed); - move |path: Vec, _| -> ParseResult, ELoad> { + move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { let loaded = loaded.try_find(&path)?; if let Loaded::Namespace(names) = loaded.as_ref() { return Ok(names.clone()); @@ -88,19 +92,19 @@ where let imports = Rc::new(Cache::new({ let preparsed = Rc::clone(&preparsed); let exports = Rc::clone(&exports); - move |path: Vec, _| -> ParseResult>, ELoad> { - let entv = preparsed.try_find(&path)?.clone(); + move |path: Mrc<[String]>, _| -> ParseResult>, ELoad> { + let entv = preparsed.try_find(&path)?; let import_entries = parse::imports(entv.iter()); - let mut imported_symbols: HashMap> = HashMap::new(); + let mut imported_symbols: HashMap> = HashMap::new(); for imp in import_entries { let export = exports.try_find(&imp.path)?; if let Some(ref name) = imp.name { - if export.contains(&name) { - imported_symbols.insert(name.clone(), imp.path.clone()); + if export.contains(name) { + imported_symbols.insert(name.clone(), Mrc::clone(&imp.path)); } } else { for exp in export.as_ref() { - imported_symbols.insert(exp.clone(), imp.path.clone()); + imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path)); } } } @@ -112,7 +116,7 @@ where let preparsed = Rc::clone(&preparsed); let imports = Rc::clone(&imports); let loaded = Rc::clone(&loaded); - move |path: Vec, _| -> ParseResult, ELoad> { + move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { let imported_ops: Vec = imports.try_find(&path)? .keys() @@ -127,45 +131,44 @@ where } else { Err(ModuleError::None) } } })); - let mut name_resolver = NameResolver::new({ + let mut name_resolver_rc = RefCell::new(NameResolver::new({ let modname = Rc::clone(&modname); move |path| { - Some(modname.try_find(path).ok()?.as_ref().clone()) + Some(modname.try_find(&path).ok()?.as_ref().clone()) } }, { let imports = Rc::clone(&imports); move |path| { - imports.try_find(path).map(|f| f.as_ref().clone()) + imports.try_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 = Rc::clone(&parsed); let exports = Rc::clone(&exports); let imports = Rc::clone(&imports); let modname = Rc::clone(&modname); - move |path: Vec, _| -> ParseResult { + move |path: Mrc<[String]>, _| -> ParseResult { + let mut name_resolver = name_resolver_rc.borrow_mut(); let module = Module { rules: parsed.try_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, &path)).collect(), - target: target.iter().map(|ex| prefix_expr(ex, &path)).collect(), + source: source.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(), + target: target.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(), prio: *prio, }) } else { None } }) .map(|rule| Ok(super::Rule { - source: rule.source.iter() + source: to_mrc_slice(rule.source.iter() .map(|ex| name_resolver.process_expression(ex)) - .collect::, _>>()?, - target: rule.target.iter() + .collect::, _>>()?), + target: to_mrc_slice(rule.target.iter() .map(|ex| name_resolver.process_expression(ex)) - .collect::, _>>()?, - // source: name_resolver.process_expression(&rule.source)?, - // target: name_resolver.process_expression(&rule.target)?, + .collect::, _>>()?), ..rule })) .collect::, ELoad>>()?, @@ -173,19 +176,20 @@ where references: imports.try_find(&path)? .values() .filter_map(|imps| { - modname.try_find(&imps).ok().map(|r| r.as_ref().clone()) + modname.try_find(imps).ok().map(|r| r.as_ref().clone()) }) .collect() }; Ok(module) } })); - let all_rules = Cache::new({ + Cache::new({ let resolved = Rc::clone(&resolved); - move |path: Vec, _| -> ParseResult, ELoad> { - let mut processed: HashSet> = HashSet::new(); + move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { + // Breadth-first search + let mut processed: HashSet> = HashSet::new(); let mut rules: Vec = Vec::new(); - let mut pending: VecDeque> = VecDeque::new(); + let mut pending: VecDeque> = VecDeque::new(); pending.push_back(path); while let Some(el) = pending.pop_front() { let resolved = resolved.try_find(&el)?; @@ -201,6 +205,5 @@ where }; Ok(rules) } - }); - return all_rules; + }) } diff --git a/src/rule/executor/execute.rs b/src/rule/executor/execute.rs new file mode 100644 index 0000000..d82fb48 --- /dev/null +++ b/src/rule/executor/execute.rs @@ -0,0 +1,151 @@ +use hashbrown::HashMap; +use mappable_rc::Mrc; + +use crate::{expression::{Expr, Clause}, utils::{iter::{box_once, into_boxed_iter}, to_mrc_slice}}; +use super::{super::RuleError, state::{State, Entry}, slice_matcher::SliceMatcherDnC}; + +fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap) +-> Result<(), String> { + let verify_clause = |clause: &Clause, is_vec: &mut HashMap| -> Result<(), String> { + match clause { + Clause::Placeh{key, vec} => { + if let Some(known) = is_vec.get(key) { + if known != &vec.is_some() { return Err(key.to_string()) } + } else { + 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()) } + } + 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()) } + } + 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::S(_, body) => { + body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?; + } + _ => () + }; + Ok(()) + }; + let Expr(val, typ_opt) = pattern; + verify_clause(val, is_vec)?; + if let Some(typ) = typ_opt { + verify_scalar_vec(typ, is_vec)?; + } + 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))}, None); + let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, None); + // 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 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()); +} + +/// 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 +fn update_first_seq_rec(input: Mrc<[Expr]>, pred: &mut F) -> Option> +where F: FnMut(Mrc<[Expr]>) -> Option> { + if let o@Some(_) = pred(Mrc::clone(&input)) {o} else { + for Expr(cls, _) in input.iter() { + if let Some(t) = cls.typ() { + if let o@Some(_) = update_first_seq_rec(t, pred) {return o} + } + if let Some(b) = cls.body() { + if let o@Some(_) = update_first_seq_rec(b, pred) {return o} + } + } + None + } +} + +/// keep re-probing the input with pred until it stops matching +fn update_all_seqs(input: Mrc<[Expr]>, pred: &mut F) -> Option> +where F: FnMut(Mrc<[Expr]>) -> Option> { + let mut tmp = update_first_seq_rec(input, pred); + while let Some(xv) = tmp { + tmp = update_first_seq_rec(Mrc::clone(&xv), pred); + if tmp.is_none() {return Some(xv)} + } + None +} + +/// Fill in a template from a state as produced by a pattern +fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> { + tpl.iter().flat_map(|Expr(clause, xpr_typ)| match clause { + Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto( + name_opt.as_ref().and_then(|name| { + let state_key = name.strip_prefix('$') + .expect("Auto template may only reference, never enforce the name"); + match &state[state_key] { + Entry::NameOpt(name) => name.as_ref().map(|s| s.as_ref().to_owned()), + Entry::Name(name) => Some(name.as_ref().to_owned()), + _ => panic!("Auto template name may only be derived from Auto or Lambda name") + } + }), + write_slice(state, typ), + write_slice(state, body) + ), xpr_typ.to_owned())), + Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda( + if let Some(state_key) = name.strip_prefix('$') { + if let Entry::Name(name) = &state[state_key] { + name.as_ref().to_owned() + } else {panic!("Lambda template name may only be derived from Lambda name")} + } else { + name.to_owned() + }, + write_slice(state, typ), + write_slice(state, body) + ), xpr_typ.to_owned())), + Clause::S(c, body) => box_once(Expr(Clause::S( + *c, + write_slice(state, body) + ), xpr_typ.to_owned())), + Clause::Placeh{key, vec: None} => if let Entry::Scalar(x) = &state[key] { + box_once(x.as_ref().to_owned()) + } else {panic!("Scalar template may only be derived from scalar placeholder")}, + Clause::Placeh{key, vec: Some(_)} => if let Entry::Vec(v) = &state[key] { + into_boxed_iter(v.as_ref().to_owned()) + } else {panic!("Vectorial template may only be derived from vectorial placeholder")}, + // Explicit base case so that we get an error if Clause gets new values + c@Clause::Literal(_) | c@Clause::Name { .. } => + box_once(Expr(c.to_owned(), xpr_typ.to_owned())) + }).collect() +} + +/// Apply a rule (a pair of pattern and template) to an expression +pub fn execute(mut src: Mrc<[Expr]>, mut tgt: Mrc<[Expr]>, input: Mrc<[Expr]>) +-> Result>, RuleError> { + // Dimension check + let mut is_vec_db = HashMap::new(); + src.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db)) + .map_err(RuleError::ScalarVecMismatch)?; + tgt.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db)) + .map_err(RuleError::ScalarVecMismatch)?; + // Padding + slice_to_vec(&mut src, &mut tgt); + // Generate matcher + let matcher = SliceMatcherDnC::new(src); + println!("Matcher: {matcher:#?}"); + let matcher_cache = SliceMatcherDnC::get_matcher_cache(); + Ok(update_all_seqs(Mrc::clone(&input), &mut |p| { + let state = matcher.match_range_cached(p, &matcher_cache)?; + Some(write_slice(&state, &tgt)) + })) +} diff --git a/src/rule/executor/mod.rs b/src/rule/executor/mod.rs index 70927a7..551036a 100644 --- a/src/rule/executor/mod.rs +++ b/src/rule/executor/mod.rs @@ -1,4 +1,8 @@ mod slice_matcher; mod state; +mod execute; +mod split_at_max_vec; -use state::State; \ No newline at end of file +use state::State; + +pub use execute::execute; diff --git a/src/rule/executor/slice_matcher.rs b/src/rule/executor/slice_matcher.rs index 2a7accd..8501d48 100644 --- a/src/rule/executor/slice_matcher.rs +++ b/src/rule/executor/slice_matcher.rs @@ -1,28 +1,27 @@ -use hashbrown::HashMap; -use itertools::Itertools; + +use std::fmt::Debug; + +use mappable_rc::Mrc; use crate::expression::{Expr, Clause}; use crate::unwrap_or_continue; -use crate::utils::{Side, Cache}; -use super::super::RuleError; -use super::State; +use crate::utils::iter::box_empty; +use crate::utils::{Side, Cache, mrc_derive, mrc_try_derive, to_mrc_slice}; -fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr])> { - let rngidx = pattern.iter().position_max_by_key(|ex| { - if let Expr(Clause::Placeh(_, Some(prio)), _) = ex { *prio as i64 } else { -1 } - })?; - let (left, not_left) = pattern.split_at(rngidx); - let (placeh, right) = if rngidx == pattern.len() { - (¬_left[0].0, [].as_slice()) - } else { - let (placeh_unary_slice, right) = pattern.split_at(rngidx + 1); - (&placeh_unary_slice[0].0, right) - }; - if let Clause::Placeh(name, Some(prio)) = placeh { - Some((left, (name, *prio), right)) - } else {None} +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) + } } + /// Matcher that applies a pattern to a slice via divide-and-conquer /// /// Upon construction, it selects the clause of highest priority, then @@ -31,134 +30,171 @@ fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr] /// /// Upon matching, it uses a cache to accelerate the process of executing /// a pattern on the entire tree. -#[derive(Debug, Clone, Eq)] -pub struct SliceMatcherDnC<'a> { +#[derive(Clone, Eq)] +pub struct SliceMatcherDnC { /// The entire pattern this will match - pattern: &'a [Expr], + pattern: Mrc<[Expr]>, /// The exact clause this can match - clause: &'a Clause, + clause: Mrc, /// Matcher for the parts of the pattern right from us - right_subm: Option>>, + right_subm: Option>, /// Matcher for the parts of the pattern left from us - left_subm: Option>>, + left_subm: Option>, /// Matcher for the body of this clause if it has one. /// Must be Some if pattern is (Auto, Lambda or S) - body_subm: Option>>, + body_subm: Option>, /// Matcher for the type of this expression if it has one (Auto usually does) /// Optional - typ_subm: Option>>, + typ_subm: Option>, } -impl<'a> PartialEq for SliceMatcherDnC<'a> { +impl PartialEq for SliceMatcherDnC { fn eq(&self, other: &Self) -> bool { self.pattern == other.pattern } } -impl<'a> std::hash::Hash for SliceMatcherDnC<'a> { +impl std::hash::Hash for SliceMatcherDnC { fn hash(&self, state: &mut H) { self.pattern.hash(state); } } -impl<'a> SliceMatcherDnC<'a> { +impl SliceMatcherDnC { /// If this is true, `clause`, `typ_subm`, `body_subm` and `clause_qual_name` are meaningless. /// If it's false, it's also false for both side matchers. pub fn clause_is_vectorial(&self) -> bool { - if let Clause::Placeh(_, Some(_)) = self.clause {true} else {false} + 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<&'a Vec> { - if let Clause::Name { qualified, .. } = self.clause {Some(qualified)} else {None} + pub fn clause_qual_name(&self) -> Option> { + if let Clause::Name { qualified, .. } = self.clause.as_ref() {Some(Mrc::clone(qualified))} else {None} } /// If clause is a Placeh, the key in the state the match will be stored at - pub fn state_key(&self) -> Option<&'a String> { - if let Clause::Placeh(key, _) = self.clause {Some(key)} else {None} + pub fn state_key(&self) -> Option<&String> { + if let Clause::Placeh { key, .. } = self.clause.as_ref() {Some(key)} else {None} } - pub fn own_max_size(&self, total: usize) -> usize { - if !self.clause_is_vectorial() {return self.len()} - return total - self.min(Side::Left) - self.min(Side::Right) + pub fn own_max_size(&self, total: usize) -> Option { + if !self.clause_is_vectorial() { + if total == self.len() {Some(total)} else {None} + } else { + let margin = self.min(Side::Left) + self.min(Side::Right); + if margin + self.own_min_size() <= total {Some(total - margin)} else {None} + } } + pub fn own_min_size(&self) -> usize { + if let Clause::Placeh { vec: Some((_, nonzero)), .. } = self.clause.as_ref() { + if *nonzero {1} else {0} + } else {self.len()} + } + /// Enumerate all valid subdivisions based on the reported size constraints of self and /// the two subranges - pub fn valid_subdivisions<'b>(&self, - range: &'b [Expr] - ) -> impl Iterator { - let own_size = self.own_max_size(range.len()); + pub fn valid_subdivisions(&self, + range: Mrc<[Expr]> + ) -> impl Iterator, Mrc<[Expr]>, Mrc<[Expr]>)> { + let own_max = { + if let Some(x) = self.own_max_size(range.len()) {x} + else {return box_empty()} + }; + let own_min = self.own_min_size(); let lmin = self.min(Side::Left); - let lmax = self.max(Side::Left, range.len()); + let _lmax = self.max(Side::Left, range.len()); let rmin = self.min(Side::Right); - let rmax = self.max(Side::Right, range.len()); + let _rmax = self.max(Side::Right, range.len()); let full_len = range.len(); - (1..=own_size).rev().flat_map(move |own_len| { + Box::new((own_min..=own_max).rev().flat_map(move |own_len| { let wiggle = full_len - lmin - rmin - own_len; - (0..wiggle).map(move |offset| { + let range = Mrc::clone(&range); + (0..=wiggle).map(move |offset| { let first_break = lmin + offset; - let (left, rest) = range.split_at(first_break); - let (mid, right) = rest.split_at(own_len); + let second_break = first_break + own_len; + let left = mrc_derive(&range, |p| &p[0..first_break]); + let mid = mrc_derive(&range, |p| &p[first_break..second_break]); + let right = mrc_derive(&range, |p| &p[second_break..]); (left, mid, right) }) - }) + })) } - pub fn new(pattern: &'a [Expr]) -> Self { - let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 { - (&pattern[0], None, None) - } else if let Some((left, _, right)) = split_at_max_vec(pattern) {( - &pattern[left.len()], - Some(Box::new(Self::new(left))), - Some(Box::new(Self::new(right))) - )} else {( - &pattern[0], - None, - Some(Box::new(Self::new(&pattern[1..]))) - )}; + pub fn new(pattern: Mrc<[Expr]>) -> Self { + let (clause, left_subm, right_subm) = mrc_try_derive(&pattern, |p| { + if p.len() == 1 {Some(&p[0].0)} else {None} + }).map(|e| (e, None, None)) + .or_else(|| split_at_max_vec(Mrc::clone(&pattern)).map(|(left, _, right)| ( + mrc_derive(&pattern, |p| &p[left.len()].0), + if !left.is_empty() {Some(Box::new(Self::new(left)))} else {None}, + if !right.is_empty() {Some(Box::new(Self::new(right)))} else {None} + ))) + .unwrap_or_else(|| ( + mrc_derive(&pattern, |p| &p[0].0), + None, + Some(Box::new(Self::new(mrc_derive(&pattern, |p| &p[1..])))) + )); + // let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 { + // (&pattern[0], None, None) + // } else if let Some((left, _, right)) = split_at_max_vec(pattern) {( + // &pattern[left.len()], + // Some(Box::new(Self::new(left))), + // Some(Box::new(Self::new(right))) + // )} else {( + // &pattern[0], + // None, + // Some(Box::new(Self::new(&pattern[1..]))) + // )}; Self { - pattern, right_subm, left_subm, clause, + pattern, right_subm, left_subm, + clause: Mrc::clone(&clause), body_subm: clause.body().map(|b| Box::new(Self::new(b))), typ_subm: clause.typ().map(|t| Box::new(Self::new(t))) } } /// The shortest slice this pattern can match - fn len(&self) -> usize {self.pattern.len()} + fn len(&self) -> usize { + if self.clause_is_vectorial() { + self.min(Side::Left) + self.min(Side::Right) + self.own_min_size() + } else {self.pattern.len()} + } /// Pick a subpattern based on the parameter - fn side(&self, side: Side) -> Option<&Box>> { + fn side(&self, side: Side) -> Option<&SliceMatcherDnC> { match side { Side::Left => &self.left_subm, Side::Right => &self.right_subm - }.as_ref() + }.as_ref().map(|b| b.as_ref()) } /// The shortest slice the given side can match fn min(&self, side: Side) -> usize {self.side(side).map_or(0, |right| right.len())} /// The longest slice the given side can match fn max(&self, side: Side, total: usize) -> usize { self.side(side).map_or(0, |m| if m.clause_is_vectorial() { - total - self.min(side.opposite()) - 1 + total - self.min(side.opposite()) - self.own_min_size() } else {m.len()}) } /// Take the smallest possible slice from the given side - fn slice_min<'b>(&self, side: Side, range: &'b [Expr]) -> &'b [Expr] { + fn slice_min<'a>(&self, side: Side, range: &'a [Expr]) -> &'a [Expr] { side.slice(self.min(side), range) } /// Matches the body on a range /// # Panics /// when called on an instance that does not have a body (not Auto, Lambda or S) - fn match_body<'b>(&'a self, - range: &'b [Expr], cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option> + fn match_body<'a>(&'a self, + range: Mrc<[Expr]>, cache: &Cache, Option> ) -> Option { - self.body_subm.as_ref().unwrap().match_range_cached(range, cache) + self.body_subm.as_ref() + .expect("Missing body matcher") + .match_range_cached(range, cache) } /// Matches the type and body on respective ranges /// # Panics /// when called on an instance that does not have a body (not Auto, Lambda or S) - fn match_parts<'b>(&'a self, - typ_range: &'b [Expr], body_range: &'b [Expr], - cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option> + fn match_parts<'a>(&'a self, + typ_range: Mrc<[Expr]>, body_range: Mrc<[Expr]>, + cache: &Cache, Option> ) -> Option { let typ_state = if let Some(typ) = &self.typ_subm { - typ.match_range_cached(&typ_range, cache)? + typ.match_range_cached(typ_range, cache)? } else {State::new()}; let body_state = self.match_body(body_range, cache)?; typ_state + body_state @@ -166,181 +202,124 @@ impl<'a> SliceMatcherDnC<'a> { /// Match the specified side-submatcher on the specified range with the cache /// In absence of a side-submatcher empty ranges are matched to empty state - fn apply_side_with_cache<'b>(&'a self, - side: Side, range: &'b [Expr], - cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option> + fn apply_side_with_cache<'a>(&'a self, + side: Side, range: Mrc<[Expr]>, + cache: &Cache, Option> ) -> Option { match &self.side(side) { None => { - if range.len() != 0 {None} + if !range.is_empty() {None} else {Some(State::new())} }, - Some(m) => cache.try_find(&(range, &m)).map(|s| s.as_ref().to_owned()) + Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned()) } } - fn match_range_scalar_cached<'b>(&'a self, - target: &'b [Expr], - cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option> + fn match_range_scalar_cached<'a>(&'a self, + target: Mrc<[Expr]>, + cache: &Cache, Option> ) -> Option { let pos = self.min(Side::Left); if target.len() != self.pattern.len() {return None} let mut own_state = ( - self.apply_side_with_cache(Side::Left, &target[0..pos], cache)? - + self.apply_side_with_cache(Side::Right, &target[pos+1..], cache) + self.apply_side_with_cache(Side::Left, mrc_derive(&target, |t| &t[0..pos]), cache)? + + self.apply_side_with_cache(Side::Right, mrc_derive(&target, |t| &t[pos+1..]), cache) )?; - match (self.clause, &target[pos].0) { + match (self.clause.as_ref(), &target.as_ref()[pos].0) { (Clause::Literal(val), Clause::Literal(tgt)) => { if val == tgt {Some(own_state)} else {None} } - (Clause::Placeh(name, None), _) => { - own_state.insert(name, &[target[pos].clone()]) + (Clause::Placeh{key, vec: None}, _) => { + own_state.insert_scalar(&key, &target[pos]) } (Clause::S(c, _), Clause::S(c_tgt, body_range)) => { if c != c_tgt {return None} - own_state + self.match_parts(&[], body_range, cache) + own_state + self.match_parts(to_mrc_slice(vec![]), Mrc::clone(body_range), cache) } (Clause::Name{qualified, ..}, Clause::Name{qualified: q_tgt, ..}) => { if qualified == q_tgt {Some(own_state)} else {None} } (Clause::Lambda(name, _, _), Clause::Lambda(name_tgt, typ_tgt, body_tgt)) => { // Primarily, the name works as a placeholder - if let Some(state_key) = name.strip_prefix("$") { - own_state = own_state.insert( - state_key, - &[Expr(Clause::Name{ - local: Some(name_tgt.clone()), - qualified: vec![name_tgt.clone()] - }, None)] - )? - // But if you're weird like that, it can also work as a constraint + if let Some(state_key) = name.strip_prefix('$') { + own_state = own_state.insert_name(state_key, name_tgt)? } else if name != name_tgt {return None} - own_state + self.match_parts(typ_tgt, body_tgt, cache) + // ^ But if you're weird like that, it can also work as a constraint + own_state + self.match_parts(Mrc::clone(typ_tgt), Mrc::clone(body_tgt), cache) } (Clause::Auto(name_opt, _, _), Clause::Auto(name_range, typ_range, body_range)) => { if let Some(name) = name_opt { - if let Some(state_name) = name.strip_prefix("$") { - own_state = own_state.insert( - state_name, - &[Expr(Clause::Name{ - local: name_range.clone(), - qualified: name_range.as_ref() - .map(|s| vec![s.clone()]) - .unwrap_or_default() - }, None)] - )? - // TODO: Enforce this at construction, on a type system level - } else {panic!("Auto patterns may only reference, never enforce the name")} + // TODO: Enforce this at construction, on a type system level + let state_key = name.strip_prefix('$') + .expect("Auto patterns may only reference, never enforce the name"); + own_state = own_state.insert_name_opt(state_key, name_range.as_ref())? } - own_state + self.match_parts(typ_range, body_range, cache) + own_state + self.match_parts(Mrc::clone(typ_range), Mrc::clone(body_range), cache) }, _ => None } } /// Match the range with a vectorial _assuming we are a vectorial_ - fn match_range_vectorial_cached<'b>(&'a self, + fn match_range_vectorial_cached<'a>(&'a self, name: &str, - target: &'b [Expr], - cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option> + target: Mrc<[Expr]>, + cache: &Cache, Option> ) -> Option { // Step through valid slicings based on reported size constraints in order // from longest own section to shortest and from left to right for (left, own, right) in self.valid_subdivisions(target) { - let left_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Left, left, cache)); - let right_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Right, right, cache)); + let sides_result = unwrap_or_continue!( + self.apply_side_with_cache(Side::Left, left, cache) + ) + self.apply_side_with_cache(Side::Right, right, cache); return Some(unwrap_or_continue!( - right_result.clone() - + left_result.insert(name, own) + unwrap_or_continue!(sides_result) + .insert_vec(name, own.as_ref()) )) } - return None + None } /// Try and match the specified range - pub fn match_range_cached<'b>(&'a self, - target: &'b [Expr], - cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option> + pub fn match_range_cached<'a>(&'a self, + target: Mrc<[Expr]>, + cache: &Cache, Option> ) -> Option { - if self.pattern.len() == 0 { - return if target.len() == 0 {Some(State::new())} else {None} + eprintln!("Matching {target:?} with {:?}", self.pattern); + if self.pattern.is_empty() { + return if target.is_empty() {Some(State::new())} else {None} } - match self.clause { - Clause::Placeh(name, Some(_)) => self.match_range_vectorial_cached(name, target, cache), + match self.clause.as_ref() { + Clause::Placeh{key, vec: Some(_)} => + self.match_range_vectorial_cached(key, target, cache), _ => self.match_range_scalar_cached(target, cache) } } - pub fn match_range(&self, target: &[Expr]) -> Option { - self.match_range_cached(target,&Cache::<(&[Expr], &SliceMatcherDnC), _>::new( - |(tgt, matcher), cache| { + pub fn get_matcher_cache<'a>() + -> Cache<'a, CacheEntry<'a>, Option> { + Cache::new( + |CacheEntry(tgt, matcher), cache| { matcher.match_range_cached(tgt, cache) } - )) + ) + } + + pub fn match_range(&self, target: Mrc<[Expr]>) -> Option { + self.match_range_cached(target, &Self::get_matcher_cache()) } } -pub fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap) --> Result<(), String> { - let verify_clause = |clause: &Clause, is_vec: &mut HashMap| -> Result<(), String> { - match clause { - Clause::Placeh(name, prio) => { - if let Some(known) = is_vec.get(name) { - if known != &prio.is_some() { return Err(name.to_string()) } - } else { - is_vec.insert(name.clone(), prio.is_some()); - } - } - Clause::Auto(name, typ, body) => { - if let Some(key) = name.as_ref().map(|key| key.strip_prefix("$")).flatten() { - if is_vec.get(key) == Some(&true) { return Err(key.to_string()) } - } - 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()) } - } - 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::S(_, body) => { - body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?; - } - _ => () - }; - Ok(()) - }; - let Expr(val, typ_opt) = pattern; - verify_clause(val, is_vec)?; - if let Some(typ) = typ_opt { - verify_scalar_vec(typ, is_vec)?; +impl Debug for SliceMatcherDnC { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Matcher") + .field("clause", &self.clause) + .field("vectorial", &self.clause_is_vectorial()) + .field("min", &self.len()) + .field("left", &self.left_subm) + .field("right", &self.right_subm) + .field("lmin", &self.min(Side::Left)) + .field("rmin", &self.min(Side::Right)) + .finish() } - return Ok(()) } - -pub fn execute(mut src: Vec, mut tgt: Vec, mut input: Vec) --> Result<(Vec, bool), RuleError> { - // Static values - let prefix_expr = Expr(Clause::Placeh("::prefix".to_string(), Some(0)), None); - let postfix_expr = Expr(Clause::Placeh("::postfix".to_string(), Some(0)), None); - // Dimension check - let mut is_vec_db = HashMap::new(); - src.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db)) - .map_err(RuleError::ScalarVecMismatch)?; - tgt.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db)) - .map_err(RuleError::ScalarVecMismatch)?; - // Prefix or postfix to match the full vector - let head_multi = if let Clause::Placeh(_, Some(_)) = src.first().unwrap().0 {true} else {false}; - let tail_multi = if let Clause::Placeh(_, Some(_)) = src.last().unwrap().0 {true} else {false}; - if !head_multi { - src.insert(0, prefix_expr.clone()); - tgt.insert(0, prefix_expr.clone()); - } - if !tail_multi { - src.push(postfix_expr.clone()); - tgt.push(postfix_expr.clone()); - } - todo!() -} \ No newline at end of file diff --git a/src/rule/executor/split_at_max_vec.rs b/src/rule/executor/split_at_max_vec.rs new file mode 100644 index 0000000..8396c53 --- /dev/null +++ b/src/rule/executor/split_at_max_vec.rs @@ -0,0 +1,33 @@ +use mappable_rc::Mrc; +use itertools::Itertools; + +use crate::expression::{Expr, Clause}; +use crate::utils::{mrc_derive, mrc_try_derive}; + +pub type MaxVecSplit = (Mrc<[Expr]>, (Mrc, usize, bool), Mrc<[Expr]>); +/// Derive the details of the central vectorial and the two sides from a slice of Expr's +pub fn split_at_max_vec(pattern: Mrc<[Expr]>) -> Option { + let rngidx = pattern.iter().position_max_by_key(|ex| { + if let Expr(Clause::Placeh{vec: Some((prio, _)), ..}, _) = ex { + *prio as i64 + } else { -1 } + })?; + let left = mrc_derive(&pattern, |p| &p[0..rngidx]); + let placeh = mrc_derive(&pattern, |p| &p[rngidx].0); + let right = if rngidx == pattern.len() { + mrc_derive(&pattern, |x| &x[0..1]) + } else { + mrc_derive(&pattern, |x| &x[rngidx + 1..]) + }; + mrc_try_derive(&placeh, |p| { + if let Clause::Placeh{key, vec: Some(_)} = p { + Some(key) + } else {None} // Repeated below on unchanged data + }).map(|key| { + let key = mrc_derive(&key, String::as_str); + if let Clause::Placeh{vec: Some((prio, nonzero)), ..} = placeh.as_ref() { + (left, (key, *prio, *nonzero), right) + } + else {panic!("Impossible branch")} // Duplicate of above + }) +} diff --git a/src/rule/executor/state.rs b/src/rule/executor/state.rs index 75fbe9f..cadc2ad 100644 --- a/src/rule/executor/state.rs +++ b/src/rule/executor/state.rs @@ -1,21 +1,31 @@ -use std::ops::{Add, Index}; +use std::{ops::{Add, Index}, rc::Rc, fmt::Debug}; -use hashbrown::{HashMap, hash_map::IntoIter}; -use mappable_rc::Mrc; +use hashbrown::HashMap; use crate::expression::Expr; +#[derive(Debug, PartialEq, Eq)] +pub enum Entry { + Vec(Rc>), + Scalar(Rc), + Name(Rc), + NameOpt(Option>) +} + /// A bucket of indexed expression fragments. Addition may fail if there's a conflict. -#[derive(PartialEq, Eq)] -pub struct State(HashMap>>); +#[derive(PartialEq, Eq, Clone)] +pub struct State(HashMap); /// Clone without also cloning arbitrarily heavy Expr objects. /// Key is expected to be a very short string with an allocator overhead close to zero. -impl Clone for State { +impl Clone for Entry { fn clone(&self) -> Self { - Self(HashMap::from_iter( - self.0.iter().map(|(k, v)| (k.clone(), Mrc::clone(v))) - )) + match self { + Self::Name(n) => Self::Name(Rc::clone(n)), + Self::Scalar(x) => Self::Scalar(Rc::clone(x)), + Self::Vec(v) => Self::Vec(Rc::clone(v)), + Self::NameOpt(o) => Self::NameOpt(o.as_ref().map(Rc::clone)) + } } } @@ -23,24 +33,65 @@ impl State { pub fn new() -> Self { Self(HashMap::new()) } - /// Insert a new element, return None on conflict, clone only on success - pub fn insert(mut self, k: &S, v: &[Expr]) -> Option + pub fn insert_vec(mut self, k: &S, v: &[Expr]) -> Option + where S: AsRef + ToString + ?Sized + Debug { + eprintln!("{:?} + {k:?}-{v:?}", self.0); + if let Some(old) = self.0.get(k.as_ref()) { + if let Entry::Vec(val) = old { + if val.as_slice() != v {return None} + } else {return None} + } else { + self.0.insert(k.to_string(), Entry::Vec(Rc::new(v.to_vec()))); + } + Some(self) + } + pub fn insert_scalar(mut self, k: &S, v: &Expr) -> Option where S: AsRef + ToString + ?Sized { if let Some(old) = self.0.get(k.as_ref()) { - if old.as_ref() != v {return None} + if let Entry::Scalar(val) = old { + if val.as_ref() != v {return None} + } else {return None} } else { - self.0.insert(k.to_string(), Mrc::new(v.to_vec())); + self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned()))); } - return Some(self) + Some(self) + } + pub fn insert_name(mut self, k: &S1, v: &S2) -> Option + where + S1: AsRef + ToString + ?Sized, + S2: AsRef + 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} + } else {return None} + } else { + self.0.insert(k.to_string(), Entry::Name(Rc::new(v.to_string()))); + } + Some(self) + } + pub fn insert_name_opt(mut self, k: &S1, v: Option<&S2>) -> Option + where + S1: AsRef + ToString + ?Sized, + S2: AsRef + 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()) {return None} + } else {return None} + } else { + self.0.insert(k.to_string(), Entry::NameOpt(v.map(|s| Rc::new(s.to_string())))); + } + Some(self) } /// Insert a new entry, return None on conflict - pub fn insert_pair(mut self, (k, v): (String, Mrc>)) -> Option { + pub fn insert_pair(mut self, (k, v): (String, Entry)) -> Option { if let Some(old) = self.0.get(&k) { if old != &v {return None} } else { self.0.insert(k, v); } - return Some(self) + Some(self) } /// Returns `true` if the state contains no data pub fn empty(&self) -> bool { @@ -58,7 +109,7 @@ impl Add for State { for pair in rhs.0 { self = self.insert_pair(pair)? } - return Some(self); + Some(self) } } @@ -70,20 +121,20 @@ impl Add> for State { } } -impl<'a, S> Index<&S> for State where S: AsRef { - type Output = Vec; +impl Index for State where S: AsRef { + type Output = Entry; - fn index(&self, index: &S) -> &Self::Output { + fn index(&self, index: S) -> &Self::Output { return &self.0[index.as_ref()] } } impl IntoIterator for State { - type Item = (String, Mrc>); + type Item = (String, Entry); - type IntoIter = IntoIter>>; + type IntoIter = hashbrown::hash_map::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } -} \ No newline at end of file +} diff --git a/src/rule/mod.rs b/src/rule/mod.rs index 77da85e..c169aca 100644 --- a/src/rule/mod.rs +++ b/src/rule/mod.rs @@ -1,6 +1,7 @@ -// mod rule; mod executor; mod rule_error; +mod repository; // pub use rule::Rule; -pub use rule_error::RuleError; \ No newline at end of file +pub use rule_error::RuleError; +pub use repository::Repository; diff --git a/src/rule/name.rs b/src/rule/name.rs deleted file mode 100644 index 7ae0606..0000000 --- a/src/rule/name.rs +++ /dev/null @@ -1,3 +0,0 @@ -struct Name { - qualified: Vec -} \ No newline at end of file diff --git a/src/rule/repository.rs b/src/rule/repository.rs new file mode 100644 index 0000000..78e8c85 --- /dev/null +++ b/src/rule/repository.rs @@ -0,0 +1,45 @@ +use std::fmt::Debug; + +use mappable_rc::Mrc; + +use crate::expression::Expr; + +use super::{super::expression::Rule, executor::execute, RuleError}; + +pub struct Repository(Vec); +impl Repository { + pub fn new(mut rules: Vec) -> Self { + rules.sort_by_key(|r| r.prio); + Self(rules) + } + + pub fn step(&self, mut code: Mrc<[Expr]>) -> Result>, RuleError> { + let mut ran_once = false; + for rule in self.0.iter() { + if let Some(tmp) = execute( + Mrc::clone(&rule.source), Mrc::clone(&rule.target), + Mrc::clone(&code) + )? { + ran_once = true; + code = tmp; + } + } + Ok(if ran_once {Some(code)} else {None}) + } + + pub fn long_step(&self, mut code: Mrc<[Expr]>) -> Result, RuleError> { + while let Some(tmp) = self.step(Mrc::clone(&code))? { + code = tmp + } + Ok(code) + } +} + +impl Debug for Repository { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for rule in self.0.iter() { + writeln!(f, "{rule:?}")? + } + Ok(()) + } +} diff --git a/src/rule/rule.rs b/src/rule/rule.rs deleted file mode 100644 index d378fd3..0000000 --- a/src/rule/rule.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{cmp::{min, max}, error::Error, fmt, ops::Range}; - -use hashbrown::HashMap; - -use crate::expression::Expr; - -use super::BadState; - -type State = HashMap; - -pub trait Rule { - type Out: Iterator; - /// The minimum and maximum set of symbols this rule may match. - fn len(&self) -> (Option, Option); - /// Check if the slice matches, and extract data - fn read(&self, input: &[Expr]) -> Option; - /// Construct item from state - fn write(&self, state: &State) -> Result; - /// Placeholders present in this pattern (all consumed must also be provided) - fn placeholders(&'_ self) -> &'_ [&'_ str]; - /// Try all subsections of Vec of appropriate size, longest first, front-to-back - /// Match the first, return the position and produced state - fn scan_slice(&self, input: &[Expr]) -> Option<(Range, State)> { - let len_range = self.len(); - let lo = max(len_range.0.unwrap_or(1), 1); - let hi = min(len_range.1.unwrap_or(input.len()), input.len()); - for width in (lo..hi).rev() { - let starts = (0..input.len() - width).into_iter(); - let first_match = starts.filter_map(|start| { - let res = self.read(&input[start..start+width])?; - Some((start..start+width, res)) - }).next(); - if first_match.is_some() { - return first_match; - } - } - None - } -} - -pub fn verify(src: &Src, tgt: &Tgt) -> Option> where Src: Rule, Tgt: Rule { - let mut amiss: Vec = Vec::new(); - for ent in tgt.placeholders() { - if src.placeholders().iter().find(|x| x == &ent).is_none() { - amiss.push(ent.to_string()) - } - } - if amiss.len() > 0 { Some(amiss) } - else { None } -} \ No newline at end of file diff --git a/src/utils/cache.rs b/src/utils/cache.rs index 97de3e6..3b064d3 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -1,37 +1,60 @@ -use std::{hash::Hash, cell::RefCell}; +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 MyClone for T where T: Clone { + default fn my_clone(&self) -> Self { self.clone() } +} + +impl MyClone for Rc { + fn my_clone(&self) -> Self { Rc::clone(self) } +} +impl MyClone for Mrc { + fn my_clone(&self) -> Self { Mrc::clone(self) } +} + /// Cache the return values of an effectless closure in a hashmap /// Inspired by the closure_cacher crate. -pub struct Cache<'a, I, O: 'static> /*where O: Clone*/ { +pub struct Cache<'a, I, O: 'static> { store: RefCell>>, - closure: RefCell Mrc + 'a>> + closure: Box Mrc + 'a> } impl<'a, I, O> Cache<'a, I, O> where - I: Eq + Hash + Clone + I: Eq + Hash + MyClone { - pub fn new(mut closure: F) -> Self where F: FnMut(I, &Self) -> O { + pub fn new(closure: F) -> Self where F: Fn(I, &Self) -> O { Self::new_raw(move |o, s| Mrc::new(closure(o, s))) } /// Take an Mrc closure rather than an O closure /// Used internally to derive caches from other systems working with Mrc-s - pub fn new_raw(closure: F) -> Self where F: FnMut(I, &Self) -> Mrc { + pub fn new_raw(closure: F) -> Self where F: Fn(I, &Self) -> Mrc { Self { store: RefCell::new(HashMap::new()), - closure: RefCell::new(Box::new(closure)) + closure: Box::new(closure) } } /// Produce and cache a result by cloning I if necessary pub fn find(&self, i: &I) -> Mrc { - let mut closure = self.closure.borrow_mut(); + let closure = &self.closure; + if let Some(v) = self.store.borrow().get(i) { + return Mrc::clone(v) + } + // In the moment of invocation the refcell is on immutable + // this is important for recursive calculations + let result = closure(i.my_clone(), self); let mut store = self.store.borrow_mut(); Mrc::clone(store.raw_entry_mut().from_key(i) - .or_insert_with(|| (i.clone(), closure(i.clone(), self))).1) + .or_insert_with(|| (i.my_clone(), result)).1) } + #[allow(dead_code)] /// Return the result if it has already been computed pub fn known(&self, i: &I) -> Option> { @@ -46,7 +69,7 @@ impl<'a, I, O> Cache<'a, I, O> where } impl<'a, I, O, E> Cache<'a, I, Result> where - I: Eq + Hash + Clone, + I: Eq + Hash + MyClone, // O: Clone, E: Clone { @@ -60,7 +83,7 @@ impl<'a, I, O, E> Cache<'a, I, Result> where } impl<'a, I, O> Cache<'a, I, Option> where - I: Eq + Hash + Clone, + I: Eq + Hash + MyClone, // O: Clone { #[allow(dead_code)] @@ -70,4 +93,4 @@ impl<'a, I, O> Cache<'a, I, Option> where let ent = self.find(i); Mrc::try_map(ent, |o| o.as_ref()).ok() } -} \ No newline at end of file +} diff --git a/src/utils/iter.rs b/src/utils/iter.rs new file mode 100644 index 0000000..15ec2af --- /dev/null +++ b/src/utils/iter.rs @@ -0,0 +1,36 @@ +/// Utility functions to get rid of explicit casts to BoxedIter which are tedious + +use std::iter; + +pub type BoxedIter<'a, T> = Box + 'a>; +pub type BoxedIterIter<'a, T> = BoxedIter<'a, BoxedIter<'a, T>>; +/// BoxedIter of a single element +pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> { + Box::new(iter::once(t)) +} +/// BoxedIter of no elements +pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { + Box::new(iter::empty()) +} + +#[macro_export] +macro_rules! box_chain { + ($curr:expr) => { + Box::new($curr) as BoxedIter<_> + }; + ($curr:expr, $($rest:expr),*) => { + Box::new($curr$(.chain($rest))*) as $crate::utils::iter::BoxedIter<_> + }; +} + +pub fn box_flatten<'a, T: 'a, I: 'a, J: 'a>(i: I) -> BoxedIter<'a, T> +where + J: Iterator, + I: Iterator, +{ + Box::new(i.flatten()) +} +pub fn into_boxed_iter<'a, T: 'a>(t: T) -> BoxedIter<'a, ::Item> +where T: IntoIterator { + Box::new(t.into_iter()) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 3edcdd4..8d7b491 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,11 +2,33 @@ mod cache; mod substack; mod side; mod merge_sorted; -mod sorted_pairs; mod unwrap_or_continue; +pub mod iter; pub use cache::Cache; +use mappable_rc::Mrc; pub use substack::Stackframe; pub use side::Side; pub use merge_sorted::merge_sorted; +pub use iter::BoxedIter; -pub type BoxedIter<'a, T> = Box + 'a>; \ No newline at end of file +pub fn mrc_derive(m: &Mrc, p: P) -> Mrc +where P: for<'a> FnOnce(&'a T) -> &'a U { + Mrc::map(Mrc::clone(m), p) +} + +pub fn mrc_try_derive(m: &Mrc, p: P) -> Option> +where P: for<'a> FnOnce(&'a T) -> Option<&'a U> { + Mrc::try_map(Mrc::clone(m), p).ok() +} + +pub fn to_mrc_slice(v: Vec) -> Mrc<[T]> { + Mrc::map(Mrc::new(v), |v| v.as_slice()) +} + +pub fn collect_to_mrc(iter: I) -> Mrc<[I::Item]> where I: Iterator { + to_mrc_slice(iter.collect()) +} + +pub fn mrc_derive_slice(mv: &Mrc>) -> Mrc<[T]> { + mrc_derive(mv, |v| v.as_slice()) +} diff --git a/src/utils/sorted_pairs.rs b/src/utils/sorted_pairs.rs deleted file mode 100644 index c728f42..0000000 --- a/src/utils/sorted_pairs.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::ops::Add; - -/// Combine two sorted iterators with their mapper function into a sorted iterator of pairs -pub struct SortedPairs { - left: IL, right: IR, - left_map: ML, right_map: MR, - left_buf: Vec<(L, O)>, right_buf: Vec<(R, O)> -} - -impl SortedPairs -where IL: Iterator, IR: Iterator, - ML: Fn(L) -> O, MR: Fn(R) -> O, - O: Ord + Add + Clone -{ - pub fn new(left: IL, right: IR, left_map: ML, right_map: MR) -> Self { - Self { - left, right, left_map, right_map, - left_buf: Vec::new(), - right_buf: Vec::new() - } - } -} - -impl<'a, L: 'a, R: 'a, IL: 'a, IR: 'a, ML: 'a, MR: 'a, O: 'a> Iterator -for &'a mut SortedPairs -where IL: Iterator, IR: Iterator, - ML: Fn(L) -> O, MR: Fn(R) -> O, - O: Ord + Add + Clone, -{ - type Item = (&'a L, &'a R); - - fn next(&mut self) -> Option { - todo!() - } -} \ No newline at end of file