almond/parser/
expression.rs

1//! Parsing for JS expressions.
2
3use crate::ast::*;
4use crate::parser::util::*;
5use crate::parser::*;
6use nom_locate::position;
7
8/// Alias for `parse_expr_bp(s, 0)`.
9pub fn parse_expr(s: Span) -> ParseResult<Node> {
10    context("expression", |s| parse_expr_bp(s, 0, false))(s)
11}
12
13/// Alias for `parse_expr_bp(s, 1)`. Should be used when parsing expressions in expression lists.
14/// This prevents matching the sequence (`,`) operator.
15pub fn parse_expr_no_seq(s: Span) -> ParseResult<Node> {
16    context("expression no seq", |s| parse_expr_bp(s, 1, false))(s)
17}
18
19/// Parse an atomic expression — either a single token that is an
20// expression, an expression started by a keyword like `function`.
21pub fn parse_primary_expr(s: Span) -> ParseResult<Node> {
22    alt((
23        parse_identifier,
24        literal::parse_literal,
25        parse_this_expr,
26        parse_function_expr,
27        parse_paren_expr,
28    ))(s)
29}
30
31/// Parse an atomic expression — either a single token that is an
32/// expression, an expression started by a keyword like `function`.
33/// This variant of `parse_primary_expr` allows parsing an identifier that is a reserved name.
34/// This method should be used instead of `parse_primary_expr` when preceding token is `.` operator (for member expression).
35/// # Spec
36/// http://www.ecma-international.org/ecma-262/#sec-property-accessors
37pub fn parse_primary_expr_allow_reserved(s: Span) -> ParseResult<Node> {
38    alt((
39        parse_identifier_name, // note that this is different from `parse_identifier` which does not allow reserved name.
40        literal::parse_literal,
41        parse_this_expr,
42        parse_function_expr,
43        parse_paren_expr,
44    ))(s)
45}
46
47pub fn parse_this_expr(s: Span) -> ParseResult<Node> {
48    map(
49        spanned(ws0(pair(tag("this"), not(identifier_continue)))),
50        |(_, start, end)| NodeKind::ThisExpression.with_pos(start, end),
51    )(s)
52}
53
54pub fn parse_paren_expr(s: Span) -> ParseResult<Node> {
55    context(
56        "paren expression",
57        delimited(ws0(char('(')), parse_expr, ws0(char(')'))),
58    )(s)
59}
60
61fn parse_opt_expr_in_list(s: Span) -> ParseResult<Option<Node>> {
62    alt((value(None, peek(char(','))), map(parse_expr_no_seq, Some)))(s)
63}
64
65pub fn parse_expr_list_with_opt_expr(s: Span) -> ParseResult<Vec<Option<Node>>> {
66    context(
67        "expression list with optional expression",
68        terminated(
69            separated_list0(ws0(char(',')), parse_opt_expr_in_list),
70            // trailing comma
71            ws0(opt(char(','))),
72        ),
73    )(s)
74}
75
76pub fn parse_expr_list(s: Span) -> ParseResult<Vec<Node>> {
77    context(
78        "expression list",
79        terminated(
80            separated_list0(ws0(char(',')), parse_expr_no_seq),
81            // trailing comma
82            ws0(opt(char(','))),
83        ),
84    )(s)
85}
86
87/// Pratt parsing for prefix operators. Called in `parse_expr_bp`.
88fn parse_prefix_expr(s: Span) -> ParseResult<Node> {
89    let (s, start) = position(s)?;
90    let (s, (prefix_op, BindingPower(_, right_bp))) = parse_prefix_operator(s)?;
91    let (s, rhs) = parse_expr_bp(
92        s, right_bp, /* reserved is only used after `.` operator */ false,
93    )?;
94
95    let (mut s, mut end) = position(s)?;
96
97    let node_kind = match prefix_op {
98        PrefixOperator::Unary(prefix_op) => NodeKind::UnaryExpression {
99            argument: Box::new(rhs),
100            operator: prefix_op,
101            prefix: true,
102        },
103        PrefixOperator::Update(prefix_op) => NodeKind::UpdateExpression {
104            argument: Box::new(rhs),
105            operator: prefix_op,
106            prefix: true,
107        },
108        PrefixOperator::New => {
109            let (s_tmp, arguments) = opt(delimited(ws0(char('(')), parse_expr_list, char(')')))(s)?;
110            s = s_tmp;
111            let (s_tmp, end_tmp) = position(s)?;
112            s = s_tmp;
113            end = end_tmp;
114
115            let (s_tmp, _) = sp0(s)?;
116            s = s_tmp;
117
118            NodeKind::NewExpression {
119                callee: Box::new(rhs),
120                arguments: arguments.unwrap_or_default(),
121            }
122        }
123        PrefixOperator::Await => NodeKind::AwaitExpression {
124            argument: Box::new(rhs),
125        },
126    };
127    Ok((s, node_kind.with_pos(start, end)))
128}
129
130/// Pratt parsing for expressions with operator precedence.
131/// Check out [https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) to see how Pratt parsing works.
132/// # Params
133/// * `min_bp` - The minimal binding power to accept.
134/// * `allow_reserved` - If `true`, will call `parse_primary_expr_allow_reserved` instead of `parse_primary_expr`. In almost all cases, this should be `false`.
135pub fn parse_expr_bp(s: Span, min_bp: i32, allow_reserved: bool) -> ParseResult<Node> {
136    let (mut s, mut lhs) = alt((
137        parse_prefix_expr,
138        if allow_reserved {
139            parse_primary_expr_allow_reserved
140        } else {
141            parse_primary_expr
142        },
143    ))(s)?;
144
145    loop {
146        if let Ok((s_tmp, (postfix_op, BindingPower(left_bp, _), mut end))) =
147            parse_postfix_operator(s)
148        {
149            if left_bp < min_bp {
150                break;
151            }
152            s = s_tmp;
153            let start = lhs.start.clone();
154
155            let node_kind = match postfix_op {
156                PostfixOperator::Update(postfix_op) => NodeKind::UpdateExpression {
157                    argument: Box::new(lhs),
158                    operator: postfix_op,
159                    prefix: false,
160                },
161                PostfixOperator::ComputedMember => {
162                    // array access
163                    let (s_tmp, property) = terminated(parse_expr, char(']'))(s)?;
164                    s = s_tmp;
165
166                    let (s_tmp, end_tmp) = position(s)?;
167                    s = s_tmp;
168                    end = end_tmp;
169
170                    let (s_tmp, _) = sp0(s)?;
171                    s = s_tmp;
172
173                    NodeKind::MemberExpression {
174                        object: Box::new(lhs),
175                        property: Box::new(property),
176                        computed: true,
177                    }
178                }
179                PostfixOperator::FuncCall => {
180                    // array access
181                    let (s_tmp, arguments) = terminated(parse_expr_list, char(')'))(s)?;
182                    s = s_tmp;
183
184                    let (s_tmp, end_tmp) = position(s)?;
185                    s = s_tmp;
186                    end = end_tmp;
187
188                    let (s_tmp, _) = sp0(s)?;
189                    s = s_tmp;
190
191                    NodeKind::CallExpression {
192                        callee: Box::new(lhs),
193                        arguments,
194                    }
195                }
196            };
197
198            lhs = node_kind.with_pos(start, end);
199
200            continue;
201        }
202
203        // do not override s just yet
204        let (s_tmp, (op, BindingPower(left_bp, right_bp))) = match parse_infix_operator(s) {
205            Ok(res) => res,
206            Err(_) => break, // do not return from function, just break from loop.
207        };
208
209        if left_bp < min_bp {
210            break;
211        }
212
213        // ok, now we can override s
214        s = s_tmp;
215
216        if let InfixOperator::TernaryOperator = op {
217            let (s_tmp, mhs) = parse_expr_bp(s, right_bp, false)?;
218            s = s_tmp;
219
220            let (s_tmp, _) = ws0(tag(":"))(s)?;
221            s = s_tmp;
222
223            let (s_tmp, rhs) = parse_expr_bp(s, right_bp, false)?;
224            s = s_tmp;
225
226            let start = lhs.start.clone();
227            let end = rhs.end.clone();
228
229            let node_kind = NodeKind::ConditionalExpression {
230                test: Box::new(lhs),
231                consequent: Box::new(mhs),
232                alternate: Box::new(rhs),
233            };
234
235            lhs = node_kind.with_pos(start, end);
236            continue;
237        }
238
239        let (s_tmp, rhs) = parse_expr_bp(s, right_bp, op == InfixOperator::DotOperator)?;
240        s = s_tmp;
241
242        let start = lhs.start.clone();
243        let end = rhs.end.clone();
244
245        let node_kind = match op {
246            InfixOperator::Binary(op) => NodeKind::BinaryExpression {
247                left: Box::new(lhs),
248                right: Box::new(rhs),
249                operator: op,
250            },
251            InfixOperator::Logical(op) => NodeKind::LogicalExpression {
252                left: Box::new(lhs),
253                right: Box::new(rhs),
254                operator: op,
255            },
256            InfixOperator::Assignment(op) => NodeKind::AssignmentExpression {
257                left: Box::new(lhs),
258                right: Box::new(rhs),
259                operator: op,
260            },
261            InfixOperator::DotOperator => NodeKind::MemberExpression {
262                object: Box::new(lhs),
263                property: Box::new(rhs),
264                computed: false,
265            },
266            InfixOperator::SequenceOperator => NodeKind::SequenceExpression {
267                expressions: match &lhs.kind {
268                    NodeKind::SequenceExpression { expressions } => {
269                        let mut expressions = expressions.clone();
270                        expressions.push(rhs);
271                        expressions
272                    }
273                    _ => vec![lhs, rhs],
274                },
275            },
276            InfixOperator::TernaryOperator => unreachable!("handled earlier"),
277        };
278        lhs = node_kind.with_pos(start, end);
279    }
280
281    Ok((s, lhs))
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287    use insta::assert_json_snapshot;
288
289    #[test]
290    fn smoke_test_this_expr() {
291        parse_this_expr("this".into()).unwrap();
292        parse_this_expr("notthis".into()).unwrap_err();
293    }
294
295    #[test]
296    fn smoke_test_primary_expr() {
297        parse_primary_expr("this".into()).unwrap();
298        parse_primary_expr("myVar".into()).unwrap();
299    }
300
301    #[test]
302    fn test_identifier_expr() {
303        assert_json_snapshot!(parse_expr("myIdentifier".into()).unwrap().1);
304    }
305
306    #[test]
307    fn test_expr_bp_guard_in() {
308        assert_json_snapshot!(
309            parse_expr_bp("myIdentifier in foo".into(), 25, false)
310                .unwrap()
311                .1
312        );
313        // should only parse myIdentifier
314    }
315
316    #[test]
317    fn test_paren_expr() {
318        assert_json_snapshot!(parse_expr("(1)".into()).unwrap().1);
319        assert_json_snapshot!(parse_expr("(((1)))".into()).unwrap().1);
320        assert_json_snapshot!(parse_expr("(((1 + 1)))".into()).unwrap().1);
321    }
322
323    #[test]
324    fn test_member_expr() {
325        assert_json_snapshot!(parse_expr("a.b".into()).unwrap().1);
326        assert_json_snapshot!(parse_expr("a.b.c".into()).unwrap().1);
327        assert_json_snapshot!(parse_expr("a[1]".into()).unwrap().1);
328        assert_json_snapshot!(parse_expr("a[0]".into()).unwrap().1);
329        assert_json_snapshot!(parse_expr("a[[]]".into()).unwrap().1);
330    }
331
332    #[test]
333    fn test_new_expr() {
334        assert_json_snapshot!(parse_expr("new Array()".into()).unwrap().1);
335        assert_json_snapshot!(parse_expr("new Array".into()).unwrap().1); // paren optional
336        assert_json_snapshot!(parse_expr("new Array(1)".into()).unwrap().1);
337        assert_json_snapshot!(parse_expr("new Array(1,)".into()).unwrap().1);
338        assert_json_snapshot!(parse_expr("new Array(1,2)".into()).unwrap().1);
339        assert_json_snapshot!(parse_expr("new Foo.Bar(true)".into()).unwrap().1);
340    }
341
342    #[test]
343    fn test_call_expr() {
344        assert_json_snapshot!(parse_expr("foo()".into()).unwrap().1);
345        assert_json_snapshot!(parse_expr("foo.bar()".into()).unwrap().1);
346        assert_json_snapshot!(parse_expr("foo.bar.baz()".into()).unwrap().1);
347        assert_json_snapshot!(parse_expr("foo.bar(baz)".into()).unwrap().1);
348        assert_json_snapshot!(parse_expr("foo.bar(baz, 1, 2, 3)".into()).unwrap().1);
349
350        assert_json_snapshot!(
351            parse_expr("console.log(\"Hello World!\")".into())
352                .unwrap()
353                .1
354        );
355    }
356
357    #[test]
358    fn test_sequence_expr() {
359        assert_json_snapshot!(parse_expr("1,2,3".into()).unwrap().1);
360        assert_json_snapshot!(parse_expr("(1,2,3)".into()).unwrap().1);
361    }
362
363    #[test]
364    fn test_ternary_expr() {
365        assert_json_snapshot!(parse_expr("true ? x : y".into()).unwrap().1);
366        assert_json_snapshot!(parse_expr("a ? x : b ? y : z".into()).unwrap().1);
367        // should be parsed as a ? x : (b ? y : z)
368    }
369
370    #[test]
371    fn test_expr_bp_infix() {
372        assert_json_snapshot!(parse_expr("1 + 2".into()).unwrap().1);
373        assert_json_snapshot!(parse_expr("1 - 2 - 3".into()).unwrap().1);
374        assert_json_snapshot!(parse_expr("1 * 2 + 3".into()).unwrap().1);
375        assert_json_snapshot!(parse_expr("1 + 2 * 3".into()).unwrap().1);
376        assert_json_snapshot!(parse_expr("1 * 2 + 3 * 4".into()).unwrap().1);
377        assert_json_snapshot!(parse_expr("(1 + 2) * 3".into()).unwrap().1);
378
379        assert_json_snapshot!(parse_expr("true && false".into()).unwrap().1);
380        assert_json_snapshot!(parse_expr("x < y".into()).unwrap().1);
381
382        // equality
383        assert_json_snapshot!(parse_expr("x == 1".into()).unwrap().1);
384        assert_json_snapshot!(parse_expr("x != 1".into()).unwrap().1);
385        assert_json_snapshot!(parse_expr("x === 1".into()).unwrap().1);
386        assert_json_snapshot!(parse_expr("x !== 1".into()).unwrap().1);
387
388        // relational
389        assert_json_snapshot!(parse_expr("x < 1".into()).unwrap().1);
390        assert_json_snapshot!(parse_expr("x > 1".into()).unwrap().1);
391        assert_json_snapshot!(parse_expr("x <= 1".into()).unwrap().1);
392        assert_json_snapshot!(parse_expr("x >= 1".into()).unwrap().1);
393
394        // bitwise
395        assert_json_snapshot!(parse_expr("x << 1".into()).unwrap().1);
396        assert_json_snapshot!(parse_expr("x >> 1".into()).unwrap().1);
397        assert_json_snapshot!(parse_expr("x >>> 1".into()).unwrap().1);
398
399        // logical
400        assert_json_snapshot!(parse_expr("x && y".into()).unwrap().1);
401        assert_json_snapshot!(parse_expr("x || y".into()).unwrap().1);
402
403        // exponentiation (right associative)
404        assert_json_snapshot!("exponentiation", parse_expr("x ** y".into()).unwrap().1);
405        assert_json_snapshot!(
406            "exponentiation-2",
407            parse_expr("x ** y ** z".into()).unwrap().1
408        );
409    }
410
411    #[test]
412    fn test_expr_bp_infix_str_concat() {
413        assert_json_snapshot!(parse_expr(r#""a" + "b""#.into()).unwrap().1);
414        assert_json_snapshot!(parse_expr(r#""a" + 123"#.into()).unwrap().1);
415        assert_json_snapshot!(parse_expr(r#""a" + {}"#.into()).unwrap().1);
416        assert_json_snapshot!(parse_expr(r#"[] + {}"#.into()).unwrap().1);
417    }
418
419    #[test]
420    fn test_expr_bp_prefix() {
421        assert_json_snapshot!(parse_expr("-1".into()).unwrap().1);
422        assert_json_snapshot!(parse_expr("+1".into()).unwrap().1);
423        assert_json_snapshot!(parse_expr("+(+1)".into()).unwrap().1);
424        assert_json_snapshot!(parse_expr("1 + -2".into()).unwrap().1); // same as 1 + (-2)
425        assert_json_snapshot!(parse_expr("++x".into()).unwrap().1);
426
427        assert_json_snapshot!(parse_expr("typeof x".into()).unwrap().1);
428        assert_json_snapshot!(parse_expr("typeof module === \"object\"".into()).unwrap().1);
429
430        assert_json_snapshot!(parse_expr("!x".into()).unwrap().1);
431        assert_json_snapshot!(parse_expr("!(x && y)".into()).unwrap().1);
432
433        assert_json_snapshot!(parse_expr("await foo".into()).unwrap().1);
434        assert_json_snapshot!(parse_expr("await foo".into()).unwrap().1);
435    }
436
437    #[test]
438    fn test_expr_bp_postfix() {
439        assert_json_snapshot!(parse_expr("x++".into()).unwrap().1);
440        assert_json_snapshot!(parse_expr("x++\t".into()).unwrap().1); // make sure end pos does not include trailing whitespace
441        assert_json_snapshot!(parse_expr("x--".into()).unwrap().1);
442        assert_json_snapshot!(parse_expr("x++ + 1".into()).unwrap().1); // ++ binds tighter than +
443    }
444
445    #[test]
446    fn test_expr_bp_assignment() {
447        assert_json_snapshot!(parse_expr("x = 1".into()).unwrap().1);
448        assert_json_snapshot!(parse_expr("x = 1 + 2".into()).unwrap().1);
449        assert_json_snapshot!(parse_expr("x += 1".into()).unwrap().1);
450        assert_json_snapshot!(parse_expr("x += y += 1".into()).unwrap().1);
451        assert_json_snapshot!(parse_expr("x += x * x".into()).unwrap().1);
452        assert_json_snapshot!(parse_expr("x = a ? b : c".into()).unwrap().1);
453        assert_json_snapshot!(
454            parse_expr("x = a ? myFunc : function () { return c; }".into())
455                .unwrap()
456                .1
457        );
458
459        assert_json_snapshot!(parse_expr("hello.value = \"world\"".into()).unwrap().1);
460        assert_json_snapshot!(
461            parse_expr("myFunc = function () { return 0; }".into())
462                .unwrap()
463                .1
464        );
465        assert_json_snapshot!(parse_expr("a = b = c;".into()).unwrap().1);
466
467        assert_json_snapshot!(parse_expr("x -= x * x".into()).unwrap().1);
468        assert_json_snapshot!(parse_expr("x **= y".into()).unwrap().1);
469        assert_json_snapshot!(parse_expr("x *= x * x".into()).unwrap().1);
470        assert_json_snapshot!(parse_expr("x /= x * x".into()).unwrap().1);
471        assert_json_snapshot!(parse_expr("x %= x * x".into()).unwrap().1);
472        assert_json_snapshot!(parse_expr("x <<= 1".into()).unwrap().1);
473        assert_json_snapshot!(parse_expr("x >>= 1".into()).unwrap().1);
474        assert_json_snapshot!(parse_expr("x >>>= 1".into()).unwrap().1);
475        assert_json_snapshot!(parse_expr("x |= 1".into()).unwrap().1);
476        assert_json_snapshot!(parse_expr("x ^= 1".into()).unwrap().1);
477        assert_json_snapshot!(parse_expr("x &= 1".into()).unwrap().1);
478
479        assert_json_snapshot!(parse_expr("x[1] = a".into()).unwrap().1);
480        assert_json_snapshot!(parse_expr("x[\"foo\"] = a".into()).unwrap().1);
481
482        assert_json_snapshot!(
483            parse_expr(
484                r#"identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
485				"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+""#
486                    .into()
487            )
488            .unwrap()
489            .1
490        );
491    }
492
493    #[test]
494    fn test_expr_bp_member_expr() {
495        assert_json_snapshot!(parse_expr("x.y".into()).unwrap().1);
496        assert_json_snapshot!(parse_expr("x.y.z".into()).unwrap().1);
497        assert_json_snapshot!(parse_expr("x.y[z]".into()).unwrap().1);
498        assert_json_snapshot!(parse_expr("x.y[\"z\"]".into()).unwrap().1);
499        assert_json_snapshot!(parse_expr("x.y[arr.length - 1]".into()).unwrap().1);
500
501        assert_json_snapshot!(
502            parse_expr("x.y()\n// abc\n/* 123 */\n.z()".into())
503                .unwrap()
504                .1
505        );
506        assert_json_snapshot!(
507            parse_expr("x.y(123)\n// abc\n/* 123 */\n.z(abc)".into())
508                .unwrap()
509                .1
510        );
511        assert_json_snapshot!(
512            parse_expr(
513                r#"test
514                    .then(fn)
515                    // properties can be reserved words ("catch" keyword)
516                    .catch(function (error) {
517                        console.error(error);
518                    });"#
519                    .into()
520            )
521            .unwrap()
522            .1
523        );
524    }
525
526    #[test]
527    fn test_expr_bp_no_seq() {
528        assert_json_snapshot!(parse_expr_no_seq("x, y".into()).unwrap().1); // should not parse ", y"
529        assert_json_snapshot!(
530            parse_expr_no_seq(
531                "test() ? function () { return 0; } : function() { return 1; }, y".into()
532            )
533            .unwrap()
534            .1
535        ); // should not parse ", y"
536    }
537}