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