plotnik_lib/ir/
matcher.rs

1//! Node matchers for transition graph.
2//!
3//! Matchers are purely for node matching - navigation is handled by `Nav`.
4
5use super::{NodeFieldId, NodeTypeId, Slice};
6
7/// Discriminant for matcher variants.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum MatcherKind {
10    Epsilon,
11    Node,
12    Anonymous,
13    Wildcard,
14}
15
16/// Matcher determines what node satisfies a transition.
17///
18/// Navigation (descend/ascend) is handled by `Nav`, not matchers.
19#[repr(C, u32)]
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Matcher {
22    /// Matches without consuming input. Used for control flow transitions.
23    Epsilon,
24
25    /// Matches a named node by kind, optionally constrained by field.
26    Node {
27        kind: NodeTypeId,
28        field: Option<NodeFieldId>,
29        negated_fields: Slice<NodeFieldId>,
30    },
31
32    /// Matches an anonymous node by kind, optionally constrained by field.
33    Anonymous {
34        kind: NodeTypeId,
35        field: Option<NodeFieldId>,
36        negated_fields: Slice<NodeFieldId>,
37    },
38
39    /// Matches any node (named or anonymous).
40    Wildcard,
41}
42
43impl Matcher {
44    /// Returns true if this matcher consumes a node.
45    #[inline]
46    pub fn consumes_node(&self) -> bool {
47        !matches!(self, Matcher::Epsilon)
48    }
49
50    /// Returns the discriminant kind.
51    #[inline]
52    pub fn kind(&self) -> MatcherKind {
53        match self {
54            Matcher::Epsilon => MatcherKind::Epsilon,
55            Matcher::Node { .. } => MatcherKind::Node,
56            Matcher::Anonymous { .. } => MatcherKind::Anonymous,
57            Matcher::Wildcard => MatcherKind::Wildcard,
58        }
59    }
60
61    /// Returns the node type ID for Node/Anonymous variants, `None` otherwise.
62    #[inline]
63    pub fn node_kind(&self) -> Option<NodeTypeId> {
64        match self {
65            Matcher::Node { kind, .. } | Matcher::Anonymous { kind, .. } => Some(*kind),
66            _ => None,
67        }
68    }
69
70    /// Returns the field constraint, if any.
71    #[inline]
72    pub fn field(&self) -> Option<NodeFieldId> {
73        match self {
74            Matcher::Node { field, .. } | Matcher::Anonymous { field, .. } => *field,
75            _ => None,
76        }
77    }
78
79    /// Returns the negated fields slice. Empty for Epsilon/Wildcard.
80    #[inline]
81    pub fn negated_fields(&self) -> Slice<NodeFieldId> {
82        match self {
83            Matcher::Node { negated_fields, .. } | Matcher::Anonymous { negated_fields, .. } => {
84                *negated_fields
85            }
86            _ => Slice::empty(),
87        }
88    }
89}