Preparation for sharing

- rustfmt
- clippy
- comments
- README
This commit is contained in:
2023-05-25 19:14:24 +01:00
parent e99ade92ba
commit bc2714aad8
144 changed files with 3734 additions and 3243 deletions

View File

@@ -1,10 +1,9 @@
use std::rc::Rc;
use crate::ast::Expr;
use super::state::State;
use crate::ast::Expr;
pub trait Matcher {
fn new(pattern: Rc<Vec<Expr>>) -> Self;
fn apply<'a>(&self, source: &'a [Expr]) -> Option<State<'a>>;
}
}

View File

@@ -1,20 +1,25 @@
use crate::{ast::Expr, rule::state::State};
use super::scal_match::scalv_match;
use super::shared::AnyMatcher;
use super::vec_match::vec_match;
use crate::ast::Expr;
use crate::rule::state::State;
use super::{shared::AnyMatcher, scal_match::scalv_match, vec_match::vec_match};
pub fn any_match<'a>(matcher: &AnyMatcher, seq: &'a [Expr])
-> Option<State<'a>>
{
pub fn any_match<'a>(
matcher: &AnyMatcher,
seq: &'a [Expr],
) -> Option<State<'a>> {
match matcher {
AnyMatcher::Scalar(scalv) => scalv_match(scalv, seq),
AnyMatcher::Vec{ left, mid, right } => {
if seq.len() < left.len() + right.len() {return None};
AnyMatcher::Vec { left, mid, right } => {
if seq.len() < left.len() + right.len() {
return None;
};
let left_split = left.len();
let right_split = seq.len() - right.len();
let mut state = scalv_match(left, &seq[..left_split])?;
state.extend(scalv_match(right, &seq[right_split..])?);
state.extend(vec_match(mid, &seq[left_split..right_split])?);
Some(state)
}
},
}
}
}

View File

