use crate::kind::{LitRef, NodeKind};
use crate::lang::Lang;
#[derive(Debug)]
pub enum Pat {
Any,
Bind(&'static str),
Kind(NodeKind, &'static [Pat]),
LitBool(bool),
LitInt(i64),
}
pub type Binds<L> = Vec<(&'static str, <L as Lang>::Id)>;
pub fn match_pat<L: Lang>(lang: &L, id: L::Id, pat: &Pat, binds: &mut Binds<L>) -> bool {
match pat {
Pat::Any => true,
Pat::Bind(name) => {
binds.push((name, id));
true
}
Pat::LitBool(v) => matches!(lang.literal(id), Some(LitRef::Bool(b)) if b == *v),
Pat::LitInt(v) => lang.literal(id).and_then(|l| l.as_int()) == Some(*v),
Pat::Kind(kind, pats) => {
if lang.kind(id) != *kind {
return false;
}
let children = lang.children(id);
if children.len() != pats.len() {
return false;
}
children
.iter()
.zip(pats.iter())
.all(|(&c, p)| match_pat(lang, c, p, binds))
}
}
}
pub fn matches<L: Lang>(lang: &L, id: L::Id, pat: &Pat) -> Option<Binds<L>> {
let mut binds: Binds<L> = Vec::new();
if match_pat(lang, id, pat, &mut binds) {
Some(binds)
} else {
None
}
}
impl Pat {
pub fn bind(name: &'static str) -> Pat {
Pat::Bind(name)
}
pub fn kind(k: NodeKind, pats: &'static [Pat]) -> Pat {
Pat::Kind(k, pats)
}
}
#[macro_export]
macro_rules! pat {
($name:ident, $($tok:tt)*) => {
static $name: $crate::pattern::Pat = $crate::pattern::__pat!($($tok)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __pat {
(Any) => { $crate::pattern::Pat::Any };
(Bind($n:literal)) => { $crate::pattern::Pat::Bind($n) };
(LitBool($v:literal)) => { $crate::pattern::Pat::LitBool($v) };
(LitInt($v:literal)) => { $crate::pattern::Pat::LitInt($v) };
(Kind($k:expr, [$($p:tt),* $(,)?])) => {
$crate::pattern::Pat::Kind($k, &[$($crate::__pat!($p)),*])
};
}