1use nom::branch::alt;
2use nom::bytes::complete::tag;
3use nom::character::complete::{char, one_of};
4use nom::combinator::{consumed, map, map_res, opt};
5use nom::multi::separated_list1;
6use nom::sequence::{pair, preceded, tuple};
7
8use crate::{
9 CharLit, ErrorContext, Num, ParseErr, ParseResult, PathOrIdentifier, State, StrLit, WithSpan,
10 bool_lit, char_lit, identifier, keyword, num_lit, path_or_identifier, str_lit, ws,
11};
12
13#[derive(Clone, Debug, PartialEq)]
14pub enum Target<'a> {
15 Name(&'a str),
16 Tuple(Vec<&'a str>, Vec<Target<'a>>),
17 Array(Vec<&'a str>, Vec<Target<'a>>),
18 Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>),
19 NumLit(&'a str, Num<'a>),
20 StrLit(StrLit<'a>),
21 CharLit(CharLit<'a>),
22 BoolLit(&'a str),
23 Path(Vec<&'a str>),
24 OrChain(Vec<Target<'a>>),
25 Placeholder(&'a str),
26 /// The `Option` is the variable name (if any) in `var_name @ ..`.
27 Rest(WithSpan<'a, Option<&'a str>>),
28}
29
30impl<'a> Target<'a> {
31 /// Parses multiple targets with `or` separating them
32 pub(super) fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
33 map(
34 separated_list1(ws(tag("or")), |i| s.nest(i, |i| Self::parse_one(i, s))),
35 |mut opts| match opts.len() {
36 1 => opts.pop().unwrap(),
37 _ => Self::OrChain(opts),
38 },
39 )(i)
40 }
41
42 /// Parses a single target without an `or`, unless it is wrapped in parentheses.
43 fn parse_one(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
44 let mut opt_opening_paren = map(opt(ws(char('('))), |o| o.is_some());
45 let mut opt_opening_brace = map(opt(ws(char('{'))), |o| o.is_some());
46 let mut opt_opening_bracket = map(opt(ws(char('['))), |o| o.is_some());
47
48 let (i, lit) = opt(Self::lit)(i)?;
49 if let Some(lit) = lit {
50 return Ok((i, lit));
51 }
52
53 // match tuples and unused parentheses
54 let (i, target_is_tuple) = opt_opening_paren(i)?;
55 if target_is_tuple {
56 let (i, (singleton, mut targets)) = collect_targets(i, s, ')', Self::unnamed)?;
57 if singleton {
58 return Ok((i, targets.pop().unwrap()));
59 }
60 return Ok((
61 i,
62 Self::Tuple(Vec::new(), only_one_rest_pattern(targets, false, "tuple")?),
63 ));
64 }
65 let (i, target_is_array) = opt_opening_bracket(i)?;
66 if target_is_array {
67 let (i, (singleton, mut targets)) = collect_targets(i, s, ']', Self::unnamed)?;
68 if singleton {
69 return Ok((i, targets.pop().unwrap()));
70 }
71 return Ok((
72 i,
73 Self::Array(Vec::new(), only_one_rest_pattern(targets, true, "array")?),
74 ));
75 }
76
77 let path = |i| {
78 map_res(path_or_identifier, |v| match v {
79 PathOrIdentifier::Path(v) => Ok(v),
80 PathOrIdentifier::Identifier(v) => Err(v),
81 })(i)
82 };
83
84 // match structs
85 let (i, path) = opt(path)(i)?;
86 if let Some(path) = path {
87 let i_before_matching_with = i;
88 let (i, _) = opt(ws(keyword("with")))(i)?;
89
90 let (i, is_unnamed_struct) = opt_opening_paren(i)?;
91 if is_unnamed_struct {
92 let (i, (_, targets)) = collect_targets(i, s, ')', Self::unnamed)?;
93 return Ok((
94 i,
95 Self::Tuple(path, only_one_rest_pattern(targets, false, "struct")?),
96 ));
97 }
98
99 let (i, is_named_struct) = opt_opening_brace(i)?;
100 if is_named_struct {
101 let (i, (_, targets)) = collect_targets(i, s, '}', Self::named)?;
102 return Ok((i, Self::Struct(path, targets)));
103 }
104
105 return Ok((i_before_matching_with, Self::Path(path)));
106 }
107
108 // neither literal nor struct nor path
109 let (new_i, name) = identifier(i)?;
110 let target = match name {
111 "_" => Self::Placeholder(name),
112 _ => verify_name(i, name)?,
113 };
114 Ok((new_i, target))
115 }
116
117 fn lit(i: &'a str) -> ParseResult<'a, Self> {
118 alt((
119 map(str_lit, Self::StrLit),
120 map(char_lit, Self::CharLit),
121 map(consumed(num_lit), |(full, num)| Target::NumLit(full, num)),
122 map(bool_lit, Self::BoolLit),
123 ))(i)
124 }
125
126 fn unnamed(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
127 alt((Self::rest, |i| Self::parse(i, s)))(i)
128 }
129
130 fn named(init_i: &'a str, s: &State<'_>) -> ParseResult<'a, (&'a str, Self)> {
131 let (i, rest) = opt(consumed(Self::rest))(init_i)?;
132 if let Some(rest) = rest {
133 let (_, chr) = ws(opt(one_of(",:")))(i)?;
134 if let Some(chr) = chr {
135 return Err(nom::Err::Failure(ErrorContext::new(
136 format!(
137 "unexpected `{chr}` character after `..`\n\
138 note that in a named struct, `..` must come last to ignore other members"
139 ),
140 i,
141 )));
142 }
143 if let Target::Rest(ref s) = rest.1 {
144 if s.inner.is_some() {
145 return Err(nom::Err::Failure(ErrorContext::new(
146 "`@ ..` cannot be used in struct",
147 s.span,
148 )));
149 }
150 }
151 return Ok((i, rest));
152 }
153
154 let (i, (src, target)) = pair(
155 identifier,
156 opt(preceded(ws(char(':')), |i| Self::parse(i, s))),
157 )(init_i)?;
158
159 if src == "_" {
160 return Err(nom::Err::Failure(ErrorContext::new(
161 "cannot use placeholder `_` as source in named struct",
162 init_i,
163 )));
164 }
165
166 let target = match target {
167 Some(target) => target,
168 None => verify_name(init_i, src)?,
169 };
170 Ok((i, (src, target)))
171 }
172
173 fn rest(start: &'a str) -> ParseResult<'a, Self> {
174 let (i, (ident, _)) = tuple((opt(tuple((identifier, ws(char('@'))))), tag("..")))(start)?;
175 Ok((
176 i,
177 Self::Rest(WithSpan::new(ident.map(|(ident, _)| ident), start)),
178 ))
179 }
180}
181
182fn verify_name<'a>(
183 input: &'a str,
184 name: &'a str,
185) -> Result<Target<'a>, nom::Err<ErrorContext<'a>>> {
186 match name {
187 "self" | "writer" => Err(nom::Err::Failure(ErrorContext::new(
188 message:format!("cannot use `{name}` as a name"),
189 input,
190 ))),
191 _ => Ok(Target::Name(name)),
192 }
193}
194
195fn collect_targets<'a, T>(
196 i: &'a str,
197 s: &State<'_>,
198 delim: char,
199 mut one: impl FnMut(&'a str, &State<'_>) -> ParseResult<'a, T>,
200) -> ParseResult<'a, (bool, Vec<T>)> {
201 let opt_comma = |i| map(ws(opt(char(','))), |o| o.is_some())(i);
202 let opt_end = |i| map(ws(opt(char(delim))), |o| o.is_some())(i);
203
204 let (i, has_end) = opt_end(i)?;
205 if has_end {
206 return Ok((i, (false, Vec::new())));
207 }
208
209 let (i, targets) = opt(separated_list1(ws(char(',')), |i| one(i, s)))(i)?;
210 let Some(targets) = targets else {
211 return Err(nom::Err::Failure(ErrorContext::new(
212 "expected comma separated list of members",
213 i,
214 )));
215 };
216
217 let (i, (has_comma, has_end)) = pair(opt_comma, opt_end)(i)?;
218 if !has_end {
219 let msg = match has_comma {
220 true => format!("expected member, or `{delim}` as terminator"),
221 false => format!("expected `,` for more members, or `{delim}` as terminator"),
222 };
223 return Err(nom::Err::Failure(ErrorContext::new(msg, i)));
224 }
225
226 let singleton = !has_comma && targets.len() == 1;
227 Ok((i, (singleton, targets)))
228}
229
230fn only_one_rest_pattern<'a>(
231 targets: Vec<Target<'a>>,
232 allow_named_rest: bool,
233 type_kind: &str,
234) -> Result<Vec<Target<'a>>, ParseErr<'a>> {
235 let mut found_rest: bool = false;
236
237 for target: &Target<'a> in &targets {
238 if let Target::Rest(s: &WithSpan<'_, Option<&str>>) = target {
239 if !allow_named_rest && s.inner.is_some() {
240 return Err(nom::Err::Failure(ErrorContext::new(
241 message:"`@ ..` is only allowed in slice patterns",
242 input:s.span,
243 )));
244 }
245 if found_rest {
246 return Err(nom::Err::Failure(ErrorContext::new(
247 message:format!("`..` can only be used once per {type_kind} pattern"),
248 input:s.span,
249 )));
250 }
251 found_rest = true;
252 }
253 }
254 Ok(targets)
255}
256