diff --git a/README.md b/README.md index 2192a9d..6d66e51 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,15 @@ Basic command line calculator import std::io::(readln, printf, in, out) main := ( - readln in >>= int |> \a. - readln in >>= \op. - readln in >>= int |> \b. - printf out "the result is {}\n", [match op ( - "+" => a + b, - "-" => a - b, - "*" => a * b, - "/" => a / b - )] + readln in >>= int |> \a. + readln in >>= \op. + readln in >>= int |> \b. + printf out "the result is {}\n", [match op ( + "+" => a + b, + "-" => a - b, + "*" => a * b, + "/" => a / b + )] ) ``` @@ -32,17 +32,17 @@ Grep import std::io::(readln, println, in, out, getarg) main := loop \r. ( - readln in >>= \line. - if (substring (getarg 1) line) - then (println out ln >>= r) - else r + readln in >>= \line. + if (substring (getarg 1) line) + then (println out ln >>= r) + else r ) ``` Filter through an arbitrary collection ```orchid filter := @C:Type -> Type. @:Map C. @T. \f:T -> Bool. \coll:C T. ( - coll >> \el. if (f el) then (Some el) else Nil + coll >> \el. if (f el) then (Some el) else Nil ):(C T) ``` @@ -76,9 +76,9 @@ it to itself. A naiive implementation of `imul` might look like this. ```orchid \a:int.\b:int. loop \r. (\i. - ifthenelse (ieq i 0) - b - (iadd b (r (isub i 1)) + ifthenelse (ieq i 0) + b + (iadd b (r (isub i 1)) ) a ``` @@ -123,8 +123,8 @@ For a demonstration, here's a sample implementation of the Option monad. ```orchid --[[ The definition of Monad ]]-- define Monad $M:(Type -> Type) as (Pair - (@T. @U. (T -> M U) -> M T -> M U) -- bind - (@T. T -> M T) -- return + (@T. @U. (T -> M U) -> M T -> M U) -- bind + (@T. T -> M T) -- return ) bind := @M:Type -> Type. @monad:Monad M. fst monad @@ -134,17 +134,17 @@ return := @M:Type -> Type. @monad:Monad M. snd monad define Option $T as @U. U -> (T -> U) -> U --[ Constructors ]-- export Some := @T. \data:T. categorise @(Option T) ( \default. \map. map data ) -export None := @T. categorise @(Option T) ( \default. \map. default ) +export None := @T. categorise @(Option T) ( \default. \map. default ) --[ Implement Monad ]-- impl Monad Option via (makePair - ( @T. @U. \f:T -> U. \opt:Option T. opt None \x. Some f ) -- bind - Some -- return + ( @T. @U. \f:T -> U. \opt:Option T. opt None \x. Some f ) -- bind + Some -- return ) --[ 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 ]-- + Turns (Option (M T)) into (M (Option T)), "raising" the unknown monad + out of the Option ]-- export raise := @M:Type -> Type. @T. @:Monad M. \opt:Option (M T). ( - opt (return None) (\m. bind m (\x. Some x)) + opt (return None) (\m. bind m (\x. Some x)) ):(M (Option T)) ``` @@ -160,7 +160,7 @@ the result: ```orchid impl @T. Add (List T) (List T) (List T) by concatListAdd over elementwiseAdd via ( - ... + ... ) ``` @@ -168,11 +168,11 @@ For completeness' sake, the original definition might look like this: ```orchid impl - @C:Type -> Type. @T. @U. @V. -- variables - @:(Applicative C). @:(Add T U V). -- conditions - Add (C T) (C U) (C V) -- target + @C:Type -> Type. @T. @U. @V. -- variables + @:(Applicative C). @:(Add T U V). -- conditions + Add (C T) (C U) (C V) -- target by elementwiseAdd via ( - ... + ... ) ``` @@ -181,11 +181,11 @@ implementation looks like: ```orchid impl @T. @:(Add T T T). Multiply T int T by iterativeMultiply via ( - \a:int. \b:T. loop \r. (\i. - ifthenelse (ieq i 0) - b - (add b (r (isub i 1)) -- notice how iadd is now add - ) a + \a:int. \b:T. loop \r. (\i. + ifthenelse (ieq i 0) + b + (add b (r (isub i 1)) -- notice how iadd is now add + ) a ) ``` @@ -193,7 +193,7 @@ This could then be applied to any type that's closed over addition ```orchid aroundTheWorldLyrics := ( - mult 18 (add (mult 4 "Around the World\n") "\n") + mult 18 (add (mult 4 "Around the World\n") "\n") ) ``` @@ -222,8 +222,8 @@ parenthesize subexpressions at the callsite. ```orchid (..$pre:2 if ...$cond then ...$true else ...$false) =10=> ( - ..$pre - (ifthenelse (...$cond) (...$true) (...$false)) + ..$pre + (ifthenelse (...$cond) (...$true) (...$false)) ) ...$a + ...$b =2=> (add (...$a) (...$b)) ...$a = ...$b =5=> (eq $a $b) @@ -234,10 +234,10 @@ The recursive addition function now looks like this ```orchid impl @T. @:(Add T T T). Multiply T int T by iterativeMultiply via ( - \a:int.\b:T. loop \r. (\i. - if (i = 0) then b - else (b + (r (i - 1))) - ) a + \a:int.\b:T. loop \r. (\i. + if (i = 0) then b + else (b + (r (i - 1))) + ) a ) ``` @@ -302,13 +302,13 @@ This is very far away so I don't want to make promises, but I have some ideas. - [ ] early execution of functions on any subset of their arguments where - it could provide substantial speedup + it could provide substantial speedup - [ ] tracking copies of expressions and evaluating them only once - [ ] Many cases of single recursion converted to loops - - [ ] tail recursion - - [ ] 2 distinct loops where the tail doesn't use the arguments - - [ ] reorder operations to favour this scenario + - [ ] tail recursion + - [ ] 2 distinct loops where the tail doesn't use the arguments + - [ ] reorder operations to favour this scenario - [ ] reactive calculation of values that are deemed to be read more often - than written + than written - [ ] automatic profiling based on performance metrics generated by debug - builds + builds diff --git a/examples/dummy_project/main.orc b/examples/dummy_project/main.orc index be64562..5e90275 100644 --- a/examples/dummy_project/main.orc +++ b/examples/dummy_project/main.orc @@ -6,7 +6,7 @@ define Add $L:type $R:type $O:type as $L -> $R -> $O define Mappable $C:(type -> type) as @T. @U. (T -> U) -> $C T -> $C U -- Dependency on existing typeclass define Zippable $C:(type -> type) as @:Mappable $C. ( - @T. @U. @V. (T -> U -> V) -> $C T -> $C U -> $C V + @T. @U. @V. (T -> U -> V) -> $C T -> $C U -> $C V ) define Default $T:type as $T -- Is the intersection of typeclasses an operation we need? @@ -15,16 +15,16 @@ define Default $T:type as $T define Cons $elem:type as loop \r. Option (Pair T $elem) nil := @T. from @(Cons T) none cons := @T. \el:T. ( - generalise @(Cons T) - |> (\list. some t[el, into list]) - |> categorise @(Cons T) + generalise @(Cons T) + |> (\list. some t[el, into list]) + |> categorise @(Cons T) ) export map := @T. @U. \f:T -> U. ( - generalise @(Cons T) - |> loop ( \recurse. \option. - map option \pair. t[f (fst pair), recurse (snd pair)] - ) - |> categorise @(Cons U) + generalise @(Cons T) + |> loop ( \recurse. \option. + map option \pair. t[f (fst pair), recurse (snd pair)] + ) + |> categorise @(Cons U) ) -- Universal typeclass implementation; no parameters, no overrides, no name for overriding impl Mappable Cons via map @@ -34,7 +34,7 @@ impl (@T. Add (Cons T) (Cons T) (Cons T)) by concatenation over elementwiseAddit -- Scratchpad filterBadWords := @C:type -> type. @:Mappable C. \strings:C String. ( - map strings \s. if intersects badWords (slice " " s) then none else some s + map strings \s. if intersects badWords (slice " " s) then none else some s ):(C (Option String)) -- /Scratchpad diff --git a/notes/papers/project_synopsis/main.tex b/notes/papers/project_synopsis/main.tex index f6fc976..bdfdc73 100644 --- a/notes/papers/project_synopsis/main.tex +++ b/notes/papers/project_synopsis/main.tex @@ -57,16 +57,16 @@ provisional feature set. A working type system should have the following parts, which I will implement in roughly this order \begin{itemize} - \item \textbf{Type inference engine and type checker} This will be an extension of - the Hindley-Milner algorithm, which simultaneously unifies and completes partial type - annotations, and recognizes conflicts. - \item \textbf{Typeclass solver} At the moment this appears to be a relatively simple piece of - code but I'm not entirely confident that complications won't arise as its responsibilities - become clearer, so I consider it a separate component - \item \textbf{Executor} Orchid is a statically typed language so it should eventually be compiled - with LLVM, but in order to demonstrate the usability of my type system I will have to write - an experimental interpreter. Since types are already basically expressions of type type, - parts of the executor will coincide with parts of the type inference engine. + \item \textbf{Type inference engine and type checker} This will be an extension of + the Hindley-Milner algorithm, which simultaneously unifies and completes partial type + annotations, and recognizes conflicts. + \item \textbf{Typeclass solver} At the moment this appears to be a relatively simple piece of + code but I'm not entirely confident that complications won't arise as its responsibilities + become clearer, so I consider it a separate component + \item \textbf{Executor} Orchid is a statically typed language so it should eventually be compiled + with LLVM, but in order to demonstrate the usability of my type system I will have to write + an experimental interpreter. Since types are already basically expressions of type type, + parts of the executor will coincide with parts of the type inference engine. \end{itemize} \section{Literature Review} @@ -99,12 +99,12 @@ in the same group. At a minimum, the following must be valid reduction steps: \begin{itemize} - \item $\beta$-reduction - \item fixed point normalization, which simply means identifying that a subexpression has - reduced to an expression that contains the original. When a fixed point is detected, the - recursive expression is converted to a form that uses the Y-combinator. This operation - is ordered before $\beta$-reductions of the expression in the BFS tree but otherwise has - the same precedence. + \item $\beta$-reduction + \item fixed point normalization, which simply means identifying that a subexpression has + reduced to an expression that contains the original. When a fixed point is detected, the + recursive expression is converted to a form that uses the Y-combinator. This operation + is ordered before $\beta$-reductions of the expression in the BFS tree but otherwise has + the same precedence. \end{itemize} \subsection{Typeclass solver} diff --git a/notes/papers/project_synopsis/references.bib b/notes/papers/project_synopsis/references.bib index b6ea2f3..3d32701 100644 --- a/notes/papers/project_synopsis/references.bib +++ b/notes/papers/project_synopsis/references.bib @@ -1,20 +1,20 @@ @online{suckerpinch, - title = {Generalized kerning is undecidable! But anagraphing is possible.}, - author = {suckerpinch}, - date = {dec, 2017}, - organization = {YouTube}, - url = {https://www.youtube.com/watch?v=8\_npHZbe3qM} + title = {Generalized kerning is undecidable! But anagraphing is possible.}, + author = {suckerpinch}, + date = {dec, 2017}, + organization = {YouTube}, + url = {https://www.youtube.com/watch?v=8\_npHZbe3qM} } @phdthesis{tubella, - author = {Jordi Tubella and Antonio González}, - school = {Universitat Politechnica de Catalunya}, - title = {A Partial Breadth-First Execution Model for Prolog}, - year = {1994} + author = {Jordi Tubella and Antonio González}, + school = {Universitat Politechnica de Catalunya}, + title = {A Partial Breadth-First Execution Model for Prolog}, + year = {1994} } @misc{yallop, - author = {Jeremy Yallop and Leo White}, - howpublished = {University of Cambridge}, - title = {Lightweight higher-kinded polymorphism} + author = {Jeremy Yallop and Leo White}, + howpublished = {University of Cambridge}, + title = {Lightweight higher-kinded polymorphism} } \ No newline at end of file diff --git a/notes/type_system/unification.md b/notes/type_system/unification.md index f57d644..fa0f62f 100644 --- a/notes/type_system/unification.md +++ b/notes/type_system/unification.md @@ -4,13 +4,13 @@ - enqueue evaluation steps for each of them and put them in a unification group - evaluation step refers to previous step, complete expression tree - unification **succeeds** if either - - the trees are syntactically identical in any two steps between the targets - - unification succeeds for all substeps: - - try to find an ancestor step that provably produces the same value as any lambda in this - step (for example, by syntactic equality) - - if found, substitute it with the recursive normal form of the lambda - - recursive normal form is `Apply(Y, \r.[body referencing r on point of recursion])` - - find all `Apply(\x.##, ##)` nodes in the tree and execute them + - the trees are syntactically identical in any two steps between the targets + - unification succeeds for all substeps: + - try to find an ancestor step that provably produces the same value as any lambda in this + step (for example, by syntactic equality) + - if found, substitute it with the recursive normal form of the lambda + - recursive normal form is `Apply(Y, \r.[body referencing r on point of recursion])` + - find all `Apply(\x.##, ##)` nodes in the tree and execute them - unification **fails** if a member of the concrete tree differs (only outermost steps add to the concrete tree so it belongs to the group and not the resolution) or no substeps are found for a resolution step _(failure: unresolved higher kinded type)_ diff --git a/.vscode/orchid.code-workspace b/orchid.code-workspace similarity index 64% rename from .vscode/orchid.code-workspace rename to orchid.code-workspace index 4591f6b..3c581f3 100644 --- a/.vscode/orchid.code-workspace +++ b/orchid.code-workspace @@ -1,15 +1,14 @@ { "folders": [ { - "path": ".." + "path": "." } ], "settings": {}, "extensions": { "recommendations": [ "tomoki1207.pdf", - "James-Yu.latex-workshop", - "rust-lang.rust-analyzer", + "james-yu.latex-workshop", "bungcip.better-toml" ] } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/src/executor/apply_lambda.rs b/src/executor/apply_lambda.rs index fedc0bc..bbf7bbd 100644 --- a/src/executor/apply_lambda.rs +++ b/src/executor/apply_lambda.rs @@ -1,79 +1,84 @@ use mappable_rc::Mrc; -use crate::utils::collect_to_mrc; +use crate::utils::{collect_to_mrc, to_mrc_slice}; use super::super::representations::typed::{Clause, Expr}; pub fn apply_lambda(id: u64, value: Mrc, body: Mrc) -> Mrc { - apply_lambda_expr_rec(id, value, Mrc::clone(&body)) - .unwrap_or(body) + apply_lambda_expr_rec(id, value, Mrc::clone(&body)) + .unwrap_or(body) } fn apply_lambda_expr_rec( - id: u64, value: Mrc, expr: Mrc + id: u64, value: Mrc, expr: Mrc ) -> Option> { - let Expr(clause, typ) = expr.as_ref(); - match clause { - Clause::Argument(arg_id) if *arg_id == id => { - let full_typ = collect_to_mrc( - value.1.iter() - .chain(typ.iter()) - .cloned() - ); - Some(Mrc::new(Expr(value.0.to_owned(), full_typ))) - } - cl => { - apply_lambda_clause_rec(id, value, clause.clone()) - .map(|c| Mrc::new(Expr(c, Mrc::clone(typ)))) - } + let Expr(clause, typ) = expr.as_ref(); + match clause { + Clause::LambdaArg(arg_id) | Clause::AutoArg(arg_id) if *arg_id == id => { + let full_typ = collect_to_mrc( + value.1.iter() + .chain(typ.iter()) + .cloned() + ); + Some(Mrc::new(Expr(value.0.to_owned(), full_typ))) } + cl => { + apply_lambda_clause_rec(id, value, cl.clone()) + .map(|c| Mrc::new(Expr(c, Mrc::clone(typ)))) + } + } } fn apply_lambda_clause_rec( - id: u64, value: Mrc, clause: Clause + id: u64, value: Mrc, clause: Clause ) -> Option { - match clause { - // Only element actually manipulated - Clause::Argument(id) => panic!( - "apply_lambda_expr_rec is supposed to eliminate this case"), - // Traverse, yield Some if either had changed. - Clause::Apply(f, x) => { - let new_f = apply_lambda_expr_rec( - id, Mrc::clone(&value), Mrc::clone(&f) - ); - let new_x = apply_lambda_expr_rec( - id, value, Mrc::clone(&x) - ); - match (new_f, new_x) { // Mind the shadows - (None, None) => None, - (None, Some(x)) => Some(Clause::Apply(f, x)), - (Some(f), None) => Some(Clause::Apply(f, x)), - (Some(f), Some(x)) => Some(Clause::Apply(f, x)) - } - }, - Clause::Lambda(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Lambda), - Clause::Auto(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Auto), - // Leaf nodes - Clause::Atom(_) | Clause::ExternFn(_) | Clause::Literal(_) => None - } + match clause { + // Only element actually manipulated + Clause::LambdaArg(_) | Clause::AutoArg(_) => Some(clause), + // Traverse, yield Some if either had changed. + Clause::Apply(f, x) => { + let new_f = apply_lambda_expr_rec( + id, Mrc::clone(&value), Mrc::clone(&f) + ); + let new_x = apply_lambda_expr_rec( + id, value, Mrc::clone(&x) + ); + match (new_f, new_x) { // Mind the shadows + (None, None) => None, + (None, Some(x)) => Some(Clause::Apply(f, x)), + (Some(f), None) => Some(Clause::Apply(f, x)), + (Some(f), Some(x)) => Some(Clause::Apply(f, x)) + } + }, + Clause::Lambda(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Lambda), + Clause::Auto(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Auto), + // Leaf nodes + Clause::Atom(_) | Clause::ExternFn(_) | Clause::Literal(_) => None + } } fn apply_lambda__traverse_param( - id: u64, value: Mrc, - own_id: u64, t: Option>, b: Mrc, - wrap: impl Fn(u64, Option>, Mrc) -> Clause + id: u64, value: Mrc, + own_id: u64, typ: Mrc<[Clause]>, b: Mrc, + wrap: impl Fn(u64, Mrc<[Clause]>, Mrc) -> Clause ) -> Option { - let new_t = t.and_then(|t| apply_lambda_clause_rec( - id, Mrc::clone(&value), t.as_ref().clone() - )); - // Respect shadowing - let new_b = if own_id == id {None} else { - apply_lambda_expr_rec(id, value, Mrc::clone(&b)) - }; - match (new_t, new_b) { // Mind the shadows - (None, None) => None, - (None, Some(b)) => Some(wrap(own_id, t, b)), - (Some(t), None) => Some(wrap(own_id, Some(Mrc::new(t)), b)), - (Some(t), Some(b)) => Some(wrap(own_id, Some(Mrc::new(t)), b)) - } + let any_t = false; + let mut t_acc = vec![]; + for t in typ.iter() { + let newt = apply_lambda_clause_rec(id, Mrc::clone(&value), t.clone()); + any_t |= newt.is_some(); + t_acc.push(newt.unwrap_or_else(|| t.clone())) + } + // Respect shadowing + let new_b = if own_id == id {None} else { + apply_lambda_expr_rec(id, value, Mrc::clone(&b)) + }; + if any_t { // mind the shadows + let typ = to_mrc_slice(t_acc); + if let Some(b) = new_b { + Some(wrap(own_id, typ, b)) + } else {Some(wrap(own_id, typ, b))} + } else if let Some(b) = new_b { + Some(wrap(own_id, typ, b)) + } else {Some(wrap(own_id, typ, b))} } \ No newline at end of file diff --git a/src/executor/foreign.rs b/src/executor/foreign.rs deleted file mode 100644 index 4bbf0e1..0000000 --- a/src/executor/foreign.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::any::Any; -use std::fmt::{Display, Debug}; -use std::hash::Hash; - -use mappable_rc::Mrc; - -use crate::representations::typed::{Expr, Clause}; - -pub trait ExternError: Display {} - -/// Represents an externally defined function from the perspective of the executor -/// Since Orchid lacks basic numerical operations, these are also external functions. -pub struct ExternFn { - name: String, param: Mrc, rttype: Mrc, - function: Mrc Result>> -} - -impl ExternFn { - pub fn new Result>>( - name: String, param: Mrc, rttype: Mrc, f: F - ) -> Self { - Self { - name, param, rttype, - function: Mrc::map(Mrc::new(f), |f| { - f as &dyn Fn(Clause) -> Result> - }) - } - } - pub fn name(&self) -> &str {&self.name} - pub fn apply(&self, arg: Clause) -> Result> {(self.function)(arg)} -} - -impl Clone for ExternFn { fn clone(&self) -> Self { Self { - name: self.name.clone(), - param: Mrc::clone(&self.param), - rttype: Mrc::clone(&self.rttype), - function: Mrc::clone(&self.function) -}}} -impl Eq for ExternFn {} -impl PartialEq for ExternFn { - fn eq(&self, other: &Self) -> bool { self.name() == other.name() } -} -impl Hash for ExternFn { - fn hash(&self, state: &mut H) { self.name.hash(state) } -} -impl Debug for ExternFn { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.param, self.rttype) - } -} - -pub trait Atomic: Any + Debug where Self: 'static { - fn as_any(&self) -> &dyn Any; - fn definitely_eq(&self, _other: &dyn Any) -> bool; - fn hash(&self, hasher: &mut dyn std::hash::Hasher); -} - -/// Represents a unit of information from the perspective of the executor. This may be -/// something like a file descriptor which functions can operate on, but it can also be -/// information in the universe of types or kinds such as the type of signed integers or -/// the kind of types. Ad absurdum it can also be just a number, although Literal is -/// preferable for types it's defined on. -pub struct Atom { - typ: Mrc, - data: Mrc -} -impl Atom { - pub fn new(data: T, typ: Mrc) -> Self { Self{ - typ, - data: Mrc::map(Mrc::new(data), |d| d as &dyn Atomic) - } } - pub fn data(&self) -> &dyn Atomic { self.data.as_ref() as &dyn Atomic } - pub fn try_cast(&self) -> Result<&T, ()> { - self.data().as_any().downcast_ref().ok_or(()) - } - pub fn is(&self) -> bool { self.data().as_any().is::() } - pub fn cast(&self) -> &T { - self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast") - } -} - -impl Clone for Atom { - fn clone(&self) -> Self { Self { - typ: Mrc::clone(&self.typ), - data: Mrc::clone(&self.data) - } } -} -impl Hash for Atom { - fn hash(&self, state: &mut H) { - self.data.hash(state); - self.typ.hash(state) - } -} -impl Debug for Atom { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ) - } -} -impl Eq for Atom {} -impl PartialEq for Atom { - fn eq(&self, other: &Self) -> bool { - self.data().definitely_eq(other.data().as_any()) - } -} \ No newline at end of file diff --git a/src/executor/normalize.rs b/src/executor/normalize.rs index c7ec3dd..aa0099d 100644 --- a/src/executor/normalize.rs +++ b/src/executor/normalize.rs @@ -5,26 +5,26 @@ use crate::utils::collect_to_mrc; use super::super::representations::typed::{Clause, Expr}; fn normalize(Expr(clause, typ): Expr) -> Expr { - todo!() + todo!() } fn collect_autos( - Expr(clause, typ): Expr, - arg_types: Vec>, - indirect_argt_trees: Vec>, - sunk_types: &mut dyn Iterator + Expr(clause, typ): Expr, + arg_types: Vec>, + indirect_argt_trees: Vec>, + sunk_types: &mut dyn Iterator ) -> (Vec>, Expr) { - if let Clause::Auto(argt, body) = clause { - - } - else {( - arg_types, - Expr( - clause, - collect_to_mrc( - typ.iter().cloned() - .chain(sunk_types) - ) - ) - )} + if let Clause::Auto(argt, body) = clause { + + } + else {( + arg_types, + Expr( + clause, + collect_to_mrc( + typ.iter().cloned() + .chain(sunk_types) + ) + ) + )} } \ No newline at end of file diff --git a/src/executor/partial_hash.rs b/src/executor/partial_hash.rs index a659852..8b4318d 100644 --- a/src/executor/partial_hash.rs +++ b/src/executor/partial_hash.rs @@ -8,49 +8,41 @@ use super::super::representations::typed::{Clause, Expr}; use super::super::utils::Stackframe; const PARAMETRICS_INLINE_COUNT:usize = 5; -type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>; +// type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>; /// Hash the parts of an expression that are required to be equal for syntactic equality. pub fn partial_hash_rec( - Expr(clause, _): &Expr, state: &mut H, - mut parametrics: Parametrics + Expr(clause, _): &Expr, state: &mut H, + parametrics: Option<&Stackframe> ) { - match clause { - // Skip autos - Clause::Auto(id, _, body) => { - parametrics.set(id, true); - partial_hash_rec(body, state, parametrics) - } - // Annotate everything else with a prefix - // - Recurse into the tree of lambdas and calls - classic lambda calc - Clause::Lambda(id, _, body) => { - state.write_u8(0); - parametrics.set(id, false); - partial_hash_rec(body, state, parametrics) - } - Clause::Apply(f, x) => { - state.write_u8(1); - partial_hash_rec(f, state, parametrics.clone()); - partial_hash_rec(x, state, parametrics); - } - // - Only recognize the depth of an argument if it refers to a non-auto parameter - Clause::Argument(own_id) => { - let (pos, is_auto) = parametrics.iter() - .filter_map(|(id, is_auto)| is_auto.map(|is_auto| (*id, is_auto))) - .find_position(|(id, is_auto)| id == own_id) - .map(|(pos, (_, is_auto))| (pos, is_auto)) - .unwrap_or((usize::MAX, false)); - // If the argument references an auto, acknowledge its existence - if is_auto { - state.write_u8(2) - } else { - state.write_u8(3); - state.write_usize(pos) - } - } - // - Hash leaves like normal - Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) } - Clause::Atom(at) => { state.write_u8(5); at.hash(state) } - Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) } + match clause { + // Skip autos + Clause::Auto(id, _, body) => { + partial_hash_rec(body, state, parametrics) } + // Annotate everything else with a prefix + // - Recurse into the tree of lambdas and calls - classic lambda calc + Clause::Lambda(id, _, body) => { + state.write_u8(0); + partial_hash_rec(body, state, Some(&Stackframe::opush(parametrics, *id))) + } + Clause::Apply(f, x) => { + state.write_u8(1); + partial_hash_rec(f, state, parametrics.clone()); + partial_hash_rec(x, state, parametrics); + } + Clause::AutoArg(..) => state.write_u8(2), + // - Only recognize the depth of an argument if it refers to a non-auto parameter + Clause::LambdaArg(own_id) => { + let pos = parametrics + .and_then(|sf| sf.iter().position(|id| id == own_id)) + .unwrap_or(usize::MAX); + state.write_u8(3); + state.write_usize(pos) + } + // - Hash leaves like normal + Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) } + Clause::Atom(at) => { state.write_u8(5); at.hash(state) } + Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) } + } } \ No newline at end of file diff --git a/src/executor/reduction_tree.rs b/src/executor/reduction_tree.rs index f5adb1d..8c9f32d 100644 --- a/src/executor/reduction_tree.rs +++ b/src/executor/reduction_tree.rs @@ -10,88 +10,88 @@ use super::super::representations::typed::{Clause, Expr}; /// Call the function with the first Expression that isn't an Auto, /// wrap all elements in the returned iterator back in the original sequence of Autos. pub fn skip_autos<'a, - F: 'a + FnOnce(Mrc) -> I, - I: Iterator> + 'static + F: 'a + FnOnce(Mrc) -> I, + I: Iterator> + 'static >( - expr: Mrc, function: F + expr: Mrc, function: F ) -> BoxedIter<'static, Mrc> { - if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() { - return Box::new(skip_autos(Mrc::clone(body), function).map({ - let arg = arg.as_ref().map(Mrc::clone); - let typ = Mrc::clone(typ); - move |body| { - Mrc::new(Expr(Clause::Auto( - *id, - arg.as_ref().map(Mrc::clone), - body - ), Mrc::clone(&typ))) - } - })) as BoxedIter<'static, Mrc> - } - Box::new(function(expr)) + if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() { + return Box::new(skip_autos(Mrc::clone(body), function).map({ + let arg = Mrc::clone(arg); + let typ = Mrc::clone(typ); + move |body| { + Mrc::new(Expr(Clause::Auto( + *id, + Mrc::clone(&arg), + body + ), Mrc::clone(&typ))) + } + })) as BoxedIter<'static, Mrc> + } + Box::new(function(expr)) } /// Produces an iterator of every expression that can be produced from this one through B-reduction. fn direct_reductions(ex: Mrc) -> impl Iterator> { - skip_autos(ex, |mexpr| { - let Expr(clause, typ_ref) = mexpr.as_ref(); - match clause { - Clause::Apply(f, x) => box_chain!( - skip_autos(Mrc::clone(f), |mexpr| { - let Expr(f, _) = mexpr.as_ref(); - match f { - Clause::Lambda(id, _, body) => box_once( - apply_lambda(*id, Mrc::clone(x), Mrc::clone(body)) - ), - Clause::ExternFn(xfn) => { - let Expr(xval, xtyp) = x.as_ref(); - xfn.apply(xval.clone()) - .map(|ret| box_once(Mrc::new(Expr(ret, Mrc::clone(xtyp))))) - .unwrap_or(box_empty()) - }, - // Parametric newtypes are atoms of function type - Clause::Atom(..) | Clause::Argument(..) | Clause::Apply(..) => box_empty(), - Clause::Literal(lit) => - panic!("Literal expression {lit:?} can't be applied as function"), - Clause::Auto(..) => unreachable!("skip_autos should have filtered this"), - } - }), - direct_reductions(Mrc::clone(f)).map({ - let typ = Mrc::clone(typ_ref); - let x = Mrc::clone(x); - move |f| Mrc::new(Expr(Clause::Apply( - f, - Mrc::clone(&x) - ), Mrc::clone(&typ))) - }), - direct_reductions(Mrc::clone(x)).map({ - let typ = Mrc::clone(typ_ref); - let f = Mrc::clone(f); - move |x| Mrc::new(Expr(Clause::Apply( - Mrc::clone(&f), - x - ), Mrc::clone(&typ))) - }) + skip_autos(ex, |mexpr| { + let Expr(clause, typ_ref) = mexpr.as_ref(); + match clause { + Clause::Apply(f, x) => box_chain!( + skip_autos(Mrc::clone(f), |mexpr| { + let Expr(f, _) = mexpr.as_ref(); + match f { + Clause::Lambda(id, _, body) => box_once( + apply_lambda(*id, Mrc::clone(x), Mrc::clone(body)) ), - Clause::Lambda(id, argt, body) => { - let id = *id; - let typ = Mrc::clone(typ_ref); - let argt = argt.as_ref().map(Mrc::clone); - let body = Mrc::clone(body); - let body_reductions = direct_reductions(body) - .map(move |body| { - let argt = argt.as_ref().map(Mrc::clone); - Mrc::new(Expr( - Clause::Lambda(id, argt, body), - Mrc::clone(&typ) - )) - }); - Box::new(body_reductions) + Clause::ExternFn(xfn) => { + let Expr(xval, xtyp) = x.as_ref(); + xfn.apply(xval.clone()) + .map(|ret| box_once(Mrc::new(Expr(ret, Mrc::clone(xtyp))))) + .unwrap_or(box_empty()) }, - Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) | Clause::Argument(..) => - box_empty(), + // Parametric newtypes are atoms of function type + Clause::Atom(..) | Clause::LambdaArg(..) | Clause::AutoArg(..) | Clause::Apply(..) => box_empty(), + Clause::Literal(lit) => + panic!("Literal expression {lit:?} can't be applied as function"), Clause::Auto(..) => unreachable!("skip_autos should have filtered this"), - } - }) + } + }), + direct_reductions(Mrc::clone(f)).map({ + let typ = Mrc::clone(typ_ref); + let x = Mrc::clone(x); + move |f| Mrc::new(Expr(Clause::Apply( + f, + Mrc::clone(&x) + ), Mrc::clone(&typ))) + }), + direct_reductions(Mrc::clone(x)).map({ + let typ = Mrc::clone(typ_ref); + let f = Mrc::clone(f); + move |x| Mrc::new(Expr(Clause::Apply( + Mrc::clone(&f), + x + ), Mrc::clone(&typ))) + }) + ), + Clause::Lambda(id, argt, body) => { + let id = *id; + let typ = Mrc::clone(typ_ref); + let argt = Mrc::clone(argt); + let body = Mrc::clone(body); + let body_reductions = direct_reductions(body) + .map(move |body| { + let argt = Mrc::clone(&argt); + Mrc::new(Expr( + Clause::Lambda(id, argt, body), + Mrc::clone(&typ) + )) + }); + Box::new(body_reductions) + }, + Clause::Auto(..) => unreachable!("skip_autos should have filtered this"), + Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) + | Clause::LambdaArg(..) | Clause::AutoArg(..) => box_empty(), + } + }) } diff --git a/src/executor/syntax_eq.rs b/src/executor/syntax_eq.rs index 39be4a3..b95179d 100644 --- a/src/executor/syntax_eq.rs +++ b/src/executor/syntax_eq.rs @@ -1,13 +1,11 @@ use std::collections::HashMap; -use std::hash::{Hasher, Hash}; -use std::iter; +use itertools::Itertools; use mappable_rc::Mrc; -use crate::utils::{ProtoMap, Side}; +use crate::utils::{ProtoMap, Side, mrc_empty_slice, collect_to_mrc, Stackframe, mrc_concat, Product2}; use super::super::representations::typed::{Clause, Expr}; -use super::super::utils::Stackframe; pub fn swap((t, u): (T, U)) -> (U, T) { (u, t) } @@ -17,24 +15,92 @@ pub fn swap((t, u): (T, U)) -> (U, T) { (u, t) } // - get rid of leftovers from Explicit // - adapt to new index-based system -// =@= =&= =%= =#= =$= =?= =!= =/= -// <@> <&> <%> <#> <$> -// |@| |&| |%| |#| |$| |?| |!| |/| -// {@} {&} {%} {#} {$} {?} {!} {/} -// (@) (&) (%) (#) ($) (?) (!) (/) -// [@] [&] [%] [#] [$] [?] [!] [/] +enum UnifError { + Conflict, +} + +type LambdaMap<'a> = Option<&'a Stackframe<'a, (u64, u64)>>; /// The context associates a given variable (by absolute index) on a given side to /// an expression on the opposite side rooted at the specified depth. /// The root depths are used to translate betwee de Brujin arguments and absolute indices. struct Context(HashMap>); impl Context { - fn set(&mut self, id: u64, value: Mrc) { - // If already defined, then it must be an argument - if let Some(value) = self.0.get(&id) { - if let Clause::Argument(opposite_up) ex.0 - } - } + fn set(&mut self, id: u64, value: &Mrc, lambdas: LambdaMap) -> Result>, UnifError> { + Ok( + if let Some(local) = self.0.get(&id) { + Some( + self.unify_expr(local, value, lambdas)? + .pick(Mrc::clone(local), Mrc::clone(value)) + ) + } else { None } + ) + } + + fn unify_expr(&mut self, + left: &Mrc, right: &Mrc, lambdas: LambdaMap + ) -> Result>, UnifError> { + let Expr(left_val, left_typs) = left.as_ref(); + let Expr(right_val, right_typs) = right.as_ref(); + let val = match (left_val, right_val) { + (Clause::AutoArg(l), Clause::AutoArg(r)) if l == r => Product2::Either, + (Clause::AutoArg(id), _) => self.set(*id, left, lambdas)?.as_ref() + .map_or(Product2::Left, |e| Product2::New(e.0.clone())), + (_, Clause::AutoArg(id)) => self.set(*id, right, lambdas)?.as_ref() + .map_or(Product2::Right, |e| Product2::New(e.0.clone())), + _ => self.unify_clause(left_val, right_val, lambdas)? + }; + Ok(match val { + Product2::Either if right_typs.is_empty() && left_typs.is_empty() => Product2::Either, + Product2::Left | Product2::Either if right_typs.is_empty() => Product2::Left, + Product2::Right | Product2::Either if left_typs.is_empty() => Product2::Right, + product => { + let all_types = mrc_concat(left_typs, right_typs); + Product2::New(Mrc::new(Expr( + product.pick(left_val.clone(), right_val.clone()), + all_types + ))) + } + }) + } + + fn unify_clause(&mut self, + left: &Clause, right: &Clause, lambdas: LambdaMap + ) -> Result, UnifError> { + Ok(match (left, right) { + (Clause::Literal(l), Clause::Literal(r)) if l == r => Product2::Either, + (Clause::Atom(l), Clause::Atom(r)) if l == r => Product2::Either, + (Clause::ExternFn(l), Clause::ExternFn(r)) if l == r => Product2::Either, + (Clause::LambdaArg(l), Clause::LambdaArg(r)) => if l == r {Product2::Either} else { + let is_equal = Stackframe::o_into_iter(lambdas) + .first_some(|(l_candidate, r_candidate)| { + if l_candidate == l && r_candidate == r {Some(true)} // match + else if l_candidate == l || r_candidate == r {Some(false)} // shadow + else {None} // irrelevant + }).unwrap_or(false); + // Reference: + if is_equal {Product2::Left} else {return Err(UnifError::Conflict)} + } + (Clause::AutoArg(_), _) | (_, Clause::AutoArg(_)) => { + unreachable!("unify_expr should have handled this") + } + (Clause::Lambda(l_id, l_arg, l_body), Clause::Lambda(r_id, r_arg, r_body)) => { + let lambdas = Stackframe::opush(lambdas, (*l_id, *r_id)); + self.unify_expr(l_body, r_body, Some(&lambdas))? + .map(|ex| Clause::Lambda(*l_id, mrc_empty_slice(), ex)) + } + (Clause::Apply(l_f, l_x), Clause::Apply(r_f, r_x)) => { + self.unify_expr(l_f, r_f, lambdas)?.join((Mrc::clone(l_f), Mrc::clone(r_f)), + self.unify_expr(l_x, r_x, lambdas)?, (Mrc::clone(l_x), Mrc::clone(r_x)) + ).map(|(f, x)| Clause::Apply(f, x)) + } + (Clause::Auto(l_id, l_arg, l_body), Clause::Auto(r_id, r_arg, r_body)) => { + let typ = self.unify(l_arg, r_arg, lambdas)?; + let body = self.unify_expr(l_body, r_body, lambdas)?; + typ.join((l_arg, r_arg), ) + } + }) + } } const IS_AUTO_INLINE:usize = 5; @@ -42,22 +108,22 @@ const IS_AUTO_INLINE:usize = 5; // All data to be forwarded during recursion about one half of a unification task #[derive(Clone)] struct UnifHalfTask<'a> { - /// The expression to be unified - expr: &'a Expr, - /// Stores whether a given uid is auto or lambda - is_auto: ProtoMap<'a, usize, bool, IS_AUTO_INLINE> + /// The expression to be unified + expr: &'a Expr, + /// Stores whether a given uid is auto or lambda + is_auto: ProtoMap<'a, usize, bool, IS_AUTO_INLINE> } impl<'a> UnifHalfTask<'a> { - fn push_auto(&mut self, body: &Expr, key: usize) { - self.expr = body; - self.is_auto.set(&key, true); - } + fn push_auto(&mut self, body: &Expr, key: usize) { + self.expr = body; + self.is_auto.set(&key, true); + } - fn push_lambda(&mut self, body: &Expr, key: usize) { - self.expr = body; - self.is_auto.set(&key, false); - } + fn push_lambda(&mut self, body: &Expr, key: usize) { + self.expr = body; + self.is_auto.set(&key, false); + } } type Ctx = HashMap>; @@ -68,63 +134,63 @@ type Ctx = HashMap>; /// /// Context associates variables with subtrees resolved on the opposite side pub fn unify_syntax_rec( // the stacks store true for autos, false for lambdas - ctx: &mut HashMap<(Side, usize), (usize, Mrc)>, - ltask@UnifHalfTask{ expr: lexpr@Expr(lclause, _), .. }: UnifHalfTask, - rtask@UnifHalfTask{ expr: rexpr@Expr(rclause, _), .. }: UnifHalfTask + ctx: &mut HashMap<(Side, usize), (usize, Mrc)>, + ltask@UnifHalfTask{ expr: lexpr@Expr(lclause, _), .. }: UnifHalfTask, + rtask@UnifHalfTask{ expr: rexpr@Expr(rclause, _), .. }: UnifHalfTask ) -> Option<(UnifResult, UnifResult)> { - // Ensure that ex1 is a value-level construct - match lclause { - Clause::Auto(id, _, body) => { - let res = unify_syntax_rec(ltask.push_auto(body).0, rtask); - return if ltask.explicits.is_some() { - res.map(|(r1, r2)| (r1.useExplicit(), r2)) - } else {res} - } - _ => () - }; - // Reduce ex2's auto handling to ex1's. In the optimizer we trust - if let Clause::Auto(..) | Clause::Explicit(..) = rclause { - return unify_syntax_rec(rtask, ltask).map(swap); + // Ensure that ex1 is a value-level construct + match lclause { + Clause::Auto(id, _, body) => { + let res = unify_syntax_rec(ltask.push_auto(body).0, rtask); + return if ltask.explicits.is_some() { + res.map(|(r1, r2)| (r1.useExplicit(), r2)) + } else {res} } - // Neither ex1 nor ex2 can be Auto or Explicit - match (lclause, rclause) { - // recurse into both - (Clause::Lambda(_, lbody), Clause::Lambda(_, rbody)) => unify_syntax_rec( - ltask.push_lambda(lbody), - rtask.push_lambda(rbody) - ), - (Clause::Apply(lf, lx), Clause::Apply(rf, rx)) => { - let (lpart, rpart) = unify_syntax_rec( - ltask.push_expr(lf), - rtask.push_expr(rf) - )?; - lpart.dropUsedExplicits(&mut ltask); - rpart.dropUsedExplicits(&mut rtask); - unify_syntax_rec(ltask.push_expr(lx), rtask.push_expr(rx)) - } - (Clause::Atom(latom), Clause::Atom(ratom)) => { - if latom != ratom { None } - else { Some((UnifResult::default(), UnifResult::default())) } - } - (Clause::ExternFn(lf), Clause::ExternFn(rf)) => { - if lf != rf { None } - else { Some((UnifResult::default(), UnifResult::default())) } - } - (Clause::Literal(llit), Clause::Literal(rlit)) => { - if llit != rlit { None } - else { Some((UnifResult::default(), UnifResult::default())) } - } - // TODO Select a representative - (Clause::Argument(depth1), Clause::Argument(depth2)) => { - !*stack1.iter().nth(*depth1).unwrap_or(&false) - && !*stack2.iter().nth(*depth2).unwrap_or(&false) - && stack1.iter().count() - depth1 == stack2.iter().count() - depth2 - } - // TODO Assign a substitute - (Clause::Argument(placeholder), _) => { + _ => () + }; + // Reduce ex2's auto handling to ex1's. In the optimizer we trust + if let Clause::Auto(..) | Clause::Explicit(..) = rclause { + return unify_syntax_rec(rtask, ltask).map(swap); + } + // Neither ex1 nor ex2 can be Auto or Explicit + match (lclause, rclause) { + // recurse into both + (Clause::Lambda(_, lbody), Clause::Lambda(_, rbody)) => unify_syntax_rec( + ltask.push_lambda(lbody), + rtask.push_lambda(rbody) + ), + (Clause::Apply(lf, lx), Clause::Apply(rf, rx)) => { + let (lpart, rpart) = unify_syntax_rec( + ltask.push_expr(lf), + rtask.push_expr(rf) + )?; + lpart.dropUsedExplicits(&mut ltask); + rpart.dropUsedExplicits(&mut rtask); + unify_syntax_rec(ltask.push_expr(lx), rtask.push_expr(rx)) + } + (Clause::Atom(latom), Clause::Atom(ratom)) => { + if latom != ratom { None } + else { Some((UnifResult::default(), UnifResult::default())) } + } + (Clause::ExternFn(lf), Clause::ExternFn(rf)) => { + if lf != rf { None } + else { Some((UnifResult::default(), UnifResult::default())) } + } + (Clause::Literal(llit), Clause::Literal(rlit)) => { + if llit != rlit { None } + else { Some((UnifResult::default(), UnifResult::default())) } + } + // TODO Select a representative + (Clause::Argument(depth1), Clause::Argument(depth2)) => { + !*stack1.iter().nth(*depth1).unwrap_or(&false) + && !*stack2.iter().nth(*depth2).unwrap_or(&false) + && stack1.iter().count() - depth1 == stack2.iter().count() - depth2 + } + // TODO Assign a substitute + (Clause::Argument(placeholder), _) => { - } } + } } // Tricky unifications diff --git a/src/foreign.rs b/src/foreign.rs new file mode 100644 index 0000000..fc84e46 --- /dev/null +++ b/src/foreign.rs @@ -0,0 +1,104 @@ +use std::any::Any; +use std::fmt::{Display, Debug}; +use std::hash::Hash; + +use mappable_rc::Mrc; + +use crate::representations::typed::{Expr, Clause}; + +pub trait ExternError: Display {} + +/// Represents an externally defined function from the perspective of the executor +/// Since Orchid lacks basic numerical operations, these are also external functions. +pub struct ExternFn { + name: String, param: Mrc, rttype: Mrc, + function: Mrc Result>> +} + +impl ExternFn { + pub fn new Result>>( + name: String, param: Mrc, rttype: Mrc, f: F + ) -> Self { + Self { + name, param, rttype, + function: Mrc::map(Mrc::new(f), |f| { + f as &dyn Fn(Clause) -> Result> + }) + } + } + pub fn name(&self) -> &str {&self.name} + pub fn apply(&self, arg: Clause) -> Result> {(self.function)(arg)} +} + +impl Clone for ExternFn { fn clone(&self) -> Self { Self { + name: self.name.clone(), + param: Mrc::clone(&self.param), + rttype: Mrc::clone(&self.rttype), + function: Mrc::clone(&self.function) +}}} +impl Eq for ExternFn {} +impl PartialEq for ExternFn { + fn eq(&self, other: &Self) -> bool { self.name() == other.name() } +} +impl Hash for ExternFn { + fn hash(&self, state: &mut H) { self.name.hash(state) } +} +impl Debug for ExternFn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.param, self.rttype) + } +} + +pub trait Atomic: Any + Debug where Self: 'static { + fn as_any(&self) -> &dyn Any; + fn definitely_eq(&self, _other: &dyn Any) -> bool; + fn hash(&self, hasher: &mut dyn std::hash::Hasher); +} + +/// Represents a unit of information from the perspective of the executor. This may be +/// something like a file descriptor which functions can operate on, but it can also be +/// information in the universe of types or kinds such as the type of signed integers or +/// the kind of types. Ad absurdum it can also be just a number, although Literal is +/// preferable for types it's defined on. +pub struct Atom { + typ: Mrc, + data: Mrc +} +impl Atom { + pub fn new(data: T, typ: Mrc) -> Self { Self{ + typ, + data: Mrc::map(Mrc::new(data), |d| d as &dyn Atomic) + } } + pub fn data(&self) -> &dyn Atomic { self.data.as_ref() as &dyn Atomic } + pub fn try_cast(&self) -> Result<&T, ()> { + self.data().as_any().downcast_ref().ok_or(()) + } + pub fn is(&self) -> bool { self.data().as_any().is::() } + pub fn cast(&self) -> &T { + self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast") + } +} + +impl Clone for Atom { + fn clone(&self) -> Self { Self { + typ: Mrc::clone(&self.typ), + data: Mrc::clone(&self.data) + } } +} +impl Hash for Atom { + fn hash(&self, state: &mut H) { + self.data.hash(state); + self.typ.hash(state) + } +} +impl Debug for Atom { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ) + } +} +impl Eq for Atom {} +impl PartialEq for Atom { + fn eq(&self, other: &Self) -> bool { + self.data().definitely_eq(other.data().as_any()) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f2d8a22..f4e51e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,16 +2,19 @@ #![feature(core_intrinsics)] #![feature(adt_const_params)] #![feature(generic_const_exprs)] +#![feature(generators, generator_trait)] + use std::env::current_dir; -mod executor; +// mod executor; mod parse; mod project; mod utils; mod representations; mod rule; -mod types; +mod scheduler; +pub(crate) mod foreign; use file_loader::LoadingError; pub use representations::ast; use ast::{Expr, Clause}; @@ -19,14 +22,14 @@ use representations::typed as t; use mappable_rc::Mrc; use project::{rule_collector, Loaded, file_loader}; use rule::Repository; -use utils::to_mrc_slice; +use utils::{to_mrc_slice, mrc_empty_slice, one_mrc_slice}; fn literal(orig: &[&str]) -> Mrc<[String]> { - to_mrc_slice(vliteral(orig)) + to_mrc_slice(vliteral(orig)) } fn vliteral(orig: &[&str]) -> Vec { - orig.iter().map(|&s| s.to_owned()).collect() + orig.iter().map(|&s| s.to_owned()).collect() } static PRELUDE:&str = r#" @@ -40,62 +43,62 @@ 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: literal(&["main", "main"]) - }, to_mrc_slice(vec![]))]) + to_mrc_slice(vec![Expr(Clause::Name { + local: None, + qualified: literal(&["main", "main"]) + }, to_mrc_slice(vec![]))]) } #[allow(unused)] fn typed_notation_debug() { - let true_ex = t::Clause::Auto(0, None, - t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))), - t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))), - t::Clause::Argument(1).wrap_t(t::Clause::Argument(0)) - ).wrap() - ).wrap() - ).wrap(); - let false_ex = t::Clause::Auto(0, None, - t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))), - t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))), - t::Clause::Argument(2).wrap_t(t::Clause::Argument(0)) - ).wrap() - ).wrap() - ).wrap(); - println!("{:?}", t::Clause::Apply(t::Clause::Apply(Mrc::clone(&true_ex), true_ex).wrap(), false_ex)) + let true_ex = t::Clause::Auto(0, mrc_empty_slice(), + t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)), + t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)), + t::Clause::LambdaArg(1).wrap_t(t::Clause::AutoArg(0)) + ).wrap() + ).wrap() + ).wrap(); + let false_ex = t::Clause::Auto(0, mrc_empty_slice(), + t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)), + t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)), + t::Clause::LambdaArg(2).wrap_t(t::Clause::AutoArg(0)) + ).wrap() + ).wrap() + ).wrap(); + println!("{:?}", t::Clause::Apply(t::Clause::Apply(Mrc::clone(&true_ex), true_ex).wrap(), false_ex)) } #[allow(unused)] fn load_project() { - let cwd = current_dir().unwrap(); - let collect_rules = rule_collector(move |n| -> Result { - if n == literal(&["prelude"]) { Ok(Loaded::Module(PRELUDE.to_string())) } - else { file_loader(cwd.clone())(n) } - }, vliteral(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"])); - let rules = match collect_rules.try_find(&literal(&["main"])) { - Ok(rules) => rules, - Err(err) => panic!("{:#?}", err) - }; - let mut tree = initial_tree(); - println!("Start processing {tree:?}"); - let repo = Repository::new(rules.as_ref().to_owned()); - println!("Ruleset: {repo:?}"); - xloop!(let mut i = 0; i < 10; i += 1; { - match repo.step(Mrc::clone(&tree)) { - Ok(Some(phase)) => { - println!("Step {i}: {phase:?}"); - tree = phase; - }, - Ok(None) => { - println!("Execution complete"); - break - }, - Err(e) => panic!("Rule error: {e:?}") - } - }; println!("Macro execution didn't halt")); + let cwd = current_dir().unwrap(); + let collect_rules = rule_collector(move |n| -> Result { + if n == literal(&["prelude"]) { Ok(Loaded::Module(PRELUDE.to_string())) } + else { file_loader(cwd.clone())(n) } + }, vliteral(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"])); + let rules = match collect_rules.try_find(&literal(&["main"])) { + Ok(rules) => rules, + Err(err) => panic!("{:#?}", err) + }; + let mut tree = initial_tree(); + println!("Start processing {tree:?}"); + let repo = Repository::new(rules.as_ref().to_owned()); + println!("Ruleset: {repo:?}"); + xloop!(let mut i = 0; i < 10; i += 1; { + match repo.step(Mrc::clone(&tree)) { + Ok(Some(phase)) => { + println!("Step {i}: {phase:?}"); + tree = phase; + }, + Ok(None) => { + println!("Execution complete"); + break + }, + Err(e) => panic!("Rule error: {e:?}") + } + }; println!("Macro execution didn't halt")); } fn main() { - // lambda_notation_debug(); - load_project(); + // lambda_notation_debug(); + load_project(); } diff --git a/src/parse/comment.rs b/src/parse/comment.rs index c0e6bb7..6b15ca0 100644 --- a/src/parse/comment.rs +++ b/src/parse/comment.rs @@ -2,12 +2,12 @@ pub use chumsky::{self, prelude::*, Parser}; /// Parses Lua-style comments pub fn comment_parser() -> impl Parser> { - choice(( - just("--[").ignore_then(take_until( - just("]--").ignored() - )), - just("--").ignore_then(take_until( - just("\n").rewind().ignored().or(end()) - )) - )).map(|(vc, ())| vc).collect().labelled("comment") + choice(( + just("--[").ignore_then(take_until( + just("]--").ignored() + )), + just("--").ignore_then(take_until( + just("\n").rewind().ignored().or(end()) + )) + )).map(|(vc, ())| vc).collect().labelled("comment") } diff --git a/src/parse/enum_parser.rs b/src/parse/enum_parser.rs index 6012405..453406a 100644 --- a/src/parse/enum_parser.rs +++ b/src/parse/enum_parser.rs @@ -6,27 +6,27 @@ /// ``` #[macro_export] macro_rules! enum_parser { - ($p:path | $m:tt) => { - { - ::chumsky::prelude::filter_map(|s, l| { - if let $p(x) = l { Ok(x) } - else { Err(::chumsky::prelude::Simple::custom(s, $m))} - }) - } - }; - ($p:path >> $q:path; $i:ident) => { - { - use $p as srcpath; - use $q as tgtpath; - enum_parser!(srcpath::$i | (concat!("Expected ", stringify!($i)))).map(tgtpath::$i) - } - }; - ($p:path >> $q:path; $($i:ident),+) => { - { - ::chumsky::prelude::choice(( - $( enum_parser!($p >> $q; $i) ),+ - )) - } - }; - ($p:path) => { enum_parser!($p | (concat!("Expected ", stringify!($p)))) }; + ($p:path | $m:tt) => { + { + ::chumsky::prelude::filter_map(|s, l| { + if let $p(x) = l { Ok(x) } + else { Err(::chumsky::prelude::Simple::custom(s, $m))} + }) + } + }; + ($p:path >> $q:path; $i:ident) => { + { + use $p as srcpath; + use $q as tgtpath; + enum_parser!(srcpath::$i | (concat!("Expected ", stringify!($i)))).map(tgtpath::$i) + } + }; + ($p:path >> $q:path; $($i:ident),+) => { + { + ::chumsky::prelude::choice(( + $( enum_parser!($p >> $q; $i) ),+ + )) + } + }; + ($p:path) => { enum_parser!($p | (concat!("Expected ", stringify!($p)))) }; } \ No newline at end of file diff --git a/src/parse/expression.rs b/src/parse/expression.rs index 34e1b53..1e031f0 100644 --- a/src/parse/expression.rs +++ b/src/parse/expression.rs @@ -8,120 +8,120 @@ use super::lexer::Lexeme; /// Parses any number of expr wrapped in (), [] or {} fn sexpr_parser

( - expr: P + expr: P ) -> impl Parser> + Clone where P: Parser> + 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, to_mrc_slice(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

( - expr: P + expr: P ) -> impl Parser> + Clone where P: Parser> + Clone { - just(Lexeme::BS) + just(Lexeme::BS) + .then_ignore(enum_parser!(Lexeme::Comment).repeated()) + .ignore_then(enum_parser!(Lexeme::Name)) + .then_ignore(enum_parser!(Lexeme::Comment).repeated()) + .then( + just(Lexeme::Type) .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .ignore_then(enum_parser!(Lexeme::Name)) + .ignore_then(expr.clone().repeated()) .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .then( - just(Lexeme::Type) - .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .ignore_then(expr.clone().repeated()) - .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .or_not().map(Option::unwrap_or_default) - ) - .then_ignore(just(Lexeme::name("."))) - .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .then(expr.repeated().at_least(1)) - .map(|((name, typ), body): ((String, Vec), Vec)| { - // for ent in &mut body { ent.bind_parameter(&name) }; - Clause::Lambda(name, to_mrc_slice(typ), to_mrc_slice(body)) - }) + .or_not().map(Option::unwrap_or_default) + ) + .then_ignore(just(Lexeme::name("."))) + .then_ignore(enum_parser!(Lexeme::Comment).repeated()) + .then(expr.repeated().at_least(1)) + .map(|((name, typ), body): ((String, Vec), Vec)| { + // for ent in &mut body { ent.bind_parameter(&name) }; + Clause::Lambda(name, to_mrc_slice(typ), to_mrc_slice(body)) + }) } /// see [lambda_parser] but `@` instead of `\` and the name is optional fn auto_parser

( - expr: P + expr: P ) -> impl Parser> + Clone where P: Parser> + Clone { - just(Lexeme::At) + just(Lexeme::At) + .then_ignore(enum_parser!(Lexeme::Comment).repeated()) + .ignore_then(enum_parser!(Lexeme::Name).or_not()) + .then_ignore(enum_parser!(Lexeme::Comment).repeated()) + .then( + just(Lexeme::Type) .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .ignore_then(enum_parser!(Lexeme::Name).or_not()) + .ignore_then(expr.clone().repeated()) .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .then( - just(Lexeme::Type) - .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .ignore_then(expr.clone().repeated()) - .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .or_not().map(Option::unwrap_or_default) - ) - .then_ignore(just(Lexeme::name("."))) - .then_ignore(enum_parser!(Lexeme::Comment).repeated()) - .then(expr.repeated().at_least(1)) - .try_map(|((name, typ), body): ((Option, Vec), Vec), 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))) - } - }) + .or_not().map(Option::unwrap_or_default) + ) + .then_ignore(just(Lexeme::name("."))) + .then_ignore(enum_parser!(Lexeme::Comment).repeated()) + .then(expr.repeated().at_least(1)) + .try_map(|((name, typ), body): ((Option, Vec), Vec), 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))) + } + }) } /// Parses a sequence of names separated by ::
/// Comments are allowed and ignored in between fn name_parser() -> impl Parser, Error = Simple> + 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) + enum_parser!(Lexeme::Name).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 `$` fn placeholder_parser() -> impl Parser> + Clone { - enum_parser!(Lexeme::Name).try_map(|name, span| { - name.strip_prefix('$').map(&str::to_string) - .ok_or_else(|| Simple::custom(span, "Not a placeholder")) - }) + enum_parser!(Lexeme::Name).try_map(|name, span| { + name.strip_prefix('$').map(&str::to_string) + .ok_or_else(|| Simple::custom(span, "Not a placeholder")) + }) } /// Parse an expression pub fn xpr_parser() -> impl Parser> { - recursive(|expr| { - let clause = - enum_parser!(Lexeme::Comment).repeated() - .ignore_then(choice(( - enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal), - 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(|((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()); - clause.clone().then( - just(Lexeme::Type) - .ignore_then(clause.clone()) - .repeated() + recursive(|expr| { + let clause = + enum_parser!(Lexeme::Comment).repeated() + .ignore_then(choice(( + enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal), + 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(|(val, typ)| Expr(val, to_mrc_slice(typ))) - }).labelled("Expression") + .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()); + clause.clone().then( + just(Lexeme::Type) + .ignore_then(clause.clone()) + .repeated() + ) + .map(|(val, typ)| Expr(val, to_mrc_slice(typ))) + }).labelled("Expression") } diff --git a/src/parse/import.rs b/src/parse/import.rs index 4f7d509..ef3cd79 100644 --- a/src/parse/import.rs +++ b/src/parse/import.rs @@ -9,15 +9,15 @@ 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 + pub path: Mrc<[String]>, + /// If name is None, this is a wildcard import + pub name: Option } /// initialize a BoxedIter> with a single element. fn init_table(name: String) -> BoxedIterIter<'static, String> { - // I'm not at all confident that this is a good approach. - box_once(box_once(name)) + // I'm not at all confident that this is a good approach. + box_once(box_once(name)) } /// Parse an import command @@ -26,44 +26,44 @@ fn init_table(name: String) -> BoxedIterIter<'static, String> { /// 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, Error = Simple> { - // TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad. - recursive(|expr: Recursive, Simple>| { - enum_parser!(Lexeme::Name) - .separated_by(just(Lexeme::NS)) - .then( - just(Lexeme::NS) - .ignore_then( - choice(( - expr.clone() - .separated_by(just(Lexeme::name(","))) - .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())) - .labelled("wildcard import"), // Just a *, wrapped - enum_parser!(Lexeme::Name).map(init_table) - .labelled("import terminal") // Just a name, wrapped - )) - ).or_not() - ) - .map(|(name, opt_post): (Vec, Option>)| -> BoxedIterIter { - if let Some(post) = opt_post { - Box::new(post.map(move |el| { - box_chain!(name.clone().into_iter(), el) - })) - } else { - box_once(into_boxed_iter(name)) - } - }) - }).map(|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()) }) - } - }).collect() - }).labelled("import") + // TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad. + recursive(|expr: Recursive, Simple>| { + enum_parser!(Lexeme::Name) + .separated_by(just(Lexeme::NS)) + .then( + just(Lexeme::NS) + .ignore_then( + choice(( + expr.clone() + .separated_by(just(Lexeme::name(","))) + .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())) + .labelled("wildcard import"), // Just a *, wrapped + enum_parser!(Lexeme::Name).map(init_table) + .labelled("import terminal") // Just a name, wrapped + )) + ).or_not() + ) + .map(|(name, opt_post): (Vec, Option>)| -> BoxedIterIter { + if let Some(post) = opt_post { + Box::new(post.map(move |el| { + box_chain!(name.clone().into_iter(), el) + })) + } else { + box_once(into_boxed_iter(name)) + } + }) + }).map(|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()) }) + } + }).collect() + }).labelled("import") } diff --git a/src/parse/lexer.rs b/src/parse/lexer.rs index b59577b..6d3d241 100644 --- a/src/parse/lexer.rs +++ b/src/parse/lexer.rs @@ -9,141 +9,141 @@ use super::{number, string, name, comment}; #[derive(Clone, PartialEq, Eq, Hash)] pub struct Entry(pub Lexeme, pub Range); impl Debug for Entry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - // f.debug_tuple("Entry").field(&self.0).field(&self.1).finish() - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + // f.debug_tuple("Entry").field(&self.0).field(&self.1).finish() + } } impl From for (Lexeme, Range) { - fn from(ent: Entry) -> Self { - (ent.0, ent.1) - } + fn from(ent: Entry) -> Self { + (ent.0, ent.1) + } } #[derive(Clone, PartialEq, Eq, Hash)] pub enum Lexeme { - Num(NotNan), - Int(u64), - Char(char), - Str(String), - Name(String), - Rule(NotNan), - NS, // namespace separator - LP(char), - RP(char), - BS, // Backslash - At, - Type, // type operator - Comment(String) + Num(NotNan), + Int(u64), + Char(char), + Str(String), + Name(String), + Rule(NotNan), + NS, // namespace separator + LP(char), + RP(char), + BS, // Backslash + At, + Type, // type operator + Comment(String) } impl Debug for Lexeme { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Num(n) => write!(f, "{}", n), - Self::Int(i) => write!(f, "{}", i), - Self::Char(c) => write!(f, "{:?}", c), - Self::Str(s) => write!(f, "{:?}", s), - Self::Name(name) => write!(f, "{}", name), - Self::Rule(prio) => write!(f, "={}=>", prio), - Self::NS => write!(f, "::"), - Self::LP(l) => write!(f, "{}", l), - Self::RP(l) => match l { - '(' => write!(f, ")"), - '[' => write!(f, "]"), - '{' => write!(f, "}}"), - _ => f.debug_tuple("RP").field(l).finish() - }, - Self::BS => write!(f, "\\"), - Self::At => write!(f, "@"), - Self::Type => write!(f, ":"), - Self::Comment(text) => write!(f, "--[{}]--", text), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Num(n) => write!(f, "{}", n), + Self::Int(i) => write!(f, "{}", i), + Self::Char(c) => write!(f, "{:?}", c), + Self::Str(s) => write!(f, "{:?}", s), + Self::Name(name) => write!(f, "{}", name), + Self::Rule(prio) => write!(f, "={}=>", prio), + Self::NS => write!(f, "::"), + Self::LP(l) => write!(f, "{}", l), + Self::RP(l) => match l { + '(' => write!(f, ")"), + '[' => write!(f, "]"), + '{' => write!(f, "}}"), + _ => f.debug_tuple("RP").field(l).finish() + }, + Self::BS => write!(f, "\\"), + Self::At => write!(f, "@"), + Self::Type => write!(f, ":"), + Self::Comment(text) => write!(f, "--[{}]--", text), } + } } impl Lexeme { - pub fn name(n: T) -> Self { - Lexeme::Name(n.to_string()) - } - pub fn rule(prio: T) -> Self where T: Into { - Lexeme::Rule(NotNan::new(prio.into()).expect("Rule priority cannot be NaN")) - } - pub fn paren_parser( - expr: P - ) -> impl Parser> + Clone - where P: Parser> + Clone { - choice(( - expr.clone().delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('('))) - .map(|t| ('(', t)), - expr.clone().delimited_by(just(Lexeme::LP('[')), just(Lexeme::RP('['))) - .map(|t| ('[', t)), - expr.delimited_by(just(Lexeme::LP('{')), just(Lexeme::RP('{'))) - .map(|t| ('{', t)), - )) - } + pub fn name(n: T) -> Self { + Lexeme::Name(n.to_string()) + } + pub fn rule(prio: T) -> Self where T: Into { + Lexeme::Rule(NotNan::new(prio.into()).expect("Rule priority cannot be NaN")) + } + pub fn paren_parser( + expr: P + ) -> impl Parser> + Clone + where P: Parser> + Clone { + choice(( + expr.clone().delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('('))) + .map(|t| ('(', t)), + expr.clone().delimited_by(just(Lexeme::LP('[')), just(Lexeme::RP('['))) + .map(|t| ('[', t)), + expr.delimited_by(just(Lexeme::LP('{')), just(Lexeme::RP('{'))) + .map(|t| ('{', t)), + )) + } } #[derive(Clone, PartialEq, Eq, Hash)] pub struct LexedText(pub Vec>); impl Debug for LexedText { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for row in &self.0 { - for tok in row { - tok.fmt(f)?; - f.write_str(" ")? - } - f.write_str("\n")? - } - Ok(()) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for row in &self.0 { + for tok in row { + tok.fmt(f)?; + f.write_str(" ")? + } + f.write_str("\n")? } + Ok(()) + } } type LexSubres<'a> = BoxedIter<'a, Entry>; fn paren_parser<'a>( - expr: Recursive<'a, char, LexSubres<'a>, Simple>, - lp: char, rp: char + expr: Recursive<'a, char, LexSubres<'a>, Simple>, + lp: char, rp: char ) -> impl Parser, Error=Simple> + 'a { - expr.padded().repeated() - .map(|x| box_flatten(x.into_iter())) - .delimited_by(just(lp), just(rp)).map_with_span(move |b, s| { - 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)) - ) - }) + expr.padded().repeated() + .map(|x| box_flatten(x.into_iter())) + .delimited_by(just(lp), just(rp)).map_with_span(move |b, s| { + 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)) + ) + }) } pub fn lexer<'a, T: 'a>(ops: &[T]) -> impl Parser> + 'a where T: AsRef + Clone { - let all_ops = ops.iter().map(|o| o.as_ref().to_string()) - .chain(iter::once(".".to_string())).collect::>(); - recursive(move |recurse: Recursive>| { - choice(( - paren_parser(recurse.clone(), '(', ')'), - paren_parser(recurse.clone(), '[', ']'), - paren_parser(recurse.clone(), '{', '}'), - choice(( - 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), - just('\\').padded().to(Lexeme::BS), - just('@').padded().to(Lexeme::At), - just(':').to(Lexeme::Type), - number::int_parser().map(Lexeme::Int), // all ints are valid floats so it takes precedence - number::float_parser().map(Lexeme::Num), - 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_once(Entry(lx, span)) as LexSubres) - )) - }).separated_by(one_of("\t ").repeated()) - .flatten().collect() - .separated_by(just('\n').then(text::whitespace()).ignored()) - .map(LexedText) + let all_ops = ops.iter().map(|o| o.as_ref().to_string()) + .chain(iter::once(".".to_string())).collect::>(); + recursive(move |recurse: Recursive>| { + choice(( + paren_parser(recurse.clone(), '(', ')'), + paren_parser(recurse.clone(), '[', ']'), + paren_parser(recurse.clone(), '{', '}'), + choice(( + 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), + just('\\').padded().to(Lexeme::BS), + just('@').padded().to(Lexeme::At), + just(':').to(Lexeme::Type), + number::int_parser().map(Lexeme::Int), // all ints are valid floats so it takes precedence + number::float_parser().map(Lexeme::Num), + 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_once(Entry(lx, span)) as LexSubres) + )) + }).separated_by(one_of("\t ").repeated()) + .flatten().collect() + .separated_by(just('\n').then(text::whitespace()).ignored()) + .map(LexedText) } diff --git a/src/parse/name.rs b/src/parse/name.rs index da5be3c..34b484b 100644 --- a/src/parse/name.rs +++ b/src/parse/name.rs @@ -2,13 +2,13 @@ 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_key(|op| -(op.len() as i64)); - sorted_ops.into_iter() - .map(|op| just(op).boxed()) - .reduce(|a, b| a.or(b).boxed()) - .unwrap_or_else(|| empty().map(|()| panic!("Empty isn't meant to match")).boxed()) - .labelled("operator").boxed() + let mut sorted_ops: Vec = ops.iter().map(|t| t.as_ref().to_string()).collect(); + 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_else(|| empty().map(|()| panic!("Empty isn't meant to match")).boxed()) + .labelled("operator").boxed() } /// Matches anything that's allowed as an operator @@ -30,31 +30,31 @@ fn op_parser<'a, T: AsRef + Clone>(ops: &[T]) -> BoxedParser<'a, char, Stri /// TODO: `.` could possibly be parsed as an operator depending on context. This operator is very /// common in maths so it's worth a try. Investigate. pub fn modname_parser<'a>() -> impl Parser> + 'a { - let not_name_char: Vec = vec![':', '\\', '@', '"', '\'', '(', ')', '[', ']', '{', '}', ',', '.']; - filter(move |c| !not_name_char.contains(c) && !c.is_whitespace()) - .repeated().at_least(1) - .collect() - .labelled("modname") + let not_name_char: Vec = vec![':', '\\', '@', '"', '\'', '(', ')', '[', ']', '{', '}', ',', '.']; + filter(move |c| !not_name_char.contains(c) && !c.is_whitespace()) + .repeated().at_least(1) + .collect() + .labelled("modname") } /// Parse an operator or name. Failing both, parse everything up to the next whitespace or /// blacklisted character as a new operator. pub fn name_parser<'a, T: AsRef + Clone>( - ops: &[T] + ops: &[T] ) -> impl Parser> + 'a { - choice(( - op_parser(ops), // First try to parse a known operator - text::ident().labelled("plain text"), // Failing that, parse plain text - modname_parser() // Finally parse everything until tne next terminal as a new operator - )) - .labelled("name") + choice(( + op_parser(ops), // First try to parse a known operator + text::ident().labelled("plain text"), // Failing that, parse plain text + modname_parser() // Finally parse everything until tne next terminal as a new operator + )) + .labelled("name") } /// Decide if a string can be an operator. Operators can include digits and text, just not at the /// start. pub fn is_op>(s: T) -> bool { - return match s.as_ref().chars().next() { - Some(x) => !x.is_alphanumeric(), - None => false - } + return match s.as_ref().chars().next() { + Some(x) => !x.is_alphanumeric(), + None => false + } } diff --git a/src/parse/number.rs b/src/parse/number.rs index 579260c..0059567 100644 --- a/src/parse/number.rs +++ b/src/parse/number.rs @@ -2,111 +2,111 @@ use chumsky::{self, prelude::*, Parser}; use ordered_float::NotNan; fn assert_not_digit(base: u32, c: char) { - if base > (10 + (c as u32 - 'a' as u32)) { - panic!("The character '{}' is a digit in base ({})", c, base) - } + if base > (10 + (c as u32 - 'a' as u32)) { + panic!("The character '{}' is a digit in base ({})", c, base) + } } /// Parse an arbitrarily grouped sequence of digits starting with an underscore. /// /// TODO: this should use separated_by and parse the leading group too fn separated_digits_parser(base: u32) -> impl Parser> { - just('_') - .ignore_then(text::digits(base)) - .repeated() - .map(|sv| sv.iter().flat_map(|s| s.chars()).collect()) + just('_') + .ignore_then(text::digits(base)) + .repeated() + .map(|sv| sv.iter().flat_map(|s| s.chars()).collect()) } /// parse a grouped uint /// /// Not to be confused with [int_parser] which does a lot more fn uint_parser(base: u32) -> impl Parser> { - text::int(base) - .then(separated_digits_parser(base)) - .map(move |(s1, s2): (String, String)| { - u64::from_str_radix(&(s1 + &s2), base).unwrap() - }) + text::int(base) + .then(separated_digits_parser(base)) + .map(move |(s1, s2): (String, String)| { + u64::from_str_radix(&(s1 + &s2), base).unwrap() + }) } /// parse exponent notation, or return 0 as the default exponent. /// The exponent is always in decimal. fn pow_parser() -> impl Parser> { - choice(( - just('p') - .ignore_then(text::int(10)) - .map(|s: String| s.parse().unwrap()), - just("p-") - .ignore_then(text::int(10)) - .map(|s: String| -s.parse::().unwrap()), - )).or_else(|_| Ok(0)) + choice(( + just('p') + .ignore_then(text::int(10)) + .map(|s: String| s.parse().unwrap()), + just("p-") + .ignore_then(text::int(10)) + .map(|s: String| -s.parse::().unwrap()), + )).or_else(|_| Ok(0)) } /// returns a mapper that converts a mantissa and an exponent into an uint /// /// TODO it panics if it finds a negative exponent fn nat2u(base: u64) -> impl Fn((u64, i32),) -> u64 { - move |(val, exp)| { - if exp == 0 {val} - else {val * base.checked_pow(exp.try_into().unwrap()).unwrap()} - } + 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 { - move |(val, exp)| { - if exp == 0 {val} - else {val * (base as f64).powf(exp.try_into().unwrap())} - } + move |(val, exp)| { + if exp == 0 {val} + else {val * (base as f64).powf(exp.try_into().unwrap())} + } } /// parse an uint from exponential notation (panics if 'p' is a digit in base) fn pow_uint_parser(base: u32) -> impl Parser> { - assert_not_digit(base, 'p'); - uint_parser(base).then(pow_parser()).map(nat2u(base.into())) + assert_not_digit(base, 'p'); + uint_parser(base).then(pow_parser()).map(nat2u(base.into())) } /// parse an uint from a base determined by its prefix or lack thereof /// /// Not to be convused with [uint_parser] which is a component of it. pub fn int_parser() -> impl Parser> { - choice(( - just("0b").ignore_then(pow_uint_parser(2)), - just("0x").ignore_then(pow_uint_parser(16)), - just('0').ignore_then(pow_uint_parser(8)), - pow_uint_parser(10), // Dec has no prefix - )) + choice(( + just("0b").ignore_then(pow_uint_parser(2)), + just("0x").ignore_then(pow_uint_parser(16)), + just('0').ignore_then(pow_uint_parser(8)), + pow_uint_parser(10), // Dec has no prefix + )) } /// parse a float from dot notation fn dotted_parser(base: u32) -> impl Parser, Error = Simple> { - uint_parser(base) - .then( - just('.').ignore_then( - text::digits(base).then(separated_digits_parser(base)) - ).map(move |(frac1, frac2)| { - let frac = frac1 + &frac2; - let frac_num = u64::from_str_radix(&frac, base).unwrap() as f64; - let dexp = base.pow(frac.len().try_into().unwrap()); - frac_num / dexp as f64 - }).or_not().map(|o| o.unwrap_or_default()) - ).try_map(|(wh, f), s| { - NotNan::new(wh as f64 + f).map_err(|_| Simple::custom(s, "Float literal evaluates to NaN")) - }) + uint_parser(base) + .then( + just('.').ignore_then( + text::digits(base).then(separated_digits_parser(base)) + ).map(move |(frac1, frac2)| { + let frac = frac1 + &frac2; + let frac_num = u64::from_str_radix(&frac, base).unwrap() as f64; + let dexp = base.pow(frac.len().try_into().unwrap()); + frac_num / dexp as f64 + }).or_not().map(|o| o.unwrap_or_default()) + ).try_map(|(wh, f), s| { + NotNan::new(wh as f64 + f).map_err(|_| Simple::custom(s, "Float literal evaluates to NaN")) + }) } /// parse a float from dotted and optionally also exponential notation fn pow_float_parser(base: u32) -> impl Parser, Error = Simple> { - assert_not_digit(base, 'p'); - dotted_parser(base).then(pow_parser()).map(nat2f(base.into())) + assert_not_digit(base, 'p'); + dotted_parser(base).then(pow_parser()).map(nat2f(base.into())) } /// parse a float with dotted and optionally exponential notation from a base determined by its /// prefix pub fn float_parser() -> impl Parser, Error = Simple> { - choice(( - just("0b").ignore_then(pow_float_parser(2)), - just("0x").ignore_then(pow_float_parser(16)), - just('0').ignore_then(pow_float_parser(8)), - pow_float_parser(10), - )).labelled("float") + choice(( + just("0b").ignore_then(pow_float_parser(2)), + just("0x").ignore_then(pow_float_parser(16)), + just('0').ignore_then(pow_float_parser(8)), + pow_float_parser(10), + )).labelled("float") } diff --git a/src/parse/parse.rs b/src/parse/parse.rs index 1117c6d..432f430 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -11,58 +11,58 @@ use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry}; #[derive(Error, Debug, Clone)] pub enum ParseError { - #[error("Could not tokenize {0:?}")] - Lex(Vec>), - #[error("Could not parse {0:#?}")] - Ast(Vec>) + #[error("Could not tokenize {0:?}")] + Lex(Vec>), + #[error("Could not parse {0:#?}")] + Ast(Vec>) } pub fn parse<'a, Iter, S, Op>(ops: &[Op], stream: S) -> Result, ParseError> where - Op: 'a + AsRef + Clone, - Iter: Iterator)> + 'a, - 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) = 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)>(); - 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 { - Ok(r) => (Some(r), vec![]), - Err(e) => (None, e) - }).unzip::<_, _, Vec<_>, Vec<_>>(); - let total_err = errors_per_line.into_iter() - .flat_map(Vec::into_iter) - .collect::>(); - if !total_err.is_empty() { Err(ParseError::Ast(total_err)) } - else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) } + Op: 'a + AsRef + Clone, + Iter: Iterator)> + 'a, + 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) = 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)>(); + 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 { + Ok(r) => (Some(r), vec![]), + Err(e) => (None, e) + }).unzip::<_, _, Vec<_>, Vec<_>>(); + let total_err = errors_per_line.into_iter() + .flat_map(Vec::into_iter) + .collect::>(); + 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: &[FileEntry]) -> Result, ParseError> where - Op: 'a + AsRef + Clone, - Iter: Iterator)> + 'a, - S: Into, Iter>> { - let result = parse(ops, stream)?; - 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 { - *source = s2.clone() - } else { - panic!("Preparse and reparse received different row types!") - } - } - output - }).collect()) + Op: 'a + AsRef + Clone, + Iter: Iterator)> + 'a, + S: Into, Iter>> { + let result = parse(ops, stream)?; + 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 { + *source = s2.clone() + } else { + panic!("Preparse and reparse received different row types!") + } + } + output + }).collect()) } diff --git a/src/parse/sourcefile.rs b/src/parse/sourcefile.rs index 82122d6..08d55cd 100644 --- a/src/parse/sourcefile.rs +++ b/src/parse/sourcefile.rs @@ -16,50 +16,50 @@ use ordered_float::NotNan; /// Anything we might encounter in a file #[derive(Debug, Clone)] pub enum FileEntry { - Import(Vec), - Comment(String), - Rule(Rule, bool), - Export(Vec>) + Import(Vec), + Comment(String), + Rule(Rule, bool), + Export(Vec>) } fn visit_all_names_clause_recur<'a, F>( - clause: &'a Clause, - binds: Stackframe, - cb: &mut F + clause: &'a Clause, + binds: Stackframe, + 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) - } - } - _ => (), + 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 @@ -68,88 +68,88 @@ fn visit_all_names_clause_recur<'a, F>( /// /// TODO: find a way to exclude parameters fn visit_all_names_expr_recur<'a, F>( - expr: &'a Expr, - binds: Stackframe, - cb: &mut F + expr: &'a Expr, + binds: Stackframe, + 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); - } + 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 + 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, NotNan, Vec), Error = Simple> { - xpr_parser().repeated() - .then(enum_parser!(Lexeme::Rule)) - .then(xpr_parser().repeated()) - // .map(|((lhs, prio), rhs)| ) - .map(|((a, b), c)| (a, b, c)) - .labelled("Rule") + xpr_parser().repeated() + .then(enum_parser!(Lexeme::Rule)) + .then(xpr_parser().repeated()) + // .map(|((lhs, prio), rhs)| ) + .map(|((a, b), c)| (a, b, c)) + .labelled("Rule") } pub fn line_parser() -> impl Parser> { - choice(( - // In case the usercode wants to parse doc - enum_parser!(Lexeme >> FileEntry; Comment), - just(Lexeme::name("import")) - .ignore_then(import_parser().map(FileEntry::Import)) - .then_ignore(enum_parser!(Lexeme::Comment)), - just(Lexeme::name("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]) - .separated_by(just(Lexeme::name(","))) - .delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('('))) - ).map(FileEntry::Export) - ).or(rule_parser().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: to_mrc_slice(source), - prio, - target: to_mrc_slice(target) - }, false)), - )) + choice(( + // In case the usercode wants to parse doc + enum_parser!(Lexeme >> FileEntry; Comment), + just(Lexeme::name("import")) + .ignore_then(import_parser().map(FileEntry::Import)) + .then_ignore(enum_parser!(Lexeme::Comment)), + just(Lexeme::name("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]) + .separated_by(just(Lexeme::name(","))) + .delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('('))) + ).map(FileEntry::Export) + ).or(rule_parser().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: 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: &[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() + 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 + src: I ) -> impl Iterator + 'a where I: Iterator + 'a { - src.filter_map(|ent| match ent { - FileEntry::Import(impv) => Some(impv.iter()), - _ => None - }).flatten() + src.filter_map(|ent| match ent { + FileEntry::Import(impv) => Some(impv.iter()), + _ => None + }).flatten() } diff --git a/src/parse/string.rs b/src/parse/string.rs index d86367d..9d56f26 100644 --- a/src/parse/string.rs +++ b/src/parse/string.rs @@ -2,45 +2,45 @@ use chumsky::{self, prelude::*, Parser}; /// Parses a text character that is not the specified delimiter fn text_parser(delim: char) -> impl Parser> { - // Copied directly from Chumsky's JSON example. - let escape = just('\\').ignore_then( - just('\\') - .or(just('/')) - .or(just('"')) - .or(just('b').to('\x08')) - .or(just('f').to('\x0C')) - .or(just('n').to('\n')) - .or(just('r').to('\r')) - .or(just('t').to('\t')) - .or(just('u').ignore_then( - filter(|c: &char| c.is_ascii_hexdigit()) - .repeated() - .exactly(4) - .collect::() - .validate(|digits, span, emit| { - char::from_u32(u32::from_str_radix(&digits, 16).unwrap()) - .unwrap_or_else(|| { - emit(Simple::custom(span, "invalid unicode character")); - '\u{FFFD}' // unicode replacement character - }) - }), - )), - ); - filter(move |&c| c != '\\' && c != delim).or(escape) + // Copied directly from Chumsky's JSON example. + let escape = just('\\').ignore_then( + just('\\') + .or(just('/')) + .or(just('"')) + .or(just('b').to('\x08')) + .or(just('f').to('\x0C')) + .or(just('n').to('\n')) + .or(just('r').to('\r')) + .or(just('t').to('\t')) + .or(just('u').ignore_then( + filter(|c: &char| c.is_ascii_hexdigit()) + .repeated() + .exactly(4) + .collect::() + .validate(|digits, span, emit| { + char::from_u32(u32::from_str_radix(&digits, 16).unwrap()) + .unwrap_or_else(|| { + emit(Simple::custom(span, "invalid unicode character")); + '\u{FFFD}' // unicode replacement character + }) + }), + )), + ); + filter(move |&c| c != '\\' && c != delim).or(escape) } /// Parse a character literal between single quotes pub fn char_parser() -> impl Parser> { - just('\'').ignore_then(text_parser('\'')).then_ignore(just('\'')) + just('\'').ignore_then(text_parser('\'')).then_ignore(just('\'')) } /// Parse a string between double quotes pub fn str_parser() -> impl Parser> { - just('"') - .ignore_then( - text_parser('"').map(Some) - .or(just("\\\n").map(|_| None)) // Newlines preceded by backslashes are ignored. - .repeated() - ).then_ignore(just('"')) - .flatten().collect() + just('"') + .ignore_then( + text_parser('"').map(Some) + .or(just("\\\n").map(|_| None)) // Newlines preceded by backslashes are ignored. + .repeated() + ).then_ignore(just('"')) + .flatten().collect() } diff --git a/src/project/file_loader.rs b/src/project/file_loader.rs index b412308..980f898 100644 --- a/src/project/file_loader.rs +++ b/src/project/file_loader.rs @@ -9,43 +9,43 @@ use super::loaded::Loaded; #[derive(Clone, Debug)] pub enum LoadingError { - IOErr(Rc), - UnknownNode(String), - Missing(String) + IOErr(Rc), + UnknownNode(String), + Missing(String) } impl From for LoadingError { - fn from(inner: io::Error) -> Self { - LoadingError::IOErr(Rc::new(inner)) - } + fn from(inner: io::Error) -> Self { + LoadingError::IOErr(Rc::new(inner)) + } } 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() { - return Ok(Loaded::Namespace( - 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()) - } else if typ.is_file() && path.extension()? == "orc" { - Some(path.file_stem()?.to_string_lossy().into_owned()) - } else { None } - }) - .collect() - )) - } - let orcfile = dirpath.with_extension("orc"); - if orcfile.is_file() { - read_to_string(orcfile).map(Loaded::Module).map_err(LoadingError::from) - } else { - let pathstr = dirpath.to_string_lossy().into_owned(); - Err(if dirpath.exists() { LoadingError::UnknownNode(pathstr) } - else { LoadingError::Missing(pathstr) }) - } + move |path| { + let dirpath = proj.join(path.join("/")); + if dirpath.is_dir() || dirpath.is_symlink() { + return Ok(Loaded::Namespace( + 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()) + } else if typ.is_file() && path.extension()? == "orc" { + Some(path.file_stem()?.to_string_lossy().into_owned()) + } else { None } + }) + .collect() + )) } + let orcfile = dirpath.with_extension("orc"); + if orcfile.is_file() { + read_to_string(orcfile).map(Loaded::Module).map_err(LoadingError::from) + } else { + let pathstr = dirpath.to_string_lossy().into_owned(); + Err(if dirpath.exists() { LoadingError::UnknownNode(pathstr) } + else { LoadingError::Missing(pathstr) }) + } + } } diff --git a/src/project/loaded.rs b/src/project/loaded.rs index 7e5a00c..a1df463 100644 --- a/src/project/loaded.rs +++ b/src/project/loaded.rs @@ -1,5 +1,5 @@ #[derive(Debug, Clone)] pub enum Loaded { - Module(String), - Namespace(Vec), + Module(String), + Namespace(Vec), } \ No newline at end of file diff --git a/src/project/module_error.rs b/src/project/module_error.rs index a346160..fae1eb6 100644 --- a/src/project/module_error.rs +++ b/src/project/module_error.rs @@ -6,26 +6,26 @@ use super::name_resolver::ResolutionError; #[derive(Error, Debug, Clone)] pub enum ModuleError where ELoad: Clone { - #[error("Resolution cycle")] - ResolutionCycle, - #[error("File not found: {0}")] - Load(ELoad), - #[error("Failed to parse: {0:?}")] - Syntax(ParseError), - #[error("Not a module")] - None + #[error("Resolution cycle")] + ResolutionCycle, + #[error("File not found: {0}")] + Load(ELoad), + #[error("Failed to parse: {0:?}")] + Syntax(ParseError), + #[error("Not a module")] + None } impl From for ModuleError where T: Clone { - fn from(pars: ParseError) -> Self { Self::Syntax(pars) } + fn from(pars: ParseError) -> Self { Self::Syntax(pars) } } impl From>> for ModuleError where T: Clone { - fn from(res: ResolutionError>) -> Self { - match res { - ResolutionError::Cycle(_) => ModuleError::ResolutionCycle, - ResolutionError::NoModule(_) => ModuleError::None, - ResolutionError::Delegate(d) => d - } + fn from(res: ResolutionError>) -> Self { + match res { + ResolutionError::Cycle(_) => ModuleError::ResolutionCycle, + ResolutionError::NoModule(_) => ModuleError::None, + ResolutionError::Delegate(d) => d } + } } \ No newline at end of file diff --git a/src/project/name_resolver.rs b/src/project/name_resolver.rs index 7176ec6..543a0b1 100644 --- a/src/project/name_resolver.rs +++ b/src/project/name_resolver.rs @@ -10,12 +10,12 @@ type ImportMap = HashMap>; #[derive(Debug, Clone, Error)] pub enum ResolutionError { - #[error("Reference cycle at {0:?}")] - Cycle(Vec>), - #[error("No module provides {0:?}")] - NoModule(Mrc<[String]>), - #[error(transparent)] - Delegate(#[from] Err) + #[error("Reference cycle at {0:?}")] + Cycle(Vec>), + #[error("No module provides {0:?}")] + NoModule(Mrc<[String]>), + #[error(transparent)] + Delegate(#[from] Err) } type ResolutionResult = Result, ResolutionError>; @@ -24,108 +24,108 @@ type ResolutionResult = Result, ResolutionError>; /// 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, ResolutionResult>, - get_modname: FSplit, - get_imports: FImps + cache: HashMap, ResolutionResult>, + get_modname: FSplit, + get_imports: FImps } impl NameResolver where - FSplit: FnMut(Mrc<[String]>) -> Option>, - FImps: FnMut(Mrc<[String]>) -> Result, - E: Clone + FSplit: FnMut(Mrc<[String]>) -> Option>, + FImps: FnMut(Mrc<[String]>) -> Result, + E: Clone { - pub fn new(get_modname: FSplit, get_imports: FImps) -> Self { - Self { - cache: HashMap::new(), - get_modname, - get_imports - } + pub fn new(get_modname: FSplit, get_imports: FImps) -> Self { + Self { + cache: HashMap::new(), + get_modname, + get_imports } + } - /// Obtains a symbol's originnal name - /// Uses a substack to detect loops - fn find_origin_rec( - &mut self, - 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)(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 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.as_ref() == new_sym.as_slice()) { - Err(ResolutionError::Cycle(import_path.iter().map(Mrc::clone).collect())) - } else { - 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, result.clone()); - result + /// Obtains a symbol's originnal name + /// Uses a substack to detect loops + fn find_origin_rec( + &mut self, + 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)(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 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.as_ref() == new_sym.as_slice()) { + Err(ResolutionError::Cycle(import_path.iter().map(Mrc::clone).collect())) + } else { + 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, 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_exprv_rec(&mut self, exv: &[Expr]) -> Result, ResolutionError> { + exv.iter().map(|ex| self.process_expression_rec(ex)).collect() + } - 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_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, 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(), - 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(), - 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(Mrc::clone(qualified))? - }, - x => x.clone() - }) - } + fn process_clause_rec(&mut self, tok: &Clause) -> Result> { + 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::, ResolutionError>>()? + )), + 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())?) + ), + 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())?) + ), + Clause::Name{local, qualified} => Clause::Name{ + local: local.clone(), + qualified: self.find_origin(Mrc::clone(qualified))? + }, + x => x.clone() + }) + } - fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result> { - Ok(Expr( - self.process_clause_rec(token)?, - typ.iter().map(|t| self.process_clause_rec(t)).collect::>()? - )) - } + fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result> { + Ok(Expr( + self.process_clause_rec(token)?, + typ.iter().map(|t| self.process_clause_rec(t)).collect::>()? + )) + } - pub fn find_origin(&mut self, symbol: Mrc<[String]>) -> Result, ResolutionError> { - self.find_origin_rec(Mrc::clone(&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)] - pub fn process_clause(&mut self, clause: &Clause) -> Result> { - self.process_clause_rec(clause) - } + #[allow(dead_code)] + pub fn process_clause(&mut self, clause: &Clause) -> Result> { + self.process_clause_rec(clause) + } - pub fn process_expression(&mut self, ex: &Expr) -> Result> { - self.process_expression_rec(ex) - } + pub fn process_expression(&mut self, ex: &Expr) -> Result> { + self.process_expression_rec(ex) + } } diff --git a/src/project/prefix.rs b/src/project/prefix.rs index 0e94c6b..d22bb4f 100644 --- a/src/project/prefix.rs +++ b/src/project/prefix.rs @@ -7,35 +7,35 @@ use crate::{ast::{Expr, Clause}, utils::{collect_to_mrc, to_mrc_slice}}; /// Produce a Token object for any value of Expr other than Typed. /// Called by [#prefix] which handles Typed. fn prefix_clause( - expr: &Clause, - namespace: Mrc<[String]> + expr: &Clause, + namespace: Mrc<[String]> ) -> Clause { - match expr { - 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(), - 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(), - 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: collect_to_mrc(namespace.iter().chain(qualified.iter()).cloned()) - }, - x => x.clone() - } + match expr { + 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(), + 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(), + 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: 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 { - Expr( - prefix_clause(clause, Mrc::clone(&namespace)), - to_mrc_slice(typ.iter().map(|e| prefix_clause(e, Mrc::clone(&namespace))).collect()) - ) + Expr( + prefix_clause(clause, Mrc::clone(&namespace)), + to_mrc_slice(typ.iter().map(|e| prefix_clause(e, Mrc::clone(&namespace))).collect()) + ) } diff --git a/src/project/rule_collector.rs b/src/project/rule_collector.rs index 918ea4b..e57924d 100644 --- a/src/project/rule_collector.rs +++ b/src/project/rule_collector.rs @@ -18,198 +18,198 @@ type ParseResult = Result>; #[derive(Debug, Clone)] pub struct Module { - pub rules: Vec, - pub exports: Vec, - pub references: Vec> + pub rules: Vec, + pub exports: Vec, + pub references: Vec> } pub type RuleCollectionResult = Result, ModuleError>; pub fn rule_collector( - load_mod: F, - prelude: Vec + load_mod: F, + prelude: Vec ) -> Cache<'static, Mrc<[String]>, RuleCollectionResult> where - F: FnMut(Mrc<[String]>) -> Result, - ELoad: Clone + Debug + 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: Mrc<[String]>, _| - -> ParseResult { - (load_mod_rc.borrow_mut())(path).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 loaded = Rc::clone(&loaded); - move |symbol: Mrc<[String]>, _| -> Result, Vec>> { - let mut errv: Vec> = Vec::new(); - let reg_err = |e, errv: &mut Vec>| { - errv.push(e); - if symbol.len() == errv.len() { Err(errv.clone()) } - else { Ok(()) } - }; - loop { - let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len() - 1]); - match loaded.try_find(&path) { - Ok(imports) => match imports.as_ref() { - Loaded::Module(_) => break Ok(path), - _ => reg_err(ModuleError::None, &mut errv)? - }, - Err(err) => reg_err(err, &mut errv)? - } - } + 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 { + (load_mod_rc.borrow_mut())(path).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 loaded = Rc::clone(&loaded); + move |symbol: Mrc<[String]>, _| -> Result, Vec>> { + let mut errv: Vec> = Vec::new(); + let reg_err = |e, errv: &mut Vec>| { + errv.push(e); + if symbol.len() == errv.len() { Err(errv.clone()) } + else { Ok(()) } + }; + loop { + let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len() - 1]); + match loaded.try_find(&path) { + Ok(imports) => match imports.as_ref() { + Loaded::Module(_) => break Ok(path), + _ => reg_err(ModuleError::None, &mut errv)? + }, + Err(err) => reg_err(err, &mut errv)? } - })); - // Preliminarily parse a file, substitution rules and imports are valid - let preparsed = Rc::new(Cache::new({ - let loaded = Rc::clone(&loaded); - let prelude2 = prelude.clone(); - 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())?) - } else {Err(ModuleError::None)} + } + } + })); + // Preliminarily parse a file, substitution rules and imports are valid + let preparsed = Rc::new(Cache::new({ + let loaded = Rc::clone(&loaded); + let prelude2 = prelude.clone(); + 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())?) + } else {Err(ModuleError::None)} + } + })); + // Collect all toplevel names exported from a given file + let exports = Rc::new(Cache::new({ + let loaded = Rc::clone(&loaded); + let preparsed = Rc::clone(&preparsed); + move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { + 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) + .into_iter() + .map(|n| n[0].clone()) + .collect()) + } + })); + // Collect all toplevel names imported by a given file + let imports = Rc::new(Cache::new({ + let preparsed = Rc::clone(&preparsed); + let exports = Rc::clone(&exports); + 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(); + 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(), Mrc::clone(&imp.path)); + } + } else { + for exp in export.as_ref() { + imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path)); + } } - })); - // Collect all toplevel names exported from a given file - let exports = Rc::new(Cache::new({ - let loaded = Rc::clone(&loaded); - let preparsed = Rc::clone(&preparsed); - move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { - 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) - .into_iter() - .map(|n| n[0].clone()) - .collect()) - } - })); - // Collect all toplevel names imported by a given file - let imports = Rc::new(Cache::new({ - let preparsed = Rc::clone(&preparsed); - let exports = Rc::clone(&exports); - 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(); - 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(), Mrc::clone(&imp.path)); - } - } else { - for exp in export.as_ref() { - imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path)); - } - } - } - Ok(imported_symbols) - } - })); - // Final parse, operators are correctly separated - let parsed = Rc::new(Cache::new({ - let preparsed = Rc::clone(&preparsed); - let imports = Rc::clone(&imports); - let loaded = Rc::clone(&loaded); - move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { - let imported_ops: Vec = - imports.try_find(&path)? - .keys() - .chain(prelude.iter()) - .filter(|s| parse::is_op(s)) - .cloned() - .collect(); - // let parser = file_parser(&prelude, &imported_ops); - let pre = preparsed.try_find(&path)?; - if let Loaded::Module(source) = loaded.try_find(&path)?.as_ref() { - Ok(parse::reparse(&imported_ops, source.as_str(), &pre)?) - } else { Err(ModuleError::None) } - } - })); - let name_resolver_rc = RefCell::new(NameResolver::new({ - let modname = Rc::clone(&modname); - move |path| { - 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()) - } - })); - // 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: 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, 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: to_mrc_slice(rule.source.iter() - .map(|ex| name_resolver.process_expression(ex)) - .collect::, _>>()?), - target: to_mrc_slice(rule.target.iter() - .map(|ex| name_resolver.process_expression(ex)) - .collect::, _>>()?), - ..rule - })) - .collect::, ELoad>>()?, - exports: exports.try_find(&path)?.as_ref().clone(), - references: imports.try_find(&path)? - .values() - .filter_map(|imps| { - modname.try_find(imps).ok().map(|r| r.as_ref().clone()) - }) - .collect() - }; - Ok(module) - } - })); - Cache::new({ - let resolved = Rc::clone(&resolved); - 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(); - pending.push_back(path); - while let Some(el) = pending.pop_front() { - let resolved = resolved.try_find(&el)?; - processed.insert(el.clone()); - pending.extend( - resolved.references.iter() - .filter(|&v| !processed.contains(v)) - .cloned() - ); - rules.extend( - resolved.rules.iter().cloned() - ) - }; - Ok(rules) - } - }) + } + Ok(imported_symbols) + } + })); + // Final parse, operators are correctly separated + let parsed = Rc::new(Cache::new({ + let preparsed = Rc::clone(&preparsed); + let imports = Rc::clone(&imports); + let loaded = Rc::clone(&loaded); + move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { + let imported_ops: Vec = + imports.try_find(&path)? + .keys() + .chain(prelude.iter()) + .filter(|s| parse::is_op(s)) + .cloned() + .collect(); + // let parser = file_parser(&prelude, &imported_ops); + let pre = preparsed.try_find(&path)?; + if let Loaded::Module(source) = loaded.try_find(&path)?.as_ref() { + Ok(parse::reparse(&imported_ops, source.as_str(), &pre)?) + } else { Err(ModuleError::None) } + } + })); + let name_resolver_rc = RefCell::new(NameResolver::new({ + let modname = Rc::clone(&modname); + move |path| { + 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()) + } + })); + // 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: 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, 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: to_mrc_slice(rule.source.iter() + .map(|ex| name_resolver.process_expression(ex)) + .collect::, _>>()?), + target: to_mrc_slice(rule.target.iter() + .map(|ex| name_resolver.process_expression(ex)) + .collect::, _>>()?), + ..rule + })) + .collect::, ELoad>>()?, + exports: exports.try_find(&path)?.as_ref().clone(), + references: imports.try_find(&path)? + .values() + .filter_map(|imps| { + modname.try_find(imps).ok().map(|r| r.as_ref().clone()) + }) + .collect() + }; + Ok(module) + } + })); + Cache::new({ + let resolved = Rc::clone(&resolved); + 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(); + pending.push_back(path); + while let Some(el) = pending.pop_front() { + let resolved = resolved.try_find(&el)?; + processed.insert(el.clone()); + pending.extend( + resolved.references.iter() + .filter(|&v| !processed.contains(v)) + .cloned() + ); + rules.extend( + resolved.rules.iter().cloned() + ) + }; + Ok(rules) + } + }) } diff --git a/src/representations/ast.rs b/src/representations/ast.rs index f94d2fb..e611d89 100644 --- a/src/representations/ast.rs +++ b/src/representations/ast.rs @@ -4,7 +4,7 @@ use ordered_float::NotNan; use std::{hash::Hash, intrinsics::likely}; use std::fmt::Debug; use crate::utils::mrc_empty_slice; -use crate::{executor::{ExternFn, Atom}, utils::one_mrc_slice}; +use crate::{foreign::{ExternFn, Atom}, utils::one_mrc_slice}; use super::Literal; @@ -12,178 +12,178 @@ use super::Literal; #[derive(PartialEq, Eq, Hash)] pub struct Expr(pub Clause, pub Mrc<[Clause]>); impl Expr { - pub fn into_clause(self) -> Clause { - if likely(self.1.len() == 0) { self.0 } - else { Clause::S('(', one_mrc_slice(self)) } - } + pub fn into_clause(self) -> Clause { + if likely(self.1.len() == 0) { self.0 } + else { Clause::S('(', one_mrc_slice(self)) } + } } impl Clone for Expr { - fn clone(&self) -> Self { - Self(self.0.clone(), Mrc::clone(&self.1)) - } + fn clone(&self) -> Self { + Self(self.0.clone(), Mrc::clone(&self.1)) + } } impl Debug for Expr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Expr(val, typ) = self; - write!(f, "{:?}", val)?; - for typ in typ.as_ref() { - write!(f, ":{:?}", typ)? - } - Ok(()) + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Expr(val, typ) = self; + write!(f, "{:?}", val)?; + for typ in typ.as_ref() { + write!(f, ":{:?}", typ)? } + Ok(()) + } } /// An S-expression as read from a source file #[derive(PartialEq, Eq, Hash)] pub enum Clause { - /// A literal value, eg. `1`, `"hello"` - Literal(Literal), - /// A c-style name or an operator, eg. `+`, `i`, `foo::bar` - Name{ - local: Option, - 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 @Int` - Explicit(Mrc), - /// A function expression, eg. `\x. x + 1` - Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>), - /// A parameterized expression with type inference, eg. `@T. T -> T` - Auto(Option, Mrc<[Expr]>, Mrc<[Expr]>), - /// An opaque function, eg. an effectful function employing CPS. - /// Preferably wrap these in an Orchid monad. - ExternFn(ExternFn), - /// An opaque non-callable value, eg. a file handle. - /// Preferably wrap these in an Orchid structure. - Atom(Atom), - /// 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) - /// nonzero is whether the vectorial matches 1..n or 0..n tokens - vec: Option<(usize, bool)> - }, + /// A literal value, eg. `1`, `"hello"` + Literal(Literal), + /// A c-style name or an operator, eg. `+`, `i`, `foo::bar` + Name{ + local: Option, + 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 @Int` + Explicit(Mrc), + /// A function expression, eg. `\x. x + 1` + Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>), + /// A parameterized expression with type inference, eg. `@T. T -> T` + Auto(Option, Mrc<[Expr]>, Mrc<[Expr]>), + /// An opaque function, eg. an effectful function employing CPS. + /// Preferably wrap these in an Orchid monad. + ExternFn(ExternFn), + /// An opaque non-callable value, eg. a file handle. + /// Preferably wrap these in an Orchid structure. + Atom(Atom), + /// 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) + /// nonzero is whether the vectorial matches 1..n or 0..n tokens + vec: Option<(usize, bool)> + }, } impl Clause { - pub fn body(&self) -> Option> { - match self { - Self::Auto(_, _, body) | - Self::Lambda(_, _, body) | - Self::S(_, body) => Some(Mrc::clone(body)), - _ => None - } + pub fn body(&self) -> Option> { + match self { + Self::Auto(_, _, body) | + Self::Lambda(_, _, body) | + Self::S(_, body) => Some(Mrc::clone(body)), + _ => None } - pub fn typ(&self) -> Option> { - match self { - Self::Auto(_, typ, _) | Self::Lambda(_, typ, _) => Some(Mrc::clone(typ)), - _ => 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()) } - } - pub fn from_exprv(exprv: Mrc<[Expr]>) -> Option { - if exprv.len() == 0 { None } - else if exprv.len() == 1 { Some(exprv[0].clone().into_clause()) } - else { Some(Self::S('(', exprv)) } + } + pub fn typ(&self) -> Option> { + match self { + Self::Auto(_, typ, _) | Self::Lambda(_, typ, _) => Some(Mrc::clone(typ)), + _ => 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()) } + } + pub fn from_exprv(exprv: Mrc<[Expr]>) -> Option { + if exprv.len() == 0 { None } + else if exprv.len() == 1 { Some(exprv[0].clone().into_clause()) } + else { Some(Self::S('(', exprv)) } + } } impl Clone for Clause { - fn clone(&self) -> Self { - 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) - }, - 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::Literal(l) => Self::Literal(l.clone()), - Self::ExternFn(nc) => Self::ExternFn(nc.clone()), - Self::Atom(a) => Self::Atom(a.clone()), - Self::Explicit(expr) => Self::Explicit(Mrc::clone(expr)) - } + fn clone(&self) -> Self { + 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) + }, + 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::Literal(l) => Self::Literal(l.clone()), + Self::ExternFn(nc) => Self::ExternFn(nc.clone()), + Self::Atom(a) => Self::Atom(a.clone()), + Self::Explicit(expr) => Self::Explicit(Mrc::clone(expr)) } + } } 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), - None => f.write_str(" "), - }? } - Ok(()) + for item in Itertools::intersperse(it.map(Some), None) { match item { + Some(expr) => write!(f, "{:?}", expr), + None => f.write_str(" "), + }? } + Ok(()) } impl Debug for Clause { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Literal(arg0) => write!(f, "{:?}", arg0), - Self::Name{local, qualified} => - 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())?; - fmt_expr_seq(&mut items.iter(), f)?; - f.write_str(match del { - '(' => ")", '[' => "]", '{' => "}", - _ => "CLOSING_DELIM" - }) - }, - 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) - }, - Self::Auto(name, 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) - }, - 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::ExternFn(nc) => write!(f, "{nc:?}"), - Self::Atom(a) => write!(f, "{a:?}"), - Self::Explicit(expr) => write!(f, "@{:?}", expr.as_ref()) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Literal(arg0) => write!(f, "{:?}", arg0), + Self::Name{local, qualified} => + 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())?; + fmt_expr_seq(&mut items.iter(), f)?; + f.write_str(match del { + '(' => ")", '[' => "]", '{' => "}", + _ => "CLOSING_DELIM" + }) + }, + 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) + }, + Self::Auto(name, 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) + }, + 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::ExternFn(nc) => write!(f, "{nc:?}"), + Self::Atom(a) => write!(f, "{a:?}"), + Self::Explicit(expr) => write!(f, "@{:?}", expr.as_ref()) } + } } /// A substitution rule as read from the source #[derive(PartialEq, Eq, Hash)] pub struct Rule { - pub source: Mrc<[Expr]>, - pub prio: NotNan, - pub target: Mrc<[Expr]> + pub source: Mrc<[Expr]>, + pub prio: NotNan, + 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) - } + 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) - } + 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/representations/ast_to_typed.rs b/src/representations/ast_to_typed.rs index 807233a..25fd769 100644 --- a/src/representations/ast_to_typed.rs +++ b/src/representations/ast_to_typed.rs @@ -1,178 +1,171 @@ use mappable_rc::Mrc; -use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap}; +use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap, one_mrc_slice}; use super::{ast, typed, get_name::get_name}; #[derive(Clone)] 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 - 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 - ExplicitBottomKind, - /// 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 - 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) - /// - /// [expr] handles this case, so it's only really possible to get this - /// error if you're calling [clause] directly - ExprToClause(typed::Expr), - /// @ tokens only ever occur between a function and a parameter - NonInfixAt + /// `()` 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 + 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 + ExplicitBottomKind, + /// 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 + 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) + /// + /// [expr] handles this case, so it's only really possible to get this + /// error if you're calling [clause] directly + ExprToClause(typed::Expr), + /// @ tokens only ever occur between a function and a parameter + NonInfixAt } /// Try to convert an expression from AST format to typed lambda pub fn expr(expr: &ast::Expr) -> Result { - Ok(expr_rec(expr, ProtoMap::new(), None)?.0) + Ok(expr_rec(expr, ProtoMap::new(), None)?.0) } /// Try and convert a single clause from AST format to typed lambda pub fn clause(clause: &ast::Clause) -> Result { - Ok(clause_rec(clause, ProtoMap::new(), None)?.0) + Ok(clause_rec(clause, ProtoMap::new(), None)?.0) } /// Try and convert a sequence of expressions from AST format to typed lambda pub fn exprv(exprv: &[ast::Expr]) -> Result { - Ok(exprv_rec(exprv, ProtoMap::new(), None)?.0) + Ok(exprv_rec(exprv, ProtoMap::new(), None)?.0) } const NAMES_INLINE_COUNT:usize = 3; /// Recursive state of [exprv] -fn exprv_rec<'a>( - v: &'a [ast::Expr], - names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, - explicits: Option<&Stackframe>>, +fn exprv_rec( + v: &[ast::Expr], + names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>, + explicits: Option<&Stackframe>>, ) -> Result<(typed::Expr, usize), Error> { - let (last, rest) = v.split_last().ok_or(Error::EmptyS)?; - if rest.len() == 0 {return expr_rec(&v[0], names, explicits)} - if let ast::Expr(ast::Clause::Explicit(inner), empty_slice) = last { - assert!(empty_slice.len() == 0, - "It is assumed that Explicit nodes can never have type annotations as the \ - wrapped expression node matches all trailing colons." - ); - let (x, _) = expr_rec(inner.as_ref(), names.clone(), None)?; - let new_explicits = Stackframe::opush(explicits, Mrc::new(x)); - let (body, used_expls) = exprv_rec(rest, names, Some(&new_explicits))?; - Ok((body, used_expls.saturating_sub(1))) - } else { - let (f, f_used_expls) = exprv_rec(rest, names.clone(), explicits)?; - let x_explicits = Stackframe::opop(explicits, f_used_expls); - let (x, x_used_expls) = expr_rec(last, names, x_explicits)?; - Ok((typed::Expr( - typed::Clause::Apply(Mrc::new(f), Mrc::new(x)), - mrc_empty_slice() - ), x_used_expls + f_used_expls)) - } + let (last, rest) = v.split_last().ok_or(Error::EmptyS)?; + if rest.len() == 0 {return expr_rec(&v[0], names, explicits)} + if let ast::Expr(ast::Clause::Explicit(inner), empty_slice) = last { + assert!(empty_slice.len() == 0, + "It is assumed that Explicit nodes can never have type annotations as the \ + wrapped expression node matches all trailing colons." + ); + let (x, _) = expr_rec(inner.as_ref(), names, None)?; + let new_explicits = Some(&Stackframe::opush(explicits, Mrc::new(x))); + let (body, used_expls) = exprv_rec(rest, names, new_explicits)?; + Ok((body, used_expls.saturating_sub(1))) + } else { + let (f, f_used_expls) = exprv_rec(rest, names, explicits)?; + let x_explicits = Stackframe::opop(explicits, f_used_expls); + let (x, x_used_expls) = expr_rec(last, names, x_explicits)?; + Ok((typed::Expr( + typed::Clause::Apply(Mrc::new(f), Mrc::new(x)), + mrc_empty_slice() + ), x_used_expls + f_used_expls)) + } } /// Recursive state of [expr] -fn expr_rec<'a>( - ast::Expr(val, typ): &'a ast::Expr, - names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, - explicits: Option<&Stackframe>> // known explicit values +fn expr_rec( + ast::Expr(val, typ): &ast::Expr, + names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>, + explicits: Option<&Stackframe>> // known explicit values ) -> Result<(typed::Expr, usize), Error> { // (output, used_explicits) - let typ: Vec = typ.iter() - .map(|c| Ok(clause_rec(c, names.clone(), None)?.0)) - .collect::>()?; - if let ast::Clause::S(paren, body) = val { - if *paren != '(' {return Err(Error::BadGroup(*paren))} - let (typed::Expr(inner, inner_t), used_expls) = exprv_rec( - body.as_ref(), names, explicits - )?; - let new_t = if typ.len() == 0 { inner_t } else { - to_mrc_slice(if inner_t.len() == 0 { typ } else { - inner_t.iter().chain(typ.iter()).cloned().collect() - }) - }; - Ok((typed::Expr(inner, new_t), used_expls)) - } else { - let (cls, used_expls) = clause_rec(&val, names, explicits)?; - Ok((typed::Expr(cls, to_mrc_slice(typ)), used_expls)) - } + let typ: Vec = typ.iter() + .map(|c| Ok(clause_rec(c, names, None)?.0)) + .collect::>()?; + if let ast::Clause::S(paren, body) = val { + if *paren != '(' {return Err(Error::BadGroup(*paren))} + let (typed::Expr(inner, inner_t), used_expls) = exprv_rec(body.as_ref(), names, explicits)?; + let new_t = if typ.len() == 0 { inner_t } else { + to_mrc_slice(if inner_t.len() == 0 { typ } else { + inner_t.iter().chain(typ.iter()).cloned().collect() + }) + }; + Ok((typed::Expr(inner, new_t), used_expls)) + } else { + let (cls, used_expls) = clause_rec(&val, names, explicits)?; + Ok((typed::Expr(cls, to_mrc_slice(typ)), used_expls)) + } } /// Recursive state of [clause] -fn clause_rec<'a>( - cls: &'a ast::Clause, - mut names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, - mut explicits: Option<&Stackframe>> +fn clause_rec( + cls: &ast::Clause, + names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>, + mut explicits: Option<&Stackframe>> ) -> Result<(typed::Clause, usize), Error> { - match cls { // (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail - ast::Clause::ExternFn(e) => Ok((typed::Clause::ExternFn(e.clone()), 0)), - ast::Clause::Atom(a) => Ok((typed::Clause::Atom(a.clone()), 0)), - ast::Clause::Auto(no, t, b) => { - // Allocate id - let id = get_name(); - // Pop an explicit if available - let (value, rest_explicits) = explicits.map( - |Stackframe{ prev, item, .. }| { - (Some(item), *prev) - } - ).unwrap_or_default(); - explicits = rest_explicits; - // Convert the type - let typ = if t.len() == 0 {None} else { - let (typed::Expr(c, t), _) = exprv_rec( - t.as_ref(), names.clone(), None - )?; - if t.len() > 0 {return Err(Error::ExplicitBottomKind)} - else {Some(Mrc::new(c))} - }; - // Traverse body with extended context - if let Some(name) = no {names.set(&&**name, id)} - let (body, used_expls) = exprv_rec( - b.as_ref(), names, explicits - )?; - // Produce a binding instead of an auto if explicit was available - if let Some(known_value) = value { - Ok((typed::Clause::Apply( - typed::Clause::Lambda(id, typ, Mrc::new(body)).wrap(), - Mrc::clone(known_value) - ), used_expls + 1)) - } else { - Ok((typed::Clause::Auto(id, typ, Mrc::new(body)), 0)) - } + match cls { // (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail + ast::Clause::ExternFn(e) => Ok((typed::Clause::ExternFn(e.clone()), 0)), + ast::Clause::Atom(a) => Ok((typed::Clause::Atom(a.clone()), 0)), + ast::Clause::Auto(no, t, b) => { + // Allocate id + let id = get_name(); + // Pop an explicit if available + let (value, rest_explicits) = explicits.map( + |Stackframe{ prev, item, .. }| { + (Some(item), *prev) } - ast::Clause::Lambda(n, t, b) => { - // Allocate id - let id = get_name(); - // Convert the type - let typ = if t.len() == 0 {None} else { - let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names.clone(), None)?; - if t.len() > 0 {return Err(Error::ExplicitBottomKind)} - else {Some(Mrc::new(c))} - }; - names.set(&&**n, id); - let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?; - Ok((typed::Clause::Lambda(id, typ, Mrc::new(body)), used_expls)) - } - ast::Clause::Literal(l) => Ok((typed::Clause::Literal(l.clone()), 0)), - ast::Clause::Name { local: Some(arg), .. } => { - let uid = names.get(&&**arg) - .ok_or_else(|| Error::Unbound(arg.clone()))?; - Ok((typed::Clause::Argument(*uid), 0)) - } - ast::Clause::S(paren, entries) => { - if *paren != '(' {return Err(Error::BadGroup(*paren))} - let (typed::Expr(val, typ), used_expls) = exprv_rec( - entries.as_ref(), names, explicits - )?; - if typ.len() == 0 {Ok((val, used_expls))} - else {Err(Error::ExprToClause(typed::Expr(val, typ)))} - }, - ast::Clause::Name { local: None, .. } => Err(Error::Symbol), - ast::Clause::Placeh { .. } => Err(Error::Placeholder), - ast::Clause::Explicit(..) => Err(Error::NonInfixAt) + ).unwrap_or_default(); + explicits = rest_explicits; + // Convert the type + let typ = if t.len() == 0 {mrc_empty_slice()} else { + let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names, None)?; + if t.len() > 0 {return Err(Error::ExplicitBottomKind)} + else {one_mrc_slice(c)} + }; + // Traverse body with extended context + if let Some(name) = no {names.set(&&**name, (id, true))} + let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?; + // Produce a binding instead of an auto if explicit was available + if let Some(known_value) = value { + Ok((typed::Clause::Apply( + typed::Clause::Lambda(id, typ, Mrc::new(body)).wrap(), + Mrc::clone(known_value) + ), used_expls + 1)) + } else { + Ok((typed::Clause::Auto(id, typ, Mrc::new(body)), 0)) + } } + ast::Clause::Lambda(n, t, b) => { + // Allocate id + let id = get_name(); + // Convert the type + let typ = if t.len() == 0 {mrc_empty_slice()} else { + let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names, None)?; + if t.len() > 0 {return Err(Error::ExplicitBottomKind)} + else {one_mrc_slice(c)} + }; + names.set(&&**n, (id, false)); + let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?; + Ok((typed::Clause::Lambda(id, typ, Mrc::new(body)), used_expls)) + } + ast::Clause::Literal(l) => Ok((typed::Clause::Literal(l.clone()), 0)), + ast::Clause::Name { local: Some(arg), .. } => { + let (uid, is_auto) = names.get(&&**arg) + .ok_or_else(|| Error::Unbound(arg.clone()))?; + let label = if *is_auto {typed::Clause::AutoArg} else {typed::Clause::LambdaArg}; + Ok((label(*uid), 0)) + } + ast::Clause::S(paren, entries) => { + if *paren != '(' {return Err(Error::BadGroup(*paren))} + let (typed::Expr(val, typ), used_expls) = exprv_rec(entries.as_ref(), names, explicits)?; + if typ.len() == 0 {Ok((val, used_expls))} + else {Err(Error::ExprToClause(typed::Expr(val, typ)))} + }, + ast::Clause::Name { local: None, .. } => Err(Error::Symbol), + ast::Clause::Placeh { .. } => Err(Error::Placeholder), + ast::Clause::Explicit(..) => Err(Error::NonInfixAt) + } } \ No newline at end of file diff --git a/src/representations/literal.rs b/src/representations/literal.rs index 6286f4b..c7162c8 100644 --- a/src/representations/literal.rs +++ b/src/representations/literal.rs @@ -4,19 +4,19 @@ use std::fmt::Debug; /// An exact value, read from the AST and unmodified in shape until compilation #[derive(Clone, PartialEq, Eq, Hash)] pub enum Literal { - Num(NotNan), - Int(u64), - Char(char), - Str(String), + Num(NotNan), + Int(u64), + Char(char), + Str(String), } impl Debug for Literal { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Num(arg0) => write!(f, "{:?}", arg0), - Self::Int(arg0) => write!(f, "{:?}", arg0), - Self::Char(arg0) => write!(f, "{:?}", arg0), - Self::Str(arg0) => write!(f, "{:?}", arg0), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Num(arg0) => write!(f, "{:?}", arg0), + Self::Int(arg0) => write!(f, "{:?}", arg0), + Self::Char(arg0) => write!(f, "{:?}", arg0), + Self::Str(arg0) => write!(f, "{:?}", arg0), } + } } \ No newline at end of file diff --git a/src/representations/typed.rs b/src/representations/typed.rs index 4e43cbc..8200fb6 100644 --- a/src/representations/typed.rs +++ b/src/representations/typed.rs @@ -1,7 +1,7 @@ use mappable_rc::Mrc; -use crate::executor::Atom; +use crate::foreign::{Atom, ExternFn}; use crate::utils::{to_mrc_slice, one_mrc_slice}; -use crate::{executor::ExternFn, utils::string_from_charset}; +use crate::utils::string_from_charset; use super::{Literal, ast_to_typed}; use super::ast; @@ -16,121 +16,124 @@ struct Wrap(bool, bool); #[derive(PartialEq, Eq, Hash)] pub struct Expr(pub Clause, pub Mrc<[Clause]>); impl Expr { - fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, tr: Wrap) -> std::fmt::Result { - let Expr(val, typ) = self; - if typ.len() > 0 { - val.deep_fmt(f, Wrap(true, true))?; - for typ in typ.as_ref() { - f.write_char(':')?; - typ.deep_fmt(f, Wrap(true, true))?; - } - } else { - val.deep_fmt(f, tr)?; - } - Ok(()) + fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, tr: Wrap) -> std::fmt::Result { + let Expr(val, typ) = self; + if typ.len() > 0 { + val.deep_fmt(f, Wrap(true, true))?; + for typ in typ.as_ref() { + f.write_char(':')?; + typ.deep_fmt(f, Wrap(true, true))?; + } + } else { + val.deep_fmt(f, tr)?; } + Ok(()) + } } impl Clone for Expr { - fn clone(&self) -> Self { - Self(self.0.clone(), Mrc::clone(&self.1)) - } + fn clone(&self) -> Self { + Self(self.0.clone(), Mrc::clone(&self.1)) + } } impl Debug for Expr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.deep_fmt(f, Wrap(false, false)) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.deep_fmt(f, Wrap(false, false)) + } } #[derive(PartialEq, Eq, Hash)] pub enum Clause { - Literal(Literal), - Apply(Mrc, Mrc), - Lambda(u64, Option>, Mrc), - Auto(u64, Option>, Mrc), - Argument(u64), - ExternFn(ExternFn), - Atom(Atom) + Literal(Literal), + Apply(Mrc, Mrc), + Lambda(u64, Mrc<[Clause]>, Mrc), + Auto(u64, Mrc<[Clause]>, Mrc), + LambdaArg(u64), AutoArg(u64), + ExternFn(ExternFn), + Atom(Atom) } const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz"; fn parametric_fmt( - f: &mut std::fmt::Formatter<'_>, - prefix: &str, argtyp: Option>, body: Mrc, uid: u64, wrap_right: bool + f: &mut std::fmt::Formatter<'_>, + prefix: &str, argtyp: Mrc<[Clause]>, body: Mrc, uid: u64, wrap_right: bool ) -> std::fmt::Result { - if wrap_right { f.write_char('(')?; } - f.write_str(prefix)?; - f.write_str(&string_from_charset(uid, ARGNAME_CHARSET))?; - if let Some(typ) = argtyp { - f.write_str(":")?; - typ.deep_fmt(f, Wrap(false, false))?; - } - f.write_str(".")?; - body.deep_fmt(f, Wrap(false, false))?; - if wrap_right { f.write_char(')')?; } - Ok(()) + if wrap_right { f.write_char('(')?; } + f.write_str(prefix)?; + f.write_str(&string_from_charset(uid, ARGNAME_CHARSET))?; + for typ in argtyp.iter() { + f.write_str(":")?; + typ.deep_fmt(f, Wrap(false, false))?; + } + f.write_str(".")?; + body.deep_fmt(f, Wrap(false, false))?; + if wrap_right { f.write_char(')')?; } + Ok(()) } impl Clause { - fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, Wrap(wl, wr): Wrap) - -> std::fmt::Result { - match self { - Self::Literal(arg0) => write!(f, "{arg0:?}"), - Self::ExternFn(nc) => write!(f, "{nc:?}"), - Self::Atom(a) => write!(f, "{a:?}"), - Self::Lambda(uid, argtyp, body) => parametric_fmt(f, - "\\", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), *uid, wr - ), - Self::Auto(uid, argtyp, body) => parametric_fmt(f, - "@", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), *uid, wr - ), - Self::Argument(uid) => f.write_str(&string_from_charset(*uid, ARGNAME_CHARSET)), - Self::Apply(func, x) => { - if wl { f.write_char('(')?; } - func.deep_fmt(f, Wrap(false, true) )?; - f.write_char(' ')?; - x.deep_fmt(f, Wrap(true, wr && !wl) )?; - if wl { f.write_char(')')?; } - Ok(()) - } - } + fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, Wrap(wl, wr): Wrap) + -> std::fmt::Result { + match self { + Self::Literal(arg0) => write!(f, "{arg0:?}"), + Self::ExternFn(nc) => write!(f, "{nc:?}"), + Self::Atom(a) => write!(f, "{a:?}"), + Self::Lambda(uid, argtyp, body) => parametric_fmt(f, + "\\", Mrc::clone(argtyp), Mrc::clone(body), *uid, wr + ), + Self::Auto(uid, argtyp, body) => parametric_fmt(f, + "@", Mrc::clone(argtyp), Mrc::clone(body), *uid, wr + ), + Self::LambdaArg(uid) | Self::AutoArg(uid) => f.write_str(& + string_from_charset(*uid, ARGNAME_CHARSET) + ), + Self::Apply(func, x) => { + if wl { f.write_char('(')?; } + func.deep_fmt(f, Wrap(false, true) )?; + f.write_char(' ')?; + x.deep_fmt(f, Wrap(true, wr && !wl) )?; + if wl { f.write_char(')')?; } + Ok(()) + } } - pub fn wrap(self) -> Mrc { Mrc::new(Expr(self, to_mrc_slice(vec![]))) } - pub fn wrap_t(self, t: Clause) -> Mrc { Mrc::new(Expr(self, one_mrc_slice(t))) } + } + pub fn wrap(self) -> Mrc { Mrc::new(Expr(self, to_mrc_slice(vec![]))) } + pub fn wrap_t(self, t: Clause) -> Mrc { Mrc::new(Expr(self, one_mrc_slice(t))) } } impl Clone for Clause { - fn clone(&self) -> Self { - match self { - Clause::Auto(uid,t, b) => Clause::Auto(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)), - Clause::Lambda(uid, t, b) => Clause::Lambda(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)), - Clause::Literal(l) => Clause::Literal(l.clone()), - Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()), - Clause::Atom(a) => Clause::Atom(a.clone()), - Clause::Apply(f, x) => Clause::Apply(Mrc::clone(f), Mrc::clone(x)), - Clause::Argument(lvl) => Clause::Argument(*lvl) - } + fn clone(&self) -> Self { + match self { + Clause::Auto(uid,t, b) => Clause::Auto(*uid, Mrc::clone(t), Mrc::clone(b)), + Clause::Lambda(uid, t, b) => Clause::Lambda(*uid, Mrc::clone(t), Mrc::clone(b)), + Clause::Literal(l) => Clause::Literal(l.clone()), + Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()), + Clause::Atom(a) => Clause::Atom(a.clone()), + Clause::Apply(f, x) => Clause::Apply(Mrc::clone(f), Mrc::clone(x)), + Clause::LambdaArg(id) => Clause::LambdaArg(*id), + Clause::AutoArg(id) => Clause::AutoArg(*id) } + } } impl Debug for Clause { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.deep_fmt(f, Wrap(false, false)) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.deep_fmt(f, Wrap(false, false)) + } } impl TryFrom<&ast::Expr> for Expr { - type Error = ast_to_typed::Error; - fn try_from(value: &ast::Expr) -> Result { - ast_to_typed::expr(value) - } + type Error = ast_to_typed::Error; + fn try_from(value: &ast::Expr) -> Result { + ast_to_typed::expr(value) + } } impl TryFrom<&ast::Clause> for Clause { - type Error = ast_to_typed::Error; - fn try_from(value: &ast::Clause) -> Result { - ast_to_typed::clause(value) - } + type Error = ast_to_typed::Error; + fn try_from(value: &ast::Clause) -> Result { + ast_to_typed::clause(value) + } } \ No newline at end of file diff --git a/src/rule/executor/execute.rs b/src/rule/executor/execute.rs index 57a0c27..1adf6c0 100644 --- a/src/rule/executor/execute.rs +++ b/src/rule/executor/execute.rs @@ -11,55 +11,55 @@ use super::super::RuleError; 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 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))?; + } + _ => () }; - let Expr(val, typ) = pattern; - verify_clause(val, is_vec)?; - for typ in typ.as_ref() { - verify_clause(typ, is_vec)?; - } Ok(()) + }; + let Expr(val, typ) = pattern; + verify_clause(val, is_vec)?; + for typ in typ.as_ref() { + verify_clause(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))}, to_mrc_slice(vec![])); - let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, to_mrc_slice(vec![])); - // 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()); + 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![])); + // 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 @@ -67,117 +67,117 @@ fn slice_to_vec(src: &mut Mrc<[Expr]>, tgt: &mut Mrc<[Expr]>) { /// 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 + 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 + 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 } // fn write_clause_rec(state: &State, clause: &Clause) -> fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box> { - let out_typ = tpl_typ.iter() - .flat_map(|c| write_expr_rec(state, &c.clone().into_expr())) - .map(Expr::into_clause) - .collect::>(); - match tpl_clause { - Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto( - name_opt.as_ref().and_then(|name| { - 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()), - Entry::Name(name) => Some(name.as_ref().to_owned()), - _ => panic!("Auto template name may only be derived from Auto or Lambda name") - } - } else { - Some(name.to_owned()) - } - }), - write_slice_rec(state, typ), - write_slice_rec(state, body) - ), out_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_rec(state, typ), - write_slice_rec(state, body) - ), out_typ.to_owned())), - Clause::S(c, body) => box_once(Expr(Clause::S( - *c, - write_slice_rec(state, body) - ), out_typ.to_owned())), - Clause::Placeh{key, vec: None} => { - let real_key = unwrap_or!(key.strip_prefix('_'); key); - match &state[real_key] { - Entry::Scalar(x) => box_once(x.as_ref().to_owned()), - Entry::Name(n) => box_once(Expr(Clause::Name { - local: Some(n.as_ref().to_owned()), - qualified: one_mrc_slice(n.as_ref().to_owned()) - }, mrc_empty_slice())), - _ => 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")}, - Clause::Explicit(param) => { - assert!(out_typ.len() == 0, "Explicit should never have a type annotation"); - box_once(Clause::Explicit(Mrc::new( - Clause::from_exprv(write_expr_rec(state, param).collect()) - .expect("Result shorter than template").into_expr() - )).into_expr()) - }, - // Explicit base case so that we get an error if Clause gets new values - c@Clause::Literal(_) | c@Clause::Name { .. } | c@Clause::ExternFn(_) | c@Clause::Atom(_) => - box_once(Expr(c.to_owned(), out_typ.to_owned())) - } + let out_typ = tpl_typ.iter() + .flat_map(|c| write_expr_rec(state, &c.clone().into_expr())) + .map(Expr::into_clause) + .collect::>(); + match tpl_clause { + Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto( + name_opt.as_ref().and_then(|name| { + 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()), + Entry::Name(name) => Some(name.as_ref().to_owned()), + _ => panic!("Auto template name may only be derived from Auto or Lambda name") + } + } else { + Some(name.to_owned()) + } + }), + write_slice_rec(state, typ), + write_slice_rec(state, body) + ), out_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_rec(state, typ), + write_slice_rec(state, body) + ), out_typ.to_owned())), + Clause::S(c, body) => box_once(Expr(Clause::S( + *c, + write_slice_rec(state, body) + ), out_typ.to_owned())), + Clause::Placeh{key, vec: None} => { + let real_key = unwrap_or!(key.strip_prefix('_'); key); + match &state[real_key] { + Entry::Scalar(x) => box_once(x.as_ref().to_owned()), + Entry::Name(n) => box_once(Expr(Clause::Name { + local: Some(n.as_ref().to_owned()), + qualified: one_mrc_slice(n.as_ref().to_owned()) + }, mrc_empty_slice())), + _ => 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")}, + Clause::Explicit(param) => { + assert!(out_typ.len() == 0, "Explicit should never have a type annotation"); + box_once(Clause::Explicit(Mrc::new( + Clause::from_exprv(write_expr_rec(state, param).collect()) + .expect("Result shorter than template").into_expr() + )).into_expr()) + }, + // Explicit base case so that we get an error if Clause gets new values + c@Clause::Literal(_) | c@Clause::Name { .. } | c@Clause::ExternFn(_) | c@Clause::Atom(_) => + box_once(Expr(c.to_owned(), out_typ.to_owned())) + } } /// Fill in a template from a state as produced by a pattern fn write_slice_rec(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> { - eprintln!("Writing {tpl:?} with state {state:?}"); - tpl.iter().flat_map(|xpr| write_expr_rec(state, xpr)).collect() + eprintln!("Writing {tpl:?} with state {state:?}"); + tpl.iter().flat_map(|xpr| write_expr_rec(state, xpr)).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); - 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_rec(&state, &tgt)) - })) + // 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); + 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_rec(&state, &tgt)) + })) } diff --git a/src/rule/executor/slice_matcher.rs b/src/rule/executor/slice_matcher.rs index 78ae783..b922e90 100644 --- a/src/rule/executor/slice_matcher.rs +++ b/src/rule/executor/slice_matcher.rs @@ -14,10 +14,10 @@ use super::split_at_max_vec::split_at_max_vec; #[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) - } + fn clone(&self) -> Self { + let CacheEntry(mrc, matcher) = self; + CacheEntry(Mrc::clone(mrc), matcher) + } } @@ -31,281 +31,281 @@ impl<'a> Clone for CacheEntry<'a> { /// a pattern on the entire tree. #[derive(Clone, Eq)] pub struct SliceMatcherDnC { - /// The entire pattern this will match - pattern: Mrc<[Expr]>, - /// The exact clause this can match - clause: Mrc, - /// Matcher for the parts of the pattern right from us - right_subm: Option>, - /// Matcher for the parts of the pattern left from us - 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>, - /// Matcher for the type of this expression if it has one (Auto usually does) - /// Optional - typ_subm: Option>, + /// The entire pattern this will match + pattern: Mrc<[Expr]>, + /// The exact clause this can match + clause: Mrc, + /// Matcher for the parts of the pattern right from us + right_subm: Option>, + /// Matcher for the parts of the pattern left from us + 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>, + /// Matcher for the type of this expression if it has one (Auto usually does) + /// Optional + typ_subm: Option>, } impl PartialEq for SliceMatcherDnC { - fn eq(&self, other: &Self) -> bool { - self.pattern == other.pattern - } + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } } impl std::hash::Hash for SliceMatcherDnC { - fn hash(&self, state: &mut H) { - self.pattern.hash(state); - } + fn hash(&self, state: &mut H) { + self.pattern.hash(state); + } } 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 { - matches!(self.clause.as_ref(), Clause::Placeh{vec: Some(..), ..}) + /// 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 { + 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> { + 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<&String> { + if let Clause::Placeh { key, .. } = self.clause.as_ref() {Some(key)} else {None} + } + 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} } - /// If clause is a name, the qualified name this can match - pub fn clause_qual_name(&self) -> Option> { - if let Clause::Name { qualified, .. } = self.clause.as_ref() {Some(Mrc::clone(qualified))} 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(&self, + range: Mrc<[Expr]> + ) -> impl Iterator, 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(); + let lmin = self.min(Side::Left); + let _lmax = self.max(Side::Left, range.len()); + let rmin = self.min(Side::Right); + let _rmax = self.max(Side::Right, range.len()); + let full_len = range.len(); + Box::new((own_min..=own_max).rev().flat_map(move |own_len| { + let wiggle = full_len - lmin - rmin - own_len; + let range = Mrc::clone(&range); + (0..=wiggle).map(move |offset| { + let first_break = lmin + offset; + 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: 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..])))) + )); + Self { + 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))) } - /// If clause is a Placeh, the key in the state the match will be stored at - pub fn state_key(&self) -> Option<&String> { - if let Clause::Placeh { key, .. } = self.clause.as_ref() {Some(key)} else {None} + } + + /// The shortest slice this pattern can match + 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<&SliceMatcherDnC> { + match side { + Side::Left => &self.left_subm, + Side::Right => &self.right_subm + }.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()) - self.own_min_size() + } else {m.len()}) + } + /// Take the smallest possible slice from the given side + 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<'a>(&'a self, + range: Mrc<[Expr]>, cache: &Cache, Option> + ) -> Option { + 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<'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)? + } else {State::new()}; + let body_state = self.match_body(body_range, cache)?; + typ_state + body_state + } + + /// 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<'a>(&'a self, + side: Side, range: Mrc<[Expr]>, + cache: &Cache, Option> + ) -> Option { + match &self.side(side) { + None => { + if !range.is_empty() {None} + else {Some(State::new())} + }, + Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned()) } - 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} + } + + 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, 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.as_ref(), &target.as_ref()[pos].0) { + (Clause::Literal(val), Clause::Literal(tgt)) => { + if val == tgt {Some(own_state)} else {None} + } + (Clause::Placeh{key, vec: None}, tgt_clause) => { + if let Some(real_key) = key.strip_prefix('_') { + if let Clause::Name { local: Some(value), .. } = tgt_clause { + own_state.insert_name(real_key, value) + } else {None} + } else {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(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_name(state_key, name_tgt)? + } else if name != name_tgt {return None} + // ^ 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 { + // 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(Mrc::clone(typ_range), Mrc::clone(body_range), cache) + }, + _ => 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(&self, - range: Mrc<[Expr]> - ) -> impl Iterator, 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(); - let lmin = self.min(Side::Left); - let _lmax = self.max(Side::Left, range.len()); - let rmin = self.min(Side::Right); - let _rmax = self.max(Side::Right, range.len()); - let full_len = range.len(); - Box::new((own_min..=own_max).rev().flat_map(move |own_len| { - let wiggle = full_len - lmin - rmin - own_len; - let range = Mrc::clone(&range); - (0..=wiggle).map(move |offset| { - let first_break = lmin + offset; - 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: 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..])))) - )); - Self { - 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))) - } + /// Match the range with a vectorial _assuming we are a vectorial_ + fn match_range_vectorial_cached<'a>(&'a self, + name: &str, + 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) { + return Some(unwrap_or!( + self.apply_side_with_cache(Side::Left, left, cache) + .and_then(|lres| lres + self.apply_side_with_cache(Side::Right, right, cache)) + .and_then(|side_res| side_res.insert_vec(name, own.as_ref())); + continue + )) } + None + } - /// The shortest slice this pattern can match - 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<&SliceMatcherDnC> { - match side { - Side::Left => &self.left_subm, - Side::Right => &self.right_subm - }.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()) - self.own_min_size() - } else {m.len()}) - } - /// Take the smallest possible slice from the given side - fn slice_min<'a>(&self, side: Side, range: &'a [Expr]) -> &'a [Expr] { - side.slice(self.min(side), range) + /// Try and match the specified range + pub fn match_range_cached<'a>(&'a self, + target: Mrc<[Expr]>, + cache: &Cache, Option> + ) -> Option { + if self.pattern.is_empty() { + return if target.is_empty() {Some(State::new())} else {None} } + if self.clause_is_vectorial() { + let key = self.state_key().expect("Vectorial implies key"); + self.match_range_vectorial_cached(key, target, cache) + } else {self.match_range_scalar_cached(target, cache)} + } - /// 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<'a>(&'a self, - range: Mrc<[Expr]>, cache: &Cache, Option> - ) -> Option { - 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<'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)? - } else {State::new()}; - let body_state = self.match_body(body_range, cache)?; - typ_state + body_state - } + pub fn get_matcher_cache<'a>() + -> Cache<'a, CacheEntry<'a>, Option> { + Cache::new( + |CacheEntry(tgt, matcher), cache| { + matcher.match_range_cached(tgt, cache) + } + ) + } - /// 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<'a>(&'a self, - side: Side, range: Mrc<[Expr]>, - cache: &Cache, Option> - ) -> Option { - match &self.side(side) { - None => { - if !range.is_empty() {None} - else {Some(State::new())} - }, - Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned()) - } - } - - 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, 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.as_ref(), &target.as_ref()[pos].0) { - (Clause::Literal(val), Clause::Literal(tgt)) => { - if val == tgt {Some(own_state)} else {None} - } - (Clause::Placeh{key, vec: None}, tgt_clause) => { - if let Some(real_key) = key.strip_prefix('_') { - if let Clause::Name { local: Some(value), .. } = tgt_clause { - own_state.insert_name(real_key, value) - } else {None} - } else {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(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_name(state_key, name_tgt)? - } else if name != name_tgt {return None} - // ^ 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 { - // 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(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<'a>(&'a self, - name: &str, - 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) { - return Some(unwrap_or!( - self.apply_side_with_cache(Side::Left, left, cache) - .and_then(|lres| lres + self.apply_side_with_cache(Side::Right, right, cache)) - .and_then(|side_res| side_res.insert_vec(name, own.as_ref())); - continue - )) - } - None - } - - /// Try and match the specified range - pub fn match_range_cached<'a>(&'a self, - target: Mrc<[Expr]>, - cache: &Cache, Option> - ) -> Option { - if self.pattern.is_empty() { - return if target.is_empty() {Some(State::new())} else {None} - } - if self.clause_is_vectorial() { - let key = self.state_key().expect("Vectorial implies key"); - self.match_range_vectorial_cached(key, target, cache) - } else {self.match_range_scalar_cached(target, 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 match_range(&self, target: Mrc<[Expr]>) -> Option { + self.match_range_cached(target, &Self::get_matcher_cache()) + } } 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() - } + 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() + } } diff --git a/src/rule/executor/split_at_max_vec.rs b/src/rule/executor/split_at_max_vec.rs index 5a19235..71fd0fa 100644 --- a/src/rule/executor/split_at_max_vec.rs +++ b/src/rule/executor/split_at_max_vec.rs @@ -7,27 +7,27 @@ 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 - }) + 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 44644e3..cafc870 100644 --- a/src/rule/executor/state.rs +++ b/src/rule/executor/state.rs @@ -6,10 +6,10 @@ use crate::ast::Expr; #[derive(Debug, PartialEq, Eq)] pub enum Entry { - Vec(Rc>), - Scalar(Rc), - Name(Rc), - NameOpt(Option>) + Vec(Rc>), + Scalar(Rc), + Name(Rc), + NameOpt(Option>) } /// A bucket of indexed expression fragments. Addition may fail if there's a conflict. @@ -19,129 +19,129 @@ 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 Entry { - fn clone(&self) -> Self { - 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)) - } + fn clone(&self) -> Self { + 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)) } + } } impl State { - pub fn new() -> Self { - Self(HashMap::new()) + pub fn new() -> Self { + Self(HashMap::new()) + } + pub fn insert_vec(mut self, k: &S, v: &[Expr]) -> Option + where S: AsRef + ToString + ?Sized + Debug { + 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()))); } - pub fn insert_vec(mut self, k: &S, v: &[Expr]) -> Option - where S: AsRef + ToString + ?Sized + Debug { - 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 let Entry::Scalar(val) = old { + if val.as_ref() != v {return None} + } else {return None} + } else { + self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned()))); + } + 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 } - Some(self) + } else {return None} + } else { + self.0.insert(k.to_string(), Entry::NameOpt(v.map(|s| Rc::new(s.to_string())))); } - 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 let Entry::Scalar(val) = old { - if val.as_ref() != v {return None} - } else {return None} - } else { - self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned()))); - } - 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, Entry)) -> Option { - if let Some(old) = self.0.get(&k) { - if old != &v {return None} - } else { - self.0.insert(k, v); - } - Some(self) - } - /// Returns `true` if the state contains no data - pub fn empty(&self) -> bool { - self.0.is_empty() + Some(self) + } + /// Insert a new entry, return None on conflict + 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); } + Some(self) + } + /// Returns `true` if the state contains no data + pub fn empty(&self) -> bool { + self.0.is_empty() + } } impl Add for State { - type Output = Option; + type Output = Option; - fn add(mut self, rhs: Self) -> Self::Output { - if self.empty() { - return Some(rhs) - } - for pair in rhs.0 { - self = self.insert_pair(pair)? - } - Some(self) + fn add(mut self, rhs: Self) -> Self::Output { + if self.empty() { + return Some(rhs) } + for pair in rhs.0 { + self = self.insert_pair(pair)? + } + Some(self) + } } impl Add> for State { - type Output = Option; + type Output = Option; - fn add(self, rhs: Option) -> Self::Output { - rhs.and_then(|s| self + s) - } + fn add(self, rhs: Option) -> Self::Output { + rhs.and_then(|s| self + s) + } } impl Index for State where S: AsRef { - type Output = Entry; + type Output = Entry; - fn index(&self, index: S) -> &Self::Output { - return &self.0[index.as_ref()] - } + fn index(&self, index: S) -> &Self::Output { + return &self.0[index.as_ref()] + } } impl IntoIterator for State { - type Item = (String, Entry); + type Item = (String, Entry); - type IntoIter = hashbrown::hash_map::IntoIter; + type IntoIter = hashbrown::hash_map::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + 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) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } } \ No newline at end of file diff --git a/src/rule/repository.rs b/src/rule/repository.rs index 7811dd4..8dfe746 100644 --- a/src/rule/repository.rs +++ b/src/rule/repository.rs @@ -9,44 +9,44 @@ use super::{super::ast::Rule, executor::execute, RuleError}; /// Manages a priority queue of substitution rules and allows to apply them pub struct Repository(Vec); impl Repository { - pub fn new(mut rules: Vec) -> Self { - rules.sort_by_key(|r| r.prio); - Self(rules) - } + pub fn new(mut rules: Vec) -> Self { + rules.sort_by_key(|r| r.prio); + Self(rules) + } - /// Attempt to run each rule in priority order once - 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}) + /// Attempt to run each rule in priority order once + 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}) + } - /// Attempt to run each rule in priority order `limit` times. Returns the final - /// tree and the number of iterations left to the limit. - pub fn long_step(&self, mut code: Mrc<[Expr]>, mut limit: usize) - -> Result<(Mrc<[Expr]>, usize), RuleError> { - while let Some(tmp) = self.step(Mrc::clone(&code))? { - if 0 >= limit {break} - limit -= 1; - code = tmp - } - Ok((code, limit)) + /// Attempt to run each rule in priority order `limit` times. Returns the final + /// tree and the number of iterations left to the limit. + pub fn long_step(&self, mut code: Mrc<[Expr]>, mut limit: usize) + -> Result<(Mrc<[Expr]>, usize), RuleError> { + while let Some(tmp) = self.step(Mrc::clone(&code))? { + if 0 >= limit {break} + limit -= 1; + code = tmp } + Ok((code, limit)) + } } 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(()) - } + 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_error.rs b/src/rule/rule_error.rs index d6a32b2..5ba1bb2 100644 --- a/src/rule/rule_error.rs +++ b/src/rule/rule_error.rs @@ -2,17 +2,17 @@ use std::{fmt, error::Error}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum RuleError { - BadState(String), - ScalarVecMismatch(String) + BadState(String), + ScalarVecMismatch(String) } impl fmt::Display for RuleError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadState(key) => write!(f, "Key {:?} not in match pattern", key), - Self::ScalarVecMismatch(key) => - write!(f, "Key {:?} used inconsistently with and without ellipsis", key) - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BadState(key) => write!(f, "Key {:?} not in match pattern", key), + Self::ScalarVecMismatch(key) => + write!(f, "Key {:?} used inconsistently with and without ellipsis", key) } + } } impl Error for RuleError {} \ No newline at end of file diff --git a/src/scheduler/generator_task.rs b/src/scheduler/generator_task.rs new file mode 100644 index 0000000..0a95f51 --- /dev/null +++ b/src/scheduler/generator_task.rs @@ -0,0 +1,51 @@ +use std::{ops::{Generator, GeneratorState}, pin::Pin}; + +use super::{Task, Nice, TaskState}; + +pub struct GeneratorTask> { + nice: Nice, + generator: Pin> +} + +impl GeneratorTask where G: Generator<(), Yield = ()> { + fn new(nice: Nice, generator: G) -> Self { Self { + nice, + generator: Box::pin(generator) + } } +} + +impl Task for GeneratorTask +where G: Generator<(), Yield = ()> { + type Result = G::Return; + + fn run_once(&mut self) -> super::TaskState { + match self.generator.as_mut().resume(()) { + GeneratorState::Yielded(()) => super::TaskState::Yield, + GeneratorState::Complete(r) => super::TaskState::Complete(r) + } + } +} + +impl Task for Pin> where T: Generator<(), Yield = ()> { + type Result = T::Return; + + fn run_once(&mut self) -> super::TaskState { + match self.as_mut().resume(()) { + GeneratorState::Yielded(()) => TaskState::Yield, + GeneratorState::Complete(r) => TaskState::Complete(r) + } + } +} + +#[macro_export] +macro_rules! subtask { + ($g:tt) => { { + let task = $g; + loop { + match task.run_once() { + TaskState::Yield => yield; + TaskState::Complete(r) => break r; + } + } + } }; +} \ No newline at end of file diff --git a/src/scheduler/mod.rs b/src/scheduler/mod.rs new file mode 100644 index 0000000..bcc5818 --- /dev/null +++ b/src/scheduler/mod.rs @@ -0,0 +1,47 @@ +mod generator_task; +mod task_pair; +mod task_vec; + +pub type Nice = u16; +pub type Priority = i32; + +pub enum TaskState { + Yield, + Complete(R) +} + +pub trait Task { + type Result; + + fn run_once(&mut self) -> TaskState; + + fn run_n_times(&mut self, count: u64) -> TaskState { + for _ in 0..count { + if let r@TaskState::Complete(_) = self.run_once() { + return r + } + } + return TaskState::Yield + } + + fn run_to_completion(&mut self) -> Self::Result { + loop { if let TaskState::Complete(r) = self.run_once() {return r} } + } + + fn boxed<'a>(self) -> TaskBox<'a, Self::Result> where Self: 'a + Sized { Box::new(self) } +} + +pub type TaskBox<'a, T> = Box + 'a>; + +impl<'a, R> Task for TaskBox<'a, R> { + type Result = R; + + fn run_once(&mut self) -> TaskState { self.as_mut().run_once() } + fn run_n_times(&mut self, count: u64) -> TaskState { + self.as_mut().run_n_times(count) + } + + fn run_to_completion(&mut self) -> Self::Result { + self.as_mut().run_to_completion() + } +} \ No newline at end of file diff --git a/src/scheduler/notes.md b/src/scheduler/notes.md new file mode 100644 index 0000000..cd90349 --- /dev/null +++ b/src/scheduler/notes.md @@ -0,0 +1,3 @@ +# Purpose + +Type expressions are trees. Any single branch could terminate the solver and any branch may be nonterminating, therefore all of them must be run concurrently. Thread-based concurrency isn't an option because a compiler must be perfectly deterministic. It is also beneficial to have fine-grained control over the relative priority of different tasks. \ No newline at end of file diff --git a/src/scheduler/task_pair.rs b/src/scheduler/task_pair.rs new file mode 100644 index 0000000..373b9c9 --- /dev/null +++ b/src/scheduler/task_pair.rs @@ -0,0 +1,67 @@ +use crate::utils::translate::process; + +use super::{Task, Nice, Priority, TaskState}; + +enum TaskPairState { + Empty, + Left(T, U::Result), + Right(T::Result, U), + Both(T, U) +} + +pub struct TaskPair { + l_nice: Nice, + r_nice: Nice, + state: TaskPairState, + tally: Priority, +} + +impl TaskPair { + pub fn new(l_nice: Nice, left: T, r_nice: Nice, right: U) -> Self { + Self { + l_nice, r_nice, + tally: 0, + state: TaskPairState::Both(left, right) + } + } +} + +impl Task for TaskPair { + type Result = (T::Result, U::Result); + + fn run_once(&mut self) -> TaskState { + let TaskPair{ state, tally, l_nice, r_nice } = self; + let ret = process(state, |s| match s { + TaskPairState::Empty => panic!("Generator completed and empty"), + TaskPairState::Left(mut l_task, r_res) => { + match l_task.run_once() { + TaskState::Complete(r) => (TaskPairState::Empty, TaskState::Complete((r, r_res))), + TaskState::Yield => (TaskPairState::Left(l_task, r_res), TaskState::Yield), + } + } + TaskPairState::Right(l_res, mut r_task) => { + match r_task.run_once() { + TaskState::Complete(r) => (TaskPairState::Empty, TaskState::Complete((l_res, r))), + TaskState::Yield => (TaskPairState::Right(l_res, r_task), TaskState::Yield), + } + } + TaskPairState::Both(mut l_task, mut r_task) => { + let state = if 0 <= *tally { + *tally -= *l_nice as Priority; + match l_task.run_once() { + TaskState::Complete(r) => TaskPairState::Right(r, r_task), + TaskState::Yield => TaskPairState::Both(l_task, r_task), + } + } else { + *tally += *r_nice as Priority; + match r_task.run_once() { + TaskState::Complete(r) => TaskPairState::Left(l_task, r), + TaskState::Yield => TaskPairState::Both(l_task, r_task), + } + }; + (state, TaskState::Yield) + } + }); + ret + } +} diff --git a/src/scheduler/task_vec.rs b/src/scheduler/task_vec.rs new file mode 100644 index 0000000..7aceeda --- /dev/null +++ b/src/scheduler/task_vec.rs @@ -0,0 +1,107 @@ +use std::iter; + +use itertools::Itertools; + +use super::{Task, Nice, TaskState}; + +const NORMALIZATION_THRESHOLD:Nice = Nice::MAX / 4; + +struct TaskEntry { + nice: Nice, + position: usize, + tally: Nice, + task: T +} + +struct TaskVec { + results: Vec>, + task_heap: Vec>>, +} + +impl TaskVec { + pub fn new(tasks: Vec<(Nice, T)>) -> Self { + let mut results = Vec::with_capacity(tasks.len()); + results.resize_with(tasks.len(), || None); + let task_heap = tasks.into_iter().enumerate() + .map(|(position, (nice, task))| Some(TaskEntry{ nice, task, position, tally: 1 })) + .collect_vec(); + Self { results, task_heap } + } + + fn entry(&self, i: usize) -> Option<&TaskEntry> { + if self.task_heap.len() <= i {None} + else {self.task_heap[i].as_ref()} + } + fn entry_mut(&mut self, i: usize) -> Option<&mut TaskEntry> { + if self.task_heap.len() <= i {None} + else {self.task_heap[i].as_mut()} + } + fn tally(&self, i: usize) -> Nice { + self.task_heap[i].as_ref().map(|e| e.tally).unwrap_or(0) + } + fn swap(&mut self, a: usize, b: usize) { + self.task_heap.swap(a, b); + } + fn iter_mut(&mut self) -> impl Iterator> { + self.task_heap.iter_mut().filter_map(|e| e.as_mut()) + } + + fn normalize(&mut self) { + let shrink_count = self.task_heap.iter().rev().take_while(|e| e.is_none()).count(); + let new_len = self.task_heap.len() - shrink_count; + self.task_heap.splice(0..new_len, iter::empty()); + let head = self.entry_mut(0); + let offset = if let Some(e) = head { + let offset = e.tally - 1; + if offset < NORMALIZATION_THRESHOLD {return} + e.tally = 1; + offset + } else {return}; + for entry in self.iter_mut() { entry.tally -= offset } + } + + fn sink(&mut self, i: usize) { + let lchi = 2*i + 1; + let rchi = 2*i + 2; + let t = self.tally(i); + let lcht = if let Some(e) = self.entry(lchi) {e.tally} else { + if self.tally(rchi) < t { + self.swap(rchi, i); + self.sink(rchi); + } + return + }; + let rcht = if let Some(e) = self.entry(rchi) {e.tally} else { + if self.tally(lchi) < t { + self.swap(lchi, i); + self.sink(lchi); + } + return + }; + let mchi = { + if rcht < t && rcht < lcht {rchi} + else if lcht < t && lcht < rcht {lchi} + else {return} + }; + self.swap(i, mchi); + self.sink(mchi); + } +} + +impl Task for TaskVec { + fn run_once(&mut self) -> super::TaskState { + let head = &mut self.task_heap[0]; + let head_entry = head.as_mut().expect("All completed, cannot run further"); + head_entry.tally += head_entry.nice; + match head_entry.task.run_once() { + TaskState::Complete(r) => { + self.results[head_entry.position] = Some(r); + *head = None; + self.sink(0); + if self.entry(0).is_some() { + + } + } + } + } +} \ No newline at end of file diff --git a/src/types/hindley_milner.rs.proto b/src/types/hindley_milner.rs.proto deleted file mode 100644 index 3bbf187..0000000 --- a/src/types/hindley_milner.rs.proto +++ /dev/null @@ -1,52 +0,0 @@ -use std::{borrow::Borrow}; -use std::hash::Hash; - -use hashbrown::HashMap; -use mappable_rc::Mrc; - -use crate::{ast::{Expr, Clause}, utils::mrc_to_iter}; - -pub struct Substitution(HashMap>); -impl Substitution { - fn new() -> Self { Self(HashMap::new()) } - fn apply(&self, q: &Q) -> Option> - where String: Borrow { - self.0.get(q).map(Mrc::clone) - } -} - -pub fn hindley_milner(a: Mrc<[Expr]>, b: Mrc<[Expr]>) -> Result { - hindley_milner_rec(Substitution::new(), a, b) -} - -pub fn hindley_milner_rec(mut s: Substitution, a: Mrc<[Expr]>, b: Mrc<[Expr]>) --> Result { - if a.len() != b.len() {return Err(())} - for (mut a, mut b) in mrc_to_iter(a).zip(mrc_to_iter(b)) { - if let Clause::Placeh{key, ..} = &a.0 { - if let Some(ex) = s.apply(key) { a = ex } - } - if let Clause::Placeh{key, ..} = &b.0 { - if let Some(ex) = s.apply(key) { b = ex } - } - if !matches!(&a.0, Clause::Placeh{..}) { (a, b) = (b, a) } - match (&a.0, &b.0) { - (Clause::Placeh{key:a_key,..}, Clause::Placeh{key:b_key,..}) => - if a_key == b_key {return Ok(s)}, - - _ => return Err(()) - } - if let (Clause::Placeh{key: a_key,..}, Clause::Placeh{key: b_key,..}) = (&a.0, &b.0) { - if a_key == b_key {return Ok(s)} - } else if let (Clause::S(_, a_body), Clause::S(_, b_body)) = (&a.0, &b.0) { - s = hindley_milner_rec(s, Mrc::clone(a_body), Mrc::clone(b_body))? - } else if let () - } - Ok(s) -} - -pub fn occurs(key: &str, val: &Expr) -> bool { - match val.0 { - Clause::Auto(_, _, body) => body. - } -} \ No newline at end of file diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index 1533102..0000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// mod hindley_milner; - -#[derive(Clone, Hash, PartialEq, Eq)] -pub enum Expression { - Literal(L), - Variable(V), - Operation(O, Vec>), - Lazy(F) -} - -pub struct Rule { - -} \ No newline at end of file diff --git a/src/types/unifier.rs b/src/types/unifier.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/bfs.rs b/src/utils/bfs.rs index f61436a..d944984 100644 --- a/src/utils/bfs.rs +++ b/src/utils/bfs.rs @@ -17,27 +17,27 @@ use crate::utils::BoxedIter; pub fn bfs(init: T, neighbors: F) -> impl Iterator where T: Eq + Hash + Clone + std::fmt::Debug, - F: Fn(T) -> I, I: Iterator + F: Fn(T) -> I, I: Iterator { - let mut visited: HashSet = HashSet::new(); - let mut visit_queue: VecDeque = VecDeque::from([init]); - let mut unpack_queue: VecDeque = VecDeque::new(); - iter::from_fn(move || { - let next = {loop { - let next = unwrap_or!(visit_queue.pop_front(); break None); - if !visited.contains(&next) { break Some(next) } - }}.or_else(|| loop { - let unpacked = unwrap_or!(unpack_queue.pop_front(); break None); - let mut nbv = neighbors(unpacked).filter(|t| !visited.contains(t)); - if let Some(next) = nbv.next() { - visit_queue.extend(nbv); - break Some(next) - } - })?; - visited.insert(next.clone()); - unpack_queue.push_back(next.clone()); - Some(next) - }) + let mut visited: HashSet = HashSet::new(); + let mut visit_queue: VecDeque = VecDeque::from([init]); + let mut unpack_queue: VecDeque = VecDeque::new(); + iter::from_fn(move || { + let next = {loop { + let next = unwrap_or!(visit_queue.pop_front(); break None); + if !visited.contains(&next) { break Some(next) } + }}.or_else(|| loop { + let unpacked = unwrap_or!(unpack_queue.pop_front(); break None); + let mut nbv = neighbors(unpacked).filter(|t| !visited.contains(t)); + if let Some(next) = nbv.next() { + visit_queue.extend(nbv); + break Some(next) + } + })?; + visited.insert(next.clone()); + unpack_queue.push_back(next.clone()); + Some(next) + }) } /// Same as [bfs] but with a recursion depth limit @@ -48,66 +48,66 @@ where T: Eq + Hash + Clone + std::fmt::Debug, pub fn bfs_upto<'a, T: 'a, F: 'a, I: 'a>(init: T, neighbors: F, limit: usize) -> impl Iterator + 'a where T: Eq + Hash + Clone + std::fmt::Debug, - F: Fn(T) -> I, I: Iterator + F: Fn(T) -> I, I: Iterator { - /// Newtype to store the recursion depth but exclude it from equality comparisons - /// Because BFS visits nodes in increasing distance order, when a node is visited for the - /// second time it will never override the earlier version of itself. This is not the case - /// with Djikstra's algorithm, which can be conceptualised as a "weighted BFS". - #[derive(Eq, Clone, Debug)] - struct Wrap(usize, U); - impl PartialEq for Wrap { - fn eq(&self, other: &Self) -> bool { self.1.eq(&other.1) } - } - impl Hash for Wrap { - fn hash(&self, state: &mut H) { self.1.hash(state) } - } - bfs(Wrap(0, init), move |Wrap(dist, t)| -> BoxedIter> { // boxed because we branch - if dist == limit {Box::new(iter::empty())} - else {Box::new(neighbors(t).map(move |t| Wrap(dist + 1, t)))} - }).map(|Wrap(_, t)| t) + /// Newtype to store the recursion depth but exclude it from equality comparisons + /// Because BFS visits nodes in increasing distance order, when a node is visited for the + /// second time it will never override the earlier version of itself. This is not the case + /// with Djikstra's algorithm, which can be conceptualised as a "weighted BFS". + #[derive(Eq, Clone, Debug)] + struct Wrap(usize, U); + impl PartialEq for Wrap { + fn eq(&self, other: &Self) -> bool { self.1.eq(&other.1) } + } + impl Hash for Wrap { + fn hash(&self, state: &mut H) { self.1.hash(state) } + } + bfs(Wrap(0, init), move |Wrap(dist, t)| -> BoxedIter> { // boxed because we branch + if dist == limit {Box::new(iter::empty())} + else {Box::new(neighbors(t).map(move |t| Wrap(dist + 1, t)))} + }).map(|Wrap(_, t)| t) } #[cfg(test)] mod tests { - use itertools::Itertools; + use itertools::Itertools; - use super::*; + use super::*; - type Graph = Vec>; - fn neighbors(graph: &Graph, pt: usize) -> impl Iterator + '_ { - graph[pt].iter().copied() - } - fn from_neighborhood_matrix(matrix: Vec>) -> Graph { - matrix.into_iter().map(|v| { - v.into_iter().enumerate().filter_map(|(i, ent)| { - if ent > 1 {panic!("Neighborhood matrices must contain binary values")} - else if ent == 1 {Some(i)} - else {None} - }).collect() - }).collect() - } + type Graph = Vec>; + fn neighbors(graph: &Graph, pt: usize) -> impl Iterator + '_ { + graph[pt].iter().copied() + } + fn from_neighborhood_matrix(matrix: Vec>) -> Graph { + matrix.into_iter().map(|v| { + v.into_iter().enumerate().filter_map(|(i, ent)| { + if ent > 1 {panic!("Neighborhood matrices must contain binary values")} + else if ent == 1 {Some(i)} + else {None} + }).collect() + }).collect() + } - #[test] - fn test_square() { - let simple_graph = from_neighborhood_matrix(vec![ - vec![0,1,0,1,1,0,0,0], - vec![1,0,1,0,0,1,0,0], - vec![0,1,0,1,0,0,1,0], - vec![1,0,1,0,0,0,0,1], - vec![1,0,0,0,0,1,0,1], - vec![0,1,0,0,1,0,1,0], - vec![0,0,1,0,0,1,0,1], - vec![0,0,0,1,1,0,1,0], - ]); - let scan = bfs(0, |n| neighbors(&simple_graph, n)).collect_vec(); - assert_eq!(scan, vec![0, 1, 3, 4, 2, 5, 7, 6]) - } - #[test] - fn test_stringbuilder() { - let scan = bfs("".to_string(), |s| { - vec![s.clone()+";", s.clone()+"a", s+"aaa"].into_iter() - }).take(30).collect_vec(); - println!("{scan:?}") - } + #[test] + fn test_square() { + let simple_graph = from_neighborhood_matrix(vec![ + vec![0,1,0,1,1,0,0,0], + vec![1,0,1,0,0,1,0,0], + vec![0,1,0,1,0,0,1,0], + vec![1,0,1,0,0,0,0,1], + vec![1,0,0,0,0,1,0,1], + vec![0,1,0,0,1,0,1,0], + vec![0,0,1,0,0,1,0,1], + vec![0,0,0,1,1,0,1,0], + ]); + let scan = bfs(0, |n| neighbors(&simple_graph, n)).collect_vec(); + assert_eq!(scan, vec![0, 1, 3, 4, 2, 5, 7, 6]) + } + #[test] + fn test_stringbuilder() { + let scan = bfs("".to_string(), |s| { + vec![s.clone()+";", s.clone()+"a", s+"aaa"].into_iter() + }).take(30).collect_vec(); + println!("{scan:?}") + } } \ No newline at end of file diff --git a/src/utils/cache.rs b/src/utils/cache.rs index 3b064d3..1109dce 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -4,93 +4,93 @@ use mappable_rc::Mrc; /// Convenience trait for overriding Mrc's strange cloning logic pub trait MyClone { - fn my_clone(&self) -> Self; + fn my_clone(&self) -> Self; } impl MyClone for T where T: Clone { - default fn my_clone(&self) -> Self { self.clone() } + default fn my_clone(&self) -> Self { self.clone() } } impl MyClone for Rc { - fn my_clone(&self) -> Self { Rc::clone(self) } + fn my_clone(&self) -> Self { Rc::clone(self) } } impl MyClone for Mrc { - fn my_clone(&self) -> Self { Mrc::clone(self) } + 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> { - store: RefCell>>, - closure: Box Mrc + 'a> + store: RefCell>>, + closure: Box Mrc + 'a> } impl<'a, I, O> Cache<'a, I, O> where - I: Eq + Hash + MyClone + I: Eq + Hash + MyClone { - pub fn new(closure: F) -> Self where F: Fn(I, &Self) -> O { - Self::new_raw(move |o, s| Mrc::new(closure(o, s))) - } + 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: Fn(I, &Self) -> Mrc { - Self { - store: RefCell::new(HashMap::new()), - closure: Box::new(closure) - } + /// 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: Fn(I, &Self) -> Mrc { + Self { + store: RefCell::new(HashMap::new()), + closure: Box::new(closure) } + } - /// Produce and cache a result by cloning I if necessary - pub fn find(&self, i: &I) -> Mrc { - 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.my_clone(), result)).1) + /// Produce and cache a result by cloning I if necessary + pub fn find(&self, i: &I) -> Mrc { + 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.my_clone(), result)).1) + } - #[allow(dead_code)] - /// Return the result if it has already been computed - pub fn known(&self, i: &I) -> Option> { - 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() - } + #[allow(dead_code)] + /// Return the result if it has already been computed + pub fn known(&self, i: &I) -> Option> { + 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() + } } impl<'a, I, O, E> Cache<'a, I, Result> where - I: Eq + Hash + MyClone, - // O: Clone, - E: Clone + 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, 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()) - } + /// 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, 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> where - I: Eq + Hash + MyClone, - // O: Clone + 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> where I: Clone { - let ent = self.find(i); - Mrc::try_map(ent, |o| o.as_ref()).ok() - } + #[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> where I: Clone { + let ent = self.find(i); + Mrc::try_map(ent, |o| o.as_ref()).ok() + } } diff --git a/src/utils/for_loop.rs b/src/utils/for_loop.rs index b5c05ad..f9d0492 100644 --- a/src/utils/for_loop.rs +++ b/src/utils/for_loop.rs @@ -11,10 +11,10 @@ /// /// ``` /// xloop!(for i in 0..10; { -/// connection.try_connect() -/// if connection.ready() { -/// break Some(connection) -/// } +/// connection.try_connect() +/// if connection.ready() { +/// break Some(connection) +/// } /// }; None) /// ``` /// @@ -22,17 +22,17 @@ /// /// ``` /// xloop!(while socket.is_open(); { -/// let (data, is_end) = socket.read(); -/// all_data.append(data) -/// if is_end { break Ok(all_data) } +/// let (data, is_end) = socket.read(); +/// all_data.append(data) +/// if is_end { break Ok(all_data) } /// }; { -/// if let Ok(new_sock) = open_socket(socket.position()) { -/// new_sock.set_position(socket.position()); -/// socket = new_sock; -/// continue -/// } else { -/// Err(DownloadError::ConnectionLost) -/// } +/// if let Ok(new_sock) = open_socket(socket.position()) { +/// new_sock.set_position(socket.position()); +/// socket = new_sock; +/// continue +/// } else { +/// Err(DownloadError::ConnectionLost) +/// } /// }) /// ``` /// @@ -40,7 +40,7 @@ /// /// ``` /// xloop!(let mut leap = 1; own_id*2 + leap < batch_size; leap *= 2; { -/// batch[own_id*2] += batch[own_id*2 + leap] +/// batch[own_id*2] += batch[own_id*2 + leap] /// }) /// ``` /// @@ -51,41 +51,41 @@ /// **todo** find a valid use case for While let for a demo #[macro_export] macro_rules! xloop { - (for $p:pat in $it:expr; $body:stmt) => { - xloop!(for $p in $it; $body; ()) - }; - (for $p:pat in $it:expr; $body:stmt; $exit:stmt) => { - { - let mut __xloop__ = $it.into_iter(); - xloop!(let Some($p) = __xloop__.next(); $body; $exit) - } - }; - (let $p:pat = $e:expr; $body:stmt) => { - xloop!(let $p = $e; $body; ()) - }; - (let $p:pat = $e:expr; $body:stmt; $exit:stmt) => { - { - loop { - if let $p = $e { $body } - else { break { $exit } } - } - } - }; - (while $cond:expr; $body:stmt) => { - xloop!($cond; $body; ()) - }; - (while $cond:expr; $body:stmt; $exit:stmt) => { - { - loop { - if $cond { break { $exit } } - else { $body } - } - } - }; - ($init:stmt; $cond:expr; $step:stmt; $body:stmt) => { - xloop!(for ( $init; $cond; $step ) $body; ()) - }; - ($init:stmt; $cond:expr; $step:stmt; $body:stmt; $exit:stmt) => { - { $init; xloop!(while !($cond); { $body; $step }; $exit) } - }; + (for $p:pat in $it:expr; $body:stmt) => { + xloop!(for $p in $it; $body; ()) + }; + (for $p:pat in $it:expr; $body:stmt; $exit:stmt) => { + { + let mut __xloop__ = $it.into_iter(); + xloop!(let Some($p) = __xloop__.next(); $body; $exit) + } + }; + (let $p:pat = $e:expr; $body:stmt) => { + xloop!(let $p = $e; $body; ()) + }; + (let $p:pat = $e:expr; $body:stmt; $exit:stmt) => { + { + loop { + if let $p = $e { $body } + else { break { $exit } } + } + } + }; + (while $cond:expr; $body:stmt) => { + xloop!($cond; $body; ()) + }; + (while $cond:expr; $body:stmt; $exit:stmt) => { + { + loop { + if $cond { break { $exit } } + else { $body } + } + } + }; + ($init:stmt; $cond:expr; $step:stmt; $body:stmt) => { + xloop!(for ( $init; $cond; $step ) $body; ()) + }; + ($init:stmt; $cond:expr; $step:stmt; $body:stmt; $exit:stmt) => { + { $init; xloop!(while !($cond); { $body; $step }; $exit) } + }; } \ No newline at end of file diff --git a/src/utils/iter.rs b/src/utils/iter.rs index 0014c56..08ea24f 100644 --- a/src/utils/iter.rs +++ b/src/utils/iter.rs @@ -6,31 +6,31 @@ 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)) + Box::new(iter::once(t)) } /// BoxedIter of no elements pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { - Box::new(iter::empty()) + 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<_> - }; + ($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, + J: Iterator, + I: Iterator, { - Box::new(i.flatten()) + 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()) + Box::new(t.into_iter()) } \ No newline at end of file diff --git a/src/utils/merge_sorted.rs b/src/utils/merge_sorted.rs index 35130b3..bb7480e 100644 --- a/src/utils/merge_sorted.rs +++ b/src/utils/merge_sorted.rs @@ -5,23 +5,23 @@ use std::mem; /// Merge two sorted iterators into a sorted iterator. pub fn merge_sorted(mut i: I, mut j: J, mut f: F) -> impl Iterator where - I: Iterator, J: Iterator, - F: FnMut(&T) -> O, O: Ord, + I: Iterator, J: Iterator, + F: FnMut(&T) -> O, O: Ord, { - let mut i_item: Option = None; - let mut j_item: Option = 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)) - }) + let mut i_item: Option = None; + let mut j_item: Option = 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)) + }) } \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index cc01026..1caefae 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,78 +1,85 @@ mod cache; +pub mod translate; +pub use cache::Cache; 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 protomap; - -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 use string_from_charset::string_from_charset; pub use protomap::ProtoMap; +mod product2; +pub use product2::Product2; + +use mappable_rc::Mrc; pub fn mrc_derive(m: &Mrc, p: P) -> Mrc where P: for<'a> FnOnce(&'a T) -> &'a U { - Mrc::map(Mrc::clone(m), p) + 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() + Mrc::try_map(Mrc::clone(m), p).ok() } pub fn mrc_empty_slice() -> Mrc<[T]> { - mrc_derive_slice(&Mrc::new(Vec::new())) + mrc_derive_slice(&Mrc::new(Vec::new())) } pub fn to_mrc_slice(v: Vec) -> Mrc<[T]> { - Mrc::map(Mrc::new(v), |v| v.as_slice()) + 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()) + to_mrc_slice(iter.collect()) } pub fn mrc_derive_slice(mv: &Mrc>) -> Mrc<[T]> { - mrc_derive(mv, |v| v.as_slice()) + mrc_derive(mv, |v| v.as_slice()) } pub fn one_mrc_slice(t: T) -> Mrc<[T]> { - Mrc::map(Mrc::new([t; 1]), |v| v.as_slice()) + Mrc::map(Mrc::new([t; 1]), |v| v.as_slice()) } pub fn mrc_to_iter(ms: Mrc<[T]>) -> impl Iterator> { - let mut i = 0; - std::iter::from_fn(move || if i < ms.len() { - let out = Some(mrc_derive(&ms, |s| &s[i])); - i += 1; - out - } else {None}) + let mut i = 0; + std::iter::from_fn(move || if i < ms.len() { + let out = Some(mrc_derive(&ms, |s| &s[i])); + i += 1; + out + } else {None}) } pub fn mrc_unnest(m: &Mrc>) -> Mrc { - Mrc::clone(m.as_ref()) + Mrc::clone(m.as_ref()) } pub fn mrc_slice_to_only(m: Mrc<[T]>) -> Result, ()> { - Mrc::try_map(m, |slice| { - if slice.len() != 1 {None} - else {Some(&slice[0])} - }).map_err(|_| ()) + Mrc::try_map(m, |slice| { + if slice.len() != 1 {None} + else {Some(&slice[0])} + }).map_err(|_| ()) } pub fn mrc_slice_to_only_option(m: Mrc<[T]>) -> Result>, ()> { - if m.len() > 1 {return Err(())} - Ok(Mrc::try_map(m, |slice| { - if slice.len() == 0 {None} - else {Some(&slice[0])} - }).ok()) + if m.len() > 1 {return Err(())} + Ok(Mrc::try_map(m, |slice| { + if slice.len() == 0 {None} + else {Some(&slice[0])} + }).ok()) +} + +pub fn mrc_concat(a: &Mrc<[T]>, b: &Mrc<[T]>) -> Mrc<[T]> { + collect_to_mrc(a.iter().chain(b.iter()).cloned()) } \ No newline at end of file diff --git a/src/utils/product2.rs b/src/utils/product2.rs new file mode 100644 index 0000000..6dff9c3 --- /dev/null +++ b/src/utils/product2.rs @@ -0,0 +1,53 @@ +use super::Side; + +/// The output of a two-part algorithm. The values are +/// +/// - [Product2::Left] or [Product2::Right] if one of the arguments is the product +/// - [Product2::Either] if the arguments are identical +/// - [Product2::New] if the product is a different value from either +pub enum Product2 { + Left, + Right, + Either, + New(T) +} +impl Product2 { + /// Convert the product into a concrete value by providing the original arguments + pub fn pick(self, left: T, right: T) -> T { + match self { + Self::Left | Self::Either => left, + Self::Right => right, + Self::New(t) => t + } + } + + /// Combine some subresults into a tuple representing a greater result + pub fn join( + self, (lt, rt): (T, T), + second: Product2, (lu, ru): (U, U) + ) -> Product2<(T, U)> { + match (self, second) { + (Self::Either, Product2::Either) => Product2::Either, + (Self::Left | Self::Either, Product2::Left | Product2::Either) => Product2::Left, + (Self::Right | Self::Either, Product2::Right | Product2::Either) => Product2::Right, + (t, u) => Product2::New((t.pick(lt, rt), u.pick(lu, ru))) + } + } + + /// Translate results back into the type of the original problem. + pub fn map A>(self, f: F) -> Product2 { + match self { + Product2::Left => Product2::Left, Product2::Right => Product2::Right, + Product2::Either => Product2::Either, + Product2::New(t) => Product2::New(f(t)) + } + } +} + +/// Technically very different but sometimes neecessary to translate +impl From for Product2 { + fn from(value: Side) -> Self {match value { + Side::Left => Self::Left, + Side::Right => Self::Right + }} +} \ No newline at end of file diff --git a/src/utils/protomap.rs b/src/utils/protomap.rs index 3d56246..eecff7c 100644 --- a/src/utils/protomap.rs +++ b/src/utils/protomap.rs @@ -13,152 +13,152 @@ const INLINE_ENTRIES: usize = 2; /// 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); STACK_COUNT]>, - prototype: Option<&'a ProtoMap<'a, K, V, STACK_COUNT>> + entries: SmallVec<[(K, Option); STACK_COUNT]>, + prototype: Option<&'a ProtoMap<'a, K, V, STACK_COUNT>> } impl<'a, K, V, const STACK_COUNT: usize> ProtoMap<'a, K, V, STACK_COUNT> { - pub fn new() -> Self { - Self { - entries: SmallVec::new(), - prototype: None - } + pub fn new() -> Self { + Self { + entries: SmallVec::new(), + prototype: None } + } - /// Mutable reference to entry without checking proto in O(m) - fn local_entry_mut<'b, Q: ?Sized>(&'b mut self, query: &Q) - -> Option<(usize, &'b mut K, &'b mut Option)> - where K: Borrow, Q: Eq - { - self.entries.iter_mut().enumerate().find_map(|(i, (k, v))| { - if query.eq((*k).borrow()) { Some((i, k, v)) } else { None } - }) - } + /// Mutable reference to entry without checking proto in O(m) + fn local_entry_mut<'b, Q: ?Sized>(&'b mut self, query: &Q) + -> Option<(usize, &'b mut K, &'b mut Option)> + where K: Borrow, Q: Eq + { + self.entries.iter_mut().enumerate().find_map(|(i, (k, v))| { + if query.eq((*k).borrow()) { Some((i, k, v)) } else { None } + }) + } - /// Entry without checking proto in O(m) - fn local_entry<'b, Q: ?Sized>(&'b self, query: &Q) - -> Option<(usize, &'b K, &'b Option)> - where K: Borrow, Q: Eq - { - self.entries.iter().enumerate().find_map(|(i, (k, v))| { - if query.eq((*k).borrow()) { Some((i, k, v)) } else { None } - }) - } + /// Entry without checking proto in O(m) + fn local_entry<'b, Q: ?Sized>(&'b self, query: &Q) + -> Option<(usize, &'b K, &'b Option)> + where K: Borrow, Q: Eq + { + self.entries.iter().enumerate().find_map(|(i, (k, v))| { + if query.eq((*k).borrow()) { Some((i, k, v)) } else { None } + }) + } - /// Find entry in prototype chain in O(n) - pub fn get<'b, Q: ?Sized>(&'b self, query: &Q) -> Option<&'b V> - where K: Borrow, Q: Eq - { - if let Some((_, _, v)) = self.local_entry(query) { - v.as_ref() - } else { - self.prototype?.get(query) - } + /// Find entry in prototype chain in O(n) + pub fn get<'b, Q: ?Sized>(&'b self, query: &Q) -> Option<&'b V> + where K: Borrow, Q: Eq + { + if let Some((_, _, v)) = self.local_entry(query) { + v.as_ref() + } else { + self.prototype?.get(query) } + } - /// Record a value for the given key in O(m) - pub fn set(&mut self, key: &K, value: V) where K: Eq + Clone { - if let Some((_, _, v)) = self.local_entry_mut(key) { - *v = Some(value); - } else { - self.entries.push((key.clone(), Some(value))) - } + /// Record a value for the given key in O(m) + pub fn set(&mut self, key: &K, value: V) where K: Eq + Clone { + if let Some((_, _, v)) = self.local_entry_mut(key) { + *v = Some(value); + } else { + self.entries.push((key.clone(), Some(value))) } + } - /// Delete in a memory-efficient way in O(n) - pub fn delete_small(&mut self, key: &K) where K: Eq + Clone { - let exists_up = self.prototype.and_then(|p| p.get(key)).is_some(); - let local_entry = self.local_entry_mut(key); - match (exists_up, local_entry) { - (false, None) => (), // nothing to do - (false, Some((i, _, _))) => { self.entries.remove(i); }, // forget locally - (true, Some((_, _, v))) => *v = None, // update local override to cover - (true, None) => self.entries.push((key.clone(), None)), // create new - } + /// Delete in a memory-efficient way in O(n) + pub fn delete_small(&mut self, key: &K) where K: Eq + Clone { + let exists_up = self.prototype.and_then(|p| p.get(key)).is_some(); + let local_entry = self.local_entry_mut(key); + match (exists_up, local_entry) { + (false, None) => (), // nothing to do + (false, Some((i, _, _))) => { self.entries.remove(i); }, // forget locally + (true, Some((_, _, v))) => *v = None, // update local override to cover + (true, None) => self.entries.push((key.clone(), None)), // create new } + } - /// Delete in O(m) without checking the prototype chain - /// May produce unnecessary cover over previously unknown key - pub fn delete_fast(&mut self, key: &K) where K: Eq + Clone { - if let Some((_, _, v)) = self.local_entry_mut(key) { - *v = None - } else { - self.entries.push((key.clone(), None)) - } + /// Delete in O(m) without checking the prototype chain + /// May produce unnecessary cover over previously unknown key + pub fn delete_fast(&mut self, key: &K) where K: Eq + Clone { + if let Some((_, _, v)) = self.local_entry_mut(key) { + *v = None + } else { + self.entries.push((key.clone(), None)) } + } - /// Iterate over the values defined herein and on the prototype chain - /// Note that this will visit keys multiple times - pub fn iter(&self) -> impl Iterator)> { - let mut map = self; - iter::from_fn(move || { - let pairs = map.entries.iter(); - map = map.prototype?; - Some(pairs) - }).flatten() - } + /// Iterate over the values defined herein and on the prototype chain + /// Note that this will visit keys multiple times + pub fn iter(&self) -> impl Iterator)> { + let mut map = self; + iter::from_fn(move || { + let pairs = map.entries.iter(); + map = map.prototype?; + Some(pairs) + }).flatten() + } - /// Visit the keys in an unsafe random order, repeated arbitrarily many times - pub fn keys(&self) -> impl Iterator { - self.iter().map(|(k, _)| k) - } + /// Visit the keys in an unsafe random order, repeated arbitrarily many times + pub fn keys(&self) -> impl Iterator { + self.iter().map(|(k, _)| k) + } - /// Visit the values in random order - pub fn values(&self) -> impl Iterator { - self.iter().filter_map(|(_, v)| v.as_ref()) - } + /// Visit the values in random order + pub fn values(&self) -> impl Iterator { + self.iter().filter_map(|(_, v)| v.as_ref()) + } - /// Update the prototype, and correspondingly the lifetime of the map - pub fn set_proto<'b>(self, proto: &'b ProtoMap<'b, K, V, STACK_COUNT>) - -> ProtoMap<'b, K, V, STACK_COUNT> { - ProtoMap { - entries: self.entries, - prototype: Some(proto) - } + /// Update the prototype, and correspondingly the lifetime of the map + pub fn set_proto<'b>(self, proto: &'b ProtoMap<'b, K, V, STACK_COUNT>) + -> ProtoMap<'b, K, V, STACK_COUNT> { + ProtoMap { + entries: self.entries, + prototype: Some(proto) } + } } impl From for ProtoMap<'_, K, V, STACK_COUNT> where T: IntoIterator { - fn from(value: T) -> Self { - Self { - entries: value.into_iter().map(|(k, v)| (k, Some(v))).collect(), - prototype: None - } + fn from(value: T) -> Self { + Self { + entries: value.into_iter().map(|(k, v)| (k, Some(v))).collect(), + prototype: None } + } } impl Index<&Q> for ProtoMap<'_, K, V, STACK_COUNT> where K: Borrow, Q: Eq { - type Output = V; - fn index(&self, index: &Q) -> &Self::Output { - self.get(index).expect("Index not found in map") - } + type Output = V; + fn index(&self, index: &Q) -> &Self::Output { + self.get(index).expect("Index not found in map") + } } impl Clone for ProtoMap<'_, K, V, STACK_COUNT> { - fn clone(&self) -> Self { - Self { - entries: self.entries.clone(), - prototype: self.prototype - } + fn clone(&self) -> Self { + Self { + entries: self.entries.clone(), + prototype: self.prototype } + } } impl<'a, K: 'a, V: 'a, const STACK_COUNT: usize> Add<(K, V)> for &'a ProtoMap<'a, K, V, STACK_COUNT> { - type Output = ProtoMap<'a, K, V, STACK_COUNT>; - fn add(self, rhs: (K, V)) -> Self::Output { - ProtoMap::from([rhs]).set_proto(self) - } + type Output = ProtoMap<'a, K, V, STACK_COUNT>; + fn add(self, rhs: (K, V)) -> Self::Output { + ProtoMap::from([rhs]).set_proto(self) + } } #[macro_export] macro_rules! protomap { - ($($ent:expr),*) => { - ProtoMap::from([$($ent:expr),*]) - }; + ($($ent:expr),*) => { + ProtoMap::from([$($ent:expr),*]) + }; } diff --git a/src/utils/side.rs b/src/utils/side.rs index e72b398..4fdae20 100644 --- a/src/utils/side.rs +++ b/src/utils/side.rs @@ -4,50 +4,50 @@ use std::fmt::Display; pub enum Side {Left, Right} impl Display for Side { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Left => write!(f, "Left"), - Self::Right => write!(f, "Right"), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Left => write!(f, "Left"), + Self::Right => write!(f, "Right"), } + } } impl Side { - pub fn opposite(&self) -> Self { - match self { - Self::Left => Self::Right, - Self::Right => Self::Left - } + pub fn opposite(&self) -> Self { + match self { + Self::Left => Self::Right, + Self::Right => Self::Left } - /// Shorthand for opposite - pub fn inv(&self) -> Self { self.opposite() } - /// take N elements from this end of a slice - pub fn slice<'a, T>(&self, size: usize, slice: &'a [T]) -> &'a [T] { - match self { - Side::Left => &slice[..size], - Side::Right => &slice[slice.len() - size..] - } + } + /// Shorthand for opposite + pub fn inv(&self) -> Self { self.opposite() } + /// take N elements from this end of a slice + pub fn slice<'a, T>(&self, size: usize, slice: &'a [T]) -> &'a [T] { + match self { + Side::Left => &slice[..size], + Side::Right => &slice[slice.len() - size..] } - /// ignore N elements from this end of a slice - 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 of a slice + 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] { + self.crop(margin, self.opposite().crop(opposite, slice)) + } + /// Pick this side from a pair of things + pub fn pick(&self, pair: (T, T)) -> T { + match self { + Side::Left => pair.0, + Side::Right => pair.1 } - /// 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 - pub fn pick(&self, pair: (T, T)) -> T { - match self { - Side::Left => pair.0, - Side::Right => pair.1 - } - } - /// Make a pair with the first element on this side - pub fn pair(&self, this: T, opposite: T) -> (T, T) { - match self { - Side::Left => (this, opposite), - Side::Right => (opposite, this) - } + } + /// Make a pair with the first element on this side + pub fn pair(&self, this: T, opposite: T) -> (T, T) { + match self { + Side::Left => (this, opposite), + Side::Right => (opposite, this) } + } } \ No newline at end of file diff --git a/src/utils/string_from_charset.rs b/src/utils/string_from_charset.rs index 7cf0943..cec678a 100644 --- a/src/utils/string_from_charset.rs +++ b/src/utils/string_from_charset.rs @@ -1,14 +1,14 @@ fn string_from_charset_rec(val: u64, digits: &str) -> String { - let radix = digits.len() as u64; - let mut prefix = if val > radix { - string_from_charset_rec(val / radix, digits) - } else {String::new()}; - prefix.push(digits.chars().nth(val as usize - 1).unwrap_or_else( - || panic!("Overindexed digit set \"{}\" with {}", digits, val - 1) - )); - prefix + let radix = digits.len() as u64; + let mut prefix = if val > radix { + string_from_charset_rec(val / radix, digits) + } else {String::new()}; + prefix.push(digits.chars().nth(val as usize - 1).unwrap_or_else( + || panic!("Overindexed digit set \"{}\" with {}", digits, val - 1) + )); + prefix } pub fn string_from_charset(val: u64, digits: &str) -> String { - string_from_charset_rec(val + 1, digits) + string_from_charset_rec(val + 1, digits) } \ No newline at end of file diff --git a/src/utils/substack.rs b/src/utils/substack.rs index cc65112..ebd9e84 100644 --- a/src/utils/substack.rs +++ b/src/utils/substack.rs @@ -5,70 +5,84 @@ use std::fmt::Debug; /// deep enough to warrant a heap-allocated set #[derive(Clone, Copy)] pub struct Stackframe<'a, T> { - pub item: T, - pub prev: Option<&'a Stackframe<'a, T>>, - pub len: usize + pub item: T, + pub prev: Option<&'a Stackframe<'a, T>>, + pub len: usize } impl<'a, T: 'a> Stackframe<'a, T> { - pub fn new(item: T) -> Self { - Self { - item, - prev: None, - len: 1 - } + pub fn new(item: T) -> Self { + Self { + item, + prev: None, + len: 1 } - /// Get the item owned by this listlike, very fast O(1) - pub fn item(&self) -> &T { &self.item } - /// Get the next link in the list, very fast O(1) - pub fn prev(&self) -> Option<&'a Stackframe> { self.prev } - /// Construct an iterator over the listlike, very fast O(1) - pub fn iter(&self) -> StackframeIterator { - StackframeIterator { curr: Some(self) } + } + /// Get the item owned by this listlike, very fast O(1) + pub fn item(&self) -> &T { &self.item } + /// Get the next link in the list, very fast O(1) + pub fn prev(&self) -> Option<&'a Stackframe> { self.prev } + /// Construct an iterator over the listlike, very fast O(1) + pub fn iter(&self) -> StackframeIterator { + StackframeIterator { curr: Some(self) } + } + pub fn push(&self, item: T) -> Stackframe<'_, T> { + Stackframe { + item, + prev: Some(self), + len: self.len + 1 } - pub fn push(&self, item: T) -> Stackframe<'_, T> { - Stackframe { - item, - prev: Some(self), - len: self.len + 1 - } - } - pub fn opush(prev: Option<&'a Self>, item: T) -> Self { - Self { - item, - prev, - len: prev.map_or(1, |s| s.len) - } - } - pub fn len(&self) -> usize { self.len } - pub fn pop(&self, count: usize) -> Option<&Self> { - if count == 0 {Some(self)} - else {self.prev.expect("Index out of range").pop(count - 1)} - } - 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)} + } + pub fn opush(prev: Option<&'a Self>, item: T) -> Self { + Self { + item, + prev, + len: prev.map_or(1, |s| s.len) } + } + pub fn len(&self) -> usize { self.len } + pub fn pop(&self, count: usize) -> Option<&Self> { + if count == 0 {Some(self)} + else {self.prev.expect("Index out of range").pop(count - 1)} + } + 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)} + } + pub fn o_into_iter(curr: Option<&Self>) -> StackframeIterator { + StackframeIterator { curr } + } } impl<'a, T> Debug for Stackframe<'a, T> where T: Debug { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Substack")?; - f.debug_list().entries(self.iter()).finish() - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Substack")?; + f.debug_list().entries(self.iter()).finish() + } } pub struct StackframeIterator<'a, T> { - curr: Option<&'a Stackframe<'a, T>> + curr: Option<&'a Stackframe<'a, T>> +} + +impl<'a, T> StackframeIterator<'a, T> { + pub fn first_some Option>(&mut self, f: F) -> Option { + while let Some(x) = self.next() { + if let Some(result) = f(x) { + return Some(result) + } + } + None + } } impl<'a, T> Iterator for StackframeIterator<'a, T> { - type Item = &'a T; - fn next(&mut self) -> Option<&'a T> { - let curr = self.curr?; - let item = curr.item(); - let prev = curr.prev(); - self.curr = prev; - Some(item) - } + type Item = &'a T; + fn next(&mut self) -> Option<&'a T> { + let curr = self.curr?; + let item = curr.item(); + let prev = curr.prev(); + self.curr = prev; + Some(item) + } } \ No newline at end of file diff --git a/src/utils/translate.rs b/src/utils/translate.rs new file mode 100644 index 0000000..ac184d5 --- /dev/null +++ b/src/utils/translate.rs @@ -0,0 +1,22 @@ +use std::mem; + +pub fn translate T>(data: &mut T, f: F) { + unsafe { + let mut acc = mem::MaybeUninit::::uninit().assume_init(); + mem::swap(&mut acc, data); + let mut new = f(acc); + mem::swap(&mut new, data); + mem::forget(new); + } +} + +pub fn process (T, U)>(data: &mut T, f: F) -> U { + unsafe { + let mut acc = mem::MaybeUninit::::uninit().assume_init(); + mem::swap(&mut acc, data); + let (mut new, ret) = f(acc); + mem::swap(&mut new, data); + mem::forget(new); + ret + } +} \ No newline at end of file diff --git a/src/utils/unless_let.rs b/src/utils/unless_let.rs index da44ed2..70313ba 100644 --- a/src/utils/unless_let.rs +++ b/src/utils/unless_let.rs @@ -1,6 +1,6 @@ #[macro_export] macro_rules! unless_let { - ($m:pat_param = $expr:tt) => { - if let $m = $expr {} else - } + ($m:pat_param = $expr:tt) => { + if let $m = $expr {} else + } } \ No newline at end of file diff --git a/src/utils/unwrap_or.rs b/src/utils/unwrap_or.rs index 24d187b..4ea81fa 100644 --- a/src/utils/unwrap_or.rs +++ b/src/utils/unwrap_or.rs @@ -1,6 +1,6 @@ #[macro_export] macro_rules! unwrap_or { - ($m:expr; $fail:expr) => { - { if let Some(res) = ($m) {res} else {$fail} } - } + ($m:expr; $fail:expr) => { + { if let Some(res) = ($m) {res} else {$fail} } + } } \ No newline at end of file