Files
orchid/src/rule/repository.rs
Lawrence Bethlenfalvy f77e4fd90a October commit
- custom parser support and infra
- type-tagging and traits (untested)
- match expressions
2023-10-24 22:17:37 +01:00

174 lines
4.9 KiB
Rust

use std::fmt::{Debug, Display};
use std::rc::Rc;
use hashbrown::HashSet;
use itertools::Itertools;
use ordered_float::NotNan;
use super::matcher::{Matcher, RuleExpr};
use super::prepare_rule::prepare_rule;
use super::state::apply_exprv;
use super::{update_first_seq, RuleError, VectreeMatcher};
use crate::ast::Rule;
use crate::interner::Interner;
use crate::parse::print_nat16;
use crate::Sym;
#[derive(Debug)]
pub struct CachedRule<M: Matcher> {
matcher: M,
pattern: Vec<RuleExpr>,
pat_glossary: HashSet<Sym>,
template: Vec<RuleExpr>,
save_location: HashSet<Sym>,
}
impl<M: Display + Matcher> Display for CachedRule<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let patterns = self.pattern.iter().join(" ");
write!(
f,
"{patterns} is matched by {} and generates {}",
self.matcher,
self.template.iter().map(|e| e.to_string()).join(" ")
)
}
}
/// Substitution rule scheduler
///
/// Manages a priority queue of rules and offers functions to apply them. The
/// rules are stored in an optimized structure but the repository is generic
/// over the implementation of this optimized form.
///
/// If you don't know what to put in the generic parameter, use [Repo]
pub struct Repository<M: Matcher> {
cache: Vec<(CachedRule<M>, NotNan<f64>)>,
}
impl<M: Matcher> Repository<M> {
/// Build a new repository to hold the given set of rules
pub fn new(
mut rules: Vec<Rule<Sym>>,
i: &Interner,
) -> Result<Self, (Rule<Sym>, RuleError)> {
rules.sort_by_key(|r| -r.prio);
let cache = rules
.into_iter()
.map(|r| {
let Rule { pattern, prio, template } =
prepare_rule(r.clone(), i).map_err(|e| (r, e))?;
let mut pat_glossary = HashSet::new();
pat_glossary.extend(
pattern.iter().flat_map(|e| e.value.collect_names().into_iter()),
);
let mut tpl_glossary = HashSet::new();
tpl_glossary.extend(
template.iter().flat_map(|e| e.value.collect_names().into_iter()),
);
let save_location =
pat_glossary.intersection(&tpl_glossary).cloned().collect();
let matcher = M::new(Rc::new(pattern.clone()));
let prep = CachedRule {
matcher,
pattern,
template,
pat_glossary,
save_location,
};
Ok((prep, prio))
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self { cache })
}
/// Attempt to run each rule in priority order once
#[must_use]
pub fn step(&self, code: &RuleExpr) -> Option<RuleExpr> {
let glossary = code.value.collect_names();
for (rule, _) in self.cache.iter() {
if !rule.pat_glossary.is_subset(&glossary) {
continue;
}
let product = update_first_seq::expr(code, &mut |exprv| {
let save_loc = |n| rule.save_location.contains(&n);
let state = rule.matcher.apply(exprv.as_slice(), &save_loc)?;
let result = apply_exprv(&rule.template, &state);
Some(Rc::new(result))
});
if let Some(newcode) = product {
return Some(newcode);
}
}
None
}
/// Keep running the matching rule with the highest priority until no
/// rules match. WARNING: this function might not terminate
#[must_use]
pub fn pass(&self, code: &RuleExpr) -> Option<RuleExpr> {
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.
#[must_use]
pub fn long_step(
&self,
code: &RuleExpr,
mut limit: usize,
) -> (RuleExpr, usize) {
if limit == 0 {
return (code.clone(), 0);
}
if let Some(mut processed) = self.step(code) {
limit -= 1;
if limit == 0 {
return (processed, 0);
}
while let Some(out) = self.step(&processed) {
limit -= 1;
if limit == 0 {
return (out, 0);
}
processed = out;
}
(processed, limit)
} else {
(code.clone(), limit)
}
}
}
impl<M: Debug + Matcher> Debug for Repository<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for rule in self.cache.iter() {
writeln!(f, "{rule:?}")?
}
Ok(())
}
}
impl<M: Display + Matcher> Display for Repository<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Repository[")?;
for (rule, p) in self.cache.iter() {
let prio = print_nat16(*p);
let deps =
rule.pat_glossary.iter().map(|t| t.extern_vec().join("::")).join(", ");
writeln!(f, " priority: {prio}\tdependencies: [{deps}]")?;
writeln!(f, " {rule}")?;
}
write!(f, "]")
}
}
/// Repository with the default matcher implementation
pub type Repo = Repository<VectreeMatcher>;