nickel_lang_core/bytecode/ast/pattern/
mod.rs1use std::collections::{hash_map::Entry, HashMap};
3
4use super::{Annotation, Ast, Number};
5
6use crate::{
7 identifier::LocIdent, impl_display_from_bytecode_pretty, parser::error::ParseError,
8 position::TermPos, traverse::*,
9};
10
11pub mod bindings;
12
13#[derive(Debug, PartialEq, Eq, Clone)]
14pub enum PatternData<'ast> {
15 Wildcard,
18 Any(LocIdent),
21 Record(&'ast RecordPattern<'ast>),
23 Array(&'ast ArrayPattern<'ast>),
25 Enum(&'ast EnumPattern<'ast>),
27 Constant(&'ast ConstantPattern<'ast>),
29 Or(&'ast OrPattern<'ast>),
31}
32
33#[derive(Debug, PartialEq, Eq, Clone)]
36pub struct Pattern<'ast> {
37 pub data: PatternData<'ast>,
39 pub alias: Option<LocIdent>,
42 pub pos: TermPos,
44}
45
46#[derive(Debug, PartialEq, Eq, Clone)]
48pub struct EnumPattern<'ast> {
49 pub tag: LocIdent,
50 pub pattern: Option<Pattern<'ast>>,
51 pub pos: TermPos,
52}
53
54#[derive(Debug, PartialEq, Eq, Clone)]
57pub struct FieldPattern<'ast> {
58 pub matched_id: LocIdent,
61 pub annotation: Annotation<'ast>,
63 pub default: Option<Ast<'ast>>,
65 pub pattern: Pattern<'ast>,
69 pub pos: TermPos,
70}
71
72#[derive(Debug, PartialEq, Eq, Clone)]
74pub struct RecordPattern<'ast> {
75 pub patterns: &'ast [FieldPattern<'ast>],
77 pub tail: TailPattern,
80 pub pos: TermPos,
81}
82
83#[derive(Debug, PartialEq, Eq, Clone)]
85pub struct ArrayPattern<'ast> {
86 pub patterns: &'ast [Pattern<'ast>],
88 pub tail: TailPattern,
91 pub pos: TermPos,
92}
93
94impl ArrayPattern<'_> {
95 pub fn is_open(&self) -> bool {
98 self.tail.is_open()
99 }
100}
101
102#[derive(Debug, PartialEq, Eq, Clone)]
104pub struct ConstantPattern<'ast> {
105 pub data: ConstantPatternData<'ast>,
106 pub pos: TermPos,
107}
108
109#[derive(Debug, PartialEq, Eq, Clone)]
110pub enum ConstantPatternData<'ast> {
111 Bool(bool),
112 Number(&'ast Number),
113 String(&'ast str),
114 Null,
115}
116
117#[derive(Debug, PartialEq, Eq, Clone)]
118pub struct OrPattern<'ast> {
119 pub patterns: &'ast [Pattern<'ast>],
120 pub pos: TermPos,
121}
122
123#[derive(Debug, PartialEq, Eq, Clone)]
126pub enum TailPattern {
127 Empty,
129 Open,
131 Capture(LocIdent),
134}
135
136impl Pattern<'_> {
137 pub fn any(id: LocIdent) -> Self {
139 let pos = id.pos;
140
141 Pattern {
142 data: PatternData::Any(id),
143 alias: None,
144 pos,
145 }
146 }
147
148 pub fn try_as_any(&self) -> Option<LocIdent> {
150 if let PatternData::Any(id) = &self.data {
151 Some(*id)
152 } else {
153 None
154 }
155 }
156}
157
158impl TailPattern {
159 pub fn is_open(&self) -> bool {
162 matches!(self, TailPattern::Open | TailPattern::Capture(_))
163 }
164}
165
166impl RecordPattern<'_> {
167 pub fn check_dup(&self) -> Result<(), ParseError> {
188 let mut bindings = HashMap::new();
189
190 for pat in self.patterns.iter() {
191 let binding = pat.matched_id;
192 let label = binding.label().to_owned();
193 match bindings.entry(label) {
194 Entry::Occupied(occupied_entry) => {
195 return Err(ParseError::DuplicateIdentInRecordPattern {
196 ident: binding,
197 prev_ident: occupied_entry.remove_entry().1,
198 })
199 }
200 Entry::Vacant(vacant_entry) => {
201 vacant_entry.insert(binding);
202 }
203 }
204 }
205
206 Ok(())
207 }
208
209 pub fn is_open(&self) -> bool {
212 self.tail.is_open()
213 }
214}
215
216impl<'ast> TraverseAlloc<'ast, Ast<'ast>> for Pattern<'ast> {
217 fn traverse<F, E>(
218 self,
219 alloc: &'ast super::AstAlloc,
220 f: &mut F,
221 order: TraverseOrder,
222 ) -> Result<Self, E>
223 where
224 F: FnMut(Ast<'ast>) -> Result<Ast<'ast>, E>,
225 {
226 match self.data {
227 data @ (PatternData::Wildcard | PatternData::Any(_) | PatternData::Constant(_)) => {
228 Ok(Pattern { data, ..self })
229 }
230 PatternData::Record(record) => {
231 let record = record.clone();
232 let patterns =
233 traverse_alloc_many(alloc, record.patterns.iter().cloned(), f, order)?;
234
235 Ok(Pattern {
236 data: PatternData::Record(alloc.alloc(RecordPattern { patterns, ..record })),
237 ..self
238 })
239 }
240 PatternData::Array(array) => {
241 let array = array.clone();
242 let patterns =
243 traverse_alloc_many(alloc, array.patterns.iter().cloned(), f, order)?;
244
245 Ok(Pattern {
246 data: PatternData::Array(alloc.alloc(ArrayPattern { patterns, ..array })),
247 ..self
248 })
249 }
250 PatternData::Enum(enum_pat) => {
251 let enum_pat = enum_pat.clone();
252 let pattern = enum_pat
253 .pattern
254 .map(|p| p.traverse(alloc, f, order))
255 .transpose()?;
256
257 Ok(Pattern {
258 data: PatternData::Enum(alloc.alloc(EnumPattern {
259 pattern,
260 ..enum_pat
261 })),
262 ..self
263 })
264 }
265 PatternData::Or(or) => {
266 let or = or.clone();
267 let patterns = traverse_alloc_many(alloc, or.patterns.iter().cloned(), f, order)?;
268
269 Ok(Pattern {
270 data: PatternData::Or(alloc.alloc(OrPattern { patterns, ..or })),
271 ..self
272 })
273 }
274 }
275 }
276
277 fn traverse_ref<S, U>(
278 &'ast self,
279 f: &mut dyn FnMut(&'ast Ast<'ast>, &S) -> TraverseControl<S, U>,
280 scope: &S,
281 ) -> Option<U> {
282 match &self.data {
283 PatternData::Wildcard | PatternData::Any(_) | PatternData::Constant(_) => None,
284 PatternData::Record(record) => record
285 .patterns
286 .iter()
287 .find_map(|field_pat| field_pat.traverse_ref(f, scope)),
288 PatternData::Array(array) => array
289 .patterns
290 .iter()
291 .find_map(|pat| pat.traverse_ref(f, scope)),
292 PatternData::Enum(enum_pat) => enum_pat
293 .pattern
294 .as_ref()
295 .and_then(|pat| pat.traverse_ref(f, scope)),
296 PatternData::Or(or) => or
297 .patterns
298 .iter()
299 .find_map(|pat| pat.traverse_ref(f, scope)),
300 }
301 }
302}
303
304impl<'ast> TraverseAlloc<'ast, Ast<'ast>> for FieldPattern<'ast> {
305 fn traverse<F, E>(
306 self,
307 alloc: &'ast super::AstAlloc,
308 f: &mut F,
309 order: TraverseOrder,
310 ) -> Result<Self, E>
311 where
312 F: FnMut(Ast<'ast>) -> Result<Ast<'ast>, E>,
313 {
314 let annotation = self.annotation.traverse(alloc, f, order)?;
315 let default = self
316 .default
317 .map(|d| d.traverse(alloc, f, order))
318 .transpose()?;
319 let pattern = self.pattern.traverse(alloc, f, order)?;
320
321 Ok(FieldPattern {
322 annotation,
323 default,
324 pattern,
325 ..self
326 })
327 }
328
329 fn traverse_ref<S, U>(
330 &'ast self,
331 f: &mut dyn FnMut(&'ast Ast<'ast>, &S) -> TraverseControl<S, U>,
332 scope: &S,
333 ) -> Option<U> {
334 self.annotation
335 .traverse_ref(f, scope)
336 .or_else(|| self.default.as_ref().and_then(|d| d.traverse_ref(f, scope)))
337 .or_else(|| self.pattern.traverse_ref(f, scope))
338 }
339}
340
341impl_display_from_bytecode_pretty!(PatternData<'_>);
342impl_display_from_bytecode_pretty!(Pattern<'_>);
343impl_display_from_bytecode_pretty!(ConstantPatternData<'_>);
344impl_display_from_bytecode_pretty!(ConstantPattern<'_>);
345impl_display_from_bytecode_pretty!(RecordPattern<'_>);
346impl_display_from_bytecode_pretty!(EnumPattern<'_>);
347impl_display_from_bytecode_pretty!(ArrayPattern<'_>);