forked from Orchid/orchid
Preparation for sharing
- rustfmt - clippy - comments - README
This commit is contained in:
@@ -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>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, "]")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user