Skip to main content

mech_syntax/
patterns.rs

1#[macro_use]
2use crate::*;
3
4// pattern := pattern_atom_struct | pattern_tuple_struct | wildcard | pattern_array | pattern_tuple | expression ;
5pub fn pattern(input: ParseString) -> ParseResult<Pattern> {
6  match pattern_atom_struct(input.clone()) {
7    Ok((input, tpl)) => {return Ok((input, Pattern::TupleStruct(tpl)))},
8    _ => ()
9  }
10  match pattern_tuple_struct(input.clone()) {
11    Ok((input, tpl)) => {return Ok((input, Pattern::TupleStruct(tpl)))},
12    _ => ()
13  }
14  match wildcard(input.clone()) {
15    Ok((input, _)) => {return Ok((input, Pattern::Wildcard))},
16    _ => ()
17  }
18  match pattern_array(input.clone()) {
19    Ok((input, arr)) => {return Ok((input, Pattern::Array(arr)))},
20    _ => ()
21  }
22  match pattern_tuple(input.clone()) {
23    Ok((input, tpl)) => {return Ok((input, Pattern::Tuple(tpl)))},
24    _ => ()
25  }
26  match expression(input.clone()) {
27    Ok((input, expr)) => {return Ok((input, Pattern::Expression(expr)))},
28    Err(err) => {return Err(err)},
29  }
30}
31
32// wildcard := "*" ;
33pub fn wildcard(input: ParseString) -> ParseResult<Pattern> {
34  let ((input, _)) = asterisk(input)?;
35  Ok((input, Pattern::Wildcard))
36}
37
38// pattern_tuple_struct := grave, identifier, "(", list1(",", pattern), ")" ;
39pub fn pattern_tuple_struct(input: ParseString) -> ParseResult<PatternTupleStruct> {
40  let (input, _) = grave(input)?;
41  let (input, id) = identifier(input)?;
42  let (input, _) = left_parenthesis(input)?;
43  let (input, _) = whitespace0(input)?;
44  let (input, patterns) = separated_list1(list_separator, pattern)(input)?;
45  let (input, _) = whitespace0(input)?;
46  let (input, _) = right_parenthesis(input)?;
47  Ok((input, PatternTupleStruct{name: id, patterns}))
48}
49
50// spread-operator := "..." | "…" ;
51fn spread_operator(input: ParseString) -> ParseResult<()> {
52  let (input, _) = alt((spread_operator_a, spread_operator_u))(input)?;
53  Ok((input, ()))
54}
55
56fn pattern_array_item(input: ParseString) -> ParseResult<Pattern> {
57  if let Ok((input, _)) = wildcard(input.clone()) {
58    return Ok((input, Pattern::Wildcard));
59  }
60  let (input, expr) = expression(input)?;
61  Ok((input, Pattern::Expression(expr)))
62}
63
64#[derive(Clone)]
65enum PatternArrayToken {
66  Spread,
67  Pipe,
68  Item(Pattern),
69}
70
71fn pattern_array_token(input: ParseString) -> ParseResult<PatternArrayToken> {
72  if let Ok((input, _)) = spread_operator(input.clone()) {
73    return Ok((input, PatternArrayToken::Spread));
74  }
75  if let Ok((input, _)) = enum_separator(input.clone()) {
76    return Ok((input, PatternArrayToken::Pipe));
77  }
78  let (input, item) = pattern_array_item(input)?;
79  Ok((input, PatternArrayToken::Item(item)))
80}
81
82// pattern_array := "[", [pattern_array_item|spread], "]" ;
83pub fn pattern_array(input: ParseString) -> ParseResult<PatternArray> {
84  let (mut input, _) = left_bracket(input)?;
85  let (next_input, _) = whitespace0(input)?;
86  input = next_input;
87
88  let mut tokens = Vec::new();
89  loop {
90    if let Ok((next_input, _)) = right_bracket(input.clone()) {
91      input = next_input;
92      break;
93    }
94
95    let (next_input, token) = pattern_array_token(input.clone())?;
96    input = next_input;
97    tokens.push(token);
98
99    let (next_input, _) = whitespace0(input)?;
100    input = next_input;
101    if let Ok((next_input, _)) = list_separator(input.clone()) {
102      input = next_input;
103      continue;
104    }
105  }
106
107  let pipe_positions = tokens
108    .iter()
109    .enumerate()
110    .filter_map(|(ix, token)| match token {
111      PatternArrayToken::Pipe => Some(ix),
112      _ => None,
113    })
114    .collect::<Vec<usize>>();
115
116  if pipe_positions.len() > 1 {
117    return Err(nom::Err::Error(ParseError::new(
118      input,
119      "Only one | rest binding is allowed in an array pattern",
120    )));
121  }
122
123  if let Some(pipe_ix) = pipe_positions.first().copied() {
124    if tokens.iter().any(|token| matches!(token, PatternArrayToken::Spread)) {
125      return Err(nom::Err::Error(ParseError::new(
126        input,
127        "Cannot mix … spread and | rest binding in an array pattern",
128      )));
129    }
130
131    let mut prefix = vec![];
132    for token in tokens[..pipe_ix].iter() {
133      match token {
134        PatternArrayToken::Item(pattern) => prefix.push(pattern.clone()),
135        _ => {
136          return Err(nom::Err::Error(ParseError::new(
137            input.clone(),
138            "Only patterns are allowed before | in an array pattern",
139          )));
140        }
141      }
142    }
143
144    let rest_tokens = &tokens[pipe_ix + 1..];
145    if rest_tokens.len() != 1 {
146      return Err(nom::Err::Error(ParseError::new(
147        input,
148        "Array rest binding must be exactly one pattern after |",
149      )));
150    }
151
152    let binding = match &rest_tokens[0] {
153      PatternArrayToken::Item(pattern) => pattern.clone(),
154      _ => {
155        return Err(nom::Err::Error(ParseError::new(
156          input,
157          "Array rest binding must be a pattern after |",
158        )));
159      }
160    };
161
162    return Ok((
163      input,
164      PatternArray {
165        prefix,
166        spread: Some(PatternArraySpread {
167          kind: PatternArraySpreadKind::Rest,
168          binding: Some(Box::new(binding)),
169        }),
170        suffix: vec![],
171      },
172    ));
173  }
174
175  let spread_positions = tokens
176    .iter()
177    .enumerate()
178    .filter_map(|(ix, token)| match token {
179      PatternArrayToken::Spread => Some(ix),
180      _ => None,
181    })
182    .collect::<Vec<usize>>();
183
184  if spread_positions.len() > 1 {
185    return Err(nom::Err::Error(ParseError::new(
186      input,
187      "Only one spread operator is allowed in an array pattern",
188    )));
189  }
190
191  let spread_ix = spread_positions.first().copied();
192  let mut prefix = vec![];
193  let mut suffix = vec![];
194
195  if let Some(ix) = spread_ix {
196    prefix = tokens[..ix]
197      .iter()
198      .filter_map(|token| match token {
199        PatternArrayToken::Item(pattern) => Some(pattern.clone()),
200        _ => None,
201      })
202      .collect();
203    suffix = tokens[ix + 1..]
204      .iter()
205      .filter_map(|token| match token {
206        PatternArrayToken::Item(pattern) => Some(pattern.clone()),
207        _ => None,
208      })
209      .collect();
210  } else {
211    prefix = tokens
212      .iter()
213      .filter_map(|token| match token {
214        PatternArrayToken::Item(pattern) => Some(pattern.clone()),
215        _ => None,
216      })
217      .collect();
218  }
219
220  let spread = spread_ix.map(|_| {
221    let prefix_ends_wildcard = matches!(prefix.last(), Some(Pattern::Wildcard));
222    let mut spread_binding: Option<Box<Pattern>> = None;
223    if prefix_ends_wildcard {
224      if suffix.len() == 1 {
225        spread_binding = suffix.pop().map(Box::new);
226      } else if suffix.len() >= 2 {
227        spread_binding = Some(Box::new(suffix.remove(0)));
228      }
229    }
230    PatternArraySpread {
231      kind: PatternArraySpreadKind::Spread,
232      binding: spread_binding,
233    }
234  });
235
236  Ok((input, PatternArray { prefix, spread, suffix }))
237}
238
239// pattern_atom_struct := ":", identifier, "(", list1(",", pattern), ")" ;
240pub fn pattern_atom_struct(input: ParseString) -> ParseResult<PatternTupleStruct> {
241  let (input, _) = colon(input)?;
242  let (input, id) = identifier(input)?;
243  let (input, _) = left_parenthesis(input)?;
244  let (input, _) = whitespace0(input)?;
245  let (input, patterns) = separated_list1(list_separator, pattern)(input)?;
246  let (input, _) = whitespace0(input)?;
247  let (input, _) = right_parenthesis(input)?;
248  Ok((input, PatternTupleStruct{name: id, patterns}))
249}
250
251// pattern-tuple := "(", [pattern, ","], ")" ;
252pub fn pattern_tuple(input: ParseString) -> ParseResult<PatternTuple> {
253  let (input, _) = left_parenthesis(input)?;
254  let (input, _) = whitespace0(input)?;
255  let (input, patterns) = separated_list1(list_separator, pattern)(input)?;
256  let (input, _) = whitespace0(input)?;
257  let (input, _) = right_parenthesis(input)?;
258  Ok((input, PatternTuple(patterns)))
259}