1use crate::kind::{LitRef, NodeKind};
7use crate::lang::Lang;
8
9#[derive(Debug)]
10pub enum Pat {
11 Any,
13 Bind(&'static str),
15 Kind(NodeKind, &'static [Pat]),
17 LitBool(bool),
19 LitInt(i64),
21}
22
23pub type Binds<L> = Vec<(&'static str, <L as Lang>::Id)>;
24
25pub fn match_pat<L: Lang>(lang: &L, id: L::Id, pat: &Pat, binds: &mut Binds<L>) -> bool {
26 match pat {
27 Pat::Any => true,
28 Pat::Bind(name) => {
29 binds.push((name, id));
30 true
31 }
32 Pat::LitBool(v) => matches!(lang.literal(id), Some(LitRef::Bool(b)) if b == *v),
33 Pat::LitInt(v) => lang.literal(id).and_then(|l| l.as_int()) == Some(*v),
34 Pat::Kind(kind, pats) => {
35 if lang.kind(id) != *kind {
36 return false;
37 }
38 let children = lang.children(id);
39 if children.len() != pats.len() {
40 return false;
41 }
42 children
43 .iter()
44 .zip(pats.iter())
45 .all(|(&c, p)| match_pat(lang, c, p, binds))
46 }
47 }
48}
49
50pub fn matches<L: Lang>(lang: &L, id: L::Id, pat: &Pat) -> Option<Binds<L>> {
52 let mut binds: Binds<L> = Vec::new();
53 if match_pat(lang, id, pat, &mut binds) {
54 Some(binds)
55 } else {
56 None
57 }
58}
59
60impl Pat {
61 pub fn bind(name: &'static str) -> Pat {
62 Pat::Bind(name)
63 }
64 pub fn kind(k: NodeKind, pats: &'static [Pat]) -> Pat {
65 Pat::Kind(k, pats)
66 }
67}
68
69#[macro_export]
70macro_rules! pat {
73 ($name:ident, $($tok:tt)*) => {
74 static $name: $crate::pattern::Pat = $crate::pattern::__pat!($($tok)*);
75 };
76}
77
78#[doc(hidden)]
79#[macro_export]
80macro_rules! __pat {
81 (Any) => { $crate::pattern::Pat::Any };
82 (Bind($n:literal)) => { $crate::pattern::Pat::Bind($n) };
83 (LitBool($v:literal)) => { $crate::pattern::Pat::LitBool($v) };
84 (LitInt($v:literal)) => { $crate::pattern::Pat::LitInt($v) };
85 (Kind($k:expr, [$($p:tt),* $(,)?])) => {
86 $crate::pattern::Pat::Kind($k, &[$($crate::__pat!($p)),*])
87 };
88}