@@ -1,34 +1,28 @@
use itertools::Itertools;
use crate::{rule::vec_attrs::vec_attrs, ast::Clause};
use crate::utils::Side;
use crate::interner::Token;
use crate::ast::{Expr, Placeholder, PHClass};
use super::shared::{AnyMatcher, ScalMatcher, VecMatcher};
use crate::ast::{Clause, Expr, PHClass, Placeholder};
use crate::interner::Tok;
use crate::rule::vec_attrs::vec_attrs;
use crate::utils::Side;
pub type MaxVecSplit<'a> = (&'a [Expr], (Tok<String>, u64, bool), &'a [Expr]);
pub type MaxVecSplit<'a> = (&'a [Expr], (Token<String>, u64, bool), &'a [Expr]);
/// Derive the details of the central vectorial and the two sides from a slice of Expr's
/// Derive the details of the central vectorial and the two sides from a
/// slice of Expr's
fn split_at_max_vec(pattern: &[Expr]) -> Option<MaxVecSplit> {
let rngidx = pattern.iter()
.position_max_by_key(|expr| {
vec_attrs(expr)
.map(|attrs| attrs.1 as i64)
.unwrap_or(-1)
})?;
let rngidx = pattern.iter().position_max_by_key(|expr| {
vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1)
})?;
let (left, not_left) = pattern.split_at(rngidx);
let (placeh, right) = not_left.split_first()
let (placeh, right) = not_left
.split_first()
.expect("The index of the greatest element must be less than the length");
vec_attrs(placeh)
.map(|attrs| (left, attrs, right))
vec_attrs(placeh).map(|attrs| (left, attrs, right))
}
fn scal_cnt<'a>(iter: impl Iterator<Item = &'a Expr>) -> usize {
iter
.take_while(|expr| vec_attrs(expr).is_none())
.count()
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
}
/// Recursively convert this pattern into a matcher that can be
@@ -36,7 +30,7 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a Expr>) -> usize {
pub fn mk_matcher(pattern: &[Expr]) -> AnyMatcher {
let left_split = scal_cnt(pattern.iter());
if pattern.len() <= left_split {
return AnyMatcher::Scalar(mk_scalv(pattern))
return AnyMatcher::Scalar(mk_scalv(pattern));
}
let (left, not_left) = pattern.split_at(left_split);
let right_split = not_left.len() - scal_cnt(pattern.iter().rev());
@@ -56,11 +50,19 @@ fn mk_scalv(pattern: &[Expr]) -> Vec<ScalMatcher> {
/// Pattern MUST start and end with a vectorial placeholder
fn mk_vec(pattern: &[Expr]) -> VecMatcher {
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
debug_assert!(pattern.last().map(vec_attrs).is_some(), "pattern must end with a vectorial");
debug_assert!(
pattern.first().map(vec_attrs).is_some(),
"pattern must start with a vectorial"
);
debug_assert!(
pattern.last().map(vec_attrs).is_some(),
"pattern must end with a vectorial"
);
let (left, (key, prio, nonzero), right) = split_at_max_vec(pattern)
.expect("pattern must have vectorial placeholders at least at either end");
if prio >= 1 {println!("Nondefault priority {} found", prio)}
if prio >= 1 {
println!("Nondefault priority {} found", prio)
}
let r_sep_size = scal_cnt(right.iter());
let (r_sep, r_side) = right.split_at(r_sep_size);
let l_sep_size = scal_cnt(left.iter().rev());
@@ -80,10 +82,11 @@ fn mk_vec(pattern: &[Expr]) -> VecMatcher {
sep: mk_scalv(l_sep),
right: Box::new(main),
},
(_, _) => {
let mut key_order = l_side.iter()
(..) => {
let mut key_order = l_side
.iter()
.chain(r_side.iter())
.filter_map(|e| vec_attrs(e))
.filter_map(vec_attrs)
.collect::<Vec<_>>();
key_order.sort_by_key(|(_, prio, _)| -(*prio as i64));
VecMatcher::Middle {
@@ -92,9 +95,9 @@ fn mk_vec(pattern: &[Expr]) -> VecMatcher {
mid: Box::new(main),
right_sep: mk_scalv(r_sep),
right: Box::new(mk_vec(r_side)),
key_order: key_order.into_iter().map(|(n, ..)| n).collect()
key_order: key_order.into_iter().map(|(n, ..)| n).collect(),
}
}
},
}
}
@@ -103,15 +106,16 @@ fn mk_scalar(pattern: &Expr) -> ScalMatcher {
match &pattern.value {
Clause::P(p) => ScalMatcher::P(p.clone()),
Clause::Name(n) => ScalMatcher::Name(*n),
Clause::Placeh(Placeholder{ name, class }) => {
debug_assert!(!matches!(class, PHClass::Vec{..}), "Scalar matcher cannot be built from vector pattern");
Clause::Placeh(Placeholder { name, class }) => {
debug_assert!(
!matches!(class, PHClass::Vec { .. }),
"Scalar matcher cannot be built from vector pattern"
);
ScalMatcher::Placeh(*name)
}
Clause::S(c, body) => ScalMatcher::S(*c, Box::new(mk_matcher(&body))),
Clause::Lambda(arg, body) => ScalMatcher::Lambda(
Box::new(mk_scalar(&arg)),
Box::new(mk_matcher(&body))
)
},
Clause::S(c, body) => ScalMatcher::S(*c, Box::new(mk_matcher(body))),
Clause::Lambda(arg, body) =>
ScalMatcher::Lambda(Box::new(mk_scalar(arg)), Box::new(mk_matcher(body))),
}
}
@@ -119,37 +123,44 @@ fn mk_scalar(pattern: &Expr) -> ScalMatcher {
mod test {
use std::rc::Rc;
use crate::interner::{Interner, InternedDisplay};
use crate::ast::{Clause, Placeholder, PHClass};
use super::mk_matcher;
use crate::ast::{Clause, PHClass, Placeholder};
use crate::interner::{InternedDisplay, Interner};
#[test]
fn test_scan() {
let i = Interner::new();
let pattern = vec![
Clause::Placeh(Placeholder{
class: PHClass::Vec{ nonzero: false, prio: 0 },
name: i.i("::prefix"),
}).into_expr(),
Clause::Name(i.i(&[i.i("prelude"), i.i("do")][..])).into_expr(),
Clause::S('(', Rc::new(vec![
Clause::Placeh(Placeholder{
class: PHClass::Vec{ nonzero: false, prio: 0 },
name: i.i("expr"),
}).into_expr(),
Clause::Name(i.i(&[i.i("prelude"), i.i(";")][..])).into_expr(),
Clause::Placeh(Placeholder {
class: PHClass::Vec{ nonzero: false, prio: 1 },
name: i.i("rest"),
}).into_expr()
])).into_expr(),
Clause::Placeh(Placeholder {
class: PHClass::Vec{ nonzero: false, prio: 0 },
class: PHClass::Vec { nonzero: false, prio: 0 },
name: i.i("::prefix"),
})
.into_expr(),
Clause::Name(i.i(&[i.i("prelude"), i.i("do")][..])).into_expr(),
Clause::S(
'(',
Rc::new(vec![
Clause::Placeh(Placeholder {
class: PHClass::Vec { nonzero: false, prio: 0 },
name: i.i("expr"),
})
.into_expr(),
Clause::Name(i.i(&[i.i("prelude"), i.i(";")][..])).into_expr(),
Clause::Placeh(Placeholder {
class: PHClass::Vec { nonzero: false, prio: 1 },
name: i.i("rest"),
})
.into_expr(),
]),
)
.into_expr(),
Clause::Placeh(Placeholder {
class: PHClass::Vec { nonzero: false, prio: 0 },
name: i.i("::suffix"),
}).into_expr(),
})
.into_expr(),
];
let matcher = mk_matcher(&pattern);
println!("{}", matcher.bundle(&i));
}
}
}

View File

@@ -1,22 +1,20 @@
/*
Construction:
convert pattern into hierarchy of plain, scan, middle
- plain: accept any sequence or any non-empty sequence
- scan: a single scalar pattern moves LTR or RTL, submatchers on either
side
- middle: two scalar patterns walk over all permutations of matches
while getting progressively closer to each other
// Construction:
// convert pattern into hierarchy of plain, scan, middle
// - plain: accept any sequence or any non-empty sequence
// - scan: a single scalar pattern moves LTR or RTL, submatchers on either
// side
// - middle: two scalar patterns walk over all permutations of matches
// while getting progressively closer to each other
//
// Application:
// walk over the current matcher's valid options and poll the submatchers
// for each of them
Application:
walk over the current matcher's valid options and poll the submatchers
for each of them
*/
mod shared;
mod vec_match;
mod scal_match;
mod any_match;
mod build;
mod scal_match;
mod shared;
mod vec_match;
pub use build::mk_matcher;
pub use shared::AnyMatcher;
pub use build::mk_matcher;

View File

@@ -1,32 +1,38 @@
use crate::{rule::state::{State, StateEntry}, ast::{Expr, Clause}};
use super::any_match::any_match;
use super::shared::ScalMatcher;
use crate::ast::{Clause, Expr};
use crate::rule::state::{State, StateEntry};
use super::{shared::ScalMatcher, any_match::any_match};
pub fn scal_match<'a>(matcher: &ScalMatcher, expr: &'a Expr)
-> Option<State<'a>> {
pub fn scal_match<'a>(
matcher: &ScalMatcher,
expr: &'a Expr,
) -> Option<State<'a>> {
match (matcher, &expr.value) {
(ScalMatcher::P(p1), Clause::P(p2)) if p1 == p2 => Some(State::new()),
(ScalMatcher::Name(n1), Clause::Name(n2)) if n1 == n2
=> Some(State::new()),
(ScalMatcher::Placeh(key), _)
=> Some(State::from([(*key, StateEntry::Scalar(expr))])),
(ScalMatcher::S(c1, b_mat), Clause::S(c2, body)) if c1 == c2
=> any_match(b_mat, &body[..]),
(ScalMatcher::Name(n1), Clause::Name(n2)) if n1 == n2 => Some(State::new()),
(ScalMatcher::Placeh(key), _) =>
Some(State::from([(*key, StateEntry::Scalar(expr))])),
(ScalMatcher::S(c1, b_mat), Clause::S(c2, body)) if c1 == c2 =>
any_match(b_mat, &body[..]),
(ScalMatcher::Lambda(arg_mat, b_mat), Clause::Lambda(arg, body)) => {
let mut state = scal_match(&*arg_mat, &*arg)?;
state.extend(any_match(&*b_mat, &*body)?);
let mut state = scal_match(arg_mat, arg)?;
state.extend(any_match(b_mat, body)?);
Some(state)
}
_ => None
},
_ => None,
}
}
pub fn scalv_match<'a>(matchers: &[ScalMatcher], seq: &'a [Expr])
-> Option<State<'a>> {
if seq.len() != matchers.len() {return None}
pub fn scalv_match<'a>(
matchers: &[ScalMatcher],
seq: &'a [Expr],
) -> Option<State<'a>> {
if seq.len() != matchers.len() {
return None;
}
let mut state = State::new();
for (matcher, expr) in matchers.iter().zip(seq.iter()) {
state.extend(scal_match(matcher, expr)?);
}
Some(state)
}
}

