ast_grep_core/match_tree/
strictness.rs1use crate::matcher::{kind_utils, PatternNode};
2use crate::meta_var::MetaVariable;
3use crate::node::Node;
4use crate::Doc;
5use std::iter::Peekable;
6use std::str::FromStr;
7
8#[derive(Clone)]
9pub enum MatchStrictness {
10 Cst, Smart, Ast, Relaxed, Signature, }
16
17pub(crate) enum MatchOneNode {
18 MatchedBoth,
19 SkipBoth,
20 SkipGoal,
21 SkipCandidate,
22 NoMatch,
23}
24
25fn skip_comment_or_unnamed(n: &Node<impl Doc>) -> bool {
26 if !n.is_named() {
27 return true;
28 }
29 let kind = n.kind();
30 kind.contains("comment")
31}
32
33impl MatchStrictness {
34 pub(crate) fn match_terminal(
35 &self,
36 is_named: bool,
37 text: &str,
38 goal_kind: u16,
39 candidate: &Node<impl Doc>,
40 ) -> MatchOneNode {
41 use MatchStrictness as M;
42 let cand_kind = candidate.kind_id();
43 let is_kind_matched = kind_utils::are_kinds_matching(goal_kind, cand_kind);
44 if is_kind_matched && (!is_named || text == candidate.text()) {
48 return MatchOneNode::MatchedBoth;
49 }
50 let (skip_goal, skip_candidate) = match self {
51 M::Cst => (false, false),
52 M::Smart => (false, !candidate.is_named()),
53 M::Ast => (!is_named, !candidate.is_named()),
54 M::Relaxed => (!is_named, skip_comment_or_unnamed(candidate)),
55 M::Signature => {
56 if is_kind_matched {
57 return MatchOneNode::MatchedBoth;
58 }
59 (!is_named, skip_comment_or_unnamed(candidate))
60 }
61 };
62 match (skip_goal, skip_candidate) {
63 (true, true) => MatchOneNode::SkipBoth,
64 (true, false) => MatchOneNode::SkipGoal,
65 (false, true) => MatchOneNode::SkipCandidate,
66 (false, false) => MatchOneNode::NoMatch,
67 }
68 }
69
70 pub(crate) fn should_skip_trailing<D: Doc>(&self, candidate: &Node<D>) -> bool {
72 use MatchStrictness as M;
73 match self {
74 M::Cst => false,
75 M::Smart => true,
76 M::Ast => false,
77 M::Relaxed => skip_comment_or_unnamed(candidate),
78 M::Signature => skip_comment_or_unnamed(candidate),
79 }
80 }
81
82 pub(crate) fn should_skip_goal<'p>(
83 &self,
84 goal_children: &mut Peekable<impl Iterator<Item = &'p PatternNode>>,
85 ) -> bool {
86 use MatchStrictness as M;
87 while let Some(pattern) = goal_children.peek() {
88 let skipped = match self {
89 M::Cst => false,
90 M::Smart => match pattern {
91 PatternNode::MetaVar { meta_var } => match meta_var {
92 MetaVariable::Multiple => true,
93 MetaVariable::MultiCapture(_) => true,
94 MetaVariable::Dropped(_) => false,
95 MetaVariable::Capture(..) => false,
96 },
97 PatternNode::Terminal { .. } => false,
98 PatternNode::Internal { .. } => false,
99 },
100 M::Ast | M::Relaxed | M::Signature => match pattern {
101 PatternNode::MetaVar { meta_var } => match meta_var {
102 MetaVariable::Multiple => true,
103 MetaVariable::MultiCapture(_) => true,
104 MetaVariable::Dropped(named) => !named,
105 MetaVariable::Capture(_, named) => !named,
106 },
107 PatternNode::Terminal { is_named, .. } => !is_named,
108 PatternNode::Internal { .. } => false,
109 },
110 };
111 if !skipped {
112 return false;
113 }
114 goal_children.next();
115 }
116 true
117 }
118}
119
120impl FromStr for MatchStrictness {
121 type Err = &'static str;
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 match s {
124 "cst" => Ok(MatchStrictness::Cst),
125 "smart" => Ok(MatchStrictness::Smart),
126 "ast" => Ok(MatchStrictness::Ast),
127 "relaxed" => Ok(MatchStrictness::Relaxed),
128 "signature" => Ok(MatchStrictness::Signature),
129 _ => Err("invalid strictness, valid options are: cst, smart, ast, relaxed, signature"),
130 }
131 }
132}