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}