View File

@@ -1,36 +1,37 @@
use std::fmt::Write;
use std::rc::Rc;
use super::any_match::any_match;
use super::build::mk_matcher;
use crate::ast::Expr;
use crate::rule::{matcher::Matcher, state::State};
use crate::unwrap_or;
use crate::utils::{Side, print_nname};
use crate::interner::{Token, InternedDisplay, Interner};
use crate::interner::{InternedDisplay, Interner, Sym, Tok};
use crate::representations::Primitive;
use super::{build::mk_matcher, any_match::any_match};
use crate::rule::matcher::Matcher;
use crate::rule::state::State;
use crate::unwrap_or;
use crate::utils::{sym2string, Side};
pub enum ScalMatcher {
P(Primitive),
Name(Token<Vec<Token<String>>>),
Name(Sym),
S(char, Box<AnyMatcher>),
Lambda(Box<ScalMatcher>, Box<AnyMatcher>),
Placeh(Token<String>),
Placeh(Tok<String>),
}
pub enum VecMatcher {
Placeh{
key: Token<String>,
nonzero: bool
Placeh {
key: Tok<String>,
nonzero: bool,
},
Scan{
Scan {
left: Box<VecMatcher>,
sep: Vec<ScalMatcher>,
right: Box<VecMatcher>,
/// The separator traverses the sequence towards this side
direction: Side
direction: Side,
},
Middle{
Middle {
/// Matches the left outer region
left: Box<VecMatcher>,
/// Matches the left separator
@@ -43,19 +44,15 @@ pub enum VecMatcher {
right: Box<VecMatcher>,
/// Order of significance for sorting equally good solutions based on
/// the length of matches on either side.
///
///
/// Vectorial keys that appear on either side, in priority order
key_order: Vec<Token<String>>
}
key_order: Vec<Tok<String>>,
},
}
pub enum AnyMatcher {
Scalar(Vec<ScalMatcher>),
Vec{
left: Vec<ScalMatcher>,
mid: VecMatcher,
right: Vec<ScalMatcher>
}
Vec { left: Vec<ScalMatcher>, mid: VecMatcher, right: Vec<ScalMatcher> },
}
impl Matcher for AnyMatcher {
fn new(pattern: Rc<Vec<Expr>>) -> Self {
@@ -70,9 +67,9 @@ impl Matcher for AnyMatcher {
// ################ InternedDisplay ################
fn disp_scalv(
scalv: &Vec<ScalMatcher>,
scalv: &[ScalMatcher],
f: &mut std::fmt::Formatter<'_>,
i: &Interner
i: &Interner,
) -> std::fmt::Result {
let (head, tail) = unwrap_or!(scalv.split_first(); return Ok(()));
head.fmt_i(f, i)?;
@@ -84,37 +81,52 @@ fn disp_scalv(
}
impl InternedDisplay for ScalMatcher {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
fn fmt_i(
&self,
f: &mut std::fmt::Formatter<'_>,
i: &Interner,
) -> std::fmt::Result {
match self {
Self::P(p) => write!(f, "{:?}", p),
Self::Placeh(n) => write!(f, "${}", i.r(*n)),
Self::Name(n) => write!(f, "{}", print_nname(*n, i)),
Self::Name(n) => write!(f, "{}", sym2string(*n, i)),
Self::S(c, body) => {
f.write_char(*c)?;
body.fmt_i(f, i)?;
f.write_char(match c {'('=>')','['=>']','{'=>'}',_=>unreachable!()})
f.write_char(match c {
'(' => ')',
'[' => ']',
'{' => '}',
_ => unreachable!(),
})
},
Self::Lambda(arg, body) => {
f.write_char('\\')?;
arg.fmt_i(f, i)?;
f.write_char('.')?;
body.fmt_i(f, i)
}
},
}
}
}
impl InternedDisplay for VecMatcher {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
fn fmt_i(
&self,
f: &mut std::fmt::Formatter<'_>,
i: &Interner,
) -> std::fmt::Result {
match self {
Self::Placeh { key, nonzero } => {
if *nonzero {f.write_char('.')?;};
if *nonzero {
f.write_char('.')?;
};
write!(f, "..${}", i.r(*key))
}
},
Self::Scan { left, sep, right, direction } => {
let arrow = match direction {
Side::Left => " <== ",
Side::Right => " ==> "
Side::Right => " ==> ",
};
write!(f, "Scan{{")?;
left.fmt_i(f, i)?;
@@ -136,19 +148,23 @@ impl InternedDisplay for VecMatcher {
f.write_str("|")?;
right.fmt_i(f, i)?;
write!(f, "}}")
}
},
}
}
}
impl InternedDisplay for AnyMatcher {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
fn fmt_i(
&self,
f: &mut std::fmt::Formatter<'_>,
i: &Interner,
) -> std::fmt::Result {
match self {
Self::Scalar(s) => {
write!(f, "(")?;
disp_scalv(s, f, i)?;
write!(f, ")")
}
},
Self::Vec { left, mid, right } => {
write!(f, "[")?;
disp_scalv(left, f, i)?;
@@ -157,7 +173,7 @@ impl InternedDisplay for AnyMatcher {
write!(f, "|")?;
disp_scalv(right, f, i)?;
write!(f, "]")
}
},
}
}
}
}

View File

@@ -2,41 +2,46 @@ use std::cmp::Ordering;
use itertools::Itertools;
use crate::unwrap_or;
use crate::ast::Expr;
use crate::rule::state::{State, StateEntry};
use super::scal_match::scalv_match;
use super::shared::VecMatcher;
use crate::ast::Expr;
use crate::rule::state::{State, StateEntry};
use crate::unwrap_or;
pub fn vec_match<'a>(matcher: &VecMatcher, seq: &'a [Expr])
-> Option<State<'a>> {
pub fn vec_match<'a>(
matcher: &VecMatcher,
seq: &'a [Expr],
) -> Option<State<'a>> {
match matcher {
VecMatcher::Placeh { key, nonzero } => {
if *nonzero && seq.len() == 0 {return None}
return Some(State::from([(*key, StateEntry::Vec(seq))]))
}
if *nonzero && seq.is_empty() {
return None;
}
return Some(State::from([(*key, StateEntry::Vec(seq))]));
},
VecMatcher::Scan { left, sep, right, direction } => {
if seq.len() < sep.len() {return None}
if seq.len() < sep.len() {
return None;
}
for lpos in direction.walk(0..=seq.len() - sep.len()) {
let rpos = lpos + sep.len();
let mut state = unwrap_or!(vec_match(left, &seq[..lpos]); continue);
state.extend(unwrap_or!(scalv_match(sep, &seq[lpos..rpos]); continue));
state.extend(unwrap_or!(vec_match(right, &seq[rpos..]); continue));
return Some(state)
return Some(state);
}
None
}
},
// XXX predict heap space usage and allocation count
VecMatcher::Middle { left, left_sep, mid, right_sep, right, key_order } => {
if seq.len() < left_sep.len() + right_sep.len() {return None}
if seq.len() < left_sep.len() + right_sep.len() {
return None;
}
// Valid locations for the left separator
let lposv = seq[..seq.len() - right_sep.len()]
.windows(left_sep.len())
.enumerate()
.filter_map(|(i, window)| {
scalv_match(left_sep, window).map(|s| (i, s))
})
.filter_map(|(i, window)| scalv_match(left_sep, window).map(|s| (i, s)))
.collect::<Vec<_>>();
// Valid locations for the right separator
let rposv = seq[left_sep.len()..]
@@ -47,7 +52,8 @@ pub fn vec_match<'a>(matcher: &VecMatcher, seq: &'a [Expr])
})
.collect::<Vec<_>>();
// Valid combinations of locations for the separators
let mut pos_pairs = lposv.into_iter()
let mut pos_pairs = lposv
.into_iter()
.cartesian_product(rposv.into_iter())
.filter(|((lpos, _), (rpos, _))| lpos + left_sep.len() <= *rpos)
.map(|((lpos, mut lstate), (rpos, rstate))| {
@@ -57,10 +63,10 @@ pub fn vec_match<'a>(matcher: &VecMatcher, seq: &'a [Expr])
.collect::<Vec<_>>();
// In descending order of size
pos_pairs.sort_by_key(|(l, r, _)| -((r - l) as i64));
let eql_clusters = pos_pairs.into_iter()
.group_by(|(al, ar, _)| ar - al);
let eql_clusters = pos_pairs.into_iter().group_by(|(al, ar, _)| ar - al);
for (_gap_size, cluster) in eql_clusters.into_iter() {
let best_candidate = cluster.into_iter()
let best_candidate = cluster
.into_iter()
.filter_map(|(lpos, rpos, mut state)| {
state.extend(vec_match(left, &seq[..lpos])?);
state.extend(vec_match(mid, &seq[lpos + left_sep.len()..rpos])?);
@@ -69,22 +75,28 @@ pub fn vec_match<'a>(matcher: &VecMatcher, seq: &'a [Expr])
})
.max_by(|a, b| {
for key in key_order {
let aslc = if let Some(StateEntry::Vec(s)) = a.get(key) {s}
else {panic!("key_order references scalar or missing")};
let bslc = if let Some(StateEntry::Vec(s)) = b.get(key) {s}
else {panic!("key_order references scalar or missing")};
let aslc = if let Some(StateEntry::Vec(s)) = a.get(key) {
s
} else {
panic!("key_order references scalar or missing")
};
let bslc = if let Some(StateEntry::Vec(s)) = b.get(key) {
s
} else {
panic!("key_order references scalar or missing")
};
match aslc.len().cmp(&bslc.len()) {
Ordering::Equal => (),
any => return any
any => return any,
}
}
Ordering::Equal
});
if let Some(state) = best_candidate {
return Some(state)
return Some(state);
}
}
None
}
},
}
}
}

