use std::collections::HashMap;
use super::fragment::Fragment;
use super::rules::{Matcher, Rep};
use super::tree::Tree;
use yog_analysis::Report;
#[derive(Clone)]
pub(crate) enum Bound {
One(Vec<Tree>, Fragment),
Many(Vec<Bound>),
}
pub(crate) type Bindings = HashMap<String, Bound>;
pub(crate) fn matched(ms: &[Matcher], trees: &[Tree], report: &Report<'_>) -> Option<Bindings> {
let mut b = Bindings::new();
(seq(ms, trees, &mut b, report)? == trees.len()).then_some(b)
}
fn seq(ms: &[Matcher], trees: &[Tree], b: &mut Bindings, report: &Report<'_>) -> Option<usize> {
let mut at = 0;
for m in ms {
at += one(m, &trees[at..], b, report)?;
}
Some(at)
}
fn one(m: &Matcher, trees: &[Tree], b: &mut Bindings, report: &Report<'_>) -> Option<usize> {
match m {
Matcher::Token(t) => {
matches!(trees.first(), Some(Tree::Leaf(l)) if l.tok == *t).then_some(1)
}
Matcher::Group { open, inner } => match trees.first() {
Some(Tree::Group {
open: o,
inner: got,
..
}) if o.tok == *open => (seq(inner, got, b, report)? == got.len()).then_some(1),
_ => None,
},
Matcher::Fragment { name, kind } => {
let n = kind.take(trees, report)?;
b.insert(name.clone(), Bound::One(trees[..n].to_vec(), *kind));
Some(n)
}
Matcher::Repeat { .. } => repeat(m, trees, b, report),
}
}
fn repeat(m: &Matcher, trees: &[Tree], b: &mut Bindings, report: &Report<'_>) -> Option<usize> {
let Matcher::Repeat { inner, sep, op } = m else {
return None;
};
let (sep, op) = (sep.as_ref(), *op);
let mut takes: Vec<Bindings> = Vec::new();
let mut at = 0;
loop {
let gap = match (takes.is_empty(), sep, trees.get(at)) {
(true, _, _) | (_, None, _) => 0,
(false, Some(s), Some(Tree::Leaf(l))) if l.tok == *s => 1,
_ => break,
};
let mut take = Bindings::new();
match seq(inner, &trees[at + gap..], &mut take, report) {
Some(n) if n > 0 => {
takes.push(take);
at += gap + n;
}
_ => break,
}
if op == Rep::Maybe {
break;
}
}
if op == Rep::Some && takes.is_empty() {
return None;
}
for name in names(inner) {
let all = takes.iter().filter_map(|t| t.get(&name).cloned()).collect();
b.insert(name, Bound::Many(all));
}
Some(at)
}
fn names(ms: &[Matcher]) -> Vec<String> {
let mut out = Vec::new();
for m in ms {
match m {
Matcher::Fragment { name, .. } => out.push(name.clone()),
Matcher::Group { inner, .. } | Matcher::Repeat { inner, .. } => {
out.extend(names(inner))
}
Matcher::Token(_) => {}
}
}
out
}