use std::fmt;
use crate::ast::{Rule, Term, TermKind};
use crate::error::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
enum Step {
App { head: String, arity: usize },
Int(i128),
Bind(String),
Same(usize),
}
#[derive(Debug, Default)]
pub(crate) struct Node {
pub(crate) heads: Vec<(String, usize, usize)>,
pub(crate) ints: Vec<(i128, usize)>,
pub(crate) same: Vec<(usize, usize)>,
pub(crate) wildcard: Option<(String, usize)>,
pub(crate) accept: Option<usize>,
}
impl Node {
fn branches(&self) -> impl Iterator<Item = (Shown<'_>, usize)> {
let heads = self.heads.iter().map(|(head, arity, next)| (Shown::App(head, *arity), *next));
let ints = self.ints.iter().map(|&(value, next)| (Shown::Int(value), next));
let same = self.same.iter().map(|&(index, next)| (Shown::Same(index), next));
heads.chain(ints).chain(same)
}
fn kinds(&self) -> usize {
usize::from(!self.heads.is_empty())
+ usize::from(!self.ints.is_empty())
+ usize::from(!self.same.is_empty())
}
}
enum Shown<'a> {
App(&'a str, usize),
Int(i128),
Same(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Shape {
pub nodes: usize,
pub widest: usize,
pub search: usize,
pub mixed: usize,
}
#[derive(Debug)]
pub struct Matcher {
pub(crate) nodes: Vec<Node>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Match<'t> {
pub rule: usize,
pub bindings: Vec<(String, &'t Term)>,
}
impl<'t> Match<'t> {
#[must_use]
pub fn get(&self, name: &str) -> Option<&'t Term> {
self.bindings.iter().find(|(bound, _)| bound == name).map(|(_, term)| *term)
}
}
impl Matcher {
pub fn build(path: &str, rules: &[Rule]) -> Result<Matcher, Vec<Error>> {
let mut matcher = Matcher { nodes: vec![Node::default()] };
let mut errors = Vec::new();
for (index, rule) in rules.iter().enumerate() {
let mut at = 0;
for step in flatten(&rule.pattern) {
at = matcher.follow(at, step);
}
match matcher.nodes[at].accept {
Some(first) => errors.push(Error {
path: path.to_owned(),
line: rule.line,
column: rule.column,
message: format!(
"this rule can never fire, because the rule on line {} matches everything it does",
rules[first].line
),
}),
None => matcher.nodes[at].accept = Some(index),
}
}
if !errors.is_empty() {
return Err(errors);
}
matcher.sort();
Ok(matcher)
}
fn follow(&mut self, at: usize, step: Step) -> usize {
match step {
Step::App { head, arity } => {
let found = self.nodes[at]
.heads
.iter()
.find(|(have, count, _)| *have == head && *count == arity);
if let Some(&(_, _, next)) = found {
return next;
}
let next = self.push();
self.nodes[at].heads.push((head, arity, next));
next
}
Step::Int(value) => {
if let Some(&(_, next)) =
self.nodes[at].ints.iter().find(|(have, _)| *have == value)
{
return next;
}
let next = self.push();
self.nodes[at].ints.push((value, next));
next
}
Step::Same(index) => {
if let Some(&(_, next)) =
self.nodes[at].same.iter().find(|(have, _)| *have == index)
{
return next;
}
let next = self.push();
self.nodes[at].same.push((index, next));
next
}
Step::Bind(name) => {
if let Some((_, next)) = &self.nodes[at].wildcard {
return *next;
}
let next = self.push();
self.nodes[at].wildcard = Some((name, next));
next
}
}
}
fn sort(&mut self) {
for node in &mut self.nodes {
node.heads.sort_by(|(head, arity, _), (other, count, _)| {
head.cmp(other).then(arity.cmp(count))
});
node.ints.sort_by_key(|&(value, _)| value);
}
}
fn push(&mut self) -> usize {
self.nodes.push(Node::default());
self.nodes.len() - 1
}
#[must_use]
pub fn find<'t>(&self, term: &'t Term) -> Option<Match<'t>> {
let mut bindings = Vec::new();
let rule = self.run(0, vec![term], &mut bindings)?;
Some(Match { rule, bindings })
}
fn run<'t>(
&self,
at: usize,
mut left: Vec<&'t Term>,
bindings: &mut Vec<(String, &'t Term)>,
) -> Option<usize> {
let Some(subject) = left.pop() else {
return self.nodes[at].accept;
};
let node = &self.nodes[at];
let mut taken: Vec<usize> = Vec::new();
if let TermKind::App { head, args } = &subject.kind {
let found = node
.heads
.binary_search_by(|(have, count, _)| {
have.as_str().cmp(head.as_str()).then(count.cmp(&args.len()))
})
.ok();
taken.extend(found.map(|at| node.heads[at].2));
}
if let TermKind::Int(value) = &subject.kind {
let found = node.ints.binary_search_by(|(have, _)| have.cmp(value)).ok();
taken.extend(found.map(|at| node.ints[at].1));
}
for &(index, next) in &node.same {
if bindings.get(index).is_some_and(|(_, bound)| alike(bound, subject)) {
taken.push(next);
}
}
for next in taken {
let mut deeper = left.clone();
if let TermKind::App { args, .. } = &subject.kind {
deeper.extend(args.iter().rev());
}
let depth = bindings.len();
if let Some(rule) = self.run(next, deeper, bindings) {
return Some(rule);
}
bindings.truncate(depth);
}
let (name, next) = node.wildcard.as_ref()?;
let depth = bindings.len();
bindings.push((name.clone(), subject));
if let Some(rule) = self.run(*next, left, bindings) {
return Some(rule);
}
bindings.truncate(depth);
None
}
#[must_use]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.nodes.len() <= 1
}
#[must_use]
pub fn shape(&self) -> Shape {
let widest = self.nodes.iter().map(|node| node.branches().count()).max().unwrap_or(0);
Shape {
nodes: self.nodes.len(),
widest,
search: usize::try_from(widest.next_power_of_two().trailing_zeros()).unwrap_or(0),
mixed: self.nodes.iter().filter(|node| node.kinds() > 1).count(),
}
}
}
fn alike(left: &Term, right: &Term) -> bool {
match (&left.kind, &right.kind) {
(TermKind::Var(a), TermKind::Var(b)) => a == b,
(TermKind::Int(a), TermKind::Int(b)) => a == b,
(TermKind::App { head: a, args: xs }, TermKind::App { head: b, args: ys }) => {
a == b && xs.len() == ys.len() && xs.iter().zip(ys).all(|(x, y)| alike(x, y))
}
_ => false,
}
}
fn flatten(pattern: &Term) -> Vec<Step> {
let mut out = Vec::new();
let mut bound: Vec<&str> = Vec::new();
push_steps(pattern, &mut bound, &mut out);
out
}
fn push_steps<'t>(term: &'t Term, bound: &mut Vec<&'t str>, out: &mut Vec<Step>) {
match &term.kind {
TermKind::Var(name) => match bound.iter().position(|have| *have == name.as_str()) {
Some(index) => out.push(Step::Same(index)),
None => {
bound.push(name.as_str());
out.push(Step::Bind(name.clone()));
}
},
TermKind::Int(value) => out.push(Step::Int(*value)),
TermKind::App { head, args } => {
out.push(Step::App { head: head.clone(), arity: args.len() });
for arg in args {
push_steps(arg, bound, out);
}
}
}
}
impl fmt::Display for Matcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.show(f, 0, 0)
}
}
impl Matcher {
fn show(&self, f: &mut fmt::Formatter<'_>, at: usize, depth: usize) -> fmt::Result {
let pad = " ".repeat(depth);
let node = &self.nodes[at];
if let Some(rule) = node.accept {
writeln!(f, "{pad}=> rule {rule}")?;
}
for (branch, next) in node.branches() {
match branch {
Shown::App(head, arity) => writeln!(f, "{pad}{head}/{arity}")?,
Shown::Int(value) => writeln!(f, "{pad}{value}")?,
Shown::Same(index) => writeln!(f, "{pad}same as binding {index}")?,
}
self.show(f, next, depth + 1)?;
}
if let Some((name, next)) = &node.wildcard {
writeln!(f, "{pad}bind {name}")?;
self.show(f, *next, depth + 1)?;
}
Ok(())
}
}