View File

@@ -1,15 +1,12 @@
// mod executor;
mod rule_error;
mod repository;
mod prepare_rule;
mod matcher;
mod update_first_seq;
mod state;
mod matcher_second;
mod prepare_rule;
mod repository;
mod rule_error;
mod state;
mod update_first_seq;
mod vec_attrs;
// pub use rule::Rule;
pub use matcher_second::AnyMatcher;
pub use repository::{Repo, Repository};
pub use rule_error::RuleError;
pub use repository::{Repository, Repo};
pub use matcher_second::AnyMatcher;

View File

@@ -3,51 +3,46 @@ use std::rc::Rc;
use hashbrown::HashMap;
use itertools::Itertools;
use crate::representations::location::Location;
use crate::interner::{Token, Interner};
use crate::ast::{PHClass, Expr, Clause, Placeholder, Rule};
use super::RuleError;
use super::vec_attrs::vec_attrs;
use super::RuleError;
use crate::ast::{Clause, Expr, PHClass, Placeholder, Rule};
use crate::interner::{Interner, Tok};
use crate::representations::location::Location;
/// Ensure that the rule's source begins and ends with a vectorial without
/// changing its meaning
fn pad(mut rule: Rule, i: &Interner) -> Rule {
let class: PHClass = PHClass::Vec { nonzero: false, prio: 0 };
let empty_exprv: &[Expr] = &[];
let prefix_exprv: &[Expr] = &[Expr{
let empty: &[Expr] = &[];
let prefix: &[Expr] = &[Expr {
location: Location::Unknown,
value: Clause::Placeh(Placeholder{
name: i.i("::prefix"),
class
}),
value: Clause::Placeh(Placeholder { name: i.i("::prefix"), class }),
}];
let suffix_exprv: &[Expr] = &[Expr{
let suffix: &[Expr] = &[Expr {
location: Location::Unknown,
value: Clause::Placeh(Placeholder{
name: i.i("::suffix"),
class
}),
value: Clause::Placeh(Placeholder { name: i.i("::suffix"), class }),
}];
let rule_head = rule.source.first().expect("Src can never be empty!");
let head_explicit = vec_attrs(rule_head).is_some();
let prefix_explicit = vec_attrs(rule_head).is_some();
let rule_tail = rule.source.last().expect("Unreachable branch!");
let tail_explicit = vec_attrs(rule_tail).is_some();
let prefix_vec = if head_explicit {empty_exprv} else {prefix_exprv};
let suffix_vec = if tail_explicit {empty_exprv} else {suffix_exprv};
let suffix_explicit = vec_attrs(rule_tail).is_some();
let prefix_v = if prefix_explicit { empty } else { prefix };
let suffix_v = if suffix_explicit { empty } else { suffix };
rule.source = Rc::new(
prefix_vec.iter()
prefix_v
.iter()
.chain(rule.source.iter())
.chain(suffix_vec.iter())
.chain(suffix_v.iter())
.cloned()
.collect()
.collect(),
);
rule.target = Rc::new(
prefix_vec.iter()
prefix_v
.iter()
.chain(rule.target.iter())
.chain(suffix_vec.iter())
.chain(suffix_v.iter())
.cloned()
.collect()
.collect(),
);
rule
}
@@ -55,59 +50,69 @@ fn pad(mut rule: Rule, i: &Interner) -> Rule {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum PHType {
Scalar,
Vec{ nonzero: bool }
Vec { nonzero: bool },
}
impl From<PHClass> for PHType {
fn from(value: PHClass) -> Self {
match value {
PHClass::Scalar => Self::Scalar,
PHClass::Vec { nonzero, .. } => Self::Vec{ nonzero }
PHClass::Vec { nonzero, .. } => Self::Vec { nonzero },
}
}
}
fn check_rec_expr(
expr: &Expr,
types: &mut HashMap<Token<String>, PHType>,
in_template: bool
types: &mut HashMap<Tok<String>, PHType>,
in_template: bool,
) -> Result<(), RuleError> {
match &expr.value {
Clause::Name(_) | Clause::P(_) => Ok(()),
Clause::Placeh(Placeholder{ name, class }) => {
let typ = class.clone().into();
Clause::Placeh(Placeholder { name, class }) => {
let typ = (*class).into();
// in a template, the type must be known and identical
// outside template (in pattern) the type must be unknown
if let Some(known) = types.insert(*name, typ) {
if !in_template { Err(RuleError::Multiple(*name)) }
else if known != typ { Err(RuleError::TypeMismatch(*name)) }
else { Ok(()) }
} else if in_template { Err(RuleError::Missing(*name)) }
else { Ok(()) }
}
if !in_template {
Err(RuleError::Multiple(*name))
} else if known != typ {
Err(RuleError::TypeMismatch(*name))
} else {
Ok(())
}
} else if in_template {
Err(RuleError::Missing(*name))
} else {
Ok(())
}
},
Clause::Lambda(arg, body) => {
check_rec_expr(arg.as_ref(), types, in_template)?;
check_rec_exprv(&body, types, in_template)
}
Clause::S(_, body) => check_rec_exprv(&body, types, in_template)
check_rec_exprv(body, types, in_template)
},
Clause::S(_, body) => check_rec_exprv(body, types, in_template),
}
}
fn check_rec_exprv(
exprv: &[Expr],
types: &mut HashMap<Token<String>, PHType>,
in_template: bool
types: &mut HashMap<Tok<String>, PHType>,
in_template: bool,
) -> Result<(), RuleError> {
for (l, r) in exprv.iter().tuple_windows::<(_, _)>() {
check_rec_expr(l, types, in_template)?;
if !in_template { // in a pattern vectorials cannot follow each other
if !in_template {
// in a pattern vectorials cannot follow each other
if let (Some(ld), Some(rd)) = (vec_attrs(l), vec_attrs(r)) {
return Err(RuleError::VecNeighbors(ld.0, rd.0))
return Err(RuleError::VecNeighbors(ld.0, rd.0));
}
}
}
if let Some(e) = exprv.last() {
check_rec_expr(e, types, in_template)
} else { Ok(()) }
} else {
Ok(())
}
}
pub fn prepare_rule(rule: Rule, i: &Interner) -> Result<Rule, RuleError> {
@@ -117,4 +122,4 @@ pub fn prepare_rule(rule: Rule, i: &Interner) -> Result<Rule, RuleError> {
check_rec_exprv(&rule.target, &mut types, true)?;
// Padding
Ok(pad(rule, i))
}
}

View File

@@ -1,29 +1,31 @@
use std::fmt::{Debug, Write};
use std::format;
use std::rc::Rc;
use std::fmt::{Debug, Write};
use hashbrown::HashSet;
use ordered_float::NotNan;
use crate::interner::{Token, Interner, InternedDisplay};
use crate::utils::Substack;
use crate::ast::{Expr, Rule};
use super::state::apply_exprv;
use super::{update_first_seq, AnyMatcher};
use super::matcher::Matcher;
use super::prepare_rule::prepare_rule;
use super::RuleError;
use super::state::apply_exprv;
use super::{update_first_seq, AnyMatcher, RuleError};
use crate::ast::{Expr, Rule};
use crate::interner::{InternedDisplay, Interner, Sym};
use crate::utils::Substack;
#[derive(Debug)]
pub struct CachedRule<M: Matcher> {
matcher: M,
source: Rc<Vec<Expr>>,
template: Rc<Vec<Expr>>
template: Rc<Vec<Expr>>,
}
impl<M: InternedDisplay + Matcher> InternedDisplay for CachedRule<M> {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
fn fmt_i(
&self,
f: &mut std::fmt::Formatter<'_>,
i: &Interner,
) -> std::fmt::Result {
for item in self.source.iter() {
item.fmt_i(f, i)?;
f.write_char(' ')?;
@@ -33,20 +35,22 @@ impl<M: InternedDisplay + Matcher> InternedDisplay for CachedRule<M> {
}
}
/// Manages a priority queue of substitution rules and allows to apply them
/// Manages a priority queue of substitution rules and allows to apply
/// them
pub struct Repository<M: Matcher> {
cache: Vec<(CachedRule<M>, HashSet<Token<Vec<Token<String>>>>, NotNan<f64>)>
cache: Vec<(CachedRule<M>, HashSet<Sym>, NotNan<f64>)>,
}
impl<M: Matcher> Repository<M> {
pub fn new(mut rules: Vec<Rule>, i: &Interner)
-> Result<Self, (Rule, RuleError)>
{
impl<M: Matcher> Repository<M> {
pub fn new(
mut rules: Vec<Rule>,
i: &Interner,
) -> Result<Self, (Rule, RuleError)> {
rules.sort_by_key(|r| -r.prio);
let cache = rules.into_iter()
let cache = rules
.into_iter()
.map(|r| {
let prio = r.prio;
let rule = prepare_rule(r.clone(), i)
.map_err(|e| (r, e))?;
let rule = prepare_rule(r.clone(), i).map_err(|e| (r, e))?;
let mut glossary = HashSet::new();
for e in rule.source.iter() {
e.visit_names(Substack::Bottom, &mut |op| {
@@ -54,30 +58,32 @@ impl<M: Matcher> Repository<M> {
})
}
let matcher = M::new(rule.source.clone());
let prep = CachedRule{
matcher,
source: rule.source,
template: rule.target
};
let prep =
CachedRule { matcher, source: rule.source, template: rule.target };
Ok((prep, glossary, prio))
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self{cache})
Ok(Self { cache })
}
/// Attempt to run each rule in priority order once
pub fn step(&self, code: &Expr) -> Option<Expr> {
let mut glossary = HashSet::new();
code.visit_names(Substack::Bottom, &mut |op| { glossary.insert(op); });
// println!("Glossary for code: {:?}", print_nname_seq(glossary.iter(), i));
code.visit_names(Substack::Bottom, &mut |op| {
glossary.insert(op);
});
for (rule, deps, _) in self.cache.iter() {
if !deps.is_subset(&glossary) { continue; }
if !deps.is_subset(&glossary) {
continue;
}
let product = update_first_seq::expr(code, &mut |exprv| {
let state = rule.matcher.apply(exprv.as_slice())?;
let result = apply_exprv(&rule.template, &state);
Some(Rc::new(result))
});
if let Some(newcode) = product {return Some(newcode)}
if let Some(newcode) = product {
return Some(newcode);
}
}
None
}
@@ -86,33 +92,43 @@ impl<M: Matcher> Repository<M> {
/// rules match. WARNING: this function might not terminate
#[allow(unused)]
pub fn pass(&self, code: &Expr) -> Option<Expr> {
todo!()
// if let Some(mut processed) = self.step(code) {
// while let Some(out) = self.step(&processed) {
// processed = out
// }
// Some(processed)
// } else {None}
if let Some(mut processed) = self.step(code) {
while let Some(out) = self.step(&processed) {
processed = out
}
Some(processed)
} else {
None
}
}
/// Attempt to run each rule in priority order `limit` times. Returns the final
/// tree and the number of iterations left to the limit.
/// Attempt to run each rule in priority order `limit` times. Returns
/// the final tree and the number of iterations left to the limit.
#[allow(unused)]
pub fn long_step(&self, code: &Expr, mut limit: usize)
-> Result<(Expr, usize), RuleError>
{
todo!()
// if limit == 0 {return Ok((code.clone(), 0))}
// if let Some(mut processed) = self.step(code) {
// limit -= 1;
// if limit == 0 {return Ok((processed.clone(), 0))}
// while let Some(out) = self.step(&processed) {
// limit -= 1;
// if limit == 0 { return Ok((out, 0)) }
// processed = out;
// }
// Ok((processed, limit))
// } else {Ok((code.clone(), limit))}
pub fn long_step(
&self,
code: &Expr,
mut limit: usize,
) -> Result<(Expr, usize), RuleError> {
if limit == 0 {
return Ok((code.clone(), 0));
}
if let Some(mut processed) = self.step(code) {
limit -= 1;
if limit == 0 {
return Ok((processed, 0));
}
while let Some(out) = self.step(&processed) {
limit -= 1;
if limit == 0 {
return Ok((out, 0));
}
processed = out;
}
Ok((processed, limit))
} else {
Ok((code.clone(), limit))
}
}
}
@@ -122,7 +138,7 @@ impl<M: Debug + Matcher> Debug for Repository<M> {
writeln!(f, "{rule:?}")?
}
Ok(())
}
}
}
fn fmt_hex(num: f64) -> String {
@@ -132,7 +148,11 @@ fn fmt_hex(num: f64) -> String {
}
impl<M: InternedDisplay + Matcher> InternedDisplay for Repository<M> {
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
fn fmt_i(
&self,
f: &mut std::fmt::Formatter<'_>,
i: &Interner,
) -> std::fmt::Result {
writeln!(f, "Repository[")?;
for (item, _, p) in self.cache.iter() {
write!(f, "\t{}", fmt_hex(f64::from(*p)))?;
@@ -143,4 +163,4 @@ impl<M: InternedDisplay + Matcher> InternedDisplay for Repository<M> {
}
}
pub type Repo = Repository<AnyMatcher>;
pub type Repo = Repository<AnyMatcher>;

View File

@@ -1,36 +1,35 @@
use std::fmt;
use crate::interner::{Token, InternedDisplay, Interner};
use crate::interner::{InternedDisplay, Interner, Tok};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleError {
Missing(Token<String>),
TypeMismatch(Token<String>),
Missing(Tok<String>),
TypeMismatch(Tok<String>),
/// Multiple occurences of a placeholder in a pattern are no longer
/// supported.
Multiple(Token<String>),
VecNeighbors(Token<String>, Token<String>),
Multiple(Tok<String>),
VecNeighbors(Tok<String>, Tok<String>),
}
impl InternedDisplay for RuleError {
fn fmt_i(&self, f: &mut fmt::Formatter<'_>, i: &Interner) -> fmt::Result {
match *self {
Self::Missing(key) => write!(f,
"Key {:?} not in match pattern",
i.r(key)
),
Self::TypeMismatch(key) => write!(f,
Self::Missing(key) =>
write!(f, "Key {:?} not in match pattern", i.r(key)),
Self::TypeMismatch(key) => write!(
f,
"Key {:?} used inconsistently with and without ellipsis",
i.r(key)
),
Self::Multiple(key) => write!(f,
"Key {:?} appears multiple times in match pattern",
i.r(key)
),
Self::VecNeighbors(left, right) => write!(f,
Self::Multiple(key) =>
write!(f, "Key {:?} appears multiple times in match pattern", i.r(key)),
Self::VecNeighbors(left, right) => write!(
f,
"Keys {:?} and {:?} are two vectorials right next to each other",
i.r(left), i.r(right)
)
i.r(left),
i.r(right)
),
}
}
}
}

View File

@@ -3,49 +3,54 @@ use std::rc::Rc;
use hashbrown::HashMap;
use itertools::Itertools;
use crate::interner::Token;
use crate::ast::{Expr, Clause, Placeholder, PHClass};
use crate::ast::{Clause, Expr, PHClass, Placeholder};
use crate::interner::Tok;
use crate::unwrap_or;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StateEntry<'a> {
Vec(&'a[Expr]),
Scalar(&'a Expr)
Vec(&'a [Expr]),
Scalar(&'a Expr),
}
pub type State<'a> = HashMap<Token<String>, StateEntry<'a>>;
pub type State<'a> = HashMap<Tok<String>, StateEntry<'a>>;
pub fn apply_exprv(template: &[Expr], state: &State) -> Vec<Expr> {
template.iter()
template
.iter()
.map(|e| apply_expr(e, state))
.flat_map(Vec::into_iter)
.collect()
}
pub fn apply_expr(template: &Expr, state: &State) -> Vec<Expr> {
let Expr{ location, value } = template;
let Expr { location, value } = template;
match value {
Clause::P(_) | Clause::Name(_) => vec![template.clone()],
Clause::S(c, body) => vec![Expr{
Clause::S(c, body) => vec![Expr {
location: location.clone(),
value: Clause::S(*c, Rc::new(apply_exprv(body.as_slice(), state)))
value: Clause::S(*c, Rc::new(apply_exprv(body.as_slice(), state))),
}],
Clause::Placeh(Placeholder{ name, class }) => {
let value = if let Some(&v) = state.get(name) {v}
else {panic!("Placeholder does not have a value in state")};
Clause::Placeh(Placeholder { name, class }) => {
let value = *unwrap_or!(state.get(name);
panic!("Placeholder does not have a value in state")
);
match (class, value) {
(PHClass::Scalar, StateEntry::Scalar(item)) => vec![item.clone()],
(PHClass::Vec{..}, StateEntry::Vec(chunk)) => chunk.to_vec(),
_ => panic!("Type mismatch between template and state")
(PHClass::Vec { .. }, StateEntry::Vec(chunk)) => chunk.to_vec(),
_ => panic!("Type mismatch between template and state"),
}
}
Clause::Lambda(arg, body) => vec![Expr{
},
Clause::Lambda(arg, body) => vec![Expr {
location: location.clone(),
value: Clause::Lambda(
Rc::new(apply_expr(arg.as_ref(), state).into_iter()
.exactly_one()
.expect("Lambda arguments can only ever be scalar")
Rc::new(
apply_expr(arg.as_ref(), state)
.into_iter()
.exactly_one()
.expect("Lambda arguments can only ever be scalar"),
),
Rc::new(apply_exprv(&body[..], state))
)
}]
Rc::new(apply_exprv(&body[..], state)),
),
}],
}
}

View File

@@ -1,39 +1,42 @@
use std::rc::Rc;
use crate::ast::{Clause, Expr};
use crate::utils::replace_first;
use crate::ast::{Expr, Clause};
/// 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
pub fn exprv<
F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>>
>(input: Rc<Vec<Expr>>, pred: &mut F) -> Option<Rc<Vec<Expr>>> {
if let o@Some(_) = pred(input.clone()) {return o}
pub fn exprv<F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>>>(
input: Rc<Vec<Expr>>,
pred: &mut F,
) -> Option<Rc<Vec<Expr>>> {
if let Some(v) = pred(input.clone()) {
return Some(v);
}
replace_first(input.as_ref(), |ex| expr(ex, pred))
.map(|i| Rc::new(i.collect()))
}
pub fn expr<
F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>>
>(input: &Expr, pred: &mut F) -> Option<Expr> {
if let Some(value) = clause(&input.value, pred) {
Some(Expr{ value, location: input.location.clone() })
} else {None}
pub fn expr<F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>>>(
input: &Expr,
pred: &mut F,
) -> Option<Expr> {
clause(&input.value, pred)
.map(|value| Expr { value, location: input.location.clone() })
}
pub fn clause<
F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>>
>(c: &Clause, pred: &mut F) -> Option<Clause> {
pub fn clause<F: FnMut(Rc<Vec<Expr>>) -> Option<Rc<Vec<Expr>>>>(
c: &Clause,
pred: &mut F,
) -> Option<Clause> {
match c {
Clause::P(_) | Clause::Placeh {..} | Clause::Name {..} => None,
Clause::Lambda(arg, body) => {
Clause::P(_) | Clause::Placeh { .. } | Clause::Name { .. } => None,
Clause::Lambda(arg, body) =>
if let Some(arg) = expr(arg.as_ref(), pred) {
Some(Clause::Lambda(Rc::new(arg), body.clone()))
} else if let Some(body) = exprv(body.clone(), pred) {
Some(Clause::Lambda(arg.clone(), body))
} else {None}
}
} else {
exprv(body.clone(), pred).map(|body| Clause::Lambda(arg.clone(), body))
},
Clause::S(c, body) => Some(Clause::S(*c, exprv(body.clone(), pred)?)),
}
}
}

View File

@@ -1,10 +1,16 @@
use crate::interner::Token;
use crate::ast::{Expr, PHClass, Placeholder, Clause};
use crate::ast::{Clause, Expr, PHClass, Placeholder};
use crate::interner::Tok;
/// Returns the name, priority and nonzero of the expression if it is
/// a vectorial placeholder
pub fn vec_attrs(expr: &Expr) -> Option<(Token<String>, u64, bool)> {
if let Clause::Placeh(
Placeholder{ class: PHClass::Vec{ prio, nonzero }, name }
) = expr.value {Some((name, prio, nonzero))} else {None}
}
pub fn vec_attrs(expr: &Expr) -> Option<(Tok<String>, u64, bool)> {
if let Clause::Placeh(Placeholder {
class: PHClass::Vec { prio, nonzero },
name,
}) = expr.value
{
Some((name, prio, nonzero))
} else {
None
}
}