nu_protocol/engine/pattern_match.rs
1use crate::{
2 Span, Value, VarId,
3 ast::{Expr, MatchPattern, Pattern, RangeInclusion},
4};
5
6pub trait Matcher {
7 fn match_value(&self, value: &Value, matches: &mut Vec<(VarId, Value)>) -> bool;
8}
9
10impl Matcher for MatchPattern {
11 fn match_value(&self, value: &Value, matches: &mut Vec<(VarId, Value)>) -> bool {
12 self.pattern.match_value(value, matches)
13 }
14}
15
16impl Matcher for Pattern {
17 fn match_value(&self, value: &Value, matches: &mut Vec<(VarId, Value)>) -> bool {
18 match self {
19 Pattern::Garbage => false,
20 Pattern::IgnoreValue => true,
21 Pattern::IgnoreRest => false, // `..` and `..$foo` only match in specific contexts
22 Pattern::Rest(_) => false, // so we return false here and handle them elsewhere
23 Pattern::Record(field_patterns) => match value {
24 Value::Record { val, .. } => {
25 'top: for field_pattern in field_patterns {
26 for (col, val) in &**val {
27 if col == &field_pattern.0 {
28 // We have found the field
29 let result = field_pattern.1.match_value(val, matches);
30 if !result {
31 return false;
32 } else {
33 continue 'top;
34 }
35 }
36 }
37 return false;
38 }
39 true
40 }
41 _ => false,
42 },
43 Pattern::Variable(var_id) => {
44 // TODO: FIXME: This needs the span of this variable
45 matches.push((*var_id, value.clone()));
46 true
47 }
48 Pattern::List(items) => match &value {
49 Value::List { vals, .. } => {
50 if items.len() > vals.len() {
51 // We only allow this is to have a rest pattern in the n+1 position
52 if items.len() == (vals.len() + 1) {
53 match &items[vals.len()].pattern {
54 Pattern::IgnoreRest => {}
55 Pattern::Rest(var_id) => matches.push((
56 *var_id,
57 Value::list(Vec::new(), items[vals.len()].span),
58 )),
59 _ => {
60 // There is a pattern which can't skip missing values, so we fail
61 return false;
62 }
63 }
64 } else {
65 // There are patterns that can't be matches, so we fail
66 return false;
67 }
68 }
69 for (val_idx, val) in vals.iter().enumerate() {
70 // We require that the pattern and the value have the same number of items, or the pattern does not match
71 // The only exception is if the pattern includes a `..` pattern
72 if let Some(pattern) = items.get(val_idx) {
73 match &pattern.pattern {
74 Pattern::IgnoreRest => {
75 break;
76 }
77 Pattern::Rest(var_id) => {
78 let rest_vals = vals[val_idx..].to_vec();
79 matches.push((*var_id, Value::list(rest_vals, pattern.span)));
80 break;
81 }
82 _ => {
83 if !pattern.match_value(val, matches) {
84 return false;
85 }
86 }
87 }
88 } else {
89 return false;
90 }
91 }
92
93 true
94 }
95 _ => false,
96 },
97 Pattern::Expression(pattern_value) => {
98 // TODO: Fill this out with the rest of them
99 match &pattern_value.expr {
100 Expr::Nothing => {
101 matches!(value, Value::Nothing { .. })
102 }
103 Expr::Int(x) => {
104 if let Value::Int { val, .. } = &value {
105 x == val
106 } else {
107 false
108 }
109 }
110 Expr::Float(x) => {
111 if let Value::Float { val, .. } = &value {
112 x == val
113 } else {
114 false
115 }
116 }
117 Expr::Binary(x) => {
118 if let Value::Binary { val, .. } = &value {
119 x == val
120 } else {
121 false
122 }
123 }
124 Expr::Bool(x) => {
125 if let Value::Bool { val, .. } = &value {
126 x == val
127 } else {
128 false
129 }
130 }
131 Expr::String(x) | Expr::RawString(x) => {
132 if let Value::String { val, .. } = &value {
133 x == val
134 } else {
135 false
136 }
137 }
138 Expr::DateTime(x) => {
139 if let Value::Date { val, .. } = &value {
140 x == val
141 } else {
142 false
143 }
144 }
145 Expr::ValueWithUnit(val) => {
146 let span = val.unit.span;
147
148 if let Expr::Int(size) = val.expr.expr {
149 match &val.unit.item.build_value(size, span) {
150 Ok(v) => v == value,
151 _ => false,
152 }
153 } else {
154 false
155 }
156 }
157 Expr::Range(range) => {
158 // TODO: Add support for floats
159
160 let start = if let Some(start) = &range.from {
161 match &start.expr {
162 Expr::Int(start) => *start,
163 _ => return false,
164 }
165 } else {
166 0
167 };
168
169 let end = if let Some(end) = &range.to {
170 match &end.expr {
171 Expr::Int(end) => *end,
172 _ => return false,
173 }
174 } else {
175 i64::MAX
176 };
177
178 let step = if let Some(step) = &range.next {
179 match &step.expr {
180 Expr::Int(step) => *step - start,
181 _ => return false,
182 }
183 } else if end < start {
184 -1
185 } else {
186 1
187 };
188
189 let (start, end) = if end < start {
190 (end, start)
191 } else {
192 (start, end)
193 };
194
195 if let Value::Int { val, .. } = &value {
196 if matches!(range.operator.inclusion, RangeInclusion::RightExclusive) {
197 *val >= start && *val < end && ((*val - start) % step) == 0
198 } else {
199 *val >= start && *val <= end && ((*val - start) % step) == 0
200 }
201 } else {
202 false
203 }
204 }
205 _ => false,
206 }
207 }
208 Pattern::Value(pattern_value) => value == pattern_value,
209 Pattern::Or(patterns) => {
210 let mut result = false;
211
212 for pattern in patterns {
213 let mut local_matches = vec![];
214 if !result {
215 if pattern.match_value(value, &mut local_matches) {
216 // TODO: do we need to replace previous variables that defaulted to nothing?
217 matches.append(&mut local_matches);
218 result = true;
219 } else {
220 // Create variables that don't match and assign them to null
221 let vars = pattern.variables();
222 for var in &vars {
223 let mut found = false;
224 for match_ in matches.iter() {
225 if match_.0 == *var {
226 found = true;
227 }
228 }
229
230 if !found {
231 // FIXME: don't use Span::unknown()
232 matches.push((*var, Value::nothing(Span::unknown())))
233 }
234 }
235 }
236 } else {
237 // We already have a match, so ignore the remaining match variables
238 // And assign them to null
239 let vars = pattern.variables();
240 for var in &vars {
241 let mut found = false;
242 for match_ in matches.iter() {
243 if match_.0 == *var {
244 found = true;
245 }
246 }
247
248 if !found {
249 // FIXME: don't use Span::unknown()
250 matches.push((*var, Value::nothing(Span::unknown())))
251 }
252 }
253 }
254 }
255 result
256 }
257 }
258 }
259}