arithmetic_parser/parser/
lvalue.rs

1//! Lvalue-related parsing functions.
2
3use 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
76/// Parse the destructuring *without* the surrounding delimiters.
77pub(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        // `destructure_tail` has fast fail path: the input must start with `...`.
86        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    // Allow for `,`-terminated lists.
93    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
131/// Simple lvalue with an optional type annotation, e.g., `x` or `x: Num`.
132fn simple_lvalue_with_type<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, GrammarLvalue<'_, T>>
133where
134    T: Parse,
135    Ty: GrammarType,
136{
137    // Do not consider `::` as a type delimiter; otherwise, parsing will be inappropriately cut
138    // when `$var_name::...` is encountered.
139    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
205/// Parses an `Lvalue`.
206pub(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}