Rule execution now runs, no tests tho
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use mappable_rc::Mrc;
|
||||
use itertools::Itertools;
|
||||
use ordered_float::NotNan;
|
||||
use std::{fmt::Debug};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// An exact value
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
@@ -23,12 +24,17 @@ impl Debug for Literal {
|
||||
}
|
||||
|
||||
/// An S-expression with a type
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Expr(pub Clause, pub Option<Box<Expr>>);
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
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 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// f.debug_tuple("Expr").field(&self.0).field(&self.1).finish()
|
||||
let Expr(val, typ) = self;
|
||||
write!(f, "{:?}", val)?;
|
||||
if let Some(typ) = typ { write!(f, "{:?}", typ) }
|
||||
@@ -37,38 +43,62 @@ impl Debug for Expr {
|
||||
}
|
||||
|
||||
/// An S-expression as read from a source file
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub enum Clause {
|
||||
Literal(Literal),
|
||||
Name{
|
||||
local: Option<String>,
|
||||
qualified: Vec<String>
|
||||
qualified: Mrc<[String]>
|
||||
},
|
||||
S(char, Vec<Expr>),
|
||||
Lambda(String, Vec<Expr>, Vec<Expr>),
|
||||
Auto(Option<String>, Vec<Expr>, Vec<Expr>),
|
||||
S(char, Mrc<[Expr]>),
|
||||
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),
|
||||
Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>),
|
||||
/// Second parameter:
|
||||
/// None => matches one token
|
||||
/// Some(prio) => prio is the sizing priority for the vectorial (higher prio grows first)
|
||||
Placeh(String, Option<usize>),
|
||||
/// Some((prio, nonzero)) =>
|
||||
/// prio is the sizing priority for the vectorial (higher prio grows first)
|
||||
/// nonzero is whether the vectorial matches 1..n or 0..n tokens
|
||||
Placeh{
|
||||
key: String,
|
||||
vec: Option<(usize, bool)>
|
||||
},
|
||||
}
|
||||
impl Clause {
|
||||
pub fn body(&self) -> Option<&Vec<Expr>> {
|
||||
pub fn body(&self) -> Option<Mrc<[Expr]>> {
|
||||
match self {
|
||||
Clause::Auto(_, _, body) |
|
||||
Clause::Lambda(_, _, body) |
|
||||
Clause::S(_, body) => Some(body),
|
||||
Clause::S(_, body) => Some(Mrc::clone(body)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn typ(&self) -> Option<&Vec<Expr>> {
|
||||
pub fn typ(&self) -> Option<Mrc<[Expr]>> {
|
||||
match self {
|
||||
Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(typ),
|
||||
Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(Mrc::clone(typ)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Clause {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Clause::S(c, b) => Clause::S(*c, Mrc::clone(b)),
|
||||
Clause::Auto(n, t, b) => Clause::Auto(
|
||||
n.clone(), Mrc::clone(t), Mrc::clone(b)
|
||||
),
|
||||
Clause::Name { local: l, qualified: q } => Clause::Name {
|
||||
local: l.clone(), qualified: Mrc::clone(q)
|
||||
},
|
||||
Clause::Lambda(n, t, b) => Clause::Lambda(
|
||||
n.clone(), Mrc::clone(t), Mrc::clone(b)
|
||||
),
|
||||
Clause::Placeh{key, vec} => Clause::Placeh{key: key.clone(), vec: *vec},
|
||||
Clause::Literal(l) => Clause::Literal(l.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_expr_seq(it: &mut dyn Iterator<Item = &Expr>, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for item in Itertools::intersperse(it.map(Some), None) { match item {
|
||||
Some(expr) => write!(f, "{:?}", expr),
|
||||
@@ -82,7 +112,7 @@ impl Debug for Clause {
|
||||
match self {
|
||||
Self::Literal(arg0) => write!(f, "{:?}", arg0),
|
||||
Self::Name{local, qualified} =>
|
||||
if let Some(local) = local {write!(f, "{}<{}>", qualified.join("::"), local)}
|
||||
if let Some(local) = local {write!(f, "{}`{}`", qualified.join("::"), local)}
|
||||
else {write!(f, "{}", qualified.join("::"))},
|
||||
Self::S(del, items) => {
|
||||
f.write_str(&del.to_string())?;
|
||||
@@ -104,23 +134,33 @@ impl Debug for Clause {
|
||||
f.write_str(":")?; fmt_expr_seq(&mut argtyp.iter(), f)?; f.write_str(".")?;
|
||||
fmt_expr_seq(&mut body.iter(), f)
|
||||
},
|
||||
// Self::Parameter(name) => write!(f, "`{}", name),
|
||||
Self::Placeh(name, None) => write!(f, "${}", name),
|
||||
Self::Placeh(name, Some(prio)) => write!(f, "...${}:{}", name, prio)
|
||||
Self::Placeh{key, vec: None} => write!(f, "${key}"),
|
||||
Self::Placeh{key, vec: Some((prio, true))} => write!(f, "...${key}:{prio}"),
|
||||
Self::Placeh{key, vec: Some((prio, false))} => write!(f, "..${key}:{prio}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A substitution rule as read from the source
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Rule {
|
||||
pub source: Vec<Expr>,
|
||||
pub source: Mrc<[Expr]>,
|
||||
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 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} ={}=> {:?}", self.source, self.prio, self.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 project;
|
||||
mod utils;
|
||||
mod expression;
|
||||
mod rule;
|
||||
use expression::{Expr, Clause};
|
||||
use mappable_rc::Mrc;
|
||||
use project::{rule_collector, Loaded, file_loader};
|
||||
use rule::Repository;
|
||||
use utils::to_mrc_slice;
|
||||
|
||||
fn literal(orig: &[&str]) -> Vec<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()
|
||||
}
|
||||
|
||||
@@ -21,15 +30,40 @@ export (match_sequence $lhs) >> (match_sequence $rhs) =100=> (bind ($lhs) (\_. $
|
||||
export (match_sequence $lhs) >>= (match_sequence $rhs) =100=> (bind ($lhs) ($rhs))
|
||||
"#;
|
||||
|
||||
|
||||
fn initial_tree() -> Mrc<[Expr]> {
|
||||
to_mrc_slice(vec![Expr(Clause::Name {
|
||||
local: None,
|
||||
qualified: to_mrc_slice(vec!["main".to_string(), "main".to_string()])
|
||||
}, None)])
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cwd = current_dir().unwrap();
|
||||
let collect_rules = rule_collector(move |n| {
|
||||
if n == vec!["prelude"] { Ok(Loaded::Module(PRELUDE.to_string())) }
|
||||
if n == literal(&["prelude"]) { Ok(Loaded::Module(PRELUDE.to_string())) }
|
||||
else { file_loader(cwd.clone())(n) }
|
||||
}, literal(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"]));
|
||||
}, vliteral(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"]));
|
||||
match collect_rules.try_find(&literal(&["main"])) {
|
||||
Ok(rules) => for rule in rules.iter() {
|
||||
println!("{rule:?}")
|
||||
Ok(rules) => {
|
||||
let mut tree = initial_tree();
|
||||
println!("Start processing {tree:?}");
|
||||
let repo = Repository::new(rules.as_ref().to_owned());
|
||||
println!("Ruleset: {repo:?}");
|
||||
let mut i = 0; loop {
|
||||
if 10 <= i {break} else {i += 1}
|
||||
match repo.step(Mrc::clone(&tree)) {
|
||||
Ok(Some(phase)) => {
|
||||
tree = phase;
|
||||
println!("Step {i}: {tree:?}")
|
||||
},
|
||||
Ok(None) => exit(0),
|
||||
Err(e) => {
|
||||
eprintln!("Rule error: {e:?}");
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => println!("{:#?}", err)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use chumsky::{self, prelude::*, Parser};
|
||||
use crate::{enum_parser, expression::{Clause, Expr, Literal}};
|
||||
use mappable_rc::Mrc;
|
||||
use crate::enum_parser;
|
||||
use crate::expression::{Clause, Expr, Literal};
|
||||
use crate::utils::to_mrc_slice;
|
||||
|
||||
use super::{lexer::Lexeme};
|
||||
use super::lexer::Lexeme;
|
||||
|
||||
fn sexpr_parser<P>(
|
||||
expr: P
|
||||
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
|
||||
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
||||
Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, b))
|
||||
Lexeme::paren_parser(expr.repeated()).map(|(del, b)| Clause::S(del, to_mrc_slice(b)))
|
||||
}
|
||||
|
||||
fn lambda_parser<P>(
|
||||
@@ -28,9 +31,9 @@ where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
||||
.then_ignore(just(Lexeme::name(".")))
|
||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
||||
.then(expr.repeated().at_least(1))
|
||||
.map(|((name, typ), 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) };
|
||||
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(enum_parser!(Lexeme::Comment).repeated())
|
||||
.then(expr.repeated().at_least(1))
|
||||
.try_map(|((name, typ), mut body), s| if name == None && typ.is_empty() {
|
||||
.try_map(|((name, typ), body), s| if name.is_none() && typ.is_empty() {
|
||||
Err(Simple::custom(s, "Auto without name or type has no effect"))
|
||||
} else {
|
||||
// if let Some(n) = &name {
|
||||
// for ent in &mut body { ent.bind_parameter(n) }
|
||||
// }
|
||||
Ok(Clause::Auto(name, typ, body))
|
||||
Ok(Clause::Auto(name, to_mrc_slice(typ), to_mrc_slice(body)))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ fn name_parser() -> impl Parser<Lexeme, Vec<String>, Error = Simple<Lexeme>> + C
|
||||
|
||||
fn placeholder_parser() -> impl Parser<Lexeme, String, Error = Simple<Lexeme>> + Clone {
|
||||
enum_parser!(Lexeme::Name).try_map(|name, span| {
|
||||
name.strip_prefix("$").map(&str::to_string)
|
||||
.ok_or(Simple::custom(span, "Not a placeholder"))
|
||||
name.strip_prefix('$').map(&str::to_string)
|
||||
.ok_or_else(|| Simple::custom(span, "Not a placeholder"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,18 +83,22 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
|
||||
enum_parser!(Lexeme::Comment).repeated()
|
||||
.ignore_then(choice((
|
||||
enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal),
|
||||
placeholder_parser().map(|n| Clause::Placeh(n, None)),
|
||||
just(Lexeme::name("..."))
|
||||
.ignore_then(placeholder_parser())
|
||||
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
|
||||
just(Lexeme::name("...")).to(true)
|
||||
.or(just(Lexeme::name("..")).to(false))
|
||||
.then(placeholder_parser())
|
||||
.then(
|
||||
just(Lexeme::Type)
|
||||
.ignore_then(enum_parser!(Lexeme::Int))
|
||||
.or_not().map(Option::unwrap_or_default)
|
||||
)
|
||||
.map(|(name, prio)| Clause::Placeh(name, Some(prio.try_into().unwrap()))),
|
||||
.map(|((nonzero, key), prio)| Clause::Placeh{key, vec: Some((
|
||||
prio.try_into().unwrap(),
|
||||
nonzero
|
||||
))}),
|
||||
name_parser().map(|qualified| Clause::Name {
|
||||
local: if qualified.len() == 1 {Some(qualified[0].clone())} else {None},
|
||||
qualified
|
||||
qualified: to_mrc_slice(qualified)
|
||||
}),
|
||||
sexpr_parser(expr.clone()),
|
||||
lambda_parser(expr.clone()),
|
||||
@@ -104,6 +108,6 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
|
||||
just(Lexeme::Type)
|
||||
.ignore_then(expr.clone()).or_not()
|
||||
)
|
||||
.map(|(val, typ)| Expr(val, typ.map(Box::new)))
|
||||
.map(|(val, typ)| Expr(val, typ.map(Mrc::new)))
|
||||
}).labelled("Expression")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
use std::iter;
|
||||
|
||||
use chumsky::{Parser, prelude::*};
|
||||
use crate::{enum_parser, utils::BoxedIter};
|
||||
use itertools::Itertools;
|
||||
use mappable_rc::Mrc;
|
||||
use crate::utils::iter::{box_once, box_flatten, into_boxed_iter, BoxedIterIter};
|
||||
use crate::utils::{to_mrc_slice, mrc_derive};
|
||||
use crate::{enum_parser, box_chain};
|
||||
|
||||
use super::lexer::Lexeme;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import {
|
||||
pub path: Vec<String>,
|
||||
pub path: Mrc<[String]>,
|
||||
pub name: Option<String>
|
||||
}
|
||||
|
||||
/// 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.
|
||||
Box::new(iter::once(Box::new(iter::once(name)) as BoxedIter<String>))
|
||||
box_once(box_once(name))
|
||||
}
|
||||
|
||||
/// Parse an import command
|
||||
@@ -24,7 +26,7 @@ fn init_table(name: String) -> BoxedIter<'static, BoxedIter<'static, String>> {
|
||||
/// to go wild. There's a blacklist in [name]
|
||||
pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme>> {
|
||||
// TODO: this algorithm isn't cache friendly, copies a lot and is generally pretty bad.
|
||||
recursive(|expr: Recursive<Lexeme, BoxedIter<BoxedIter<String>>, Simple<Lexeme>>| {
|
||||
recursive(|expr: Recursive<Lexeme, BoxedIterIter<String>, Simple<Lexeme>>| {
|
||||
enum_parser!(Lexeme::Name)
|
||||
.separated_by(just(Lexeme::NS))
|
||||
.then(
|
||||
@@ -34,7 +36,7 @@ pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme
|
||||
expr.clone()
|
||||
.separated_by(just(Lexeme::name(",")))
|
||||
.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"),
|
||||
// Each expr returns a list of imports, flatten those into a common list
|
||||
just(Lexeme::name("*")).map(|_| init_table("*".to_string()))
|
||||
@@ -44,22 +46,23 @@ pub fn import_parser() -> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme
|
||||
))
|
||||
).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 {
|
||||
Box::new(post.map(move |el| {
|
||||
Box::new(name.clone().into_iter().chain(el)) as BoxedIter<String>
|
||||
})) as BoxedIter<BoxedIter<String>>
|
||||
box_chain!(name.clone().into_iter(), el)
|
||||
}))
|
||||
} else {
|
||||
Box::new(iter::once(Box::new(name.into_iter()) as BoxedIter<String>))
|
||||
box_once(into_boxed_iter(name))
|
||||
}
|
||||
})
|
||||
}).map(|paths| {
|
||||
paths.filter_map(|namespaces| {
|
||||
let mut path: Vec<String> = namespaces.collect();
|
||||
match path.pop()?.as_str() {
|
||||
"*" => Some(Import { path, name: None }),
|
||||
name => Some(Import { path, name: Some(name.to_owned()) })
|
||||
let path = to_mrc_slice(namespaces.collect_vec());
|
||||
let path_prefix = mrc_derive(&path, |p| &p[..p.len() - 1]);
|
||||
match path.last()?.as_str() {
|
||||
"*" => Some(Import { path: path_prefix, name: None }),
|
||||
name => Some(Import { path: path_prefix, name: Some(name.to_owned()) })
|
||||
}
|
||||
}).collect()
|
||||
}).labelled("import")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{ops::Range, iter, fmt};
|
||||
use ordered_float::NotNan;
|
||||
use chumsky::{Parser, prelude::*};
|
||||
use std::fmt::Debug;
|
||||
use crate::utils::BoxedIter;
|
||||
use crate::{utils::{BoxedIter, iter::{box_once, box_flatten}}, box_chain};
|
||||
|
||||
use super::{number, string, name, comment};
|
||||
|
||||
@@ -14,9 +14,10 @@ impl Debug for Entry {
|
||||
// f.debug_tuple("Entry").field(&self.0).field(&self.1).finish()
|
||||
}
|
||||
}
|
||||
impl Into<(Lexeme, Range<usize>)> for Entry {
|
||||
fn into(self) -> (Lexeme, Range<usize>) {
|
||||
(self.0, self.1)
|
||||
|
||||
impl From<Entry> for (Lexeme, Range<usize>) {
|
||||
fn from(ent: Entry) -> Self {
|
||||
(ent.0, ent.1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,13 +108,13 @@ fn paren_parser<'a>(
|
||||
lp: char, rp: char
|
||||
) -> impl Parser<char, LexSubres<'a>, Error=Simple<char>> + 'a {
|
||||
expr.padded().repeated()
|
||||
.map(|x| Box::new(x.into_iter().flatten()) as LexSubres)
|
||||
.map(|x| box_flatten(x.into_iter()))
|
||||
.delimited_by(just(lp), just(rp)).map_with_span(move |b, s| {
|
||||
Box::new(
|
||||
iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1))
|
||||
.chain(b)
|
||||
.chain(iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end)))
|
||||
) as LexSubres
|
||||
box_chain!(
|
||||
iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1)),
|
||||
b,
|
||||
iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -127,7 +128,7 @@ where T: AsRef<str> + Clone {
|
||||
paren_parser(recurse.clone(), '[', ']'),
|
||||
paren_parser(recurse.clone(), '{', '}'),
|
||||
choice((
|
||||
just("==").padded().to(Lexeme::rule(0f64)),
|
||||
just(":=").padded().to(Lexeme::rule(0f64)),
|
||||
just("=").ignore_then(number::float_parser()).then_ignore(just("=>")).map(Lexeme::rule),
|
||||
comment::comment_parser().map(Lexeme::Comment),
|
||||
just("::").padded().to(Lexeme::NS),
|
||||
@@ -139,7 +140,7 @@ where T: AsRef<str> + Clone {
|
||||
string::char_parser().map(Lexeme::Char),
|
||||
string::str_parser().map(Lexeme::Str),
|
||||
name::name_parser(&all_ops).map(Lexeme::Name), // includes namespacing
|
||||
)).map_with_span(|lx, span| Box::new(iter::once(Entry(lx, span))) as LexSubres)
|
||||
)).map_with_span(|lx, span| box_once(Entry(lx, span)) as LexSubres)
|
||||
))
|
||||
}).separated_by(one_of("\t ").repeated())
|
||||
.flatten().collect()
|
||||
|
||||
@@ -3,11 +3,11 @@ use chumsky::{self, prelude::*, Parser};
|
||||
/// 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>> {
|
||||
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()
|
||||
.map(|op| just(op).boxed())
|
||||
.reduce(|a, b| a.or(b).boxed())
|
||||
.unwrap_or(empty().map(|()| panic!("Empty isn't meant to match")).boxed())
|
||||
.unwrap_or_else(|| empty().map(|()| panic!("Empty isn't meant to match")).boxed())
|
||||
.labelled("operator").boxed()
|
||||
}
|
||||
|
||||
@@ -56,4 +56,4 @@ pub fn is_op<T: AsRef<str>>(s: T) -> bool {
|
||||
Some(x) => !x.is_alphanumeric(),
|
||||
None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ fn separated_digits_parser(base: u32) -> impl Parser<char, String, Error = Simpl
|
||||
just('_')
|
||||
.ignore_then(text::digits(base))
|
||||
.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
|
||||
@@ -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.
|
||||
/// The exponent is always in decimal.
|
||||
fn pow_parser() -> impl Parser<char, i32, Error = Simple<char>> {
|
||||
return choice((
|
||||
choice((
|
||||
just('p')
|
||||
.ignore_then(text::int(10))
|
||||
.map(|s: String| s.parse().unwrap()),
|
||||
@@ -45,15 +45,15 @@ fn pow_parser() -> impl Parser<char, i32, Error = Simple<char>> {
|
||||
///
|
||||
/// TODO it panics if it finds a negative exponent
|
||||
fn nat2u(base: u64) -> impl Fn((u64, i32),) -> u64 {
|
||||
return move |(val, exp)| {
|
||||
move |(val, exp)| {
|
||||
if exp == 0 {val}
|
||||
else {val * base.checked_pow(exp.try_into().unwrap()).unwrap()}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// returns a mapper that converts a mantissa and an exponent into a float
|
||||
fn nat2f(base: u64) -> impl Fn((NotNan<f64>, i32),) -> NotNan<f64> {
|
||||
return move |(val, exp)| {
|
||||
move |(val, exp)| {
|
||||
if exp == 0 {val}
|
||||
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 thiserror::Error;
|
||||
|
||||
use crate::expression::Rule;
|
||||
use crate::{expression::Rule, parse::lexer::LexedText};
|
||||
|
||||
use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry};
|
||||
|
||||
@@ -24,14 +24,17 @@ where
|
||||
S: Into<Stream<'a, char, Range<usize>, Iter>> {
|
||||
let lexed = lexer(ops).parse(stream).map_err(ParseError::Lex)?;
|
||||
println!("Lexed:\n{:?}", lexed);
|
||||
let LexedText(token_batchv) = lexed;
|
||||
let parsr = line_parser().then_ignore(end());
|
||||
let (parsed_lines, errors_per_line) = lexed.0.into_iter().filter_map(|v| {
|
||||
let (parsed_lines, errors_per_line) = token_batchv.into_iter().filter(|v| {
|
||||
!v.is_empty()
|
||||
}).map(|v| {
|
||||
// Find the first invalid position for Stream::for_iter
|
||||
let LexerEntry(_, Range{ end, .. }) = v.last().unwrap().clone();
|
||||
// Stream expects tuples, lexer outputs structs
|
||||
let tuples = v.into_iter().map_into::<(Lexeme, Range<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
|
||||
// end of input should make little difference
|
||||
}).map(|res| match res {
|
||||
@@ -39,13 +42,13 @@ where
|
||||
Err(e) => (None, e)
|
||||
}).unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
let total_err = errors_per_line.into_iter()
|
||||
.map(Vec::into_iter).flatten()
|
||||
.flat_map(Vec::into_iter)
|
||||
.collect::<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()) }
|
||||
}
|
||||
|
||||
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>
|
||||
where
|
||||
Op: 'a + AsRef<str> + Clone,
|
||||
@@ -62,4 +65,4 @@ where
|
||||
}
|
||||
output
|
||||
}).collect())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
use std::iter;
|
||||
|
||||
use crate::enum_parser;
|
||||
use crate::{enum_parser, box_chain};
|
||||
use crate::expression::{Expr, Clause, Rule};
|
||||
use crate::utils::BoxedIter;
|
||||
use crate::utils::to_mrc_slice;
|
||||
use crate::utils::Stackframe;
|
||||
use crate::utils::iter::box_empty;
|
||||
|
||||
use super::expression::xpr_parser;
|
||||
use super::import;
|
||||
@@ -24,12 +24,12 @@ pub enum FileEntry {
|
||||
fn visit_all_names_clause_recur<'a, F>(
|
||||
clause: &'a Clause,
|
||||
binds: Stackframe<String>,
|
||||
mut cb: &mut F
|
||||
) where F: FnMut(&'a Vec<String>) {
|
||||
cb: &mut F
|
||||
) where F: FnMut(&'a [String]) {
|
||||
match clause {
|
||||
Clause::Auto(name, typ, body) => {
|
||||
for x in typ.iter() {
|
||||
visit_all_names_expr_recur(x, binds.clone(), &mut cb)
|
||||
visit_all_names_expr_recur(x, binds.clone(), cb)
|
||||
}
|
||||
let binds_dup = binds.clone();
|
||||
let new_binds = if let Some(n) = name {
|
||||
@@ -38,25 +38,23 @@ fn visit_all_names_clause_recur<'a, F>(
|
||||
binds
|
||||
};
|
||||
for x in body.iter() {
|
||||
visit_all_names_expr_recur(x, new_binds.clone(), &mut cb)
|
||||
visit_all_names_expr_recur(x, new_binds.clone(), cb)
|
||||
}
|
||||
},
|
||||
Clause::Lambda(name, typ, body) => {
|
||||
for x in typ.iter() {
|
||||
visit_all_names_expr_recur(x, binds.clone(), &mut cb)
|
||||
visit_all_names_expr_recur(x, binds.clone(), cb)
|
||||
}
|
||||
for x in body.iter() {
|
||||
visit_all_names_expr_recur(x, binds.push(name.to_owned()), &mut cb)
|
||||
visit_all_names_expr_recur(x, binds.push(name.to_owned()), cb)
|
||||
}
|
||||
},
|
||||
Clause::S(_, body) => for x in body.iter() {
|
||||
visit_all_names_expr_recur(x, binds.clone(), &mut cb)
|
||||
visit_all_names_expr_recur(x, binds.clone(), cb)
|
||||
},
|
||||
Clause::Name{ local, qualified } => {
|
||||
if let Some(name) = local {
|
||||
if binds.iter().all(|x| x != name) {
|
||||
cb(qualified)
|
||||
}
|
||||
Clause::Name{ local: Some(name), qualified } => {
|
||||
if binds.iter().all(|x| x != name) {
|
||||
cb(qualified)
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
@@ -72,7 +70,7 @@ fn visit_all_names_expr_recur<'a, F>(
|
||||
expr: &'a Expr,
|
||||
binds: Stackframe<String>,
|
||||
cb: &mut F
|
||||
) where F: FnMut(&'a Vec<String>) {
|
||||
) where F: FnMut(&'a [String]) {
|
||||
let Expr(val, typ) = expr;
|
||||
visit_all_names_clause_recur(val, binds.clone(), cb);
|
||||
if let Some(t) = typ {
|
||||
@@ -81,10 +79,10 @@ fn visit_all_names_expr_recur<'a, F>(
|
||||
}
|
||||
|
||||
/// Collect all names that occur in an expression
|
||||
fn find_all_names(expr: &Expr) -> HashSet<&Vec<String>> {
|
||||
fn find_all_names(expr: &Expr) -> HashSet<&[String]> {
|
||||
let mut ret = HashSet::new();
|
||||
visit_all_names_expr_recur(expr, Stackframe::new(String::new()), &mut |n| {
|
||||
if !n.last().unwrap().starts_with("$") {
|
||||
if !n.last().unwrap().starts_with('$') {
|
||||
ret.insert(n);
|
||||
}
|
||||
});
|
||||
@@ -111,19 +109,27 @@ pub fn line_parser() -> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> {
|
||||
println!("{:?} could not yield an export", s); e
|
||||
})
|
||||
.ignore_then(rule_parser())
|
||||
.map(|(source, prio, target)| FileEntry::Rule(Rule{source, prio, target}, true)),
|
||||
.map(|(source, prio, target)| FileEntry::Rule(Rule {
|
||||
source: to_mrc_slice(source),
|
||||
prio,
|
||||
target: to_mrc_slice(target)
|
||||
}, true)),
|
||||
// This could match almost anything so it has to go last
|
||||
rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{source, prio, target}, false)),
|
||||
rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{
|
||||
source: to_mrc_slice(source),
|
||||
prio,
|
||||
target: to_mrc_slice(target)
|
||||
}, false)),
|
||||
))
|
||||
}
|
||||
|
||||
/// Collect all exported names (and a lot of other words) from a file
|
||||
pub fn exported_names(src: &Vec<FileEntry>) -> HashSet<&Vec<String>> {
|
||||
pub fn exported_names(src: &[FileEntry]) -> HashSet<&[String]> {
|
||||
src.iter().flat_map(|ent| match ent {
|
||||
FileEntry::Rule(Rule{source, target, ..}, true) =>
|
||||
Box::new(source.iter().chain(target.iter())) as BoxedIter<&Expr>,
|
||||
_ => Box::new(iter::empty())
|
||||
}).map(find_all_names).flatten().collect()
|
||||
box_chain!(source.iter(), target.iter()),
|
||||
_ => box_empty()
|
||||
}).flat_map(find_all_names).collect()
|
||||
}
|
||||
|
||||
/// Summarize all imports from a file in a single list of qualified names
|
||||
@@ -135,4 +141,4 @@ where I: Iterator<Item = &'b FileEntry> + 'a {
|
||||
FileEntry::Import(impv) => Some(impv.iter()),
|
||||
_ => None
|
||||
}).flatten()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ fn text_parser(delim: char) -> impl Parser<char, char, Error = Simple<char>> {
|
||||
.or(just('r').to('\r'))
|
||||
.or(just('t').to('\t'))
|
||||
.or(just('u').ignore_then(
|
||||
filter(|c: &char| c.is_digit(16))
|
||||
filter(|c: &char| c.is_ascii_hexdigit())
|
||||
.repeated()
|
||||
.exactly(4)
|
||||
.collect::<String>()
|
||||
@@ -43,4 +43,4 @@ pub fn str_parser() -> impl Parser<char, String, Error = Simple<char>> {
|
||||
.repeated()
|
||||
).then_ignore(just('"'))
|
||||
.flatten().collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ use std::rc::Rc;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use super::loaded::Loaded;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -18,7 +20,7 @@ impl From<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| {
|
||||
let dirpath = proj.join(path.join("/"));
|
||||
if dirpath.is_dir() || dirpath.is_symlink() {
|
||||
@@ -46,4 +48,4 @@ pub fn file_loader(proj: PathBuf) -> impl FnMut(Vec<String>) -> Result<Loaded, L
|
||||
else { LoadingError::Missing(pathstr) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
use std::{collections::HashMap};
|
||||
use std::collections::HashMap;
|
||||
use mappable_rc::Mrc;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::utils::Stackframe;
|
||||
use crate::utils::{Stackframe, to_mrc_slice};
|
||||
|
||||
use crate::expression::{Expr, Clause};
|
||||
|
||||
type ImportMap = HashMap<String, Vec<String>>;
|
||||
type ImportMap = HashMap<String, Mrc<[String]>>;
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum ResolutionError<Err> {
|
||||
#[error("Reference cycle at {0:?}")]
|
||||
Cycle(Vec<Vec<String>>),
|
||||
Cycle(Vec<Mrc<[String]>>),
|
||||
#[error("No module provides {0:?}")]
|
||||
NoModule(Vec<String>),
|
||||
NoModule(Mrc<[String]>),
|
||||
#[error(transparent)]
|
||||
Delegate(#[from] Err)
|
||||
}
|
||||
|
||||
type ResolutionResult<E> = Result<Mrc<[String]>, ResolutionError<E>>;
|
||||
|
||||
/// Recursively resolves symbols to their original names in expressions while caching every
|
||||
/// resolution. This makes the resolution process lightning fast and invalidation completely
|
||||
/// impossible since the intermediate steps of a resolution aren't stored.
|
||||
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_imports: FImps
|
||||
}
|
||||
|
||||
|
||||
impl<FSplit, FImps, E> NameResolver<FSplit, FImps, E>
|
||||
where
|
||||
FSplit: FnMut(&Vec<String>) -> Option<Vec<String>>,
|
||||
FImps: FnMut(&Vec<String>) -> Result<ImportMap, E>,
|
||||
FSplit: FnMut(Mrc<[String]>) -> Option<Mrc<[String]>>,
|
||||
FImps: FnMut(Mrc<[String]>) -> Result<ImportMap, E>,
|
||||
E: Clone
|
||||
{
|
||||
pub fn new(get_modname: FSplit, get_imports: FImps) -> Self {
|
||||
@@ -43,58 +45,63 @@ where
|
||||
|
||||
/// Obtains a symbol's originnal name
|
||||
/// Uses a substack to detect loops
|
||||
fn find_origin_rec<'a>(
|
||||
fn find_origin_rec(
|
||||
&mut self,
|
||||
symbol: &'a Vec<String>,
|
||||
import_path: Stackframe<'a, &'a Vec<String>>
|
||||
) -> Result<Vec<String>, ResolutionError<E>> {
|
||||
if let Some(cached) = self.cache.get(symbol) { return cached.clone() }
|
||||
symbol: Mrc<[String]>,
|
||||
import_path: Stackframe<Mrc<[String]>>
|
||||
) -> Result<Mrc<[String]>, ResolutionError<E>> {
|
||||
if let Some(cached) = self.cache.get(&symbol) { return cached.as_ref().map_err(|e| e.clone()).map(Mrc::clone) }
|
||||
// The imports and path of the referenced file and the local name
|
||||
let path = (self.get_modname)(symbol).ok_or(ResolutionError::NoModule(symbol.clone()))?;
|
||||
let (_, name) = symbol.split_at(path.len());
|
||||
let imports = (self.get_imports)(&path)?;
|
||||
let path = (self.get_modname)(Mrc::clone(&symbol)).ok_or_else(|| {
|
||||
ResolutionError::NoModule(Mrc::clone(&symbol))
|
||||
})?;
|
||||
let name = &symbol[path.len()..];
|
||||
if name.is_empty() {
|
||||
panic!("Something's really broken\n{:?}", import_path)
|
||||
}
|
||||
let imports = (self.get_imports)(Mrc::clone(&path))?;
|
||||
let result = if let Some(source) = imports.get(&name[0]) {
|
||||
let new_sym: Vec<String> = source.iter().chain(name.iter()).cloned().collect();
|
||||
if import_path.iter().any(|el| el == &&new_sym) {
|
||||
Err(ResolutionError::Cycle(import_path.iter().cloned().cloned().collect()))
|
||||
if import_path.iter().any(|el| el.as_ref() == new_sym.as_slice()) {
|
||||
Err(ResolutionError::Cycle(import_path.iter().map(Mrc::clone).collect()))
|
||||
} else {
|
||||
self.find_origin_rec(&new_sym, import_path.push(symbol))
|
||||
self.find_origin_rec(to_mrc_slice(new_sym), import_path.push(Mrc::clone(&symbol)))
|
||||
}
|
||||
} else {
|
||||
Ok(symbol.clone()) // If not imported, it must be locally defined
|
||||
};
|
||||
self.cache.insert(symbol.clone(), result.clone());
|
||||
return result
|
||||
self.cache.insert(symbol, result.clone());
|
||||
result
|
||||
}
|
||||
|
||||
fn process_exprv_rec(&mut self, exv: &[Expr]) -> Result<Vec<Expr>, ResolutionError<E>> {
|
||||
exv.iter().map(|ex| self.process_expression_rec(ex)).collect()
|
||||
}
|
||||
|
||||
fn process_exprboxopt_rec(&mut self,
|
||||
exbo: &Option<Box<Expr>>
|
||||
) -> Result<Option<Box<Expr>>, ResolutionError<E>> {
|
||||
exbo.iter().map(|exb| Ok(Box::new(self.process_expression_rec(exb.as_ref())?)))
|
||||
fn process_exprmrcopt_rec(&mut self,
|
||||
exbo: &Option<Mrc<Expr>>
|
||||
) -> Result<Option<Mrc<Expr>>, ResolutionError<E>> {
|
||||
exbo.iter().map(|exb| Ok(Mrc::new(self.process_expression_rec(exb.as_ref())?)))
|
||||
.next().transpose()
|
||||
}
|
||||
|
||||
fn process_clause_rec(&mut self, tok: &Clause) -> Result<Clause, ResolutionError<E>> {
|
||||
Ok(match tok {
|
||||
Clause::S(c, exv) => Clause::S(*c,
|
||||
exv.iter().map(|e| self.process_expression_rec(e))
|
||||
Clause::S(c, exv) => Clause::S(*c, to_mrc_slice(
|
||||
exv.as_ref().iter().map(|e| self.process_expression_rec(e))
|
||||
.collect::<Result<Vec<Expr>, ResolutionError<E>>>()?
|
||||
),
|
||||
)),
|
||||
Clause::Lambda(name, typ, body) => Clause::Lambda(name.clone(),
|
||||
self.process_exprv_rec(typ)?,
|
||||
self.process_exprv_rec(body)?
|
||||
to_mrc_slice(self.process_exprv_rec(typ.as_ref())?),
|
||||
to_mrc_slice(self.process_exprv_rec(body.as_ref())?)
|
||||
),
|
||||
Clause::Auto(name, typ, body) => Clause::Auto(name.clone(),
|
||||
self.process_exprv_rec(typ)?,
|
||||
self.process_exprv_rec(body)?
|
||||
to_mrc_slice(self.process_exprv_rec(typ.as_ref())?),
|
||||
to_mrc_slice(self.process_exprv_rec(body.as_ref())?)
|
||||
),
|
||||
Clause::Name{local, qualified} => Clause::Name{
|
||||
local: local.clone(),
|
||||
qualified: self.find_origin(qualified)?
|
||||
qualified: self.find_origin(Mrc::clone(qualified))?
|
||||
},
|
||||
x => x.clone()
|
||||
})
|
||||
@@ -103,12 +110,12 @@ where
|
||||
fn process_expression_rec(&mut self, Expr(token, typ): &Expr) -> Result<Expr, ResolutionError<E>> {
|
||||
Ok(Expr(
|
||||
self.process_clause_rec(token)?,
|
||||
self.process_exprboxopt_rec(typ)?
|
||||
self.process_exprmrcopt_rec(typ)?
|
||||
))
|
||||
}
|
||||
|
||||
pub fn find_origin(&mut self, symbol: &Vec<String>) -> Result<Vec<String>, ResolutionError<E>> {
|
||||
self.find_origin_rec(symbol, Stackframe::new(symbol))
|
||||
pub fn find_origin(&mut self, symbol: Mrc<[String]>) -> Result<Mrc<[String]>, ResolutionError<E>> {
|
||||
self.find_origin_rec(Mrc::clone(&symbol), Stackframe::new(symbol))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -119,4 +126,4 @@ where
|
||||
pub fn process_expression(&mut self, ex: &Expr) -> Result<Expr, ResolutionError<E>> {
|
||||
self.process_expression_rec(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::expression::{Expr, Clause};
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::{expression::{Expr, Clause}, utils::collect_to_mrc};
|
||||
|
||||
/// Replaces the first element of a name with the matching prefix from a prefix map
|
||||
|
||||
@@ -6,32 +8,34 @@ use crate::expression::{Expr, Clause};
|
||||
/// Called by [#prefix] which handles Typed.
|
||||
fn prefix_clause(
|
||||
expr: &Clause,
|
||||
namespace: &Vec<String>
|
||||
namespace: Mrc<[String]>
|
||||
) -> Clause {
|
||||
match expr {
|
||||
Clause::S(c, v) => Clause::S(*c, v.iter().map(|e| prefix_expr(e, namespace)).collect()),
|
||||
Clause::S(c, v) => Clause::S(*c,
|
||||
collect_to_mrc(v.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace))))
|
||||
),
|
||||
Clause::Auto(name, typ, body) => Clause::Auto(
|
||||
name.clone(),
|
||||
typ.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
||||
body.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
||||
collect_to_mrc(typ.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||
collect_to_mrc(body.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||
),
|
||||
Clause::Lambda(name, typ, body) => Clause::Lambda(
|
||||
name.clone(),
|
||||
typ.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
||||
body.iter().map(|e| prefix_expr(e, namespace)).collect(),
|
||||
collect_to_mrc(typ.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||
collect_to_mrc(body.iter().map(|e| prefix_expr(e, Mrc::clone(&namespace)))),
|
||||
),
|
||||
Clause::Name{local, qualified} => Clause::Name{
|
||||
local: local.clone(),
|
||||
qualified: namespace.iter().chain(qualified.iter()).cloned().collect()
|
||||
qualified: collect_to_mrc(namespace.iter().chain(qualified.iter()).cloned())
|
||||
},
|
||||
x => x.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce an Expr object for any value of Expr
|
||||
pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: &Vec<String>) -> Expr {
|
||||
pub fn prefix_expr(Expr(clause, typ): &Expr, namespace: Mrc<[String]>) -> Expr {
|
||||
Expr(
|
||||
prefix_clause(clause, namespace),
|
||||
typ.as_ref().map(|e| Box::new(prefix_expr(e, namespace)))
|
||||
prefix_clause(clause, Mrc::clone(&namespace)),
|
||||
typ.as_ref().map(|e| Mrc::new(prefix_expr(e, namespace)))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::expression::Rule;
|
||||
use crate::parse::{self, FileEntry};
|
||||
use crate::utils::Cache;
|
||||
use crate::utils::{Cache, mrc_derive, to_mrc_slice};
|
||||
|
||||
use super::name_resolver::NameResolver;
|
||||
use super::module_error::ModuleError;
|
||||
@@ -17,27 +20,29 @@ type ParseResult<T, ELoad> = Result<T, ModuleError<ELoad>>;
|
||||
pub struct Module {
|
||||
pub rules: Vec<Rule>,
|
||||
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>(
|
||||
mut load_mod: F,
|
||||
prelude: Vec<String>
|
||||
// ) -> impl FnMut(Vec<String>) -> Result<&'a Vec<super::Rule>, ParseError<ELoad>> + 'a
|
||||
) -> Cache<'static, Vec<String>, Result<Vec<super::Rule>, ModuleError<ELoad>>>
|
||||
) -> Cache<'static, Mrc<[String]>, RuleCollectionResult<ELoad>>
|
||||
where
|
||||
F: FnMut(Vec<String>) -> Result<Loaded, ELoad>,
|
||||
F: FnMut(Mrc<[String]>) -> Result<Loaded, ELoad>,
|
||||
ELoad: Clone + Debug
|
||||
{
|
||||
let load_mod_rc = RefCell::new(load_mod);
|
||||
// Map paths to a namespace with name list (folder) or module with source text (file)
|
||||
let loaded = Rc::new(Cache::new(move |path: Vec<String>, _|
|
||||
let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _|
|
||||
-> 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
|
||||
let modname = Rc::new(Cache::new({
|
||||
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 reg_err = |e, errv: &mut Vec<ModuleError<ELoad>>| {
|
||||
errv.push(e);
|
||||
@@ -45,11 +50,10 @@ where
|
||||
else { Ok(()) }
|
||||
};
|
||||
loop {
|
||||
let (path, _) = symbol.split_at(symbol.len() - errv.len());
|
||||
let pathv = path.to_vec();
|
||||
match loaded.try_find(&pathv) {
|
||||
let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len()]);
|
||||
match loaded.try_find(&path) {
|
||||
Ok(imports) => match imports.as_ref() {
|
||||
Loaded::Module(_) => break Ok(pathv.clone()),
|
||||
Loaded::Module(_) => break Ok(path),
|
||||
_ => reg_err(ModuleError::None, &mut errv)?
|
||||
},
|
||||
Err(err) => reg_err(err, &mut errv)?
|
||||
@@ -61,7 +65,7 @@ where
|
||||
let preparsed = Rc::new(Cache::new({
|
||||
let loaded = Rc::clone(&loaded);
|
||||
let prelude2 = prelude.clone();
|
||||
move |path: Vec<String>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
||||
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
||||
let loaded = loaded.try_find(&path)?;
|
||||
if let Loaded::Module(source) = loaded.as_ref() {
|
||||
Ok(parse::parse(&prelude2, source.as_str())?)
|
||||
@@ -72,7 +76,7 @@ where
|
||||
let exports = Rc::new(Cache::new({
|
||||
let loaded = Rc::clone(&loaded);
|
||||
let preparsed = Rc::clone(&preparsed);
|
||||
move |path: Vec<String>, _| -> ParseResult<Vec<String>, ELoad> {
|
||||
move |path: Mrc<[String]>, _| -> ParseResult<Vec<String>, ELoad> {
|
||||
let loaded = loaded.try_find(&path)?;
|
||||
if let Loaded::Namespace(names) = loaded.as_ref() {
|
||||
return Ok(names.clone());
|
||||
@@ -88,19 +92,19 @@ where
|
||||
let imports = Rc::new(Cache::new({
|
||||
let preparsed = Rc::clone(&preparsed);
|
||||
let exports = Rc::clone(&exports);
|
||||
move |path: Vec<String>, _| -> ParseResult<HashMap<String, Vec<String>>, ELoad> {
|
||||
let entv = preparsed.try_find(&path)?.clone();
|
||||
move |path: Mrc<[String]>, _| -> ParseResult<HashMap<String, Mrc<[String]>>, ELoad> {
|
||||
let entv = preparsed.try_find(&path)?;
|
||||
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 {
|
||||
let export = exports.try_find(&imp.path)?;
|
||||
if let Some(ref name) = imp.name {
|
||||
if export.contains(&name) {
|
||||
imported_symbols.insert(name.clone(), imp.path.clone());
|
||||
if export.contains(name) {
|
||||
imported_symbols.insert(name.clone(), Mrc::clone(&imp.path));
|
||||
}
|
||||
} else {
|
||||
for exp in export.as_ref() {
|
||||
imported_symbols.insert(exp.clone(), imp.path.clone());
|
||||
imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +116,7 @@ where
|
||||
let preparsed = Rc::clone(&preparsed);
|
||||
let imports = Rc::clone(&imports);
|
||||
let loaded = Rc::clone(&loaded);
|
||||
move |path: Vec<String>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
||||
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
|
||||
let imported_ops: Vec<String> =
|
||||
imports.try_find(&path)?
|
||||
.keys()
|
||||
@@ -127,45 +131,44 @@ where
|
||||
} else { Err(ModuleError::None) }
|
||||
}
|
||||
}));
|
||||
let mut name_resolver = NameResolver::new({
|
||||
let mut name_resolver_rc = RefCell::new(NameResolver::new({
|
||||
let modname = Rc::clone(&modname);
|
||||
move |path| {
|
||||
Some(modname.try_find(path).ok()?.as_ref().clone())
|
||||
Some(modname.try_find(&path).ok()?.as_ref().clone())
|
||||
}
|
||||
}, {
|
||||
let imports = Rc::clone(&imports);
|
||||
move |path| {
|
||||
imports.try_find(path).map(|f| f.as_ref().clone())
|
||||
imports.try_find(&path).map(|f| f.as_ref().clone())
|
||||
}
|
||||
});
|
||||
}));
|
||||
// Turn parsed files into a bag of rules and a list of toplevel export names
|
||||
let resolved = Rc::new(Cache::new({
|
||||
let parsed = Rc::clone(&parsed);
|
||||
let exports = Rc::clone(&exports);
|
||||
let imports = Rc::clone(&imports);
|
||||
let modname = Rc::clone(&modname);
|
||||
move |path: Vec<String>, _| -> ParseResult<Module, ELoad> {
|
||||
move |path: Mrc<[String]>, _| -> ParseResult<Module, ELoad> {
|
||||
let mut name_resolver = name_resolver_rc.borrow_mut();
|
||||
let module = Module {
|
||||
rules: parsed.try_find(&path)?
|
||||
.iter()
|
||||
.filter_map(|ent| {
|
||||
if let FileEntry::Rule(Rule{source, prio, target}, _) = ent {
|
||||
Some(Rule {
|
||||
source: source.iter().map(|ex| prefix_expr(ex, &path)).collect(),
|
||||
target: target.iter().map(|ex| prefix_expr(ex, &path)).collect(),
|
||||
source: source.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(),
|
||||
target: target.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(),
|
||||
prio: *prio,
|
||||
})
|
||||
} else { None }
|
||||
})
|
||||
.map(|rule| Ok(super::Rule {
|
||||
source: rule.source.iter()
|
||||
source: to_mrc_slice(rule.source.iter()
|
||||
.map(|ex| name_resolver.process_expression(ex))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
target: rule.target.iter()
|
||||
.collect::<Result<Vec<_>, _>>()?),
|
||||
target: to_mrc_slice(rule.target.iter()
|
||||
.map(|ex| name_resolver.process_expression(ex))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
// source: name_resolver.process_expression(&rule.source)?,
|
||||
// target: name_resolver.process_expression(&rule.target)?,
|
||||
.collect::<Result<Vec<_>, _>>()?),
|
||||
..rule
|
||||
}))
|
||||
.collect::<ParseResult<Vec<super::Rule>, ELoad>>()?,
|
||||
@@ -173,19 +176,20 @@ where
|
||||
references: imports.try_find(&path)?
|
||||
.values()
|
||||
.filter_map(|imps| {
|
||||
modname.try_find(&imps).ok().map(|r| r.as_ref().clone())
|
||||
modname.try_find(imps).ok().map(|r| r.as_ref().clone())
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
Ok(module)
|
||||
}
|
||||
}));
|
||||
let all_rules = Cache::new({
|
||||
Cache::new({
|
||||
let resolved = Rc::clone(&resolved);
|
||||
move |path: Vec<String>, _| -> ParseResult<Vec<super::Rule>, ELoad> {
|
||||
let mut processed: HashSet<Vec<String>> = HashSet::new();
|
||||
move |path: Mrc<[String]>, _| -> ParseResult<Vec<super::Rule>, ELoad> {
|
||||
// Breadth-first search
|
||||
let mut processed: HashSet<Mrc<[String]>> = HashSet::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);
|
||||
while let Some(el) = pending.pop_front() {
|
||||
let resolved = resolved.try_find(&el)?;
|
||||
@@ -201,6 +205,5 @@ where
|
||||
};
|
||||
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 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::unwrap_or_continue;
|
||||
use crate::utils::{Side, Cache};
|
||||
use super::super::RuleError;
|
||||
use super::State;
|
||||
use crate::utils::iter::box_empty;
|
||||
use crate::utils::{Side, Cache, mrc_derive, mrc_try_derive, to_mrc_slice};
|
||||
|
||||
fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr])> {
|
||||
let rngidx = pattern.iter().position_max_by_key(|ex| {
|
||||
if let Expr(Clause::Placeh(_, Some(prio)), _) = ex { *prio as i64 } else { -1 }
|
||||
})?;
|
||||
let (left, not_left) = pattern.split_at(rngidx);
|
||||
let (placeh, right) = if rngidx == pattern.len() {
|
||||
(¬_left[0].0, [].as_slice())
|
||||
} else {
|
||||
let (placeh_unary_slice, right) = pattern.split_at(rngidx + 1);
|
||||
(&placeh_unary_slice[0].0, right)
|
||||
};
|
||||
if let Clause::Placeh(name, Some(prio)) = placeh {
|
||||
Some((left, (name, *prio), right))
|
||||
} else {None}
|
||||
use super::State;
|
||||
use super::split_at_max_vec::split_at_max_vec;
|
||||
|
||||
/// Tuple with custom cloning logic
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
pub struct CacheEntry<'a>(Mrc<[Expr]>, &'a SliceMatcherDnC);
|
||||
impl<'a> Clone for CacheEntry<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
let CacheEntry(mrc, matcher) = self;
|
||||
CacheEntry(Mrc::clone(mrc), matcher)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Matcher that applies a pattern to a slice via divide-and-conquer
|
||||
///
|
||||
/// Upon construction, it selects the clause of highest priority, then
|
||||
@@ -31,134 +30,171 @@ fn split_at_max_vec(pattern: &[Expr]) -> Option<(&[Expr], (&str, usize), &[Expr]
|
||||
///
|
||||
/// Upon matching, it uses a cache to accelerate the process of executing
|
||||
/// a pattern on the entire tree.
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct SliceMatcherDnC<'a> {
|
||||
#[derive(Clone, Eq)]
|
||||
pub struct SliceMatcherDnC {
|
||||
/// The entire pattern this will match
|
||||
pattern: &'a [Expr],
|
||||
pattern: Mrc<[Expr]>,
|
||||
/// The exact clause this can match
|
||||
clause: &'a Clause,
|
||||
clause: Mrc<Clause>,
|
||||
/// 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
|
||||
left_subm: Option<Box<SliceMatcherDnC<'a>>>,
|
||||
left_subm: Option<Box<SliceMatcherDnC>>,
|
||||
/// Matcher for the body of this clause if it has one.
|
||||
/// 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)
|
||||
/// 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 {
|
||||
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) {
|
||||
self.pattern.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SliceMatcherDnC<'a> {
|
||||
impl SliceMatcherDnC {
|
||||
/// If this is true, `clause`, `typ_subm`, `body_subm` and `clause_qual_name` are meaningless.
|
||||
/// If it's false, it's also false for both side matchers.
|
||||
pub fn clause_is_vectorial(&self) -> bool {
|
||||
if let Clause::Placeh(_, Some(_)) = self.clause {true} else {false}
|
||||
matches!(self.clause.as_ref(), Clause::Placeh{vec: Some(..), ..})
|
||||
}
|
||||
/// If clause is a name, the qualified name this can match
|
||||
pub fn clause_qual_name(&self) -> Option<&'a Vec<String>> {
|
||||
if let Clause::Name { qualified, .. } = self.clause {Some(qualified)} else {None}
|
||||
pub fn clause_qual_name(&self) -> Option<Mrc<[String]>> {
|
||||
if let Clause::Name { qualified, .. } = self.clause.as_ref() {Some(Mrc::clone(qualified))} else {None}
|
||||
}
|
||||
/// If clause is a Placeh, the key in the state the match will be stored at
|
||||
pub fn state_key(&self) -> Option<&'a String> {
|
||||
if let Clause::Placeh(key, _) = self.clause {Some(key)} else {None}
|
||||
pub fn state_key(&self) -> Option<&String> {
|
||||
if let Clause::Placeh { key, .. } = self.clause.as_ref() {Some(key)} else {None}
|
||||
}
|
||||
pub fn own_max_size(&self, total: usize) -> usize {
|
||||
if !self.clause_is_vectorial() {return self.len()}
|
||||
return total - self.min(Side::Left) - self.min(Side::Right)
|
||||
pub fn own_max_size(&self, total: usize) -> Option<usize> {
|
||||
if !self.clause_is_vectorial() {
|
||||
if total == self.len() {Some(total)} else {None}
|
||||
} else {
|
||||
let margin = self.min(Side::Left) + self.min(Side::Right);
|
||||
if margin + self.own_min_size() <= total {Some(total - margin)} else {None}
|
||||
}
|
||||
}
|
||||
pub fn own_min_size(&self) -> usize {
|
||||
if let Clause::Placeh { vec: Some((_, nonzero)), .. } = self.clause.as_ref() {
|
||||
if *nonzero {1} else {0}
|
||||
} else {self.len()}
|
||||
}
|
||||
|
||||
/// Enumerate all valid subdivisions based on the reported size constraints of self and
|
||||
/// the two subranges
|
||||
pub fn valid_subdivisions<'b>(&self,
|
||||
range: &'b [Expr]
|
||||
) -> impl Iterator<Item = (&'b [Expr], &'b [Expr], &'b [Expr])> {
|
||||
let own_size = self.own_max_size(range.len());
|
||||
pub fn valid_subdivisions(&self,
|
||||
range: Mrc<[Expr]>
|
||||
) -> impl Iterator<Item = (Mrc<[Expr]>, Mrc<[Expr]>, Mrc<[Expr]>)> {
|
||||
let own_max = {
|
||||
if let Some(x) = self.own_max_size(range.len()) {x}
|
||||
else {return box_empty()}
|
||||
};
|
||||
let own_min = self.own_min_size();
|
||||
let lmin = self.min(Side::Left);
|
||||
let lmax = self.max(Side::Left, range.len());
|
||||
let _lmax = self.max(Side::Left, range.len());
|
||||
let rmin = self.min(Side::Right);
|
||||
let rmax = self.max(Side::Right, range.len());
|
||||
let _rmax = self.max(Side::Right, range.len());
|
||||
let full_len = range.len();
|
||||
(1..=own_size).rev().flat_map(move |own_len| {
|
||||
Box::new((own_min..=own_max).rev().flat_map(move |own_len| {
|
||||
let wiggle = full_len - lmin - rmin - own_len;
|
||||
(0..wiggle).map(move |offset| {
|
||||
let range = Mrc::clone(&range);
|
||||
(0..=wiggle).map(move |offset| {
|
||||
let first_break = lmin + offset;
|
||||
let (left, rest) = range.split_at(first_break);
|
||||
let (mid, right) = rest.split_at(own_len);
|
||||
let second_break = first_break + own_len;
|
||||
let left = mrc_derive(&range, |p| &p[0..first_break]);
|
||||
let mid = mrc_derive(&range, |p| &p[first_break..second_break]);
|
||||
let right = mrc_derive(&range, |p| &p[second_break..]);
|
||||
(left, mid, right)
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn new(pattern: &'a [Expr]) -> Self {
|
||||
let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 {
|
||||
(&pattern[0], None, None)
|
||||
} else if let Some((left, _, right)) = split_at_max_vec(pattern) {(
|
||||
&pattern[left.len()],
|
||||
Some(Box::new(Self::new(left))),
|
||||
Some(Box::new(Self::new(right)))
|
||||
)} else {(
|
||||
&pattern[0],
|
||||
None,
|
||||
Some(Box::new(Self::new(&pattern[1..])))
|
||||
)};
|
||||
pub fn new(pattern: Mrc<[Expr]>) -> Self {
|
||||
let (clause, left_subm, right_subm) = mrc_try_derive(&pattern, |p| {
|
||||
if p.len() == 1 {Some(&p[0].0)} else {None}
|
||||
}).map(|e| (e, None, None))
|
||||
.or_else(|| split_at_max_vec(Mrc::clone(&pattern)).map(|(left, _, right)| (
|
||||
mrc_derive(&pattern, |p| &p[left.len()].0),
|
||||
if !left.is_empty() {Some(Box::new(Self::new(left)))} else {None},
|
||||
if !right.is_empty() {Some(Box::new(Self::new(right)))} else {None}
|
||||
)))
|
||||
.unwrap_or_else(|| (
|
||||
mrc_derive(&pattern, |p| &p[0].0),
|
||||
None,
|
||||
Some(Box::new(Self::new(mrc_derive(&pattern, |p| &p[1..]))))
|
||||
));
|
||||
// let (Expr(clause, _), left_subm, right_subm) = if pattern.len() == 1 {
|
||||
// (&pattern[0], None, None)
|
||||
// } else if let Some((left, _, right)) = split_at_max_vec(pattern) {(
|
||||
// &pattern[left.len()],
|
||||
// Some(Box::new(Self::new(left))),
|
||||
// Some(Box::new(Self::new(right)))
|
||||
// )} else {(
|
||||
// &pattern[0],
|
||||
// None,
|
||||
// Some(Box::new(Self::new(&pattern[1..])))
|
||||
// )};
|
||||
Self {
|
||||
pattern, right_subm, left_subm, clause,
|
||||
pattern, right_subm, left_subm,
|
||||
clause: Mrc::clone(&clause),
|
||||
body_subm: clause.body().map(|b| Box::new(Self::new(b))),
|
||||
typ_subm: clause.typ().map(|t| Box::new(Self::new(t)))
|
||||
}
|
||||
}
|
||||
|
||||
/// The shortest slice this pattern can match
|
||||
fn len(&self) -> usize {self.pattern.len()}
|
||||
fn len(&self) -> usize {
|
||||
if self.clause_is_vectorial() {
|
||||
self.min(Side::Left) + self.min(Side::Right) + self.own_min_size()
|
||||
} else {self.pattern.len()}
|
||||
}
|
||||
/// Pick a subpattern based on the parameter
|
||||
fn side(&self, side: Side) -> Option<&Box<SliceMatcherDnC<'a>>> {
|
||||
fn side(&self, side: Side) -> Option<&SliceMatcherDnC> {
|
||||
match side {
|
||||
Side::Left => &self.left_subm,
|
||||
Side::Right => &self.right_subm
|
||||
}.as_ref()
|
||||
}.as_ref().map(|b| b.as_ref())
|
||||
}
|
||||
/// The shortest slice the given side can match
|
||||
fn min(&self, side: Side) -> usize {self.side(side).map_or(0, |right| right.len())}
|
||||
/// The longest slice the given side can match
|
||||
fn max(&self, side: Side, total: usize) -> usize {
|
||||
self.side(side).map_or(0, |m| if m.clause_is_vectorial() {
|
||||
total - self.min(side.opposite()) - 1
|
||||
total - self.min(side.opposite()) - self.own_min_size()
|
||||
} else {m.len()})
|
||||
}
|
||||
/// Take the smallest possible slice from the given side
|
||||
fn slice_min<'b>(&self, side: Side, range: &'b [Expr]) -> &'b [Expr] {
|
||||
fn slice_min<'a>(&self, side: Side, range: &'a [Expr]) -> &'a [Expr] {
|
||||
side.slice(self.min(side), range)
|
||||
}
|
||||
|
||||
/// Matches the body on a range
|
||||
/// # Panics
|
||||
/// when called on an instance that does not have a body (not Auto, Lambda or S)
|
||||
fn match_body<'b>(&'a self,
|
||||
range: &'b [Expr], cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
||||
fn match_body<'a>(&'a self,
|
||||
range: Mrc<[Expr]>, cache: &Cache<CacheEntry<'a>, 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
|
||||
/// # Panics
|
||||
/// when called on an instance that does not have a body (not Auto, Lambda or S)
|
||||
fn match_parts<'b>(&'a self,
|
||||
typ_range: &'b [Expr], body_range: &'b [Expr],
|
||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
||||
fn match_parts<'a>(&'a self,
|
||||
typ_range: Mrc<[Expr]>, body_range: Mrc<[Expr]>,
|
||||
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||
) -> Option<State> {
|
||||
let typ_state = if let Some(typ) = &self.typ_subm {
|
||||
typ.match_range_cached(&typ_range, cache)?
|
||||
typ.match_range_cached(typ_range, cache)?
|
||||
} else {State::new()};
|
||||
let body_state = self.match_body(body_range, cache)?;
|
||||
typ_state + body_state
|
||||
@@ -166,181 +202,124 @@ impl<'a> SliceMatcherDnC<'a> {
|
||||
|
||||
/// Match the specified side-submatcher on the specified range with the cache
|
||||
/// In absence of a side-submatcher empty ranges are matched to empty state
|
||||
fn apply_side_with_cache<'b>(&'a self,
|
||||
side: Side, range: &'b [Expr],
|
||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
||||
fn apply_side_with_cache<'a>(&'a self,
|
||||
side: Side, range: Mrc<[Expr]>,
|
||||
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||
) -> Option<State> {
|
||||
match &self.side(side) {
|
||||
None => {
|
||||
if range.len() != 0 {None}
|
||||
if !range.is_empty() {None}
|
||||
else {Some(State::new())}
|
||||
},
|
||||
Some(m) => cache.try_find(&(range, &m)).map(|s| s.as_ref().to_owned())
|
||||
Some(m) => cache.try_find(&CacheEntry(range, m)).map(|s| s.as_ref().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
fn match_range_scalar_cached<'b>(&'a self,
|
||||
target: &'b [Expr],
|
||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
||||
fn match_range_scalar_cached<'a>(&'a self,
|
||||
target: Mrc<[Expr]>,
|
||||
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||
) -> Option<State> {
|
||||
let pos = self.min(Side::Left);
|
||||
if target.len() != self.pattern.len() {return None}
|
||||
let mut own_state = (
|
||||
self.apply_side_with_cache(Side::Left, &target[0..pos], cache)?
|
||||
+ self.apply_side_with_cache(Side::Right, &target[pos+1..], cache)
|
||||
self.apply_side_with_cache(Side::Left, mrc_derive(&target, |t| &t[0..pos]), cache)?
|
||||
+ self.apply_side_with_cache(Side::Right, mrc_derive(&target, |t| &t[pos+1..]), cache)
|
||||
)?;
|
||||
match (self.clause, &target[pos].0) {
|
||||
match (self.clause.as_ref(), &target.as_ref()[pos].0) {
|
||||
(Clause::Literal(val), Clause::Literal(tgt)) => {
|
||||
if val == tgt {Some(own_state)} else {None}
|
||||
}
|
||||
(Clause::Placeh(name, None), _) => {
|
||||
own_state.insert(name, &[target[pos].clone()])
|
||||
(Clause::Placeh{key, vec: None}, _) => {
|
||||
own_state.insert_scalar(&key, &target[pos])
|
||||
}
|
||||
(Clause::S(c, _), Clause::S(c_tgt, body_range)) => {
|
||||
if c != c_tgt {return None}
|
||||
own_state + self.match_parts(&[], body_range, cache)
|
||||
own_state + self.match_parts(to_mrc_slice(vec![]), Mrc::clone(body_range), cache)
|
||||
}
|
||||
(Clause::Name{qualified, ..}, Clause::Name{qualified: q_tgt, ..}) => {
|
||||
if qualified == q_tgt {Some(own_state)} else {None}
|
||||
}
|
||||
(Clause::Lambda(name, _, _), Clause::Lambda(name_tgt, typ_tgt, body_tgt)) => {
|
||||
// Primarily, the name works as a placeholder
|
||||
if let Some(state_key) = name.strip_prefix("$") {
|
||||
own_state = own_state.insert(
|
||||
state_key,
|
||||
&[Expr(Clause::Name{
|
||||
local: Some(name_tgt.clone()),
|
||||
qualified: vec![name_tgt.clone()]
|
||||
}, None)]
|
||||
)?
|
||||
// But if you're weird like that, it can also work as a constraint
|
||||
if let Some(state_key) = name.strip_prefix('$') {
|
||||
own_state = own_state.insert_name(state_key, name_tgt)?
|
||||
} else if name != name_tgt {return None}
|
||||
own_state + self.match_parts(typ_tgt, body_tgt, cache)
|
||||
// ^ But if you're weird like that, it can also work as a constraint
|
||||
own_state + self.match_parts(Mrc::clone(typ_tgt), Mrc::clone(body_tgt), cache)
|
||||
}
|
||||
(Clause::Auto(name_opt, _, _), Clause::Auto(name_range, typ_range, body_range)) => {
|
||||
if let Some(name) = name_opt {
|
||||
if let Some(state_name) = name.strip_prefix("$") {
|
||||
own_state = own_state.insert(
|
||||
state_name,
|
||||
&[Expr(Clause::Name{
|
||||
local: name_range.clone(),
|
||||
qualified: name_range.as_ref()
|
||||
.map(|s| vec![s.clone()])
|
||||
.unwrap_or_default()
|
||||
}, None)]
|
||||
)?
|
||||
// TODO: Enforce this at construction, on a type system level
|
||||
} else {panic!("Auto patterns may only reference, never enforce the name")}
|
||||
// TODO: Enforce this at construction, on a type system level
|
||||
let state_key = name.strip_prefix('$')
|
||||
.expect("Auto patterns may only reference, never enforce the name");
|
||||
own_state = own_state.insert_name_opt(state_key, name_range.as_ref())?
|
||||
}
|
||||
own_state + self.match_parts(typ_range, body_range, cache)
|
||||
own_state + self.match_parts(Mrc::clone(typ_range), Mrc::clone(body_range), cache)
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Match the range with a vectorial _assuming we are a vectorial_
|
||||
fn match_range_vectorial_cached<'b>(&'a self,
|
||||
fn match_range_vectorial_cached<'a>(&'a self,
|
||||
name: &str,
|
||||
target: &'b [Expr],
|
||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
||||
target: Mrc<[Expr]>,
|
||||
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||
) -> Option<State> {
|
||||
// Step through valid slicings based on reported size constraints in order
|
||||
// from longest own section to shortest and from left to right
|
||||
for (left, own, right) in self.valid_subdivisions(target) {
|
||||
let left_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Left, left, cache));
|
||||
let right_result = unwrap_or_continue!(self.apply_side_with_cache(Side::Right, right, cache));
|
||||
let sides_result = unwrap_or_continue!(
|
||||
self.apply_side_with_cache(Side::Left, left, cache)
|
||||
) + self.apply_side_with_cache(Side::Right, right, cache);
|
||||
return Some(unwrap_or_continue!(
|
||||
right_result.clone()
|
||||
+ left_result.insert(name, own)
|
||||
unwrap_or_continue!(sides_result)
|
||||
.insert_vec(name, own.as_ref())
|
||||
))
|
||||
}
|
||||
return None
|
||||
None
|
||||
}
|
||||
|
||||
/// Try and match the specified range
|
||||
pub fn match_range_cached<'b>(&'a self,
|
||||
target: &'b [Expr],
|
||||
cache: &Cache<(&'b [Expr], &'a SliceMatcherDnC<'a>), Option<State>>
|
||||
pub fn match_range_cached<'a>(&'a self,
|
||||
target: Mrc<[Expr]>,
|
||||
cache: &Cache<CacheEntry<'a>, Option<State>>
|
||||
) -> Option<State> {
|
||||
if self.pattern.len() == 0 {
|
||||
return if target.len() == 0 {Some(State::new())} else {None}
|
||||
eprintln!("Matching {target:?} with {:?}", self.pattern);
|
||||
if self.pattern.is_empty() {
|
||||
return if target.is_empty() {Some(State::new())} else {None}
|
||||
}
|
||||
match self.clause {
|
||||
Clause::Placeh(name, Some(_)) => self.match_range_vectorial_cached(name, target, cache),
|
||||
match self.clause.as_ref() {
|
||||
Clause::Placeh{key, vec: Some(_)} =>
|
||||
self.match_range_vectorial_cached(key, target, cache),
|
||||
_ => self.match_range_scalar_cached(target, cache)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_range(&self, target: &[Expr]) -> Option<State> {
|
||||
self.match_range_cached(target,&Cache::<(&[Expr], &SliceMatcherDnC), _>::new(
|
||||
|(tgt, matcher), cache| {
|
||||
pub fn get_matcher_cache<'a>()
|
||||
-> Cache<'a, CacheEntry<'a>, Option<State>> {
|
||||
Cache::new(
|
||||
|CacheEntry(tgt, matcher), 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>)
|
||||
-> Result<(), String> {
|
||||
let verify_clause = |clause: &Clause, is_vec: &mut HashMap<String, bool>| -> Result<(), String> {
|
||||
match clause {
|
||||
Clause::Placeh(name, prio) => {
|
||||
if let Some(known) = is_vec.get(name) {
|
||||
if known != &prio.is_some() { return Err(name.to_string()) }
|
||||
} else {
|
||||
is_vec.insert(name.clone(), prio.is_some());
|
||||
}
|
||||
}
|
||||
Clause::Auto(name, typ, body) => {
|
||||
if let Some(key) = name.as_ref().map(|key| key.strip_prefix("$")).flatten() {
|
||||
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
|
||||
}
|
||||
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||
}
|
||||
Clause::Lambda(name, typ, body) => {
|
||||
if let Some(key) = name.strip_prefix("$") {
|
||||
if is_vec.get(key) == Some(&true) { return Err(key.to_string()) }
|
||||
}
|
||||
typ.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||
}
|
||||
Clause::S(_, body) => {
|
||||
body.iter().try_for_each(|e| verify_scalar_vec(e, is_vec))?;
|
||||
}
|
||||
_ => ()
|
||||
};
|
||||
Ok(())
|
||||
};
|
||||
let Expr(val, typ_opt) = pattern;
|
||||
verify_clause(val, is_vec)?;
|
||||
if let Some(typ) = typ_opt {
|
||||
verify_scalar_vec(typ, is_vec)?;
|
||||
impl Debug for SliceMatcherDnC {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Matcher")
|
||||
.field("clause", &self.clause)
|
||||
.field("vectorial", &self.clause_is_vectorial())
|
||||
.field("min", &self.len())
|
||||
.field("left", &self.left_subm)
|
||||
.field("right", &self.right_subm)
|
||||
.field("lmin", &self.min(Side::Left))
|
||||
.field("rmin", &self.min(Side::Right))
|
||||
.finish()
|
||||
}
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
pub fn execute(mut src: Vec<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 mappable_rc::Mrc;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
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.
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct State(HashMap<String, Mrc<Vec<Expr>>>);
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct State(HashMap<String, Entry>);
|
||||
|
||||
/// Clone without also cloning arbitrarily heavy Expr objects.
|
||||
/// Key is expected to be a very short string with an allocator overhead close to zero.
|
||||
impl Clone for State {
|
||||
impl Clone for Entry {
|
||||
fn clone(&self) -> Self {
|
||||
Self(HashMap::from_iter(
|
||||
self.0.iter().map(|(k, v)| (k.clone(), Mrc::clone(v)))
|
||||
))
|
||||
match self {
|
||||
Self::Name(n) => Self::Name(Rc::clone(n)),
|
||||
Self::Scalar(x) => Self::Scalar(Rc::clone(x)),
|
||||
Self::Vec(v) => Self::Vec(Rc::clone(v)),
|
||||
Self::NameOpt(o) => Self::NameOpt(o.as_ref().map(Rc::clone))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,24 +33,65 @@ impl State {
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
/// Insert a new element, return None on conflict, clone only on success
|
||||
pub fn insert<S>(mut self, k: &S, v: &[Expr]) -> Option<State>
|
||||
pub fn insert_vec<S>(mut self, k: &S, v: &[Expr]) -> Option<Self>
|
||||
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 {
|
||||
if let Some(old) = self.0.get(k.as_ref()) {
|
||||
if old.as_ref() != v {return None}
|
||||
if let Entry::Scalar(val) = old {
|
||||
if val.as_ref() != v {return None}
|
||||
} else {return None}
|
||||
} else {
|
||||
self.0.insert(k.to_string(), Mrc::new(v.to_vec()));
|
||||
self.0.insert(k.to_string(), Entry::Scalar(Rc::new(v.to_owned())));
|
||||
}
|
||||
return Some(self)
|
||||
Some(self)
|
||||
}
|
||||
pub fn insert_name<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
|
||||
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 old != &v {return None}
|
||||
} else {
|
||||
self.0.insert(k, v);
|
||||
}
|
||||
return Some(self)
|
||||
Some(self)
|
||||
}
|
||||
/// Returns `true` if the state contains no data
|
||||
pub fn empty(&self) -> bool {
|
||||
@@ -58,7 +109,7 @@ impl Add for State {
|
||||
for pair in rhs.0 {
|
||||
self = self.insert_pair(pair)?
|
||||
}
|
||||
return Some(self);
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,20 +121,20 @@ impl Add<Option<State>> for State {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Index<&S> for State where S: AsRef<str> {
|
||||
type Output = Vec<Expr>;
|
||||
impl<S> Index<S> for State where S: AsRef<str> {
|
||||
type Output = Entry;
|
||||
|
||||
fn index(&self, index: &S) -> &Self::Output {
|
||||
fn index(&self, index: S) -> &Self::Output {
|
||||
return &self.0[index.as_ref()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for State {
|
||||
type Item = (String, Mrc<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 {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// mod rule;
|
||||
mod executor;
|
||||
mod rule_error;
|
||||
mod repository;
|
||||
|
||||
// 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 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
|
||||
/// 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>>>,
|
||||
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
|
||||
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)))
|
||||
}
|
||||
|
||||
/// Take an Mrc<O> closure rather than an O closure
|
||||
/// Used internally to derive caches from other systems working with Mrc-s
|
||||
pub fn new_raw<F: 'a>(closure: F) -> Self where F: FnMut(I, &Self) -> Mrc<O> {
|
||||
pub fn new_raw<F: 'a>(closure: F) -> Self where F: Fn(I, &Self) -> Mrc<O> {
|
||||
Self {
|
||||
store: RefCell::new(HashMap::new()),
|
||||
closure: RefCell::new(Box::new(closure))
|
||||
closure: Box::new(closure)
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce and cache a result by cloning I if necessary
|
||||
pub fn find(&self, i: &I) -> Mrc<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();
|
||||
Mrc::clone(store.raw_entry_mut().from_key(i)
|
||||
.or_insert_with(|| (i.clone(), closure(i.clone(), self))).1)
|
||||
.or_insert_with(|| (i.my_clone(), result)).1)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Return the result if it has already been computed
|
||||
pub fn known(&self, i: &I) -> Option<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
|
||||
I: Eq + Hash + Clone,
|
||||
I: Eq + Hash + MyClone,
|
||||
// O: Clone,
|
||||
E: Clone
|
||||
{
|
||||
@@ -60,7 +83,7 @@ impl<'a, I, O, E> Cache<'a, I, Result<O, E>> where
|
||||
}
|
||||
|
||||
impl<'a, I, O> Cache<'a, I, Option<O>> where
|
||||
I: Eq + Hash + Clone,
|
||||
I: Eq + Hash + MyClone,
|
||||
// O: Clone
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
@@ -70,4 +93,4 @@ impl<'a, I, O> Cache<'a, I, Option<O>> where
|
||||
let ent = self.find(i);
|
||||
Mrc::try_map(ent, |o| o.as_ref()).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 side;
|
||||
mod merge_sorted;
|
||||
mod sorted_pairs;
|
||||
mod unwrap_or_continue;
|
||||
pub mod iter;
|
||||
pub use cache::Cache;
|
||||
use mappable_rc::Mrc;
|
||||
pub use substack::Stackframe;
|
||||
pub use side::Side;
|
||||
pub use merge_sorted::merge_sorted;
|
||||
pub use iter::BoxedIter;
|
||||
|
||||
pub type BoxedIter<'a, T> = Box<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