forked from Orchid/orchid
Rule execution now runs, no tests tho
This commit is contained in:
44
README.md
44
README.md
@@ -7,14 +7,14 @@ Hello World in Orchid
|
|||||||
```orchid
|
```orchid
|
||||||
import std::io::(println, out)
|
import std::io::(println, out)
|
||||||
|
|
||||||
main == println out "Hello World!"
|
main := println out "Hello World!"
|
||||||
```
|
```
|
||||||
|
|
||||||
Basic command line calculator
|
Basic command line calculator
|
||||||
```orchid
|
```orchid
|
||||||
import std::io::(readln, printf, in, out)
|
import std::io::(readln, printf, in, out)
|
||||||
|
|
||||||
main == (
|
main := (
|
||||||
readln in >>= int |> \a.
|
readln in >>= int |> \a.
|
||||||
readln in >>= \op.
|
readln in >>= \op.
|
||||||
readln in >>= int |> \b.
|
readln in >>= int |> \b.
|
||||||
@@ -31,7 +31,7 @@ Grep
|
|||||||
```orchid
|
```orchid
|
||||||
import std::io::(readln, println, in, out, getarg)
|
import std::io::(readln, println, in, out, getarg)
|
||||||
|
|
||||||
main == loop \r. (
|
main := loop \r. (
|
||||||
readln in >>= \line.
|
readln in >>= \line.
|
||||||
if (substring (getarg 1) line)
|
if (substring (getarg 1) line)
|
||||||
then (println out ln >>= r)
|
then (println out ln >>= r)
|
||||||
@@ -41,7 +41,7 @@ main == loop \r. (
|
|||||||
|
|
||||||
Filter through an arbitrary collection
|
Filter through an arbitrary collection
|
||||||
```orchid
|
```orchid
|
||||||
filter == @C:Type -> Type. @:Map C. @T. \f:T -> Bool. \coll:C T. (
|
filter := @C:Type -> Type. @:Map C. @T. \f:T -> Bool. \coll:C T. (
|
||||||
coll >> \el. if (f el) then (Some el) else Nil
|
coll >> \el. if (f el) then (Some el) else Nil
|
||||||
):(C T)
|
):(C T)
|
||||||
```
|
```
|
||||||
@@ -122,9 +122,9 @@ types whose defaults have implmentations based on your defaults.
|
|||||||
For a demonstration, here's a sample implementation of the Option monad.
|
For a demonstration, here's a sample implementation of the Option monad.
|
||||||
```orchid
|
```orchid
|
||||||
--[[ The definition of Monad ]]--
|
--[[ The definition of Monad ]]--
|
||||||
Bind == \M:Type -> Type. @T -> @U -> (T -> M U) -> M T -> M U
|
Bind := \M:Type -> Type. @T -> @U -> (T -> M U) -> M T -> M U
|
||||||
Return == \M:Type -> Type. @T -> T -> M T
|
Return := \M:Type -> Type. @T -> T -> M T
|
||||||
Monad == \M:Type -> Type. (
|
Monad := \M:Type -> Type. (
|
||||||
@:Bind M.
|
@:Bind M.
|
||||||
@:Return M.
|
@:Return M.
|
||||||
0 --[ Note that empty expressions are forbidden so those that exist
|
0 --[ Note that empty expressions are forbidden so those that exist
|
||||||
@@ -134,19 +134,19 @@ Monad == \M:Type -> Type. (
|
|||||||
)
|
)
|
||||||
|
|
||||||
--[[ The definition of Option ]]--
|
--[[ The definition of Option ]]--
|
||||||
export Option == \T:Type. @U -> U -> (T -> U) -> U
|
export Option := \T:Type. @U -> U -> (T -> U) -> U
|
||||||
--[ Constructors ]--
|
--[ Constructors ]--
|
||||||
export Some == @T. \data:T. ( \default. \map. map data ):(Option T)
|
export Some := @T. \data:T. ( \default. \map. map data ):(Option T)
|
||||||
export None == @T. ( \default. \map. default ):(Option T)
|
export None := @T. ( \default. \map. default ):(Option T)
|
||||||
--[ Implement Monad ]--
|
--[ Implement Monad ]--
|
||||||
default returnOption == Some:(Return Option)
|
default returnOption := Some:(Return Option)
|
||||||
default bindOption == ( @T:Type. @U:Type.
|
default bindOption := ( @T:Type. @U:Type.
|
||||||
\f:T -> U. \opt:Option T. opt None f
|
\f:T -> U. \opt:Option T. opt None f
|
||||||
):(Bind Option)
|
):(Bind Option)
|
||||||
--[ Sample function that works on unknown monad to demonstrate HKTs.
|
--[ Sample function that works on unknown monad to demonstrate HKTs.
|
||||||
Turns (Option (M T)) into (M (Option T)), "raising" the unknown monad
|
Turns (Option (M T)) into (M (Option T)), "raising" the unknown monad
|
||||||
out of the Option ]--
|
out of the Option ]--
|
||||||
export raise == @M:Type -> Type. @T:Type. @:Monad M. \opt:Option (M T). (
|
export raise := @M:Type -> Type. @T:Type. @:Monad M. \opt:Option (M T). (
|
||||||
opt (return None) (\m. bind m (\x. Some x))
|
opt (return None) (\m. bind m (\x. Some x))
|
||||||
):(M (Option T))
|
):(M (Option T))
|
||||||
```
|
```
|
||||||
@@ -162,7 +162,7 @@ Add has three arguments, two are the types of the operands and one is
|
|||||||
the result:
|
the result:
|
||||||
|
|
||||||
```orchid
|
```orchid
|
||||||
default concatListAdd replacing applicativeAdd == @T. (
|
default concatListAdd replacing elementwiseAdd := @T. (
|
||||||
...
|
...
|
||||||
):(Add (List T) (List T) (List T))
|
):(Add (List T) (List T) (List T))
|
||||||
```
|
```
|
||||||
@@ -170,7 +170,7 @@ default concatListAdd replacing applicativeAdd == @T. (
|
|||||||
For completeness' sake, the original definition might look like this:
|
For completeness' sake, the original definition might look like this:
|
||||||
|
|
||||||
```orchid
|
```orchid
|
||||||
default elementwiseAdd == @C:Type -> Type. @T. @U. @V. @:(Applicative C). @:(Add T U V). (
|
default elementwiseAdd := @C:Type -> Type. @T. @U. @V. @:(Applicative C). @:(Add T U V). (
|
||||||
...
|
...
|
||||||
):(Add (C T) (C U) (C V))
|
):(Add (C T) (C U) (C V))
|
||||||
```
|
```
|
||||||
@@ -179,7 +179,7 @@ With the use of autos, here's what the recursive multiplication
|
|||||||
implementation looks like:
|
implementation looks like:
|
||||||
|
|
||||||
```orchid
|
```orchid
|
||||||
default iterativeMultiply == @T. @:(Add T T T). (
|
default iterativeMultiply := @T. @:(Add T T T). (
|
||||||
\a:int.\b:T. loop \r. (\i.
|
\a:int.\b:T. loop \r. (\i.
|
||||||
ifthenelse (ieq i 0)
|
ifthenelse (ieq i 0)
|
||||||
b
|
b
|
||||||
@@ -191,7 +191,7 @@ default iterativeMultiply == @T. @:(Add T T T). (
|
|||||||
This could then be applied to any type that's closed over addition
|
This could then be applied to any type that's closed over addition
|
||||||
|
|
||||||
```orchid
|
```orchid
|
||||||
aroundTheWorldLyrics == (
|
aroundTheWorldLyrics := (
|
||||||
mult 18 (add (mult 4 "Around the World\n") "\n")
|
mult 18 (add (mult 4 "Around the World\n") "\n")
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@@ -218,20 +218,20 @@ are searched back-to-front. If order is still a problem, you can always
|
|||||||
parenthesize subexpressions at the callsite.
|
parenthesize subexpressions at the callsite.
|
||||||
|
|
||||||
```orchid
|
```orchid
|
||||||
(...$pre:(seq 2) if $1 then $2 else $3 ...$post:(seq 1)) =2=> (
|
(..$pre:2 if $1 then $2 else $3 ..$post:1) =2=> (
|
||||||
...$pre
|
..$pre
|
||||||
(ifthenelse $1 $2 $3)
|
(ifthenelse $1 $2 $3)
|
||||||
...$post
|
...$post
|
||||||
)
|
)
|
||||||
$a + $b =10=> (add $a $b)
|
$a + $b =10=> (add $a $b)
|
||||||
$a == $b =5=> (eq $a $b)
|
$a = $b =5=> (eq $a $b)
|
||||||
$a - $b =10=> (sub $a $b)
|
$a - $b =10=> (sub $a $b)
|
||||||
```
|
```
|
||||||
|
|
||||||
The recursive addition function now looks like this
|
The recursive addition function now looks like this
|
||||||
|
|
||||||
```orchid
|
```orchid
|
||||||
default iterativeMultiply == @T. @:(Add T T T). (
|
default iterativeMultiply := @T. @:(Add T T T). (
|
||||||
\a:int.\b:T. loop \r. (\i.
|
\a:int.\b:T. loop \r. (\i.
|
||||||
if (i = 0) then b
|
if (i = 0) then b
|
||||||
else (b + (r (i - 1)))
|
else (b + (r (i - 1)))
|
||||||
@@ -249,7 +249,7 @@ environment and can carry structured data payloads.
|
|||||||
Here's an example of a carriage being used to turn a square-bracketed
|
Here's an example of a carriage being used to turn a square-bracketed
|
||||||
list expression into a lambda expression that matches a conslist. Notice
|
list expression into a lambda expression that matches a conslist. Notice
|
||||||
how the square brackets pair up, as all three variants of brackets
|
how the square brackets pair up, as all three variants of brackets
|
||||||
group nodes.
|
are considered branches in the S-tree rather than individual tokens.
|
||||||
|
|
||||||
```
|
```
|
||||||
-- Initial step, eliminates entry condition (square brackets) and constructs
|
-- Initial step, eliminates entry condition (square brackets) and constructs
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ export ;> $a =200=> (greet $a)
|
|||||||
reeee := \$a.b
|
reeee := \$a.b
|
||||||
|
|
||||||
-- single-word exported rule
|
-- single-word exported rule
|
||||||
export main == (
|
export main := (
|
||||||
print "What is your name?" >>
|
print "What is your name?" >>
|
||||||
readln >>= \name.
|
readln >>= \name.
|
||||||
greet name
|
greet name
|
||||||
)
|
)
|
||||||
|
|
||||||
export < $a ...$rest /> == (createElement (tok_to_str $a) [(props_carriage ...$rest)])
|
export < $a ...$rest /> := (createElement (tok_to_str $a) [(props_carriage ...$rest)])
|
||||||
export (props_carriage $key = $value) == (tok_to_str $key) => $value
|
export (props_carriage $key = $value) := (tok_to_str $key) => $value
|
||||||
|
|
||||||
-- The broadest trait definition in existence
|
-- The broadest trait definition in existence
|
||||||
Foo == (Bar Baz)
|
Foo := (Bar Baz)
|
||||||
-- default anyFoo = @T. @impl:(T (Bar Baz)). impl:(T Foo)
|
-- default anyFoo = @T. @impl:(T (Bar Baz)). impl:(T Foo)
|
||||||
1
examples/rule_demo/main.orc
Normal file
1
examples/rule_demo/main.orc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
expor main := foo bar baz
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use mappable_rc::Mrc;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
use std::{fmt::Debug};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// An exact value
|
/// An exact value
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -23,12 +24,17 @@ impl Debug for Literal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An S-expression with a type
|
/// An S-expression with a type
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
pub struct Expr(pub Clause, pub Option<Box<Expr>>);
|
pub struct Expr(pub Clause, pub Option<Mrc<Expr>>);
|
||||||
|
|
||||||
|
impl Clone for Expr {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone(), self.1.as_ref().map(Mrc::clone))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Expr {
|
impl Debug for Expr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
// f.debug_tuple("Expr").field(&self.0).field(&self.1).finish()
|
|
||||||
let Expr(val, typ) = self;
|
let Expr(val, typ) = self;
|
||||||
write!(f, "{:?}", val)?;
|
write!(f, "{:?}", val)?;
|
||||||
if let Some(typ) = typ { write!(f, "{:?}", typ) }
|
if let Some(typ) = typ { write!(f, "{:?}", typ) }
|
||||||
@@ -37,38 +43,62 @@ impl Debug for Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An S-expression as read from a source file
|
/// An S-expression as read from a source file
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
pub enum Clause {
|
pub enum Clause {
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
Name{
|
Name{
|
||||||
local: Option<String>,
|
local: Option<String>,
|
||||||
qualified: Vec<String>
|
qualified: Mrc<[String]>
|
||||||
},
|
},
|
||||||
S(char, Vec<Expr>),
|
S(char, Mrc<[Expr]>),
|
||||||
Lambda(String, Vec<Expr>, Vec<Expr>),
|
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),
|
||||||
Auto(Option<String>, Vec<Expr>, Vec<Expr>),
|
Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>),
|
||||||
/// Second parameter:
|
/// Second parameter:
|
||||||
/// None => matches one token
|
/// None => matches one token
|
||||||
/// Some(prio) => prio is the sizing priority for the vectorial (higher prio grows first)
|
/// Some((prio, nonzero)) =>
|
||||||
Placeh(String, Option<usize>),
|
/// prio is the sizing priority for the vectorial (higher prio grows first)
|
||||||
|
/// nonzero is whether the vectorial matches 1..n or 0..n tokens
|
||||||
|
Placeh{
|
||||||
|
key: String,
|
||||||
|
vec: Option<(usize, bool)>
|
||||||
|
},
|
||||||
}
|
}
|
||||||
impl Clause {
|
impl Clause {
|
||||||
pub fn body(&self) -> Option<&Vec<Expr>> {
|
pub fn body(&self) -> Option<Mrc<[Expr]>> {
|
||||||
match self {
|
match self {
|
||||||
Clause::Auto(_, _, body) |
|
Clause::Auto(_, _, body) |
|
||||||
Clause::Lambda(_, _, body) |
|
Clause::Lambda(_, _, body) |
|
||||||
Clause::S(_, body) => Some(body),
|
Clause::S(_, body) => Some(Mrc::clone(body)),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn typ(&self) -> Option<&Vec<Expr>> {
|
pub fn typ(&self) -> Option<Mrc<[Expr]>> {
|
||||||
match self {
|
match self {
|
||||||
Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(typ),
|
Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(Mrc::clone(typ)),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Clause {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Clause::S(c, b) => Clause::S(*c, Mrc::clone(b)),
|
||||||
|
Clause::Auto(n, t, b) => Clause::Auto(
|
||||||
|
n.clone(), Mrc::clone(t), Mrc::clone(b)
|
||||||
|
),
|
||||||
|
Clause::Name { local: l, qualified: q } => Clause::Name {
|
||||||
|
local: l.clone(), qualified: Mrc::clone(q)
|
||||||
|
},
|
||||||
|
Clause::Lambda(n, t, b) => Clause::Lambda(
|
||||||
|
n.clone(), Mrc::clone(t), Mrc::clone(b)
|
||||||
|
),
|
||||||
|
Clause::Placeh{key, vec} => Clause::Placeh{key: key.clone(), vec: *vec},
|
||||||
|
Clause::Literal(l) => Clause::Literal(l.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fmt_expr_seq(it: &mut dyn Iterator<Item = &Expr>, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt_expr_seq(it: &mut dyn Iterator<Item = &Expr>, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for item in Itertools::intersperse(it.map(Some), None) { match item {
|
for item in Itertools::intersperse(it.map(Some), None) { match item {
|
||||||
Some(expr) => write!(f, "{:?}", expr),
|
Some(expr) => write!(f, "{:?}", expr),
|
||||||
@@ -82,7 +112,7 @@ impl Debug for Clause {
|
|||||||
match self {
|
match self {
|
||||||
Self::Literal(arg0) => write!(f, "{:?}", arg0),
|
Self::Literal(arg0) => write!(f, "{:?}", arg0),
|
||||||
Self::Name{local, qualified} =>
|
Self::Name{local, qualified} =>
|
||||||
if let Some(local) = local {write!(f, "{}<{}>", qualified.join("::"), local)}
|
if let Some(local) = local {write!(f, "{}`{}`", qualified.join("::"), local)}
|
||||||
else {write!(f, "{}", qualified.join("::"))},
|
else {write!(f, "{}", qualified.join("::"))},
|
||||||
Self::S(del, items) => {
|
Self::S(del, items) => {
|
||||||
f.write_str(&del.to_string())?;
|
f.write_str(&del.to_string())?;
|
||||||
@@ -104,19 +134,29 @@ impl Debug for Clause {
|
|||||||
f.write_str(":")?; fmt_expr_seq(&mut argtyp.iter(), f)?; f.write_str(".")?;
|
f.write_str(":")?; fmt_expr_seq(&mut argtyp.iter(), f)?; f.write_str(".")?;
|
||||||
fmt_expr_seq(&mut body.iter(), f)
|
fmt_expr_seq(&mut body.iter(), f)
|
||||||
},
|
},
|
||||||
// Self::Parameter(name) => write!(f, "`{}", name),
|
Self::Placeh{key, vec: None} => write!(f, "${key}"),
|
||||||
Self::Placeh(name, None) => write!(f, "${}", name),
|
Self::Placeh{key, vec: Some((prio, true))} => write!(f, "...${key}:{prio}"),
|
||||||
Self::Placeh(name, Some(prio)) => write!(f, "...${}:{}", name, prio)
|
Self::Placeh{key, vec: Some((prio, false))} => write!(f, "..${key}:{prio}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A substitution rule as read from the source
|
/// A substitution rule as read from the source
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
pub struct Rule {
|
pub struct Rule {
|
||||||
pub source: Vec<Expr>,
|
pub source: Mrc<[Expr]>,
|
||||||
pub prio: NotNan<f64>,
|
pub prio: NotNan<f64>,
|
||||||
pub target: Vec<Expr>
|
pub target: Mrc<[Expr]>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Rule {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
source: Mrc::clone(&self.source),
|
||||||
|
prio: self.prio,
|
||||||
|
target: Mrc::clone(&self.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Rule {
|
impl Debug for Rule {
|
||||||
|
|||||||
46
src/main.rs
46
src/main.rs
@@ -1,14 +1,23 @@
|
|||||||
|
#![feature(specialization)]
|
||||||
|
|
||||||
use std::env::current_dir;
|
use std::{env::current_dir, process::exit};
|
||||||
|
|
||||||
mod parse;
|
mod parse;
|
||||||
mod project;
|
mod project;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod expression;
|
mod expression;
|
||||||
mod rule;
|
mod rule;
|
||||||
|
use expression::{Expr, Clause};
|
||||||
|
use mappable_rc::Mrc;
|
||||||
use project::{rule_collector, Loaded, file_loader};
|
use project::{rule_collector, Loaded, file_loader};
|
||||||
|
use rule::Repository;
|
||||||
|
use utils::to_mrc_slice;
|
||||||
|
|
||||||
fn literal(orig: &[&str]) -> Vec<String> {
|
fn literal(orig: &[&str]) -> Mrc<[String]> {
|
||||||
|
to_mrc_slice(vliteral(orig))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vliteral(orig: &[&str]) -> Vec<String> {
|
||||||
orig.iter().map(|&s| s.to_owned()).collect()
|
orig.iter().map(|&s| s.to_owned()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,15 +30,40 @@ export (match_sequence $lhs) >> (match_sequence $rhs) =100=> (bind ($lhs) (\_. $
|
|||||||
export (match_sequence $lhs) >>= (match_sequence $rhs) =100=> (bind ($lhs) ($rhs))
|
export (match_sequence $lhs) >>= (match_sequence $rhs) =100=> (bind ($lhs) ($rhs))
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
|
||||||
|
fn initial_tree() -> Mrc<[Expr]> {
|
||||||
|
to_mrc_slice(vec![Expr(Clause::Name {
|
||||||
|
local: None,
|
||||||
|
qualified: to_mrc_slice(vec!["main".to_string(), "main".to_string()])
|
||||||
|
}, None)])
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cwd = current_dir().unwrap();
|
let cwd = current_dir().unwrap();
|
||||||
let collect_rules = rule_collector(move |n| {
|
let collect_rules = rule_collector(move |n| {
|
||||||
if n == vec!["prelude"] { Ok(Loaded::Module(PRELUDE.to_string())) }
|
if n == literal(&["prelude"]) { Ok(Loaded::Module(PRELUDE.to_string())) }
|
||||||
else { file_loader(cwd.clone())(n) }
|
else { file_loader(cwd.clone())(n) }
|
||||||
}, literal(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"]));
|
}, vliteral(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"]));
|
||||||
match collect_rules.try_find(&literal(&["main"])) {
|
match collect_rules.try_find(&literal(&["main"])) {
|
||||||
Ok(rules) => for rule in rules.iter() {
|
Ok(rules) => {
|
||||||
println!("{rule:?}")
|
let mut tree = initial_tree();
|
||||||
|
println!("Start processing {tree:?}");
|
||||||
|
let repo = Repository::new(rules.as_ref().to_owned());
|
||||||
|
println!("Ruleset: {repo:?}");
|
||||||
|
let mut i = 0; loop {
|
||||||
|
if 10 <= i {break} else {i += 1}
|
||||||
|
match repo.step(Mrc::clone(&tree)) {
|
||||||
|
Ok(Some(phase)) => {
|
||||||
|
tree = phase;
|
||||||
|
println!("Step {i}: {tree:?}")
|
||||||
|
},
|
||||||
|
Ok(None) => exit(0),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Rule error: {e:?}");
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => println!("{:#?}", err)
|
Err(err) => println!("{:#?}", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
use chumsky::{self, prelude::*, Parser};
|
use chumsky::{self, prelude::*, Parser};
|
||||||
use crate::{enum_parser, expression::{Clause, Expr, Literal}};
|
use mappable_rc::Mrc;
|
||||||
|
use crate::enum_parser;
|
||||||
|
use crate::expression::{Clause, Expr, Literal};
|
||||||
|
use crate::utils::to_mrc_slice;
|
||||||
|
|
||||||
use super::{lexer::Lexeme};
|
use super::lexer::Lexeme;
|
||||||
|
|
||||||
fn sexpr_parser<P>(
|
fn sexpr_parser<P>(
|
||||||
expr: P
|
expr: P
|
||||||
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
|
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
|
||||||
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
||||||
Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, b))
|
Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, to_mrc_slice(b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lambda_parser<P>(
|
fn lambda_parser<P>(
|
||||||
@@ -28,9 +31,9 @@ where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
|||||||
.then_ignore(just(Lexeme::name(".")))
|
.then_ignore(just(Lexeme::name(".")))
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
||||||
.then(expr.repeated().at_least(1))
|
.then(expr.repeated().at_least(1))
|
||||||
.map(|((name, typ), mut body): ((String, Vec<Expr>), Vec<Expr>)| {
|
.map(|((name, typ), body): ((String, Vec<Expr>), Vec<Expr>)| {
|
||||||
// for ent in &mut body { ent.bind_parameter(&name) };
|
// for ent in &mut body { ent.bind_parameter(&name) };
|
||||||
Clause::Lambda(name, typ, body)
|
Clause::Lambda(name, to_mrc_slice(typ), to_mrc_slice(body))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,13 +54,10 @@ where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
|||||||
.then_ignore(just(Lexeme::name(".")))
|
.then_ignore(just(Lexeme::name(".")))
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
||||||
.then(expr.repeated().at_least(1))
|
.then(expr.repeated().at_least(1))
|
||||||
.try_map(|((name, typ), mut body), s| if name == None && typ.is_empty() {
|
.try_map(|((name, typ), body), s| if name.is_none() && typ.is_empty() {
|
||||||
Err(Simple::custom(s, "Auto without name or type has no effect"))
|
Err(Simple::custom(s, "Auto without name or type has no effect"))
|
||||||
} else {
|
} else {
|
||||||
// if let Some(n) = &name {
|
Ok(Clause::Auto(name, to_mrc_slice(typ), to_mrc_slice(body)))
|
||||||
// for ent in &mut body { ent.bind_parameter(n) }
|
|
||||||
// }
|
|
||||||
Ok(Clause::Auto(name, typ, body))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +71,8 @@ fn name_parser() -> impl Parser<Lexeme, Vec<String>, Error = Simple<Lexeme>> + C
|
|||||||
|
|
||||||
fn placeholder_parser() -> impl Parser<Lexeme, String, Error = Simple<Lexeme>> + Clone {
|
fn placeholder_parser() -> impl Parser<Lexeme, String, Error = Simple<Lexeme>> + Clone {
|
||||||
enum_parser!(Lexeme::Name).try_map(|name, span| {
|
enum_parser!(Lexeme::Name).try_map(|name, span| {
|
||||||
name.strip_prefix("$").map(&str::to_string)
|
name.strip_prefix('$').map(&str::to_string)
|
||||||
.ok_or(Simple::custom(span, "Not a placeholder"))
|
.ok_or_else(|| Simple::custom(span, "Not a placeholder"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,18 +83,22 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
|
|||||||
enum_parser!(Lexeme::Comment).repeated()
|
enum_parser!(Lexeme::Comment).repeated()
|
||||||
.ignore_then(choice((
|
.ignore_then(choice((
|
||||||
enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal),
|
enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal),
|
||||||
placeholder_parser().map(|n| Clause::Placeh(n, None)),
|
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
|
||||||
just(Lexeme::name("..."))
|
just(Lexeme::name("...")).to(true)
|
||||||
.ignore_then(placeholder_parser())
|
.or(just(Lexeme::name("..")).to(false))
|
||||||
|
.then(placeholder_parser())
|
||||||
.then(
|
.then(
|
||||||
just(Lexeme::Type)
|
just(Lexeme::Type)
|
||||||
.ignore_then(enum_parser!(Lexeme::Int))
|
.ignore_then(enum_parser!(Lexeme::Int))
|
||||||
.or_not().map(Option::unwrap_or_default)
|
.or_not().map(Option::unwrap_or_default)
|
||||||
)
|
)
|
||||||
.map(|(name, prio)| Clause::Placeh(name, Some(prio.try_into().unwrap()))),
|
.map(|((nonzero, key), prio)| Clause::Placeh{key, vec: Some((
|
||||||
|
prio.try_into().unwrap(),
|
||||||
|
nonzero
|
||||||
|
))}),
|
||||||
name_parser().map(|qualified| Clause::Name {
|
name_parser().map(|qualified| Clause::Name {
|
||||||
local: if qualified.len() == 1 {Some(qualified[0].clone())} else {None},
|
local: if qualified.len() == 1 {Some(qualified[0].clone())} else {None},
|
||||||
qualified
|
qualified: to_mrc_slice(qualified)
|
||||||
}),
|
}),
|
||||||
sexpr_parser(expr.clone()),
|
sexpr_parser(expr.clone()),
|
||||||
lambda_parser(expr.clone()),
|
lambda_parser(expr.clone()),
|
||||||
@@ -104,6 +108,6 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
|
|||||||
just(Lexeme::Type)
|
just(Lexeme::Type)
|
||||||
.ignore_then(expr.clone()).or_not()
|
.ignore_then(expr.clone()).or_not()
|
||||||
)
|
)
|
||||||
.map(|(val, typ)| Expr(val, typ.map(Box::new)))
|
.map(|(val, typ)| Expr(val, typ.map(Mrc::new)))
|
||||||
}).labelled("Expression")
|
}).labelled("Expression")
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
use std::iter;
|
|
||||||
|
|
||||||
use chumsky::{Parser, prelude::*};
|
use chumsky::{Parser, prelude::*};
|
||||||
use crate::{enum_parser, utils::BoxedIter};
|
use itertools::Itertools;
|
||||||
|
use mappable_rc::Mrc;
|
||||||
|
use crate::utils::iter::{box_once, box_flatten, into_boxed_iter, BoxedIterIter};
|
||||||
|
use crate::utils::{to_mrc_slice, mrc_derive};
|
||||||
|
use crate::{enum_parser, box_chain};
|
||||||
|
|
||||||
use super::lexer::Lexeme;
|
use super::lexer::Lexeme;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Import {
|
pub struct Import {
|
||||||
pub path: Vec<String>,
|
pub path: Mrc<[String]>,
|
||||||
pub name: Option<String>
|
pub name: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// initialize a BoxedIter<BoxedIter<String>> with a single element.
|
/// initialize a BoxedIter<BoxedIter<String>> with a single element.
|
||||||
fn init_table(name: String) -> BoxedIter<'static, BoxedIter<'static, String>> {
|
fn init_table(name: String) -> BoxedIterIter<'static, String> {
|
||||||
// I'm not at all confident that this is a good approach.
|
// I'm not at all confident that this is a good approach.
|
||||||
Box::new(iter::once(Box::new(iter::once(name)) as BoxedIter<String>))
|
box_once(box_once(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an import command
|
/// Parse an import command
|
||||||
@@ -24,7 +26,7 @@ fn init_table(name: String) -> BoxedIter<'static, BoxedIter<'static, String>> {
|
|||||||
/// to go wild. There's a blacklist in [name]
|
/// to go wild. There's a blacklist in [name]
|
||||||
pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme>> {
|
pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme>> {
|
||||||
// TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad.
|
// TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad.
|
||||||
recursive(|expr: Recursive<Lexeme, BoxedIter<BoxedIter<String>>, Simple<Lexeme>>| {
|
recursive(|expr: Recursive<Lexeme, BoxedIterIter<String>, Simple<Lexeme>>| {
|
||||||
enum_parser!(Lexeme::Name)
|
enum_parser!(Lexeme::Name)
|
||||||
.separated_by(just(Lexeme::NS))
|
.separated_by(just(Lexeme::NS))
|
||||||
.then(
|
.then(
|
||||||
@@ -34,7 +36,7 @@ pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme
|
|||||||
expr.clone()
|
expr.clone()
|
||||||
.separated_by(just(Lexeme::name(",")))
|
.separated_by(just(Lexeme::name(",")))
|
||||||
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
|
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
|
||||||
.map(|v| Box::new(v.into_iter().flatten()) as BoxedIter<BoxedIter<String>>)
|
.map(|v| box_flatten(v.into_iter()))
|
||||||
.labelled("import group"),
|
.labelled("import group"),
|
||||||
// Each expr returns a list of imports, flatten those into a common list
|
// Each expr returns a list of imports, flatten those into a common list
|
||||||
just(Lexeme::name("*")).map(|_| init_table("*".to_string()))
|
just(Lexeme::name("*")).map(|_| init_table("*".to_string()))
|
||||||
@@ -44,21 +46,22 @@ pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme
|
|||||||
))
|
))
|
||||||
).or_not()
|
).or_not()
|
||||||
)
|
)
|
||||||
.map(|(name, opt_post): (Vec<String>, Option<BoxedIter<BoxedIter<String>>>)| -> BoxedIter<BoxedIter<String>> {
|
.map(|(name, opt_post): (Vec<String>, Option<BoxedIterIter<String>>)| -> BoxedIterIter<String> {
|
||||||
if let Some(post) = opt_post {
|
if let Some(post) = opt_post {
|
||||||
Box::new(post.map(move |el| {
|
Box::new(post.map(move |el| {
|
||||||
Box::new(name.clone().into_iter().chain(el)) as BoxedIter<String>
|
box_chain!(name.clone().into_iter(), el)
|
||||||
})) as BoxedIter<BoxedIter<String>>
|
}))
|
||||||
} else {
|
} else {
|
||||||
Box::new(iter::once(Box::new(name.into_iter()) as BoxedIter<String>))
|
box_once(into_boxed_iter(name))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).map(|paths| {
|
}).map(|paths| {
|
||||||
paths.filter_map(|namespaces| {
|
paths.filter_map(|namespaces| {
|
||||||
let mut path: Vec<String> = namespaces.collect();
|
let path = to_mrc_slice(namespaces.collect_vec());
|
||||||
match path.pop()?.as_str() {
|
let path_prefix = mrc_derive(&path, |p| &p[..p.len() - 1]);
|
||||||
"*" => Some(Import { path, name: None }),
|
match path.last()?.as_str() {
|
||||||
name => Some(Import { path, name: Some(name.to_owned()) })
|
"*" => Some(Import { path: path_prefix, name: None }),
|
||||||
|
name => Some(Import { path: path_prefix, name: Some(name.to_owned()) })
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
}).labelled("import")
|
}).labelled("import")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::{ops::Range, iter, fmt};
|
|||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
use chumsky::{Parser, prelude::*};
|
use chumsky::{Parser, prelude::*};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use crate::utils::BoxedIter;
|
use crate::{utils::{BoxedIter, iter::{box_once, box_flatten}}, box_chain};
|
||||||
|
|
||||||
use super::{number, string, name, comment};
|
use super::{number, string, name, comment};
|
||||||
|
|
||||||
@@ -14,9 +14,10 @@ impl Debug for Entry {
|
|||||||
// f.debug_tuple("Entry").field(&self.0).field(&self.1).finish()
|
// f.debug_tuple("Entry").field(&self.0).field(&self.1).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Into<(Lexeme, Range<usize>)> for Entry {
|
|
||||||
fn into(self) -> (Lexeme, Range<usize>) {
|
impl From<Entry> for (Lexeme, Range<usize>) {
|
||||||
(self.0, self.1)
|
fn from(ent: Entry) -> Self {
|
||||||
|
(ent.0, ent.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,13 +108,13 @@ fn paren_parser<'a>(
|
|||||||
lp: char, rp: char
|
lp: char, rp: char
|
||||||
) -> impl Parser<char, LexSubres<'a>, Error=Simple<char>> + 'a {
|
) -> impl Parser<char, LexSubres<'a>, Error=Simple<char>> + 'a {
|
||||||
expr.padded().repeated()
|
expr.padded().repeated()
|
||||||
.map(|x| Box::new(x.into_iter().flatten()) as LexSubres)
|
.map(|x| box_flatten(x.into_iter()))
|
||||||
.delimited_by(just(lp), just(rp)).map_with_span(move |b, s| {
|
.delimited_by(just(lp), just(rp)).map_with_span(move |b, s| {
|
||||||
Box::new(
|
box_chain!(
|
||||||
iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1))
|
iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1)),
|
||||||
.chain(b)
|
b,
|
||||||
.chain(iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end)))
|
iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end))
|
||||||
) as LexSubres
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ where T: AsRef<str> + Clone {
|
|||||||
paren_parser(recurse.clone(), '[', ']'),
|
paren_parser(recurse.clone(), '[', ']'),
|
||||||
paren_parser(recurse.clone(), '{', '}'),
|
paren_parser(recurse.clone(), '{', '}'),
|
||||||
choice((
|
choice((
|
||||||
just("==").padded().to(Lexeme::rule(0f64)),
|
just(":=").padded().to(Lexeme::rule(0f64)),
|
||||||
just("=").ignore_then(number::float_parser()).then_ignore(just("=>")).map(Lexeme::rule),
|
just("=").ignore_then(number::float_parser()).then_ignore(just("=>")).map(Lexeme::rule),
|
||||||
comment::comment_parser().map(Lexeme::Comment),
|
comment::comment_parser().map(Lexeme::Comment),
|
||||||
just("::").padded().to(Lexeme::NS),
|
just("::").padded().to(Lexeme::NS),
|
||||||
@@ -139,7 +140,7 @@ where T: AsRef<str> + Clone {
|
|||||||
string::char_parser().map(Lexeme::Char),
|
string::char_parser().map(Lexeme::Char),
|
||||||
string::str_parser().map(Lexeme::Str),
|
string::str_parser().map(Lexeme::Str),
|
||||||
name::name_parser(&all_ops).map(Lexeme::Name), // includes namespacing
|
name::name_parser(&all_ops).map(Lexeme::Name), // includes namespacing
|
||||||
)).map_with_span(|lx, span| Box::new(iter::once(Entry(lx, span))) as LexSubres)
|
)).map_with_span(|lx, span| box_once(Entry(lx, span)) as LexSubres)
|
||||||
))
|
))
|
||||||
}).separated_by(one_of("\t ").repeated())
|
}).separated_by(one_of("\t ").repeated())
|
||||||
.flatten().collect()
|
.flatten().collect()
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ use chumsky::{self, prelude::*, Parser};
|
|||||||
/// Matches any one of the passed operators, longest-first
|
/// Matches any one of the passed operators, longest-first
|
||||||
fn op_parser<'a, T: AsRef<str> + Clone>(ops: &[T]) -> BoxedParser<'a, char, String, Simple<char>> {
|
fn op_parser<'a, T: AsRef<str> + Clone>(ops: &[T]) -> BoxedParser<'a, char, String, Simple<char>> {
|
||||||
let mut sorted_ops: Vec<String> = ops.iter().map(|t| t.as_ref().to_string()).collect();
|
let mut sorted_ops: Vec<String> = ops.iter().map(|t| t.as_ref().to_string()).collect();
|
||||||
sorted_ops.sort_by(|a, b| b.len().cmp(&a.len()));
|
sorted_ops.sort_by_key(|op| -(op.len() as i64));
|
||||||
sorted_ops.into_iter()
|
sorted_ops.into_iter()
|
||||||
.map(|op| just(op).boxed())
|
.map(|op| just(op).boxed())
|
||||||
.reduce(|a, b| a.or(b).boxed())
|
.reduce(|a, b| a.or(b).boxed())
|
||||||
.unwrap_or(empty().map(|()| panic!("Empty isn't meant to match")).boxed())
|
.unwrap_or_else(|| empty().map(|()| panic!("Empty isn't meant to match")).boxed())
|
||||||
.labelled("operator").boxed()
|
.labelled("operator").boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ fn separated_digits_parser(base: u32) -> impl Parser<char, String, Error = Simpl
|
|||||||
just('_')
|
just('_')
|
||||||
.ignore_then(text::digits(base))
|
.ignore_then(text::digits(base))
|
||||||
.repeated()
|
.repeated()
|
||||||
.map(|sv| sv.iter().map(|s| s.chars()).flatten().collect())
|
.map(|sv| sv.iter().flat_map(|s| s.chars()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse a grouped uint
|
/// parse a grouped uint
|
||||||
@@ -31,7 +31,7 @@ fn uint_parser(base: u32) -> impl Parser<char, u64, Error = Simple<char>> {
|
|||||||
/// parse exponent notation, or return 0 as the default exponent.
|
/// parse exponent notation, or return 0 as the default exponent.
|
||||||
/// The exponent is always in decimal.
|
/// The exponent is always in decimal.
|
||||||
fn pow_parser() -> impl Parser<char, i32, Error = Simple<char>> {
|
fn pow_parser() -> impl Parser<char, i32, Error = Simple<char>> {
|
||||||
return choice((
|
choice((
|
||||||
just('p')
|
just('p')
|
||||||
.ignore_then(text::int(10))
|
.ignore_then(text::int(10))
|
||||||
.map(|s: String| s.parse().unwrap()),
|
.map(|s: String| s.parse().unwrap()),
|
||||||
@@ -45,15 +45,15 @@ fn pow_parser() -> impl Parser<char, i32, Error = Simple<char>> {
|
|||||||
///
|
///
|
||||||
/// TODO it panics if it finds a negative exponent
|
/// TODO it panics if it finds a negative exponent
|
||||||
fn nat2u(base: u64) -> impl Fn((u64, i32),) -> u64 {
|
fn nat2u(base: u64) -> impl Fn((u64, i32),) -> u64 {
|
||||||
return move |(val, exp)| {
|
move |(val, exp)| {
|
||||||
if exp == 0 {val}
|
if exp == 0 {val}
|
||||||
else {val * base.checked_pow(exp.try_into().unwrap()).unwrap()}
|
else {val * base.checked_pow(exp.try_into().unwrap()).unwrap()}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns a mapper that converts a mantissa and an exponent into a float
|
/// returns a mapper that converts a mantissa and an exponent into a float
|
||||||
fn nat2f(base: u64) -> impl Fn((NotNan<f64>, i32),) -> NotNan<f64> {
|
fn nat2f(base: u64) -> impl Fn((NotNan<f64>, i32),) -> NotNan<f64> {
|
||||||
return move |(val, exp)| {
|
move |(val, exp)| {
|
||||||
if exp == 0 {val}
|
if exp == 0 {val}
|
||||||
else {val * (base as f64).powf(exp.try_into().unwrap())}
|
else {val * (base as f64).powf(exp.try_into().unwrap())}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use chumsky::{prelude::{Simple, end}, Stream, Parser};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::expression::Rule;
|
use crate::{expression::Rule, parse::lexer::LexedText};
|
||||||
|
|
||||||
use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry};
|
use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry};
|
||||||
|
|
||||||
@@ -24,14 +24,17 @@ where
|
|||||||
S: Into<Stream<'a, char, Range<usize>, Iter>> {
|
S: Into<Stream<'a, char, Range<usize>, Iter>> {
|
||||||
let lexed = lexer(ops).parse(stream).map_err(ParseError::Lex)?;
|
let lexed = lexer(ops).parse(stream).map_err(ParseError::Lex)?;
|
||||||
println!("Lexed:\n{:?}", lexed);
|
println!("Lexed:\n{:?}", lexed);
|
||||||
|
let LexedText(token_batchv) = lexed;
|
||||||
let parsr = line_parser().then_ignore(end());
|
let parsr = line_parser().then_ignore(end());
|
||||||
let (parsed_lines, errors_per_line) = lexed.0.into_iter().filter_map(|v| {
|
let (parsed_lines, errors_per_line) = token_batchv.into_iter().filter(|v| {
|
||||||
|
!v.is_empty()
|
||||||
|
}).map(|v| {
|
||||||
// Find the first invalid position for Stream::for_iter
|
// Find the first invalid position for Stream::for_iter
|
||||||
let LexerEntry(_, Range{ end, .. }) = v.last().unwrap().clone();
|
let LexerEntry(_, Range{ end, .. }) = v.last().unwrap().clone();
|
||||||
// Stream expects tuples, lexer outputs structs
|
// Stream expects tuples, lexer outputs structs
|
||||||
let tuples = v.into_iter().map_into::<(Lexeme, Range<usize>)>();
|
let tuples = v.into_iter().map_into::<(Lexeme, Range<usize>)>();
|
||||||
Some(parsr.parse(Stream::from_iter(end..end+1, tuples)))
|
parsr.parse(Stream::from_iter(end..end+1, tuples))
|
||||||
// ^^^^^^^^^^
|
// ^^^^^^^^^^
|
||||||
// I haven't the foggiest idea why this is needed, parsers are supposed to be lazy so the
|
// 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
|
// end of input should make little difference
|
||||||
}).map(|res| match res {
|
}).map(|res| match res {
|
||||||
@@ -39,13 +42,13 @@ where
|
|||||||
Err(e) => (None, e)
|
Err(e) => (None, e)
|
||||||
}).unzip::<_, _, Vec<_>, Vec<_>>();
|
}).unzip::<_, _, Vec<_>, Vec<_>>();
|
||||||
let total_err = errors_per_line.into_iter()
|
let total_err = errors_per_line.into_iter()
|
||||||
.map(Vec::into_iter).flatten()
|
.flat_map(Vec::into_iter)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if total_err.len() > 0 { Err(ParseError::Ast(total_err)) }
|
if !total_err.is_empty() { Err(ParseError::Ast(total_err)) }
|
||||||
else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) }
|
else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reparse<'a, Iter, S, Op>(ops: &[Op], stream: S, pre: &Vec<FileEntry>)
|
pub fn reparse<'a, Iter, S, Op>(ops: &[Op], stream: S, pre: &[FileEntry])
|
||||||
-> Result<Vec<FileEntry>, ParseError>
|
-> Result<Vec<FileEntry>, ParseError>
|
||||||
where
|
where
|
||||||
Op: 'a + AsRef<str> + Clone,
|
Op: 'a + AsRef<str> + Clone,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
use crate::enum_parser;
|
use crate::{enum_parser, box_chain};
|
||||||
use crate::expression::{Expr, Clause, Rule};
|
use crate::expression::{Expr, Clause, Rule};
|
||||||
use crate::utils::BoxedIter;
|
use crate::utils::to_mrc_slice;
|
||||||
use crate::utils::Stackframe;
|
use crate::utils::Stackframe;
|
||||||
|
use crate::utils::iter::box_empty;
|
||||||
|
|
||||||
use super::expression::xpr_parser;
|
use super::expression::xpr_parser;
|
||||||
use super::import;
|
use super::import;
|
||||||
@@ -24,12 +24,12 @@ pub enum FileEntry {
|
|||||||
fn visit_all_names_clause_recur<'a, F>(
|
fn visit_all_names_clause_recur<'a, F>(
|
||||||
clause: &'a Clause,
|
clause: &'a Clause,
|
||||||
binds: Stackframe<String>,
|
binds: Stackframe<String>,
|
||||||
mut cb: &mut F
|
cb: &mut F
|
||||||
) where F: FnMut(&'a Vec<String>) {
|
) where F: FnMut(&'a [String]) {
|
||||||
match clause {
|
match clause {
|
||||||
Clause::Auto(name, typ, body) => {
|
Clause::Auto(name, typ, body) => {
|
||||||
for x in typ.iter() {
|
for x in typ.iter() {
|
||||||
visit_all_names_expr_recur(x, binds.clone(), &mut cb)
|
visit_all_names_expr_recur(x, binds.clone(), cb)
|
||||||
}
|
}
|
||||||
let binds_dup = binds.clone();
|
let binds_dup = binds.clone();
|
||||||
let new_binds = if let Some(n) = name {
|
let new_binds = if let Some(n) = name {
|
||||||
@@ -38,25 +38,23 @@ fn visit_all_names_clause_recur<'a, F>(
|
|||||||
binds
|
binds
|
||||||
};
|
};
|
||||||
for x in body.iter() {
|
for x in body.iter() {
|
||||||
visit_all_names_expr_recur(x, new_binds.clone(), &mut cb)
|
visit_all_names_expr_recur(x, new_binds.clone(), cb)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Clause::Lambda(name, typ, body) => {
|
Clause::Lambda(name, typ, body) => {
|
||||||
for x in typ.iter() {
|
for x in typ.iter() {
|
||||||
visit_all_names_expr_recur(x, binds.clone(), &mut cb)
|
visit_all_names_expr_recur(x, binds.clone(), cb)
|
||||||
}
|
}
|
||||||
for x in body.iter() {
|
for x in body.iter() {
|
||||||
visit_all_names_expr_recur(x, binds.push(name.to_owned()), &mut cb)
|
visit_all_names_expr_recur(x, binds.push(name.to_owned()), cb)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Clause::S(_, body) => for x in body.iter() {
|
Clause::S(_, body) => for x in body.iter() {
|
||||||
visit_all_names_expr_recur(x, binds.clone(), &mut cb)
|
visit_all_names_expr_recur(x, binds.clone(), cb)
|
||||||
},
|
},
|
||||||
Clause::Name{ local, qualified } => {
|
Clause::Name{ local: Some(name), qualified } => {
|
||||||
if let Some(name) = local {
|
if binds.iter().all(|x| x != name) {
|
||||||
if binds.iter().all(|x| x != name) {
|
cb(qualified)
|
||||||
cb(qualified)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -72,7 +70,7 @@ fn visit_all_names_expr_recur<'a, F>(
|
|||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
binds: Stackframe<String>,
|
binds: Stackframe<String>,
|
||||||
cb: &mut F
|
cb: &mut F
|
||||||
) where F: FnMut(&'a Vec<String>) {
|
) where F: FnMut(&'a [String]) {
|
||||||
let Expr(val, typ) = expr;
|
let Expr(val, typ) = expr;
|
||||||
visit_all_names_clause_recur(val, binds.clone(), cb);
|
visit_all_names_clause_recur(val, binds.clone(), cb);
|
||||||
if let Some(t) = typ {
|
if let Some(t) = typ {
|
||||||
@@ -81,10 +79,10 @@ fn visit_all_names_expr_recur<'a, F>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Collect all names that occur in an expression
|
/// Collect all names that occur in an expression
|
||||||
fn find_all_names(expr: &Expr) -> HashSet<&Vec<String>> {
|
fn find_all_names(expr: &Expr) -> HashSet<&[String]> {
|
||||||
let mut ret = HashSet::new();
|
let mut ret = HashSet::new();
|
||||||
visit_all_names_expr_recur(expr, Stackframe::new(String::new()), &mut |n| {
|
visit_all_names_expr_recur(expr, Stackframe::new(String::new()), &mut |n| {
|
||||||
if !n.last().unwrap().starts_with("$") {
|
if !n.last().unwrap().starts_with('$') {
|
||||||
ret.insert(n);
|
ret.insert(n);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -111,19 +109,27 @@ pub fn line_parser() -> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> {
|
|||||||
println!("{:?} could not yield an export", s); e
|
println!("{:?} could not yield an export", s); e
|
||||||
})
|
})
|
||||||
.ignore_then(rule_parser())
|
.ignore_then(rule_parser())
|
||||||
.map(|(source, prio, target)| FileEntry::Rule(Rule{source, prio, target}, true)),
|
.map(|(source, prio, target)| FileEntry::Rule(Rule {
|
||||||
|
source: to_mrc_slice(source),
|
||||||
|
prio,
|
||||||
|
target: to_mrc_slice(target)
|
||||||
|
}, true)),
|
||||||
// This could match almost anything so it has to go last
|
// This could match almost anything so it has to go last
|
||||||
rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{source, prio, target}, false)),
|
rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{
|
||||||
|
source: to_mrc_slice(source),
|
||||||
|
prio,
|
||||||
|
target: to_mrc_slice(target)
|
||||||
|
}, false)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect all exported names (and a lot of other words) from a file
|
/// Collect all exported names (and a lot of other words) from a file
|
||||||
pub fn exported_names(src: &Vec<FileEntry>) -> HashSet<&Vec<String>> {
|
pub fn exported_names(src: &[FileEntry]) -> HashSet<&[String]> {
|
||||||
src.iter().flat_map(|ent| match ent {
|
src.iter().flat_map(|ent| match ent {
|
||||||
FileEntry::Rule(Rule{source, target, ..}, true) =>
|
FileEntry::Rule(Rule{source, target, ..}, true) =>
|
||||||
Box::new(source.iter().chain(target.iter())) as BoxedIter<&Expr>,
|
box_chain!(source.iter(), target.iter()),
|
||||||
_ => Box::new(iter::empty())
|
_ => box_empty()
|
||||||
}).map(find_all_names).flatten().collect()
|
}).flat_map(find_all_names).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Summarize all imports from a file in a single list of qualified names
|
/// Summarize all imports from a file in a single list of qualified names
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ fn text_parser(delim: char) -> impl Parser<char, char, Error = Simple<char>> {
|
|||||||
.or(just('r').to('\r'))
|
.or(just('r').to('\r'))
|
||||||
.or(just('t').to('\t'))
|
.or(just('t').to('\t'))
|
||||||
.or(just('u').ignore_then(
|
.or(just('u').ignore_then(
|
||||||
filter(|c: &char| c.is_digit(16))
|
filter(|c: &char| c.is_ascii_hexdigit())
|
||||||
.repeated()
|
.repeated()
|
||||||
.exactly(4)
|
.exactly(4)
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use std::rc::Rc;
|
|||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
use super::loaded::Loaded;
|
use super::loaded::Loaded;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -18,7 +20,7 @@ impl From<io::Error> for LoadingError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_loader(proj: PathBuf) -> impl FnMut(Vec<String>) -> Result<Loaded, LoadingError> + 'static {
|
pub fn file_loader(proj: PathBuf) -> impl FnMut(Mrc<[String]>) -> Result<Loaded, LoadingError> + 'static {
|
||||||
move |path| {
|
move |path| {
|
||||||
let dirpath = proj.join(path.join("/"));
|
let dirpath = proj.join(path.join("/"));
|
||||||
if dirpath.is_dir() || dirpath.is_symlink() {
|
if dirpath.is_dir() || dirpath.is_symlink() {
|
||||||
|
|||||||
@@ -1,36 +1,38 @@
|
|||||||
use std::{collections::HashMap};
|
use std::collections::HashMap;
|
||||||
|
use mappable_rc::Mrc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::utils::Stackframe;
|
use crate::utils::{Stackframe, to_mrc_slice};
|
||||||
|
|
||||||
use crate::expression::{Expr, Clause};
|
use crate::expression::{Expr, Clause};
|
||||||
|
|
||||||
type ImportMap = HashMap<String, Vec<String>>;
|
type ImportMap = HashMap<String, Mrc<[String]>>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum ResolutionError<Err> {
|
pub enum ResolutionError<Err> {
|
||||||
#[error("Reference cycle at {0:?}")]
|
#[error("Reference cycle at {0:?}")]
|
||||||
Cycle(Vec<Vec<String>>),
|
Cycle(Vec<Mrc<[String]>>),
|
||||||
#[error("No module provides {0:?}")]
|
#[error("No module provides {0:?}")]
|
||||||
NoModule(Vec<String>),
|
NoModule(Mrc<[String]>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Delegate(#[from] Err)
|
Delegate(#[from] Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResolutionResult<E> = Result<Mrc<[String]>, ResolutionError<E>>;
|
||||||
|
|
||||||
/// Recursively resolves symbols to their original names in expressions while caching every
|
/// Recursively resolves symbols to their original names in expressions while caching every
|
||||||
/// resolution. This makes the resolution process lightning fast and invalidation completely
|
/// resolution. This makes the resolution process lightning fast and invalidation completely
|
||||||
/// impossible since the intermediate steps of a resolution aren't stored.
|
/// impossible since the intermediate steps of a resolution aren't stored.
|
||||||
pub struct NameResolver<FSplit, FImps, E> {
|
pub struct NameResolver<FSplit, FImps, E> {
|
||||||
cache: HashMap<Vec<String>, Result<Vec<String>, ResolutionError<E>>>,
|
cache: HashMap<Mrc<[String]>, ResolutionResult<E>>,
|
||||||
get_modname: FSplit,
|
get_modname: FSplit,
|
||||||
get_imports: FImps
|
get_imports: FImps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<FSplit, FImps, E> NameResolver<FSplit, FImps, E>
|
impl<FSplit, FImps, E> NameResolver<FSplit, FImps, E>
|
||||||
where
|
where
|
||||||
FSplit: FnMut(&Vec<String>) -> Option<Vec<String>>,
|
FSplit: FnMut(Mrc<[String]>) -> Option<Mrc<[String]>>,
|
||||||
FImps: FnMut(&Vec<String>) -> Result<ImportMap, E>,
|
FImps: FnMut(Mrc<[String]>) -> Result<ImportMap, E>,
|
||||||
E: Clone
|
E: Clone
|
||||||
{
|
{
|
||||||
pub fn new(get_modname: FSplit, get_imports: FImps) -> Self {
|
pub fn new(get_modname: FSplit, get_imports: FImps) -> Self {
|
||||||
@@ -43,58 +45,63 @@ where
|
|||||||
|
|
||||||
/// Obtains a symbol's originnal name
|
/// Obtains a symbol's originnal name
|
||||||
/// Uses a substack to detect loops
|
/// Uses a substack to detect loops
|
||||||
fn find_origin_rec<'a>(
|
fn find_origin_rec(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol: &'a Vec<String>,
|
symbol: Mrc<[String]>,
|
||||||
import_path: Stackframe<'a, &'a Vec<String>>
|
import_path: Stackframe<Mrc<[String]>>
|
||||||
) -> Result<Vec<String>, ResolutionError<E>> {
|
) -> Result<Mrc<[String]>, ResolutionError<E>> {
|
||||||
if let Some(cached) = self.cache.get(symbol) { return cached.clone() }
|
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
|
// The imports and path of the referenced file and the local name
|
||||||
let path = (self.get_modname)(symbol).ok_or(ResolutionError::NoModule(symbol.clone()))?;
|
let path = (self.get_modname)(Mrc::clone(&symbol)).ok_or_else(|| {
|
||||||
let (_, name) = symbol.split_at(path.len());
|
ResolutionError::NoModule(Mrc::clone(&symbol))
|
||||||
let imports = (self.get_imports)(&path)?;
|
})?;
|
||||||
|
let name = &symbol[path.len()..];
|
||||||
|
if name.is_empty() {
|
||||||
|
panic!("Something's really broken\n{:?}", import_path)
|
||||||
|
}
|
||||||
|
let imports = (self.get_imports)(Mrc::clone(&path))?;
|
||||||
let result = if let Some(source) = imports.get(&name[0]) {
|
let result = if let Some(source) = imports.get(&name[0]) {
|
||||||
let new_sym: Vec<String> = source.iter().chain(name.iter()).cloned().collect();
|
let new_sym: Vec<String> = source.iter().chain(name.iter()).cloned().collect();
|
||||||
if import_path.iter().any(|el| el == &&new_sym) {
|
if import_path.iter().any(|el| el.as_ref() == new_sym.as_slice()) {
|
||||||
Err(ResolutionError::Cycle(import_path.iter().cloned().cloned().collect()))
|
Err(ResolutionError::Cycle(import_path.iter().map(Mrc::clone).collect()))
|
||||||
} else {
|
} else {
|
||||||
self.find_origin_rec(&new_sym, import_path.push(symbol))
|
self.find_origin_rec(to_mrc_slice(new_sym), import_path.push(Mrc::clone(&symbol)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(symbol.clone()) // If not imported, it must be locally defined
|
Ok(symbol.clone()) // If not imported, it must be locally defined
|
||||||
};
|
};
|
||||||
self.cache.insert(symbol.clone(), result.clone());
|
self.cache.insert(symbol, result.clone());
|
||||||
return result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_exprv_rec(&mut self, exv: &[Expr]) -> Result<Vec<Expr>, ResolutionError<E>> {
|
fn process_exprv_rec(&mut self, exv: &[Expr]) -> Result<Vec<Expr>, ResolutionError<E>> {
|
||||||
exv.iter().map(|ex| self.process_expression_rec(ex)).collect()
|
exv.iter().map(|ex| self.process_expression_rec(ex)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_exprboxopt_rec(&mut self,
|
fn process_exprmrcopt_rec(&mut self,
|
||||||
exbo: &Option<Box<Expr>>
|
exbo: &Option<Mrc<Expr>>
|
||||||
) -> Result<Option<Box<Expr>>, ResolutionError<E>> {
|
) -> Result<Option<Mrc<Expr>>, ResolutionError<E>> {
|
||||||
exbo.iter().map(|exb| Ok(Box::new(self.process_expression_rec(exb.as_ref())?)))
|
exbo.iter().map(|exb| Ok(Mrc::new(self.process_expression_rec(exb.as_ref())?)))
|
||||||
.next().transpose()
|
.next().transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_clause_rec(&mut self, tok: &Clause) -> Result<Clause, ResolutionError<E>> {
|
fn process_clause_rec(&mut self, tok: &Clause) -> Result<Clause, ResolutionError<E>> {
|
||||||
Ok(match tok {
|
Ok(match tok {
|
||||||
Clause::S(c, exv) => Clause::S(*c,
|
Clause::S(c, exv) => Clause::S(*c, to_mrc_slice(
|
||||||
exv.iter().map(|e| self.process_expression_rec(e))
|
exv.as_ref().iter().map(|e| self.process_expression_rec(e))
|
||||||
.collect::<Result<Vec<Expr>, ResolutionError<E>>>()?
|
.collect::<Result<Vec<Expr>, ResolutionError<E>>>()?
|
||||||
),
|
)),
|
||||||
Clause::Lambda(name, typ, body) => Clause::Lambda(name.clone(),
|
Clause::Lambda(name, typ, body) => Clause::Lambda(name.clone(),
|
||||||
self.process_exprv_rec(typ)?,
|
to_mrc_slice(self.process_exprv_rec(typ.as_ref())?),
|
||||||
self.process_exprv_rec(body)?
|
to_mrc_slice(self.process_exprv_rec(body.as_ref())?)
|
||||||
),
|
),
|
||||||
Clause::Auto(name, typ, body) => Clause::Auto(name.clone(),
|
Clause::Auto(name, typ, body) => Clause::Auto(name.clone(),
|
||||||
self.process_exprv_rec(typ)?,
|
to_mrc_slice(self.process_exprv_rec(typ.as_ref())?),
|
||||||
self.process_exprv_rec(body)?
|
to_mrc_slice(self.process_exprv_rec(body.as_ref())?)
|
||||||
),
|
),
|
||||||
Clause::Name{local, qualified} => Clause::Name{
|
Clause::Name{local, qualified} => Clause::Name{
|
||||||
local: local.clone(),
|
local: local.clone(),
|
||||||
qualified: self.find_origin(qualified)?
|
qualified: self.find_origin(Mrc::clone(qualified))?
|
||||||
},
|
},
|
||||||
x => x.clone()
|
x => x.clone()
|
||||||
})
|
})
|
||||||
@@ -103,12 +110,12 @@ where
|
|||||||
fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result<Expr, ResolutionError<E>> {
|
fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result<Expr, ResolutionError<E>> {
|
||||||
Ok(Expr(
|
Ok(Expr(
|
||||||
self.process_clause_rec(token)?,
|
self.process_clause_rec(token)?,
|
||||||
self.process_exprboxopt_rec(typ)?
|
self.process_exprmrcopt_rec(typ)?
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_origin(&mut self, symbol: &Vec<String>) -> Result<Vec<String>, ResolutionError<E>> {
|
pub fn find_origin(&mut self, symbol: Mrc<[String]>) -> Result<Mrc<[String]>, ResolutionError<E>> {
|
||||||
self.find_origin_rec(symbol, Stackframe::new(symbol))
|
self.find_origin_rec(Mrc::clone(&symbol), Stackframe::new(symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::expression::{Expr, Clause};
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
|
use crate::{expression::{Expr, Clause}, utils::collect_to_mrc};
|
||||||
|
|
||||||
/// Replaces the first element of a name with the matching prefix from a prefix map
|
/// Replaces the first element of a name with the matching prefix from a prefix map
|
||||||
|
|
||||||
@@ -6,32 +8,34 @@ use crate::expression::{Expr, Clause};
|
|||||||
/// Called by [#prefix] which handles Typed.
|
/// Called by [#prefix] which handles Typed.
|
||||||
fn prefix_clause(
|
fn prefix_clause(
|
||||||
expr: &Clause,
|
expr: &Clause,
|
||||||
namespace: &Vec<String>
|
namespace: Mrc<[String]>
|
||||||
) -> Clause {
|
) -> Clause {
|
||||||
match expr {
|
match expr {
|
||||||
Clause::S(c, v) => Clause::S(*c, v.iter().map(|e| prefix_expr(e, namespace)).collect()),
|
Clause::S(c, v) => Clause::S(*c,
|
||||||
|
collect_to_mrc(v.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace))))
|
||||||
|
),
|
||||||
Clause::Auto(name, typ, body) => Clause::Auto(
|
Clause::Auto(name, typ, body) => Clause::Auto(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
typ.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
collect_to_mrc(typ.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||||
body.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
collect_to_mrc(body.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||||
),
|
),
|
||||||
Clause::Lambda(name, typ, body) => Clause::Lambda(
|
Clause::Lambda(name, typ, body) => Clause::Lambda(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
typ.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
collect_to_mrc(typ.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||||
body.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
collect_to_mrc(body.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||||
),
|
),
|
||||||
Clause::Name{local, qualified} => Clause::Name{
|
Clause::Name{local, qualified} => Clause::Name{
|
||||||
local: local.clone(),
|
local: local.clone(),
|
||||||
qualified: namespace.iter().chain(qualified.iter()).cloned().collect()
|
qualified: collect_to_mrc(namespace.iter().chain(qualified.iter()).cloned())
|
||||||
},
|
},
|
||||||
x => x.clone()
|
x => x.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce an Expr object for any value of Expr
|
/// Produce an Expr object for any value of Expr
|
||||||
pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: &Vec<String>) -> Expr {
|
pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: Mrc<[String]>) -> Expr {
|
||||||
Expr(
|
Expr(
|
||||||
prefix_clause(clause, namespace),
|
prefix_clause(clause, Mrc::clone(&namespace)),
|
||||||
typ.as_ref().map(|e| Box::new(prefix_expr(e, namespace)))
|
typ.as_ref().map(|e| Mrc::new(prefix_expr(e, namespace)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
use crate::expression::Rule;
|
use crate::expression::Rule;
|
||||||
use crate::parse::{self, FileEntry};
|
use crate::parse::{self, FileEntry};
|
||||||
use crate::utils::Cache;
|
use crate::utils::{Cache, mrc_derive, to_mrc_slice};
|
||||||
|
|
||||||
use super::name_resolver::NameResolver;
|
use super::name_resolver::NameResolver;
|
||||||
use super::module_error::ModuleError;
|
use super::module_error::ModuleError;
|
||||||
@@ -17,27 +20,29 @@ type ParseResult<T, ELoad> = Result<T, ModuleError<ELoad>>;
|
|||||||
pub struct Module {
|
pub struct Module {
|
||||||
pub rules: Vec<Rule>,
|
pub rules: Vec<Rule>,
|
||||||
pub exports: Vec<String>,
|
pub exports: Vec<String>,
|
||||||
pub references: Vec<Vec<String>>
|
pub references: Vec<Mrc<[String]>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type RuleCollectionResult<ELoad> = Result<Vec<super::Rule>, ModuleError<ELoad>>;
|
||||||
|
|
||||||
pub fn rule_collector<F: 'static, ELoad>(
|
pub fn rule_collector<F: 'static, ELoad>(
|
||||||
mut load_mod: F,
|
mut load_mod: F,
|
||||||
prelude: Vec<String>
|
prelude: Vec<String>
|
||||||
// ) -> impl FnMut(Vec<String>) -> Result<&'a Vec<super::Rule>, ParseError<ELoad>> + 'a
|
) -> Cache<'static, Mrc<[String]>, RuleCollectionResult<ELoad>>
|
||||||
) -> Cache<'static, Vec<String>, Result<Vec<super::Rule>, ModuleError<ELoad>>>
|
|
||||||
where
|
where
|
||||||
F: FnMut(Vec<String>) -> Result<Loaded, ELoad>,
|
F: FnMut(Mrc<[String]>) -> Result<Loaded, ELoad>,
|
||||||
ELoad: Clone + Debug
|
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)
|
// Map paths to a namespace with name list (folder) or module with source text (file)
|
||||||
let loaded = Rc::new(Cache::new(move |path: Vec<String>, _|
|
let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _|
|
||||||
-> ParseResult<Loaded, ELoad> {
|
-> ParseResult<Loaded, ELoad> {
|
||||||
load_mod(path).map_err(ModuleError::Load)
|
(load_mod_rc.borrow_mut())(path).map_err(ModuleError::Load)
|
||||||
}));
|
}));
|
||||||
// Map names to the longest prefix that points to a valid module
|
// Map names to the longest prefix that points to a valid module
|
||||||
let modname = Rc::new(Cache::new({
|
let modname = Rc::new(Cache::new({
|
||||||
let loaded = Rc::clone(&loaded);
|
let loaded = Rc::clone(&loaded);
|
||||||
move |symbol: Vec<String>, _| -> Result<Vec<String>, Vec<ModuleError<ELoad>>> {
|
move |symbol: Mrc<[String]>, _| -> Result<Mrc<[String]>, Vec<ModuleError<ELoad>>> {
|
||||||
let mut errv: Vec<ModuleError<ELoad>> = Vec::new();
|
let mut errv: Vec<ModuleError<ELoad>> = Vec::new();
|
||||||
let reg_err = |e, errv: &mut Vec<ModuleError<ELoad>>| {
|
let reg_err = |e, errv: &mut Vec<ModuleError<ELoad>>| {
|
||||||
errv.push(e);
|
errv.push(e);
|
||||||
@@ -45,11 +50,10 @@ where
|
|||||||
else { Ok(()) }
|
else { Ok(()) }
|
||||||
};
|
};
|
||||||
loop {
|
loop {
|
||||||
let (path, _) = symbol.split_at(symbol.len() - errv.len());
|
let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len()]);
|
||||||
let pathv = path.to_vec();
|
match loaded.try_find(&path) {
|
||||||
match loaded.try_find(&pathv) {
|
|
||||||
Ok(imports) => match imports.as_ref() {
|
Ok(imports) => match imports.as_ref() {
|
||||||
Loaded::Module(_) => break Ok(pathv.clone()),
|
Loaded::Module(_) => break Ok(path),
|
||||||
_ => reg_err(ModuleError::None, &mut errv)?
|
_ => reg_err(ModuleError::None, &mut errv)?
|
||||||
},
|
},
|
||||||
Err(err) => reg_err(err, &mut errv)?
|
Err(err) => reg_err(err, &mut errv)?
|
||||||
@@ -61,7 +65,7 @@ where
|
|||||||
let preparsed = Rc::new(Cache::new({
|
let preparsed = Rc::new(Cache::new({
|
||||||
let loaded = Rc::clone(&loaded);
|
let loaded = Rc::clone(&loaded);
|
||||||
let prelude2 = prelude.clone();
|
let prelude2 = prelude.clone();
|
||||||
move |path: Vec<String>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
||||||
let loaded = loaded.try_find(&path)?;
|
let loaded = loaded.try_find(&path)?;
|
||||||
if let Loaded::Module(source) = loaded.as_ref() {
|
if let Loaded::Module(source) = loaded.as_ref() {
|
||||||
Ok(parse::parse(&prelude2, source.as_str())?)
|
Ok(parse::parse(&prelude2, source.as_str())?)
|
||||||
@@ -72,7 +76,7 @@ where
|
|||||||
let exports = Rc::new(Cache::new({
|
let exports = Rc::new(Cache::new({
|
||||||
let loaded = Rc::clone(&loaded);
|
let loaded = Rc::clone(&loaded);
|
||||||
let preparsed = Rc::clone(&preparsed);
|
let preparsed = Rc::clone(&preparsed);
|
||||||
move |path: Vec<String>, _| -> ParseResult<Vec<String>, ELoad> {
|
move |path: Mrc<[String]>, _| -> ParseResult<Vec<String>, ELoad> {
|
||||||
let loaded = loaded.try_find(&path)?;
|
let loaded = loaded.try_find(&path)?;
|
||||||
if let Loaded::Namespace(names) = loaded.as_ref() {
|
if let Loaded::Namespace(names) = loaded.as_ref() {
|
||||||
return Ok(names.clone());
|
return Ok(names.clone());
|
||||||
@@ -88,19 +92,19 @@ where
|
|||||||
let imports = Rc::new(Cache::new({
|
let imports = Rc::new(Cache::new({
|
||||||
let preparsed = Rc::clone(&preparsed);
|
let preparsed = Rc::clone(&preparsed);
|
||||||
let exports = Rc::clone(&exports);
|
let exports = Rc::clone(&exports);
|
||||||
move |path: Vec<String>, _| -> ParseResult<HashMap<String, Vec<String>>, ELoad> {
|
move |path: Mrc<[String]>, _| -> ParseResult<HashMap<String, Mrc<[String]>>, ELoad> {
|
||||||
let entv = preparsed.try_find(&path)?.clone();
|
let entv = preparsed.try_find(&path)?;
|
||||||
let import_entries = parse::imports(entv.iter());
|
let import_entries = parse::imports(entv.iter());
|
||||||
let mut imported_symbols: HashMap<String, Vec<String>> = HashMap::new();
|
let mut imported_symbols: HashMap<String, Mrc<[String]>> = HashMap::new();
|
||||||
for imp in import_entries {
|
for imp in import_entries {
|
||||||
let export = exports.try_find(&imp.path)?;
|
let export = exports.try_find(&imp.path)?;
|
||||||
if let Some(ref name) = imp.name {
|
if let Some(ref name) = imp.name {
|
||||||
if export.contains(&name) {
|
if export.contains(name) {
|
||||||
imported_symbols.insert(name.clone(), imp.path.clone());
|
imported_symbols.insert(name.clone(), Mrc::clone(&imp.path));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for exp in export.as_ref() {
|
for exp in export.as_ref() {
|
||||||
imported_symbols.insert(exp.clone(), imp.path.clone());
|
imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +116,7 @@ where
|
|||||||
let preparsed = Rc::clone(&preparsed);
|
let preparsed = Rc::clone(&preparsed);
|
||||||
let imports = Rc::clone(&imports);
|
let imports = Rc::clone(&imports);
|
||||||
let loaded = Rc::clone(&loaded);
|
let loaded = Rc::clone(&loaded);
|
||||||
move |path: Vec<String>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
||||||
let imported_ops: Vec<String> =
|
let imported_ops: Vec<String> =
|
||||||
imports.try_find(&path)?
|
imports.try_find(&path)?
|
||||||
.keys()
|
.keys()
|
||||||
@@ -127,45 +131,44 @@ where
|
|||||||
} else { Err(ModuleError::None) }
|
} else { Err(ModuleError::None) }
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
let mut name_resolver = NameResolver::new({
|
let mut name_resolver_rc = RefCell::new(NameResolver::new({
|
||||||
let modname = Rc::clone(&modname);
|
let modname = Rc::clone(&modname);
|
||||||
move |path| {
|
move |path| {
|
||||||
Some(modname.try_find(path).ok()?.as_ref().clone())
|
Some(modname.try_find(&path).ok()?.as_ref().clone())
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
let imports = Rc::clone(&imports);
|
let imports = Rc::clone(&imports);
|
||||||
move |path| {
|
move |path| {
|
||||||
imports.try_find(path).map(|f| f.as_ref().clone())
|
imports.try_find(&path).map(|f| f.as_ref().clone())
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
// Turn parsed files into a bag of rules and a list of toplevel export names
|
// Turn parsed files into a bag of rules and a list of toplevel export names
|
||||||
let resolved = Rc::new(Cache::new({
|
let resolved = Rc::new(Cache::new({
|
||||||
let parsed = Rc::clone(&parsed);
|
let parsed = Rc::clone(&parsed);
|
||||||
let exports = Rc::clone(&exports);
|
let exports = Rc::clone(&exports);
|
||||||
let imports = Rc::clone(&imports);
|
let imports = Rc::clone(&imports);
|
||||||
let modname = Rc::clone(&modname);
|
let modname = Rc::clone(&modname);
|
||||||
move |path: Vec<String>, _| -> ParseResult<Module, ELoad> {
|
move |path: Mrc<[String]>, _| -> ParseResult<Module, ELoad> {
|
||||||
|
let mut name_resolver = name_resolver_rc.borrow_mut();
|
||||||
let module = Module {
|
let module = Module {
|
||||||
rules: parsed.try_find(&path)?
|
rules: parsed.try_find(&path)?
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|ent| {
|
.filter_map(|ent| {
|
||||||
if let FileEntry::Rule(Rule{source, prio, target}, _) = ent {
|
if let FileEntry::Rule(Rule{source, prio, target}, _) = ent {
|
||||||
Some(Rule {
|
Some(Rule {
|
||||||
source: source.iter().map(|ex| prefix_expr(ex, &path)).collect(),
|
source: source.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(),
|
||||||
target: target.iter().map(|ex| prefix_expr(ex, &path)).collect(),
|
target: target.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(),
|
||||||
prio: *prio,
|
prio: *prio,
|
||||||
})
|
})
|
||||||
} else { None }
|
} else { None }
|
||||||
})
|
})
|
||||||
.map(|rule| Ok(super::Rule {
|
.map(|rule| Ok(super::Rule {
|
||||||
source: rule.source.iter()
|
source: to_mrc_slice(rule.source.iter()
|
||||||
.map(|ex| name_resolver.process_expression(ex))
|
.map(|ex| name_resolver.process_expression(ex))
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?),
|
||||||
target: rule.target.iter()
|
target: to_mrc_slice(rule.target.iter()
|
||||||
.map(|ex| name_resolver.process_expression(ex))
|
.map(|ex| name_resolver.process_expression(ex))
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?),
|
||||||
// source: name_resolver.process_expression(&rule.source)?,
|
|
||||||
// target: name_resolver.process_expression(&rule.target)?,
|
|
||||||
..rule
|
..rule
|
||||||
}))
|
}))
|
||||||
.collect::<ParseResult<Vec<super::Rule>, ELoad>>()?,
|
.collect::<ParseResult<Vec<super::Rule>, ELoad>>()?,
|
||||||
@@ -173,19 +176,20 @@ where
|
|||||||
references: imports.try_find(&path)?
|
references: imports.try_find(&path)?
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|imps| {
|
.filter_map(|imps| {
|
||||||
modname.try_find(&imps).ok().map(|r| r.as_ref().clone())
|
modname.try_find(imps).ok().map(|r| r.as_ref().clone())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
let all_rules = Cache::new({
|
Cache::new({
|
||||||
let resolved = Rc::clone(&resolved);
|
let resolved = Rc::clone(&resolved);
|
||||||
move |path: Vec<String>, _| -> ParseResult<Vec<super::Rule>, ELoad> {
|
move |path: Mrc<[String]>, _| -> ParseResult<Vec<super::Rule>, ELoad> {
|
||||||
let mut processed: HashSet<Vec<String>> = HashSet::new();
|
// Breadth-first search
|
||||||
|
let mut processed: HashSet<Mrc<[String]>> = HashSet::new();
|
||||||
let mut rules: Vec<super::Rule> = Vec::new();
|
let mut rules: Vec<super::Rule> = Vec::new();
|
||||||
let mut pending: VecDeque<Vec<String>> = VecDeque::new();
|
let mut pending: VecDeque<Mrc<[String]>> = VecDeque::new();
|
||||||
pending.push_back(path);
|
pending.push_back(path);
|
||||||
while let Some(el) = pending.pop_front() {
|
while let Some(el) = pending.pop_front() {
|
||||||
let resolved = resolved.try_find(&el)?;
|
let resolved = resolved.try_find(&el)?;
|
||||||
@@ -201,6 +205,5 @@ where
|
|||||||
};
|
};
|
||||||
Ok(rules)
|
Ok(rules)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return all_rules;
|
|
||||||
}
|
}
|
||||||
|
|||||||
151
src/rule/executor/execute.rs
Normal file
151
src/rule/executor/execute.rs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
|
use crate::{expression::{Expr, Clause}, utils::{iter::{box_once, into_boxed_iter}, to_mrc_slice}};
|
||||||
|
use super::{super::RuleError, state::{State, Entry}, slice_matcher::SliceMatcherDnC};
|
||||||
|
|
||||||
|
fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
|
||||||
|
-> Result<(), String> {
|
||||||
|
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| -> Result<(), String> {
|
||||||
|
match clause {
|
||||||
|
Clause::Placeh{key, vec} => {
|
||||||
|
if let Some(known) = is_vec.get(key) {
|
||||||
|
if known != &vec.is_some() { return Err(key.to_string()) }
|
||||||
|
} else {
|
||||||
|
is_vec.insert(key.clone(), vec.is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Clause::Auto(name, typ, body) => {
|
||||||
|
if let Some(key) = name.as_ref().and_then(|key| key.strip_prefix('$')) {
|
||||||
|
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
|
||||||
|
}
|
||||||
|
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||||
|
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||||
|
}
|
||||||
|
Clause::Lambda(name, typ, body) => {
|
||||||
|
if let Some(key) = name.strip_prefix('$') {
|
||||||
|
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
|
||||||
|
}
|
||||||
|
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||||
|
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||||
|
}
|
||||||
|
Clause::S(_, body) => {
|
||||||
|
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
let Expr(val, typ_opt) = pattern;
|
||||||
|
verify_clause(val, is_vec)?;
|
||||||
|
if let Some(typ) = typ_opt {
|
||||||
|
verify_scalar_vec(typ, is_vec)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn slice_to_vec(src: &mut Mrc<[Expr]>, tgt: &mut Mrc<[Expr]>) {
|
||||||
|
let prefix_expr = Expr(Clause::Placeh{key: "::prefix".to_string(), vec: Some((0, false))}, None);
|
||||||
|
let postfix_expr = Expr(Clause::Placeh{key: "::postfix".to_string(), vec: Some((0, false))}, None);
|
||||||
|
// Prefix or postfix to match the full vector
|
||||||
|
let head_multi = matches!(src.first().expect("Src can never be empty!").0, Clause::Placeh{vec: Some(_), ..});
|
||||||
|
let tail_multi = matches!(src.last().expect("Impossible branch!").0, Clause::Placeh{vec: Some(_), ..});
|
||||||
|
let prefix_vec = if head_multi {vec![]} else {vec![prefix_expr]};
|
||||||
|
let postfix_vec = if tail_multi {vec![]} else {vec![postfix_expr]};
|
||||||
|
*src = to_mrc_slice(prefix_vec.iter().chain(src.iter()).chain(postfix_vec.iter()).cloned().collect());
|
||||||
|
*tgt = to_mrc_slice(prefix_vec.iter().chain(tgt.iter()).chain(postfix_vec.iter()).cloned().collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traverse the tree, calling pred on every sibling list until it returns some vec
|
||||||
|
/// then replace the sibling list with that vec and return true
|
||||||
|
/// return false if pred never returned some
|
||||||
|
fn update_first_seq_rec<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
|
||||||
|
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
|
||||||
|
if let o@Some(_) = pred(Mrc::clone(&input)) {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<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
|
||||||
|
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
|
||||||
|
let mut tmp = update_first_seq_rec(input, pred);
|
||||||
|
while let Some(xv) = tmp {
|
||||||
|
tmp = update_first_seq_rec(Mrc::clone(&xv), pred);
|
||||||
|
if tmp.is_none() {return Some(xv)}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill in a template from a state as produced by a pattern
|
||||||
|
fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
|
||||||
|
tpl.iter().flat_map(|Expr(clause, xpr_typ)| match clause {
|
||||||
|
Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto(
|
||||||
|
name_opt.as_ref().and_then(|name| {
|
||||||
|
let state_key = name.strip_prefix('$')
|
||||||
|
.expect("Auto template may only reference, never enforce the name");
|
||||||
|
match &state[state_key] {
|
||||||
|
Entry::NameOpt(name) => name.as_ref().map(|s| s.as_ref().to_owned()),
|
||||||
|
Entry::Name(name) => Some(name.as_ref().to_owned()),
|
||||||
|
_ => panic!("Auto template name may only be derived from Auto or Lambda name")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
write_slice(state, typ),
|
||||||
|
write_slice(state, body)
|
||||||
|
), xpr_typ.to_owned())),
|
||||||
|
Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda(
|
||||||
|
if let Some(state_key) = name.strip_prefix('$') {
|
||||||
|
if let Entry::Name(name) = &state[state_key] {
|
||||||
|
name.as_ref().to_owned()
|
||||||
|
} else {panic!("Lambda template name may only be derived from Lambda name")}
|
||||||
|
} else {
|
||||||
|
name.to_owned()
|
||||||
|
},
|
||||||
|
write_slice(state, typ),
|
||||||
|
write_slice(state, body)
|
||||||
|
), xpr_typ.to_owned())),
|
||||||
|
Clause::S(c, body) => box_once(Expr(Clause::S(
|
||||||
|
*c,
|
||||||
|
write_slice(state, body)
|
||||||
|
), xpr_typ.to_owned())),
|
||||||
|
Clause::Placeh{key, vec: None} => if let Entry::Scalar(x) = &state[key] {
|
||||||
|
box_once(x.as_ref().to_owned())
|
||||||
|
} else {panic!("Scalar template may only be derived from scalar placeholder")},
|
||||||
|
Clause::Placeh{key, vec: Some(_)} => if let Entry::Vec(v) = &state[key] {
|
||||||
|
into_boxed_iter(v.as_ref().to_owned())
|
||||||
|
} else {panic!("Vectorial template may only be derived from vectorial placeholder")},
|
||||||
|
// Explicit base case so that we get an error if Clause gets new values
|
||||||
|
c@Clause::Literal(_) | c@Clause::Name { .. } =>
|
||||||
|
box_once(Expr(c.to_owned(), xpr_typ.to_owned()))
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a rule (a pair of pattern and template) to an expression
|
||||||
|
pub fn execute(mut src: Mrc<[Expr]>, mut tgt: Mrc<[Expr]>, input: Mrc<[Expr]>)
|
||||||
|
-> Result<Option<Mrc<[Expr]>>, RuleError> {
|
||||||
|
// Dimension check
|
||||||
|
let mut is_vec_db = HashMap::new();
|
||||||
|
src.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
|
||||||
|
.map_err(RuleError::ScalarVecMismatch)?;
|
||||||
|
tgt.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
|
||||||
|
.map_err(RuleError::ScalarVecMismatch)?;
|
||||||
|
// Padding
|
||||||
|
slice_to_vec(&mut src, &mut tgt);
|
||||||
|
// Generate matcher
|
||||||
|
let matcher = SliceMatcherDnC::new(src);
|
||||||
|
println!("Matcher: {matcher:#?}");
|
||||||
|
let matcher_cache = SliceMatcherDnC::get_matcher_cache();
|
||||||
|
Ok(update_all_seqs(Mrc::clone(&input), &mut |p| {
|
||||||
|
let state = matcher.match_range_cached(p, &matcher_cache)?;
|
||||||
|
Some(write_slice(&state, &tgt))
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
mod slice_matcher;
|
mod slice_matcher;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod execute;
|
||||||
|
mod split_at_max_vec;
|
||||||
|
|
||||||
use state::State;
|
use state::State;
|
||||||
|
|
||||||
|
pub use execute::execute;
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
use hashbrown::HashMap;
|
|
||||||
use itertools::Itertools;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
use crate::expression::{Expr, Clause};
|
use crate::expression::{Expr, Clause};
|
||||||
use crate::unwrap_or_continue;
|
use crate::unwrap_or_continue;
|
||||||
use crate::utils::{Side, Cache};
|
use crate::utils::iter::box_empty;
|
||||||
use super::super::RuleError;
|
use crate::utils::{Side, Cache, mrc_derive, mrc_try_derive, to_mrc_slice};
|
||||||
use super::State;
|
|
||||||
|
|
||||||
fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr])> {
|
use super::State;
|
||||||
let rngidx = pattern.iter().position_max_by_key(|ex| {
|
use super::split_at_max_vec::split_at_max_vec;
|
||||||
if let Expr(Clause::Placeh(_, Some(prio)), _) = ex { *prio as i64 } else { -1 }
|
|
||||||
})?;
|
/// Tuple with custom cloning logic
|
||||||
let (left, not_left) = pattern.split_at(rngidx);
|
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||||
let (placeh, right) = if rngidx == pattern.len() {
|
pub struct CacheEntry<'a>(Mrc<[Expr]>, &'a SliceMatcherDnC);
|
||||||
(¬_left[0].0, [].as_slice())
|
impl<'a> Clone for CacheEntry<'a> {
|
||||||
} else {
|
fn clone(&self) -> Self {
|
||||||
let (placeh_unary_slice, right) = pattern.split_at(rngidx + 1);
|
let CacheEntry(mrc, matcher) = self;
|
||||||
(&placeh_unary_slice[0].0, right)
|
CacheEntry(Mrc::clone(mrc), matcher)
|
||||||
};
|
}
|
||||||
if let Clause::Placeh(name, Some(prio)) = placeh {
|
|
||||||
Some((left, (name, *prio), right))
|
|
||||||
} else {None}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Matcher that applies a pattern to a slice via divide-and-conquer
|
/// Matcher that applies a pattern to a slice via divide-and-conquer
|
||||||
///
|
///
|
||||||
/// Upon construction, it selects the clause of highest priority, then
|
/// Upon construction, it selects the clause of highest priority, then
|
||||||
@@ -31,134 +30,171 @@ fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr]
|
|||||||
///
|
///
|
||||||
/// Upon matching, it uses a cache to accelerate the process of executing
|
/// Upon matching, it uses a cache to accelerate the process of executing
|
||||||
/// a pattern on the entire tree.
|
/// a pattern on the entire tree.
|
||||||
#[derive(Debug, Clone, Eq)]
|
#[derive(Clone, Eq)]
|
||||||
pub struct SliceMatcherDnC<'a> {
|
pub struct SliceMatcherDnC {
|
||||||
/// The entire pattern this will match
|
/// The entire pattern this will match
|
||||||
pattern: &'a [Expr],
|
pattern: Mrc<[Expr]>,
|
||||||
/// The exact clause this can match
|
/// The exact clause this can match
|
||||||
clause: &'a Clause,
|
clause: Mrc<Clause>,
|
||||||
/// Matcher for the parts of the pattern right from us
|
/// Matcher for the parts of the pattern right from us
|
||||||
right_subm: Option<Box<SliceMatcherDnC<'a>>>,
|
right_subm: Option<Box<SliceMatcherDnC>>,
|
||||||
/// Matcher for the parts of the pattern left from us
|
/// Matcher for the parts of the pattern left from us
|
||||||
left_subm: Option<Box<SliceMatcherDnC<'a>>>,
|
left_subm: Option<Box<SliceMatcherDnC>>,
|
||||||
/// Matcher for the body of this clause if it has one.
|
/// Matcher for the body of this clause if it has one.
|
||||||
/// Must be Some if pattern is (Auto, Lambda or S)
|
/// Must be Some if pattern is (Auto, Lambda or S)
|
||||||
body_subm: Option<Box<SliceMatcherDnC<'a>>>,
|
body_subm: Option<Box<SliceMatcherDnC>>,
|
||||||
/// Matcher for the type of this expression if it has one (Auto usually does)
|
/// Matcher for the type of this expression if it has one (Auto usually does)
|
||||||
/// Optional
|
/// Optional
|
||||||
typ_subm: Option<Box<SliceMatcherDnC<'a>>>,
|
typ_subm: Option<Box<SliceMatcherDnC>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PartialEq for SliceMatcherDnC<'a> {
|
impl PartialEq for SliceMatcherDnC {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.pattern == other.pattern
|
self.pattern == other.pattern
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::hash::Hash for SliceMatcherDnC<'a> {
|
impl std::hash::Hash for SliceMatcherDnC {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.pattern.hash(state);
|
self.pattern.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SliceMatcherDnC<'a> {
|
impl SliceMatcherDnC {
|
||||||
/// If this is true, `clause`, `typ_subm`, `body_subm` and `clause_qual_name` are meaningless.
|
/// If 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.
|
/// If it's false, it's also false for both side matchers.
|
||||||
pub fn clause_is_vectorial(&self) -> bool {
|
pub fn clause_is_vectorial(&self) -> bool {
|
||||||
if let Clause::Placeh(_, Some(_)) = self.clause {true} else {false}
|
matches!(self.clause.as_ref(), Clause::Placeh{vec: Some(..), ..})
|
||||||
}
|
}
|
||||||
/// If clause is a name, the qualified name this can match
|
/// If clause is a name, the qualified name this can match
|
||||||
pub fn clause_qual_name(&self) -> Option<&'a Vec<String>> {
|
pub fn clause_qual_name(&self) -> Option<Mrc<[String]>> {
|
||||||
if let Clause::Name { qualified, .. } = self.clause {Some(qualified)} else {None}
|
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
|
/// If clause is a Placeh, the key in the state the match will be stored at
|
||||||
pub fn state_key(&self) -> Option<&'a String> {
|
pub fn state_key(&self) -> Option<&String> {
|
||||||
if let Clause::Placeh(key, _) = self.clause {Some(key)} else {None}
|
if let Clause::Placeh { key, .. } = self.clause.as_ref() {Some(key)} else {None}
|
||||||
}
|
}
|
||||||
pub fn own_max_size(&self, total: usize) -> usize {
|
pub fn own_max_size(&self, total: usize) -> Option<usize> {
|
||||||
if !self.clause_is_vectorial() {return self.len()}
|
if !self.clause_is_vectorial() {
|
||||||
return total - self.min(Side::Left) - self.min(Side::Right)
|
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}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Enumerate all valid subdivisions based on the reported size constraints of self and
|
pub fn own_min_size(&self) -> usize {
|
||||||
/// the two subranges
|
if let Clause::Placeh { vec: Some((_, nonzero)), .. } = self.clause.as_ref() {
|
||||||
pub fn valid_subdivisions<'b>(&self,
|
if *nonzero {1} else {0}
|
||||||
range: &'b [Expr]
|
} else {self.len()}
|
||||||
) -> impl Iterator<Item = (&'b [Expr], &'b [Expr], &'b [Expr])> {
|
|
||||||
let own_size = self.own_max_size(range.len());
|
|
||||||
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();
|
|
||||||
(1..=own_size).rev().flat_map(move |own_len| {
|
|
||||||
let wiggle = full_len - lmin - rmin - own_len;
|
|
||||||
(0..wiggle).map(move |offset| {
|
|
||||||
let first_break = lmin + offset;
|
|
||||||
let (left, rest) = range.split_at(first_break);
|
|
||||||
let (mid, right) = rest.split_at(own_len);
|
|
||||||
(left, mid, right)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(pattern: &'a [Expr]) -> Self {
|
/// Enumerate all valid subdivisions based on the reported size constraints of self and
|
||||||
let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 {
|
/// the two subranges
|
||||||
(&pattern[0], None, None)
|
pub fn valid_subdivisions(&self,
|
||||||
} else if let Some((left, _, right)) = split_at_max_vec(pattern) {(
|
range: Mrc<[Expr]>
|
||||||
&pattern[left.len()],
|
) -> impl Iterator<Item = (Mrc<[Expr]>, Mrc<[Expr]>, Mrc<[Expr]>)> {
|
||||||
Some(Box::new(Self::new(left))),
|
let own_max = {
|
||||||
Some(Box::new(Self::new(right)))
|
if let Some(x) = self.own_max_size(range.len()) {x}
|
||||||
)} else {(
|
else {return box_empty()}
|
||||||
&pattern[0],
|
};
|
||||||
|
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,
|
None,
|
||||||
Some(Box::new(Self::new(&pattern[1..])))
|
Some(Box::new(Self::new(mrc_derive(&pattern, |p| &p[1..]))))
|
||||||
)};
|
));
|
||||||
|
// let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 {
|
||||||
|
// (&pattern[0], None, None)
|
||||||
|
// } else if let Some((left, _, right)) = split_at_max_vec(pattern) {(
|
||||||
|
// &pattern[left.len()],
|
||||||
|
// Some(Box::new(Self::new(left))),
|
||||||
|
// Some(Box::new(Self::new(right)))
|
||||||
|
// )} else {(
|
||||||
|
// &pattern[0],
|
||||||
|
// None,
|
||||||
|
// Some(Box::new(Self::new(&pattern[1..])))
|
||||||
|
// )};
|
||||||
Self {
|
Self {
|
||||||
pattern, right_subm, left_subm, clause,
|
pattern, right_subm, left_subm,
|
||||||
|
clause: Mrc::clone(&clause),
|
||||||
body_subm: clause.body().map(|b| Box::new(Self::new(b))),
|
body_subm: clause.body().map(|b| Box::new(Self::new(b))),
|
||||||
typ_subm: clause.typ().map(|t| Box::new(Self::new(t)))
|
typ_subm: clause.typ().map(|t| Box::new(Self::new(t)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The shortest slice this pattern can match
|
/// The shortest slice this pattern can match
|
||||||
fn len(&self) -> usize {self.pattern.len()}
|
fn len(&self) -> usize {
|
||||||
|
if self.clause_is_vectorial() {
|
||||||
|
self.min(Side::Left) + self.min(Side::Right) + self.own_min_size()
|
||||||
|
} else {self.pattern.len()}
|
||||||
|
}
|
||||||
/// Pick a subpattern based on the parameter
|
/// Pick a subpattern based on the parameter
|
||||||
fn side(&self, side: Side) -> Option<&Box<SliceMatcherDnC<'a>>> {
|
fn side(&self, side: Side) -> Option<&SliceMatcherDnC> {
|
||||||
match side {
|
match side {
|
||||||
Side::Left => &self.left_subm,
|
Side::Left => &self.left_subm,
|
||||||
Side::Right => &self.right_subm
|
Side::Right => &self.right_subm
|
||||||
}.as_ref()
|
}.as_ref().map(|b| b.as_ref())
|
||||||
}
|
}
|
||||||
/// The shortest slice the given side can match
|
/// The shortest slice the given side can match
|
||||||
fn min(&self, side: Side) -> usize {self.side(side).map_or(0, |right| right.len())}
|
fn min(&self, side: Side) -> usize {self.side(side).map_or(0, |right| right.len())}
|
||||||
/// The longest slice the given side can match
|
/// The longest slice the given side can match
|
||||||
fn max(&self, side: Side, total: usize) -> usize {
|
fn max(&self, side: Side, total: usize) -> usize {
|
||||||
self.side(side).map_or(0, |m| if m.clause_is_vectorial() {
|
self.side(side).map_or(0, |m| if m.clause_is_vectorial() {
|
||||||
total - self.min(side.opposite()) - 1
|
total - self.min(side.opposite()) - self.own_min_size()
|
||||||
} else {m.len()})
|
} else {m.len()})
|
||||||
}
|
}
|
||||||
/// Take the smallest possible slice from the given side
|
/// Take the smallest possible slice from the given side
|
||||||
fn slice_min<'b>(&self, side: Side, range: &'b [Expr]) -> &'b [Expr] {
|
fn slice_min<'a>(&self, side: Side, range: &'a [Expr]) -> &'a [Expr] {
|
||||||
side.slice(self.min(side), range)
|
side.slice(self.min(side), range)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matches the body on a range
|
/// Matches the body on a range
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// when called on an instance that does not have a body (not Auto, Lambda or S)
|
/// when called on an instance that does not have a body (not Auto, Lambda or S)
|
||||||
fn match_body<'b>(&'a self,
|
fn match_body<'a>(&'a self,
|
||||||
range: &'b [Expr], cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
range: Mrc<[Expr]>, cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||||
) -> Option<State> {
|
) -> Option<State> {
|
||||||
self.body_subm.as_ref().unwrap().match_range_cached(range, cache)
|
self.body_subm.as_ref()
|
||||||
|
.expect("Missing body matcher")
|
||||||
|
.match_range_cached(range, cache)
|
||||||
}
|
}
|
||||||
/// Matches the type and body on respective ranges
|
/// Matches the type and body on respective ranges
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// when called on an instance that does not have a body (not Auto, Lambda or S)
|
/// when called on an instance that does not have a body (not Auto, Lambda or S)
|
||||||
fn match_parts<'b>(&'a self,
|
fn match_parts<'a>(&'a self,
|
||||||
typ_range: &'b [Expr], body_range: &'b [Expr],
|
typ_range: Mrc<[Expr]>, body_range: Mrc<[Expr]>,
|
||||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||||
) -> Option<State> {
|
) -> Option<State> {
|
||||||
let typ_state = if let Some(typ) = &self.typ_subm {
|
let typ_state = if let Some(typ) = &self.typ_subm {
|
||||||
typ.match_range_cached(&typ_range, cache)?
|
typ.match_range_cached(typ_range, cache)?
|
||||||
} else {State::new()};
|
} else {State::new()};
|
||||||
let body_state = self.match_body(body_range, cache)?;
|
let body_state = self.match_body(body_range, cache)?;
|
||||||
typ_state + body_state
|
typ_state + body_state
|
||||||
@@ -166,181 +202,124 @@ impl<'a> SliceMatcherDnC<'a> {
|
|||||||
|
|
||||||
/// Match the specified side-submatcher on the specified range with the 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
|
/// In absence of a side-submatcher empty ranges are matched to empty state
|
||||||
fn apply_side_with_cache<'b>(&'a self,
|
fn apply_side_with_cache<'a>(&'a self,
|
||||||
side: Side, range: &'b [Expr],
|
side: Side, range: Mrc<[Expr]>,
|
||||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||||
) -> Option<State> {
|
) -> Option<State> {
|
||||||
match &self.side(side) {
|
match &self.side(side) {
|
||||||
None => {
|
None => {
|
||||||
if range.len() != 0 {None}
|
if !range.is_empty() {None}
|
||||||
else {Some(State::new())}
|
else {Some(State::new())}
|
||||||
},
|
},
|
||||||
Some(m) => cache.try_find(&(range, &m)).map(|s| s.as_ref().to_owned())
|
Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_range_scalar_cached<'b>(&'a self,
|
fn match_range_scalar_cached<'a>(&'a self,
|
||||||
target: &'b [Expr],
|
target: Mrc<[Expr]>,
|
||||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||||
) -> Option<State> {
|
) -> Option<State> {
|
||||||
let pos = self.min(Side::Left);
|
let pos = self.min(Side::Left);
|
||||||
if target.len() != self.pattern.len() {return None}
|
if target.len() != self.pattern.len() {return None}
|
||||||
let mut own_state = (
|
let mut own_state = (
|
||||||
self.apply_side_with_cache(Side::Left, &target[0..pos], cache)?
|
self.apply_side_with_cache(Side::Left, mrc_derive(&target, |t| &t[0..pos]), cache)?
|
||||||
+ self.apply_side_with_cache(Side::Right, &target[pos+1..], cache)
|
+ self.apply_side_with_cache(Side::Right, mrc_derive(&target, |t| &t[pos+1..]), cache)
|
||||||
)?;
|
)?;
|
||||||
match (self.clause, &target[pos].0) {
|
match (self.clause.as_ref(), &target.as_ref()[pos].0) {
|
||||||
(Clause::Literal(val), Clause::Literal(tgt)) => {
|
(Clause::Literal(val), Clause::Literal(tgt)) => {
|
||||||
if val == tgt {Some(own_state)} else {None}
|
if val == tgt {Some(own_state)} else {None}
|
||||||
}
|
}
|
||||||
(Clause::Placeh(name, None), _) => {
|
(Clause::Placeh{key, vec: None}, _) => {
|
||||||
own_state.insert(name, &[target[pos].clone()])
|
own_state.insert_scalar(&key, &target[pos])
|
||||||
}
|
}
|
||||||
(Clause::S(c, _), Clause::S(c_tgt, body_range)) => {
|
(Clause::S(c, _), Clause::S(c_tgt, body_range)) => {
|
||||||
if c != c_tgt {return None}
|
if c != c_tgt {return None}
|
||||||
own_state + self.match_parts(&[], body_range, cache)
|
own_state + self.match_parts(to_mrc_slice(vec![]), Mrc::clone(body_range), cache)
|
||||||
}
|
}
|
||||||
(Clause::Name{qualified, ..}, Clause::Name{qualified: q_tgt, ..}) => {
|
(Clause::Name{qualified, ..}, Clause::Name{qualified: q_tgt, ..}) => {
|
||||||
if qualified == q_tgt {Some(own_state)} else {None}
|
if qualified == q_tgt {Some(own_state)} else {None}
|
||||||
}
|
}
|
||||||
(Clause::Lambda(name, _, _), Clause::Lambda(name_tgt, typ_tgt, body_tgt)) => {
|
(Clause::Lambda(name, _, _), Clause::Lambda(name_tgt, typ_tgt, body_tgt)) => {
|
||||||
// Primarily, the name works as a placeholder
|
// Primarily, the name works as a placeholder
|
||||||
if let Some(state_key) = name.strip_prefix("$") {
|
if let Some(state_key) = name.strip_prefix('$') {
|
||||||
own_state = own_state.insert(
|
own_state = own_state.insert_name(state_key, name_tgt)?
|
||||||
state_key,
|
|
||||||
&[Expr(Clause::Name{
|
|
||||||
local: Some(name_tgt.clone()),
|
|
||||||
qualified: vec![name_tgt.clone()]
|
|
||||||
}, None)]
|
|
||||||
)?
|
|
||||||
// But if you're weird like that, it can also work as a constraint
|
|
||||||
} else if name != name_tgt {return None}
|
} else if name != name_tgt {return None}
|
||||||
own_state + self.match_parts(typ_tgt, body_tgt, cache)
|
// ^ But if you're weird like that, it can also work as a constraint
|
||||||
|
own_state + self.match_parts(Mrc::clone(typ_tgt), Mrc::clone(body_tgt), cache)
|
||||||
}
|
}
|
||||||
(Clause::Auto(name_opt, _, _), Clause::Auto(name_range, typ_range, body_range)) => {
|
(Clause::Auto(name_opt, _, _), Clause::Auto(name_range, typ_range, body_range)) => {
|
||||||
if let Some(name) = name_opt {
|
if let Some(name) = name_opt {
|
||||||
if let Some(state_name) = name.strip_prefix("$") {
|
// TODO: Enforce this at construction, on a type system level
|
||||||
own_state = own_state.insert(
|
let state_key = name.strip_prefix('$')
|
||||||
state_name,
|
.expect("Auto patterns may only reference, never enforce the name");
|
||||||
&[Expr(Clause::Name{
|
own_state = own_state.insert_name_opt(state_key, name_range.as_ref())?
|
||||||
local: name_range.clone(),
|
|
||||||
qualified: name_range.as_ref()
|
|
||||||
.map(|s| vec![s.clone()])
|
|
||||||
.unwrap_or_default()
|
|
||||||
}, None)]
|
|
||||||
)?
|
|
||||||
// TODO: Enforce this at construction, on a type system level
|
|
||||||
} else {panic!("Auto patterns may only reference, never enforce the name")}
|
|
||||||
}
|
}
|
||||||
own_state + self.match_parts(typ_range, body_range, cache)
|
own_state + self.match_parts(Mrc::clone(typ_range), Mrc::clone(body_range), cache)
|
||||||
},
|
},
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Match the range with a vectorial _assuming we are a vectorial_
|
/// Match the range with a vectorial _assuming we are a vectorial_
|
||||||
fn match_range_vectorial_cached<'b>(&'a self,
|
fn match_range_vectorial_cached<'a>(&'a self,
|
||||||
name: &str,
|
name: &str,
|
||||||
target: &'b [Expr],
|
target: Mrc<[Expr]>,
|
||||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||||
) -> Option<State> {
|
) -> Option<State> {
|
||||||
// Step through valid slicings based on reported size constraints in order
|
// Step through valid slicings based on reported size constraints in order
|
||||||
// from longest own section to shortest and from left to right
|
// from longest own section to shortest and from left to right
|
||||||
for (left, own, right) in self.valid_subdivisions(target) {
|
for (left, own, right) in self.valid_subdivisions(target) {
|
||||||
let left_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Left, left, cache));
|
let sides_result = unwrap_or_continue!(
|
||||||
let right_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Right, right, cache));
|
self.apply_side_with_cache(Side::Left, left, cache)
|
||||||
|
) + self.apply_side_with_cache(Side::Right, right, cache);
|
||||||
return Some(unwrap_or_continue!(
|
return Some(unwrap_or_continue!(
|
||||||
right_result.clone()
|
unwrap_or_continue!(sides_result)
|
||||||
+ left_result.insert(name, own)
|
.insert_vec(name, own.as_ref())
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
return None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try and match the specified range
|
/// Try and match the specified range
|
||||||
pub fn match_range_cached<'b>(&'a self,
|
pub fn match_range_cached<'a>(&'a self,
|
||||||
target: &'b [Expr],
|
target: Mrc<[Expr]>,
|
||||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||||
) -> Option<State> {
|
) -> Option<State> {
|
||||||
if self.pattern.len() == 0 {
|
eprintln!("Matching {target:?} with {:?}", self.pattern);
|
||||||
return if target.len() == 0 {Some(State::new())} else {None}
|
if self.pattern.is_empty() {
|
||||||
|
return if target.is_empty() {Some(State::new())} else {None}
|
||||||
}
|
}
|
||||||
match self.clause {
|
match self.clause.as_ref() {
|
||||||
Clause::Placeh(name, Some(_)) => self.match_range_vectorial_cached(name, target, cache),
|
Clause::Placeh{key, vec: Some(_)} =>
|
||||||
|
self.match_range_vectorial_cached(key, target, cache),
|
||||||
_ => self.match_range_scalar_cached(target, cache)
|
_ => self.match_range_scalar_cached(target, cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn match_range(&self, target: &[Expr]) -> Option<State> {
|
pub fn get_matcher_cache<'a>()
|
||||||
self.match_range_cached(target,&Cache::<(&[Expr], &SliceMatcherDnC), _>::new(
|
-> Cache<'a, CacheEntry<'a>, Option<State>> {
|
||||||
|(tgt, matcher), cache| {
|
Cache::new(
|
||||||
|
|CacheEntry(tgt, matcher), cache| {
|
||||||
matcher.match_range_cached(tgt, cache)
|
matcher.match_range_cached(tgt, cache)
|
||||||
}
|
}
|
||||||
))
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn match_range(&self, target: Mrc<[Expr]>) -> Option<State> {
|
||||||
|
self.match_range_cached(target, &Self::get_matcher_cache())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
|
impl Debug for SliceMatcherDnC {
|
||||||
-> Result<(), String> {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| -> Result<(), String> {
|
f.debug_struct("Matcher")
|
||||||
match clause {
|
.field("clause", &self.clause)
|
||||||
Clause::Placeh(name, prio) => {
|
.field("vectorial", &self.clause_is_vectorial())
|
||||||
if let Some(known) = is_vec.get(name) {
|
.field("min", &self.len())
|
||||||
if known != &prio.is_some() { return Err(name.to_string()) }
|
.field("left", &self.left_subm)
|
||||||
} else {
|
.field("right", &self.right_subm)
|
||||||
is_vec.insert(name.clone(), prio.is_some());
|
.field("lmin", &self.min(Side::Left))
|
||||||
}
|
.field("rmin", &self.min(Side::Right))
|
||||||
}
|
.finish()
|
||||||
Clause::Auto(name, typ, body) => {
|
|
||||||
if let Some(key) = name.as_ref().map(|key| key.strip_prefix("$")).flatten() {
|
|
||||||
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
|
|
||||||
}
|
|
||||||
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
|
||||||
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
|
||||||
}
|
|
||||||
Clause::Lambda(name, typ, body) => {
|
|
||||||
if let Some(key) = name.strip_prefix("$") {
|
|
||||||
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
|
|
||||||
}
|
|
||||||
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
|
||||||
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
|
||||||
}
|
|
||||||
Clause::S(_, body) => {
|
|
||||||
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
let Expr(val, typ_opt) = pattern;
|
|
||||||
verify_clause(val, is_vec)?;
|
|
||||||
if let Some(typ) = typ_opt {
|
|
||||||
verify_scalar_vec(typ, is_vec)?;
|
|
||||||
}
|
}
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(mut src: Vec<Expr>, mut tgt: Vec<Expr>, mut input: Vec<Expr>)
|
|
||||||
-> Result<(Vec<Expr>, bool), RuleError> {
|
|
||||||
// Static values
|
|
||||||
let prefix_expr = Expr(Clause::Placeh("::prefix".to_string(), Some(0)), None);
|
|
||||||
let postfix_expr = Expr(Clause::Placeh("::postfix".to_string(), Some(0)), None);
|
|
||||||
// Dimension check
|
|
||||||
let mut is_vec_db = HashMap::new();
|
|
||||||
src.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
|
|
||||||
.map_err(RuleError::ScalarVecMismatch)?;
|
|
||||||
tgt.iter().try_for_each(|e| verify_scalar_vec(e, &mut is_vec_db))
|
|
||||||
.map_err(RuleError::ScalarVecMismatch)?;
|
|
||||||
// Prefix or postfix to match the full vector
|
|
||||||
let head_multi = if let Clause::Placeh(_, Some(_)) = src.first().unwrap().0 {true} else {false};
|
|
||||||
let tail_multi = if let Clause::Placeh(_, Some(_)) = src.last().unwrap().0 {true} else {false};
|
|
||||||
if !head_multi {
|
|
||||||
src.insert(0, prefix_expr.clone());
|
|
||||||
tgt.insert(0, prefix_expr.clone());
|
|
||||||
}
|
|
||||||
if !tail_multi {
|
|
||||||
src.push(postfix_expr.clone());
|
|
||||||
tgt.push(postfix_expr.clone());
|
|
||||||
}
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
33
src/rule/executor/split_at_max_vec.rs
Normal file
33
src/rule/executor/split_at_max_vec.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use mappable_rc::Mrc;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::expression::{Expr, Clause};
|
||||||
|
use crate::utils::{mrc_derive, mrc_try_derive};
|
||||||
|
|
||||||
|
pub type MaxVecSplit = (Mrc<[Expr]>, (Mrc<str>, 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<MaxVecSplit> {
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,21 +1,31 @@
|
|||||||
use std::ops::{Add, Index};
|
use std::{ops::{Add, Index}, rc::Rc, fmt::Debug};
|
||||||
|
|
||||||
use hashbrown::{HashMap, hash_map::IntoIter};
|
use hashbrown::HashMap;
|
||||||
use mappable_rc::Mrc;
|
|
||||||
|
|
||||||
use crate::expression::Expr;
|
use crate::expression::Expr;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Entry {
|
||||||
|
Vec(Rc<Vec<Expr>>),
|
||||||
|
Scalar(Rc<Expr>),
|
||||||
|
Name(Rc<String>),
|
||||||
|
NameOpt(Option<Rc<String>>)
|
||||||
|
}
|
||||||
|
|
||||||
/// A bucket of indexed expression fragments. Addition may fail if there's a conflict.
|
/// A bucket of indexed expression fragments. Addition may fail if there's a conflict.
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub struct State(HashMap<String, Mrc<Vec<Expr>>>);
|
pub struct State(HashMap<String, Entry>);
|
||||||
|
|
||||||
/// Clone without also cloning arbitrarily heavy Expr objects.
|
/// Clone without also cloning arbitrarily heavy Expr objects.
|
||||||
/// Key is expected to be a very short string with an allocator overhead close to zero.
|
/// Key is expected to be a very short string with an allocator overhead close to zero.
|
||||||
impl Clone for State {
|
impl Clone for Entry {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self(HashMap::from_iter(
|
match self {
|
||||||
self.0.iter().map(|(k, v)| (k.clone(), Mrc::clone(v)))
|
Self::Name(n) => Self::Name(Rc::clone(n)),
|
||||||
))
|
Self::Scalar(x) => Self::Scalar(Rc::clone(x)),
|
||||||
|
Self::Vec(v) => Self::Vec(Rc::clone(v)),
|
||||||
|
Self::NameOpt(o) => Self::NameOpt(o.as_ref().map(Rc::clone))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,24 +33,65 @@ impl State {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(HashMap::new())
|
Self(HashMap::new())
|
||||||
}
|
}
|
||||||
/// Insert a new element, return None on conflict, clone only on success
|
pub fn insert_vec<S>(mut self, k: &S, v: &[Expr]) -> Option<Self>
|
||||||
pub fn insert<S>(mut self, k: &S, v: &[Expr]) -> Option<State>
|
where S: AsRef<str> + ToString + ?Sized + Debug {
|
||||||
|
eprintln!("{:?} + {k:?}-{v:?}", self.0);
|
||||||
|
if let Some(old) = self.0.get(k.as_ref()) {
|
||||||
|
if let Entry::Vec(val) = old {
|
||||||
|
if val.as_slice() != v {return None}
|
||||||
|
} else {return None}
|
||||||
|
} else {
|
||||||
|
self.0.insert(k.to_string(), Entry::Vec(Rc::new(v.to_vec())));
|
||||||
|
}
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
pub fn insert_scalar<S>(mut self, k: &S, v: &Expr) -> Option<Self>
|
||||||
where S: AsRef<str> + ToString + ?Sized {
|
where S: AsRef<str> + ToString + ?Sized {
|
||||||
if let Some(old) = self.0.get(k.as_ref()) {
|
if let Some(old) = self.0.get(k.as_ref()) {
|
||||||
if old.as_ref() != v {return None}
|
if let Entry::Scalar(val) = old {
|
||||||
|
if val.as_ref() != v {return None}
|
||||||
|
} else {return None}
|
||||||
} else {
|
} else {
|
||||||
self.0.insert(k.to_string(), Mrc::new(v.to_vec()));
|
self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned())));
|
||||||
}
|
}
|
||||||
return Some(self)
|
Some(self)
|
||||||
|
}
|
||||||
|
pub fn insert_name<S1, S2>(mut self, k: &S1, v: &S2) -> Option<Self>
|
||||||
|
where
|
||||||
|
S1: AsRef<str> + ToString + ?Sized,
|
||||||
|
S2: AsRef<str> + ToString + ?Sized
|
||||||
|
{
|
||||||
|
if let Some(old) = self.0.get(k.as_ref()) {
|
||||||
|
if let Entry::Name(val) = old {
|
||||||
|
if val.as_str() != v.as_ref() {return None}
|
||||||
|
} else {return None}
|
||||||
|
} else {
|
||||||
|
self.0.insert(k.to_string(), Entry::Name(Rc::new(v.to_string())));
|
||||||
|
}
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
pub fn insert_name_opt<S1, S2>(mut self, k: &S1, v: Option<&S2>) -> Option<Self>
|
||||||
|
where
|
||||||
|
S1: AsRef<str> + ToString + ?Sized,
|
||||||
|
S2: AsRef<str> + ToString + ?Sized
|
||||||
|
{
|
||||||
|
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
|
/// Insert a new entry, return None on conflict
|
||||||
pub fn insert_pair(mut self, (k, v): (String, Mrc<Vec<Expr>>)) -> Option<State> {
|
pub fn insert_pair(mut self, (k, v): (String, Entry)) -> Option<State> {
|
||||||
if let Some(old) = self.0.get(&k) {
|
if let Some(old) = self.0.get(&k) {
|
||||||
if old != &v {return None}
|
if old != &v {return None}
|
||||||
} else {
|
} else {
|
||||||
self.0.insert(k, v);
|
self.0.insert(k, v);
|
||||||
}
|
}
|
||||||
return Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
/// Returns `true` if the state contains no data
|
/// Returns `true` if the state contains no data
|
||||||
pub fn empty(&self) -> bool {
|
pub fn empty(&self) -> bool {
|
||||||
@@ -58,7 +109,7 @@ impl Add for State {
|
|||||||
for pair in rhs.0 {
|
for pair in rhs.0 {
|
||||||
self = self.insert_pair(pair)?
|
self = self.insert_pair(pair)?
|
||||||
}
|
}
|
||||||
return Some(self);
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,18 +121,18 @@ impl Add<Option<State>> for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S> Index<&S> for State where S: AsRef<str> {
|
impl<S> Index<S> for State where S: AsRef<str> {
|
||||||
type Output = Vec<Expr>;
|
type Output = Entry;
|
||||||
|
|
||||||
fn index(&self, index: &S) -> &Self::Output {
|
fn index(&self, index: S) -> &Self::Output {
|
||||||
return &self.0[index.as_ref()]
|
return &self.0[index.as_ref()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for State {
|
impl IntoIterator for State {
|
||||||
type Item = (String, Mrc<Vec<Expr>>);
|
type Item = (String, Entry);
|
||||||
|
|
||||||
type IntoIter = IntoIter<String, Mrc<Vec<Expr>>>;
|
type IntoIter = hashbrown::hash_map::IntoIter<String, Entry>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// mod rule;
|
|
||||||
mod executor;
|
mod executor;
|
||||||
mod rule_error;
|
mod rule_error;
|
||||||
|
mod repository;
|
||||||
|
|
||||||
// pub use rule::Rule;
|
// pub use rule::Rule;
|
||||||
pub use rule_error::RuleError;
|
pub use rule_error::RuleError;
|
||||||
|
pub use repository::Repository;
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
struct Name {
|
|
||||||
qualified: Vec<String>
|
|
||||||
}
|
|
||||||
45
src/rule/repository.rs
Normal file
45
src/rule/repository.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
|
use crate::expression::Expr;
|
||||||
|
|
||||||
|
use super::{super::expression::Rule, executor::execute, RuleError};
|
||||||
|
|
||||||
|
pub struct Repository(Vec<Rule>);
|
||||||
|
impl Repository {
|
||||||
|
pub fn new(mut rules: Vec<Rule>) -> Self {
|
||||||
|
rules.sort_by_key(|r| r.prio);
|
||||||
|
Self(rules)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&self, mut code: Mrc<[Expr]>) -> Result<Option<Mrc<[Expr]>>, RuleError> {
|
||||||
|
let mut ran_once = false;
|
||||||
|
for rule in self.0.iter() {
|
||||||
|
if let Some(tmp) = execute(
|
||||||
|
Mrc::clone(&rule.source), Mrc::clone(&rule.target),
|
||||||
|
Mrc::clone(&code)
|
||||||
|
)? {
|
||||||
|
ran_once = true;
|
||||||
|
code = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(if ran_once {Some(code)} else {None})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn long_step(&self, mut code: Mrc<[Expr]>) -> Result<Mrc<[Expr]>, RuleError> {
|
||||||
|
while let Some(tmp) = self.step(Mrc::clone(&code))? {
|
||||||
|
code = tmp
|
||||||
|
}
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Repository {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for rule in self.0.iter() {
|
||||||
|
writeln!(f, "{rule:?}")?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
use std::{cmp::{min, max}, error::Error, fmt, ops::Range};
|
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
|
|
||||||
use crate::expression::Expr;
|
|
||||||
|
|
||||||
use super::BadState;
|
|
||||||
|
|
||||||
type State = HashMap<String, Expr>;
|
|
||||||
|
|
||||||
pub trait Rule {
|
|
||||||
type Out: Iterator<Item = Expr>;
|
|
||||||
/// The minimum and maximum set of symbols this rule may match.
|
|
||||||
fn len(&self) -> (Option<usize>, Option<usize>);
|
|
||||||
/// Check if the slice matches, and extract data
|
|
||||||
fn read(&self, input: &[Expr]) -> Option<State>;
|
|
||||||
/// Construct item from state
|
|
||||||
fn write(&self, state: &State) -> Result<Self::Out, BadState>;
|
|
||||||
/// Placeholders present in this pattern (all consumed must also be provided)
|
|
||||||
fn placeholders(&'_ self) -> &'_ [&'_ str];
|
|
||||||
/// Try all subsections of Vec of appropriate size, longest first, front-to-back
|
|
||||||
/// Match the first, return the position and produced state
|
|
||||||
fn scan_slice(&self, input: &[Expr]) -> Option<(Range<usize>, State)> {
|
|
||||||
let len_range = self.len();
|
|
||||||
let lo = max(len_range.0.unwrap_or(1), 1);
|
|
||||||
let hi = min(len_range.1.unwrap_or(input.len()), input.len());
|
|
||||||
for width in (lo..hi).rev() {
|
|
||||||
let starts = (0..input.len() - width).into_iter();
|
|
||||||
let first_match = starts.filter_map(|start| {
|
|
||||||
let res = self.read(&input[start..start+width])?;
|
|
||||||
Some((start..start+width, res))
|
|
||||||
}).next();
|
|
||||||
if first_match.is_some() {
|
|
||||||
return first_match;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify<Src, Tgt>(src: &Src, tgt: &Tgt) -> Option<Vec<String>> where Src: Rule, Tgt: Rule {
|
|
||||||
let mut amiss: Vec<String> = Vec::new();
|
|
||||||
for ent in tgt.placeholders() {
|
|
||||||
if src.placeholders().iter().find(|x| x == &ent).is_none() {
|
|
||||||
amiss.push(ent.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if amiss.len() > 0 { Some(amiss) }
|
|
||||||
else { None }
|
|
||||||
}
|
|
||||||
@@ -1,37 +1,60 @@
|
|||||||
use std::{hash::Hash, cell::RefCell};
|
use std::{hash::Hash, cell::RefCell, rc::Rc};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use mappable_rc::Mrc;
|
use mappable_rc::Mrc;
|
||||||
|
|
||||||
|
/// Convenience trait for overriding Mrc's strange cloning logic
|
||||||
|
pub trait MyClone {
|
||||||
|
fn my_clone(&self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MyClone for T where T: Clone {
|
||||||
|
default fn my_clone(&self) -> Self { self.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> MyClone for Rc<T> {
|
||||||
|
fn my_clone(&self) -> Self { Rc::clone(self) }
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> MyClone for Mrc<T> {
|
||||||
|
fn my_clone(&self) -> Self { Mrc::clone(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Cache the return values of an effectless closure in a hashmap
|
/// Cache the return values of an effectless closure in a hashmap
|
||||||
/// Inspired by the closure_cacher crate.
|
/// Inspired by the closure_cacher crate.
|
||||||
pub struct Cache<'a, I, O: 'static> /*where O: Clone*/ {
|
pub struct Cache<'a, I, O: 'static> {
|
||||||
store: RefCell<HashMap<I, Mrc<O>>>,
|
store: RefCell<HashMap<I, Mrc<O>>>,
|
||||||
closure: RefCell<Box<dyn FnMut (I, &Self) -> Mrc<O> + 'a>>
|
closure: Box<dyn Fn (I, &Self) -> Mrc<O> + 'a>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O> Cache<'a, I, O> where
|
impl<'a, I, O> Cache<'a, I, O> where
|
||||||
I: Eq + Hash + Clone
|
I: Eq + Hash + MyClone
|
||||||
{
|
{
|
||||||
pub fn new<F: 'a>(mut closure: F) -> Self where F: FnMut(I, &Self) -> O {
|
pub fn new<F: 'a>(closure: F) -> Self where F: Fn(I, &Self) -> O {
|
||||||
Self::new_raw(move |o, s| Mrc::new(closure(o, s)))
|
Self::new_raw(move |o, s| Mrc::new(closure(o, s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take an Mrc<O> closure rather than an O closure
|
/// Take an Mrc<O> closure rather than an O closure
|
||||||
/// Used internally to derive caches from other systems working with Mrc-s
|
/// Used internally to derive caches from other systems working with Mrc-s
|
||||||
pub fn new_raw<F: 'a>(closure: F) -> Self where F: FnMut(I, &Self) -> Mrc<O> {
|
pub fn new_raw<F: 'a>(closure: F) -> Self where F: Fn(I, &Self) -> Mrc<O> {
|
||||||
Self {
|
Self {
|
||||||
store: RefCell::new(HashMap::new()),
|
store: RefCell::new(HashMap::new()),
|
||||||
closure: RefCell::new(Box::new(closure))
|
closure: Box::new(closure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce and cache a result by cloning I if necessary
|
/// Produce and cache a result by cloning I if necessary
|
||||||
pub fn find(&self, i: &I) -> Mrc<O> {
|
pub fn find(&self, i: &I) -> Mrc<O> {
|
||||||
let mut closure = self.closure.borrow_mut();
|
let closure = &self.closure;
|
||||||
|
if let Some(v) = self.store.borrow().get(i) {
|
||||||
|
return Mrc::clone(v)
|
||||||
|
}
|
||||||
|
// In the moment of invocation the refcell is on immutable
|
||||||
|
// this is important for recursive calculations
|
||||||
|
let result = closure(i.my_clone(), self);
|
||||||
let mut store = self.store.borrow_mut();
|
let mut store = self.store.borrow_mut();
|
||||||
Mrc::clone(store.raw_entry_mut().from_key(i)
|
Mrc::clone(store.raw_entry_mut().from_key(i)
|
||||||
.or_insert_with(|| (i.clone(), closure(i.clone(), self))).1)
|
.or_insert_with(|| (i.my_clone(), result)).1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Return the result if it has already been computed
|
/// Return the result if it has already been computed
|
||||||
pub fn known(&self, i: &I) -> Option<Mrc<O>> {
|
pub fn known(&self, i: &I) -> Option<Mrc<O>> {
|
||||||
@@ -46,7 +69,7 @@ impl<'a, I, O> Cache<'a, I, O> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O, E> Cache<'a, I, Result<O, E>> where
|
impl<'a, I, O, E> Cache<'a, I, Result<O, E>> where
|
||||||
I: Eq + Hash + Clone,
|
I: Eq + Hash + MyClone,
|
||||||
// O: Clone,
|
// O: Clone,
|
||||||
E: Clone
|
E: Clone
|
||||||
{
|
{
|
||||||
@@ -60,7 +83,7 @@ impl<'a, I, O, E> Cache<'a, I, Result<O, E>> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I, O> Cache<'a, I, Option<O>> where
|
impl<'a, I, O> Cache<'a, I, Option<O>> where
|
||||||
I: Eq + Hash + Clone,
|
I: Eq + Hash + MyClone,
|
||||||
// O: Clone
|
// O: Clone
|
||||||
{
|
{
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
36
src/utils/iter.rs
Normal file
36
src/utils/iter.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/// Utility functions to get rid of explicit casts to BoxedIter which are tedious
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
|
||||||
|
pub type BoxedIterIter<'a, T> = BoxedIter<'a, BoxedIter<'a, T>>;
|
||||||
|
/// BoxedIter of a single element
|
||||||
|
pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> {
|
||||||
|
Box::new(iter::once(t))
|
||||||
|
}
|
||||||
|
/// BoxedIter of no elements
|
||||||
|
pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> {
|
||||||
|
Box::new(iter::empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! box_chain {
|
||||||
|
($curr:expr) => {
|
||||||
|
Box::new($curr) as BoxedIter<_>
|
||||||
|
};
|
||||||
|
($curr:expr, $($rest:expr),*) => {
|
||||||
|
Box::new($curr$(.chain($rest))*) as $crate::utils::iter::BoxedIter<_>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn box_flatten<'a, T: 'a, I: 'a, J: 'a>(i: I) -> BoxedIter<'a, T>
|
||||||
|
where
|
||||||
|
J: Iterator<Item = T>,
|
||||||
|
I: Iterator<Item = J>,
|
||||||
|
{
|
||||||
|
Box::new(i.flatten())
|
||||||
|
}
|
||||||
|
pub fn into_boxed_iter<'a, T: 'a>(t: T) -> BoxedIter<'a, <T as IntoIterator>::Item>
|
||||||
|
where T: IntoIterator {
|
||||||
|
Box::new(t.into_iter())
|
||||||
|
}
|
||||||
@@ -2,11 +2,33 @@ mod cache;
|
|||||||
mod substack;
|
mod substack;
|
||||||
mod side;
|
mod side;
|
||||||
mod merge_sorted;
|
mod merge_sorted;
|
||||||
mod sorted_pairs;
|
|
||||||
mod unwrap_or_continue;
|
mod unwrap_or_continue;
|
||||||
|
pub mod iter;
|
||||||
pub use cache::Cache;
|
pub use cache::Cache;
|
||||||
|
use mappable_rc::Mrc;
|
||||||
pub use substack::Stackframe;
|
pub use substack::Stackframe;
|
||||||
pub use side::Side;
|
pub use side::Side;
|
||||||
pub use merge_sorted::merge_sorted;
|
pub use merge_sorted::merge_sorted;
|
||||||
|
pub use iter::BoxedIter;
|
||||||
|
|
||||||
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
|
pub fn mrc_derive<T: ?Sized, P, U: ?Sized>(m: &Mrc<T>, p: P) -> Mrc<U>
|
||||||
|
where P: for<'a> FnOnce(&'a T) -> &'a U {
|
||||||
|
Mrc::map(Mrc::clone(m), p)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mrc_try_derive<T: ?Sized, P, U: ?Sized>(m: &Mrc<T>, p: P) -> Option<Mrc<U>>
|
||||||
|
where P: for<'a> FnOnce(&'a T) -> Option<&'a U> {
|
||||||
|
Mrc::try_map(Mrc::clone(m), p).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_mrc_slice<T>(v: Vec<T>) -> Mrc<[T]> {
|
||||||
|
Mrc::map(Mrc::new(v), |v| v.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_to_mrc<I>(iter: I) -> Mrc<[I::Item]> where I: Iterator {
|
||||||
|
to_mrc_slice(iter.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mrc_derive_slice<T>(mv: &Mrc<Vec<T>>) -> Mrc<[T]> {
|
||||||
|
mrc_derive(mv, |v| v.as_slice())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
use std::ops::Add;
|
|
||||||
|
|
||||||
/// Combine two sorted iterators with their mapper function into a sorted iterator of pairs
|
|
||||||
pub struct SortedPairs<L, R, IL, IR, ML, MR, O> {
|
|
||||||
left: IL, right: IR,
|
|
||||||
left_map: ML, right_map: MR,
|
|
||||||
left_buf: Vec<(L, O)>, right_buf: Vec<(R, O)>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L, R, IL, IR, ML, MR, O> SortedPairs<L, R, IL, IR, ML, MR, O>
|
|
||||||
where IL: Iterator<Item = L>, IR: Iterator<Item = R>,
|
|
||||||
ML: Fn(L) -> O, MR: Fn(R) -> O,
|
|
||||||
O: Ord + Add + Clone
|
|
||||||
{
|
|
||||||
pub fn new(left: IL, right: IR, left_map: ML, right_map: MR) -> Self {
|
|
||||||
Self {
|
|
||||||
left, right, left_map, right_map,
|
|
||||||
left_buf: Vec::new(),
|
|
||||||
right_buf: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, L: 'a, R: 'a, IL: 'a, IR: 'a, ML: 'a, MR: 'a, O: 'a> Iterator
|
|
||||||
for &'a mut SortedPairs<L, R, IL, IR, ML, MR, O>
|
|
||||||
where IL: Iterator<Item = L>, IR: Iterator<Item = R>,
|
|
||||||
ML: Fn(L) -> O, MR: Fn(R) -> O,
|
|
||||||
O: Ord + Add + Clone,
|
|
||||||
{
|
|
||||||
type Item = (&'a L, &'a R);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user