Rule execution now runs, no tests tho

This commit is contained in:
2022-08-19 12:55:02 +02:00
parent 329dea72b7
commit 891d78c112
30 changed files with 925 additions and 560 deletions

View File

@@ -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)
}
}
}

View File

@@ -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)
}

View File

@@ -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")
}
}

View File

@@ -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")
}
}

View File

@@ -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()

View File

@@ -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
}
}
}

View File

@@ -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())}
}

View File

@@ -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())
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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) })
}
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)))
)
}

View File

@@ -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;
})
}

View 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))
}))
}

View File

@@ -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;

View File

@@ -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() {
(&not_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!()
}

View 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
})
}

View File

@@ -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()
}
}
}

View File

@@ -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;

View File

@@ -1,3 +0,0 @@
struct Name {
qualified: Vec<String>
}

45
src/rule/repository.rs Normal file
View 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(())
}
}

View File

@@ -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 }
}

View File

@@ -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
View 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())
}

View File

@@ -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())
}

View File

@@ -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!()
}
}