1use nom::{
4 branch::alt,
5 bytes::complete::tag,
6 character::complete::char as tag_char,
7 combinator::{cut, map, not, opt, peek},
8 multi::{separated_list0, separated_list1},
9 sequence::{delimited, preceded, terminated},
10 Err as NomErr, Parser,
11};
12
13use super::helpers::{comma_sep, var_name, ws, GrammarType};
14use crate::{
15 alloc::{vec, Vec},
16 grammars::{Features, Grammar, Parse},
17 spans::with_span,
18 Destructure, DestructureRest, ErrorKind, InputSpan, Lvalue, NomResult, ObjectDestructure,
19 ObjectDestructureField, Spanned, SpannedLvalue,
20};
21
22fn comma_separated_lvalues<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, Vec<GrammarLvalue<'_, T>>>
23where
24 T: Parse,
25 Ty: GrammarType,
26{
27 separated_list0(comma_sep::<Ty>, lvalue::<T, Ty>).parse(input)
28}
29
30fn destructure_rest<T, Ty>(
31 input: InputSpan<'_>,
32) -> NomResult<'_, Spanned<'_, DestructureRest<'_, <T::Base as Grammar>::Type<'_>>>>
33where
34 T: Parse,
35 Ty: GrammarType,
36{
37 map(
38 with_span(preceded(
39 terminated(tag("..."), ws::<Ty>),
40 cut(opt(simple_lvalue_with_type::<T, Ty>)),
41 )),
42 |spanned| {
43 spanned.map_extra(|maybe_lvalue| {
44 maybe_lvalue.map_or(DestructureRest::Unnamed, |lvalue| DestructureRest::Named {
45 variable: lvalue.with_no_extra(),
46 ty: match lvalue.extra {
47 Lvalue::Variable { ty } => ty,
48 _ => None,
49 },
50 })
51 })
52 },
53 )
54 .parse(input)
55}
56
57type DestructureTail<'a, T> = (
58 Spanned<'a, DestructureRest<'a, T>>,
59 Option<Vec<SpannedLvalue<'a, T>>>,
60);
61
62fn destructure_tail<T, Ty>(
63 input: InputSpan<'_>,
64) -> NomResult<'_, DestructureTail<'_, <T::Base as Grammar>::Type<'_>>>
65where
66 T: Parse,
67 Ty: GrammarType,
68{
69 (
70 destructure_rest::<T, Ty>,
71 opt(preceded(comma_sep::<Ty>, comma_separated_lvalues::<T, Ty>)),
72 )
73 .parse(input)
74}
75
76pub(super) fn destructure<T, Ty>(
78 input: InputSpan<'_>,
79) -> NomResult<'_, Destructure<'_, <T::Base as Grammar>::Type<'_>>>
80where
81 T: Parse,
82 Ty: GrammarType,
83{
84 let main_parser = alt((
85 map(destructure_tail::<T, Ty>, |rest| (vec![], Some(rest))),
87 (
88 comma_separated_lvalues::<T, Ty>,
89 opt(preceded(comma_sep::<Ty>, destructure_tail::<T, Ty>)),
90 ),
91 ));
92 let main_parser = terminated(main_parser, opt(comma_sep::<Ty>));
94
95 map(main_parser, |(start, maybe_rest)| {
96 if let Some((middle, end)) = maybe_rest {
97 Destructure {
98 start,
99 middle: Some(middle),
100 end: end.unwrap_or_default(),
101 }
102 } else {
103 Destructure {
104 start,
105 middle: None,
106 end: vec![],
107 }
108 }
109 })
110 .parse(input)
111}
112
113type GrammarLvalue<'a, T> = SpannedLvalue<'a, <<T as Parse>::Base as Grammar>::Type<'a>>;
114
115fn parenthesized_destructure<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
116where
117 T: Parse,
118 Ty: GrammarType,
119{
120 with_span(map(
121 delimited(
122 terminated(tag_char('('), ws::<Ty>),
123 destructure::<T, Ty>,
124 preceded(ws::<Ty>, tag_char(')')),
125 ),
126 Lvalue::Tuple,
127 ))
128 .parse(input)
129}
130
131fn simple_lvalue_with_type<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
133where
134 T: Parse,
135 Ty: GrammarType,
136{
137 let type_delimiter = terminated(tag_char(':'), peek(not(tag_char(':'))));
140 map(
141 (
142 var_name,
143 opt(preceded(
144 delimited(ws::<Ty>, type_delimiter, ws::<Ty>),
145 cut(with_span(<T::Base>::parse_type)),
146 )),
147 ),
148 |(name, ty)| Spanned::new(name, Lvalue::Variable { ty }),
149 )
150 .parse(input)
151}
152
153fn simple_lvalue_without_type<T>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
154where
155 T: Parse,
156{
157 map(var_name, |name| {
158 Spanned::new(name, Lvalue::Variable { ty: None })
159 })
160 .parse(input)
161}
162
163fn object_destructure_field<T, Ty>(
164 input: InputSpan<'_>,
165) -> NomResult<'_, ObjectDestructureField<'_, <T::Base as Grammar>::Type<'_>>>
166where
167 T: Parse,
168 Ty: GrammarType,
169{
170 let field_sep = alt((tag(":"), tag("->")));
171 let field_sep = (ws::<Ty>, field_sep, ws::<Ty>);
172 let field = (var_name, opt(preceded(field_sep, lvalue::<T, Ty>)));
173 map(field, |(name, maybe_binding)| ObjectDestructureField {
174 field_name: Spanned::new(name, ()),
175 binding: maybe_binding,
176 })
177 .parse(input)
178}
179
180pub(super) fn object_destructure<T, Ty>(
181 input: InputSpan<'_>,
182) -> NomResult<'_, ObjectDestructure<'_, <T::Base as Grammar>::Type<'_>>>
183where
184 T: Parse,
185 Ty: GrammarType,
186{
187 let inner = separated_list1(comma_sep::<Ty>, object_destructure_field::<T, Ty>);
188 let inner = terminated(inner, opt(comma_sep::<Ty>));
189 let inner = delimited(
190 terminated(tag_char('{'), ws::<Ty>),
191 inner,
192 preceded(ws::<Ty>, tag_char('}')),
193 );
194 map(inner, |fields| ObjectDestructure { fields }).parse(input)
195}
196
197fn mapped_object_destructure<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
198where
199 T: Parse,
200 Ty: GrammarType,
201{
202 with_span(map(object_destructure::<T, Ty>, Lvalue::Object)).parse(input)
203}
204
205pub(super) fn lvalue<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
207where
208 T: Parse,
209 Ty: GrammarType,
210{
211 fn error<T>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
212 where
213 T: Parse,
214 {
215 let e = ErrorKind::Leftovers.with_span(&input.into());
216 Err(NomErr::Error(e))
217 }
218
219 let simple_lvalue = if T::FEATURES.contains(Features::TYPE_ANNOTATIONS) {
220 simple_lvalue_with_type::<T, Ty>
221 } else {
222 simple_lvalue_without_type::<T>
223 };
224 let destructure = if T::FEATURES.contains(Features::TUPLES) {
225 parenthesized_destructure::<T, Ty>
226 } else {
227 error::<T>
228 };
229 let object_destructure = if T::FEATURES.contains(Features::OBJECTS) {
230 mapped_object_destructure::<T, Ty>
231 } else {
232 error::<T>
233 };
234 alt((destructure, object_destructure, simple_lvalue)).parse(input)
235}