Skip to main content

nodejs/
parser.rs

1//! JavaScript parser: token stream → AST.
2//!
3//! Recursive descent with precedence climbing for binary operators. Automatic
4//! Semicolon Insertion is applied at statement boundaries using the
5//! `newline_before` flag the lexer records on every token. Arrow functions are
6//! detected at assignment level by looking ahead for `=>` after a parameter
7//! list. Template-literal `${...}` fields are re-parsed here from the raw source
8//! the lexer captured.
9
10use crate::ast::*;
11use crate::lexer::{lex, Tok, Token};
12
13const KEYWORDS: &[&str] = &[
14    "var",
15    "let",
16    "const",
17    "function",
18    "return",
19    "if",
20    "else",
21    "while",
22    "do",
23    "for",
24    "of",
25    "in",
26    "switch",
27    "case",
28    "default",
29    "break",
30    "continue",
31    "true",
32    "false",
33    "null",
34    "this",
35    "new",
36    "typeof",
37    "void",
38    "delete",
39    "instanceof",
40    "throw",
41    "try",
42    "catch",
43    "finally",
44];
45
46fn is_keyword(s: &str) -> bool {
47    KEYWORDS.contains(&s)
48}
49
50/// The private names one class body declares and the ones its code refers to.
51#[derive(Default)]
52struct PrivateScope {
53    declared: Vec<String>,
54    used: Vec<String>,
55}
56
57fn private_not_declared(name: &str) -> String {
58    format!("SyntaxError: Private field '{name}' must be declared in an enclosing class")
59}
60
61struct Parser {
62    toks: Vec<Token>,
63    pos: usize,
64    /// True while parsing a generator body — enables `yield` as an operator.
65    in_generator: bool,
66    /// True while parsing an async body — enables `await` as an operator.
67    in_async: bool,
68    /// True while parsing a `for` init in LHS position — suppresses `in` as a
69    /// relational operator so `for (x in obj)` (no declaration keyword) parses
70    /// the `in` as the loop separator, not a binary expression. Cleared inside
71    /// any parenthesised/bracketed sub-expression, where `in` is legal again.
72    no_in: bool,
73    /// One frame per enclosing class body, each holding the private names that
74    /// body DECLARES and the ones its code USES. A private name is only in
75    /// scope inside a class that declares it, so an undeclared use is an EARLY
76    /// error (`SyntaxError`) rather than a runtime `TypeError` if the read ever
77    /// happens — a typo in a dead branch used to ship silently.
78    class_scopes: Vec<PrivateScope>,
79    /// Whether token offsets point into the script text, so a function's
80    /// [`Span`] can be recorded. Off for a re-parsed template field.
81    spans: bool,
82}
83
84/// Parse a complete JS program into a statement list. Inline `rust { ... }` FFI
85/// blocks are desugared to `__rust_compile(...)` calls before lexing.
86pub fn parse(src: &str) -> Result<Vec<Stmt>, String> {
87    let desugared = crate::rust_ffi::desugar(src);
88    // A `rust { … }` block is rewritten before lexing, so offsets would point
89    // into text the caller never sees; record spans only when nothing moved.
90    let spans = desugared == src;
91    let toks = lex(&desugared)?;
92    let mut p = Parser {
93        toks,
94        pos: 0,
95        in_generator: false,
96        in_async: false,
97        no_in: false,
98        class_scopes: Vec::new(),
99        spans,
100    };
101    let mut out = Vec::new();
102    while !p.at_eof() {
103        out.push(p.parse_stmt()?);
104    }
105    Ok(out)
106}
107
108impl Parser {
109    // ── token helpers ────────────────────────────────────────────────────
110    fn cur(&self) -> &Token {
111        &self.toks[self.pos]
112    }
113    /// Parse one `${…}` field of a template token, keeping its spans in this
114    /// script's coordinates.
115    fn parse_field(&self, src: &str, at: u32) -> Result<Expr, String> {
116        parse_expr_source(src, self.spans.then_some(at))
117    }
118    /// Byte offset where token `i` begins.
119    fn start_at(&self, i: usize) -> u32 {
120        self.toks[i].start
121    }
122    /// The source range from `start` to the end of the last consumed token.
123    fn span_from(&self, start: u32) -> Span {
124        if !self.spans || self.pos == 0 {
125            return (0, 0);
126        }
127        (start, self.toks[self.pos - 1].end)
128    }
129    fn tok(&self) -> &Tok {
130        &self.toks[self.pos].tok
131    }
132    fn line(&self) -> u32 {
133        self.toks[self.pos].line
134    }
135    fn at_eof(&self) -> bool {
136        matches!(self.tok(), Tok::Eof)
137    }
138    fn newline_before(&self) -> bool {
139        self.cur().newline_before
140    }
141    fn advance(&mut self) -> Tok {
142        let t = self.toks[self.pos].tok.clone();
143        if self.pos + 1 < self.toks.len() {
144            self.pos += 1;
145        }
146        t
147    }
148
149    /// True if the current token is the punctuation `s`.
150    fn is_punct(&self, s: &str) -> bool {
151        matches!(self.tok(), Tok::Punct(p) if p == s)
152    }
153    /// True if the current token is the identifier/keyword `s`.
154    fn is_kw(&self, s: &str) -> bool {
155        matches!(self.tok(), Tok::Ident(i) if i == s)
156    }
157    /// Consume the punctuation `s` if present.
158    fn eat_punct(&mut self, s: &str) -> bool {
159        if self.is_punct(s) {
160            self.advance();
161            true
162        } else {
163            false
164        }
165    }
166    fn eat_kw(&mut self, s: &str) -> bool {
167        if self.is_kw(s) {
168            self.advance();
169            true
170        } else {
171            false
172        }
173    }
174    fn expect_punct(&mut self, s: &str) -> Result<(), String> {
175        if self.eat_punct(s) {
176            Ok(())
177        } else {
178            Err(format!(
179                "SyntaxError: expected '{s}' but found {:?} (line {})",
180                self.tok(),
181                self.line()
182            ))
183        }
184    }
185
186    /// Consume an identifier name (any non-punct ident, including keywords used
187    /// as property names when `allow_kw`).
188    fn ident_name(&mut self) -> Result<String, String> {
189        match self.tok().clone() {
190            Tok::Ident(s) => {
191                self.advance();
192                Ok(s)
193            }
194            other => Err(format!(
195                "SyntaxError: expected identifier but found {other:?} (line {})",
196                self.line()
197            )),
198        }
199    }
200
201    /// Apply ASI: consume an explicit `;`, or accept a newline / `}` / EOF.
202    fn semicolon(&mut self) -> Result<(), String> {
203        if self.eat_punct(";") {
204            return Ok(());
205        }
206        if self.newline_before() || self.is_punct("}") || self.at_eof() {
207            return Ok(());
208        }
209        Err(format!(
210            "SyntaxError: expected ';' but found {:?} (line {})",
211            self.tok(),
212            self.line()
213        ))
214    }
215
216    // ── statements ───────────────────────────────────────────────────────
217    fn parse_stmt(&mut self) -> Result<Stmt, String> {
218        let line = self.line();
219        let kind = match self.tok().clone() {
220            Tok::Punct(p) if p == "{" => {
221                self.advance();
222                StmtKind::Block(self.parse_block_body()?)
223            }
224            Tok::Punct(p) if p == ";" => {
225                self.advance();
226                StmtKind::Empty
227            }
228            Tok::Ident(kw) if kw == "var" || kw == "let" || kw == "const" => {
229                let k = self.parse_decl_kind();
230                let decls = self.parse_declarators()?;
231                self.semicolon()?;
232                StmtKind::Decl { kind: k, decls }
233            }
234            Tok::Ident(kw) if kw == "function" => self.parse_func_decl(false)?,
235            // `async function …` (declaration). `async` stays a plain identifier
236            // anywhere else (contextual keyword).
237            Tok::Ident(kw)
238                if kw == "async" && self.peek_kw(1, "function") && !self.peek_newline(1) =>
239            {
240                self.advance(); // async
241                self.parse_func_decl(true)?
242            }
243            Tok::Ident(kw) if kw == "class" => {
244                let node = self.parse_class(true)?;
245                StmtKind::ClassDecl(node)
246            }
247            Tok::Ident(kw) if kw == "if" => self.parse_if()?,
248            Tok::Ident(kw) if kw == "while" => self.parse_while()?,
249            Tok::Ident(kw) if kw == "do" => self.parse_do_while()?,
250            Tok::Ident(kw) if kw == "for" => self.parse_for()?,
251            Tok::Ident(kw) if kw == "switch" => self.parse_switch()?,
252            Tok::Ident(kw) if kw == "return" => {
253                self.advance();
254                let arg = if self.is_punct(";")
255                    || self.is_punct("}")
256                    || self.newline_before()
257                    || self.at_eof()
258                {
259                    None
260                } else {
261                    Some(self.parse_expr()?)
262                };
263                self.semicolon()?;
264                StmtKind::Return(arg)
265            }
266            Tok::Ident(kw) if kw == "break" => {
267                self.advance();
268                let label = self.opt_label();
269                self.semicolon()?;
270                StmtKind::Break(label)
271            }
272            Tok::Ident(kw) if kw == "continue" => {
273                self.advance();
274                let label = self.opt_label();
275                self.semicolon()?;
276                StmtKind::Continue(label)
277            }
278            Tok::Ident(kw) if kw == "throw" => {
279                self.advance();
280                let e = self.parse_expr()?;
281                self.semicolon()?;
282                StmtKind::Throw(e)
283            }
284            Tok::Ident(kw) if kw == "try" => self.parse_try()?,
285            // `label: stmt` — a bare identifier immediately followed by `:` at
286            // statement position is a label (never an expression; the reserved
287            // control keywords are all matched above, and switch `case`/`default`
288            // labels are parsed inside `parse_switch`).
289            Tok::Ident(name) if matches!(self.toks.get(self.pos + 1).map(|t| &t.tok), Some(Tok::Punct(p)) if p == ":") =>
290            {
291                self.advance(); // the label identifier
292                self.advance(); // the ':'
293                let body = Box::new(self.parse_stmt()?);
294                StmtKind::Labeled { label: name, body }
295            }
296            _ => {
297                let e = self.parse_expr()?;
298                self.semicolon()?;
299                StmtKind::Expr(e)
300            }
301        };
302        Ok(Stmt::new(kind, line))
303    }
304
305    /// Whether the token `n` ahead is the identifier `kw`.
306    fn peek_kw(&self, n: usize, kw: &str) -> bool {
307        matches!(self.toks.get(self.pos + n).map(|t| &t.tok), Some(Tok::Ident(s)) if s == kw)
308    }
309    /// Whether the token `n` ahead has a newline before it.
310    fn peek_newline(&self, n: usize) -> bool {
311        self.toks
312            .get(self.pos + n)
313            .map(|t| t.newline_before)
314            .unwrap_or(false)
315    }
316
317    /// Parse a `function` declaration (the `function`/`async function` keyword is
318    /// current). `is_async` is true when a preceding `async` was consumed.
319    fn parse_func_decl(&mut self, is_async: bool) -> Result<StmtKind, String> {
320        // `async` was consumed by the caller and is part of the source text.
321        let start = self.start_at(if is_async { self.pos - 1 } else { self.pos });
322        self.advance(); // function
323        let is_generator = self.eat_punct("*");
324        let name = self.ident_name()?;
325        let params = self.parse_params()?;
326        self.expect_punct("{")?;
327        let body = self.parse_fn_body_block(is_generator, is_async)?;
328        Ok(StmtKind::FuncDecl {
329            name,
330            params,
331            body,
332            is_generator,
333            is_async,
334            span: self.span_from(start),
335        })
336    }
337
338    /// Parse a brace-delimited function body under the given generator/async
339    /// context (so `yield`/`await` inside are operators, not identifiers).
340    fn parse_fn_body_block(
341        &mut self,
342        is_generator: bool,
343        is_async: bool,
344    ) -> Result<Vec<Stmt>, String> {
345        let (pg, pa) = (self.in_generator, self.in_async);
346        self.in_generator = is_generator;
347        self.in_async = is_async;
348        let body = self.parse_block_body();
349        self.in_generator = pg;
350        self.in_async = pa;
351        body
352    }
353
354    /// Parse a function *expression* (`function`/`async function`, keyword
355    /// current). Supports `function*` generators.
356    fn parse_function_expr(&mut self, is_async: bool) -> Result<Expr, String> {
357        let start = self.start_at(if is_async { self.pos - 1 } else { self.pos });
358        self.advance(); // function
359        let is_generator = self.eat_punct("*");
360        let name = if let Tok::Ident(n) = self.tok() {
361            if !is_keyword(n) {
362                let n = n.clone();
363                self.advance();
364                Some(n)
365            } else {
366                None
367            }
368        } else {
369            None
370        };
371        let params = self.parse_params()?;
372        self.expect_punct("{")?;
373        let body = self.parse_fn_body_block(is_generator, is_async)?;
374        Ok(Expr::Function {
375            params,
376            body: FnBody::Block(body),
377            is_arrow: false,
378            name,
379            is_generator,
380            is_async,
381            is_method: false,
382            span: self.span_from(start),
383        })
384    }
385
386    /// Parse a `class` (the `class` keyword is current). `_decl` distinguishes a
387    /// declaration (name required in strict mode, but we accept optional) from an
388    /// expression.
389    /// 15.7.1: a private name must be declared by an enclosing class body.
390    /// Outside one, `this.#x` is a SyntaxError at parse time — it used to parse
391    /// and then throw a `TypeError` only if the read ever ran, so a typo in a
392    /// dead branch shipped silently.
393    fn check_private_in_scope(&mut self, property: &str) -> Result<(), String> {
394        if !property.starts_with('#') {
395            return Ok(());
396        }
397        match self.class_scopes.last_mut() {
398            // Recorded, not resolved: the declaration may still be ahead of the
399            // use in the same body, so the check runs when the body closes.
400            Some(scope) => scope.used.push(property.to_string()),
401            None => return Err(private_not_declared(property)),
402        }
403        Ok(())
404    }
405
406    fn parse_class(&mut self, _decl: bool) -> Result<ClassNode, String> {
407        self.class_scopes.push(PrivateScope::default());
408        let out = self.parse_class_inner();
409        let scope = self.class_scopes.pop().unwrap_or_default();
410        let out = out?;
411        for name in &scope.used {
412            if scope.declared.contains(name) {
413                continue;
414            }
415            // A nested class may reference a name an OUTER class declares, so an
416            // unresolved use is handed outwards rather than rejected here.
417            match self.class_scopes.last_mut() {
418                Some(outer) => outer.used.push(name.clone()),
419                None => return Err(private_not_declared(name)),
420            }
421        }
422        Ok(out)
423    }
424
425    fn parse_class_inner(&mut self) -> Result<ClassNode, String> {
426        let start = self.start_at(self.pos);
427        self.advance(); // class
428        let name = if let Tok::Ident(n) = self.tok() {
429            if !is_keyword(n) && n != "extends" {
430                let n = n.clone();
431                self.advance();
432                Some(n)
433            } else {
434                None
435            }
436        } else {
437            None
438        };
439        let parent = if self.eat_kw("extends") {
440            // The superclass is a left-hand-side expression (`extends Base`,
441            // `extends foo.Bar`).
442            Some(Box::new(self.parse_call_member()?))
443        } else {
444            None
445        };
446        self.expect_punct("{")?;
447        let mut members = Vec::new();
448        while !self.is_punct("}") && !self.at_eof() {
449            if self.eat_punct(";") {
450                continue; // stray semicolons between members
451            }
452            let member = self.parse_class_member()?;
453            if let Expr::Str(k) = &member.key {
454                if k.starts_with('#') {
455                    if let Some(scope) = self.class_scopes.last_mut() {
456                        scope.declared.push(k.clone());
457                    }
458                }
459            }
460            members.push(member);
461        }
462        self.expect_punct("}")?;
463        Ok(ClassNode {
464            name,
465            parent,
466            members,
467            span: self.span_from(start),
468        })
469    }
470
471    /// Parse one class member: `[static] [get|set|async|*] name(params){…}` or a
472    /// `[static] name [= init];` field.
473    fn parse_class_member(&mut self) -> Result<ClassMember, String> {
474        let is_static = self.is_kw("static") && !self.peek_is_member_punct(1) && {
475            self.advance();
476            true
477        };
478        let start = self.start_at(self.pos);
479        // `static { … }` — a class static initialization block (ES2022). A brace
480        // where a member key would be is unambiguous: no member name can start
481        // with `{`, so this is checked before the key parse (which otherwise
482        // rejects it as `bad member key Punct("{")`).
483        if is_static && self.is_punct("{") {
484            self.advance();
485            // Its own function context: `yield`/`await` are plain identifiers
486            // inside a static block, whatever encloses the class.
487            let body = self.parse_fn_body_block(false, false)?;
488            return Ok(ClassMember {
489                key: Expr::Str(String::new()),
490                computed: false,
491                kind: MemberKind::StaticBlock,
492                is_static: true,
493                is_generator: false,
494                is_async: false,
495                params: Vec::new(),
496                body,
497                field_init: None,
498                span: self.span_from(start),
499            });
500        }
501        // Accessor / async / generator prefixes (each contextual: only a prefix
502        // when followed by another member name, not itself the member name).
503        let mut kind = MemberKind::Method;
504        let mut is_async = false;
505        let mut is_generator = false;
506        if self.is_kw("get") && !self.peek_is_member_punct(1) {
507            self.advance();
508            kind = MemberKind::Get;
509        } else if self.is_kw("set") && !self.peek_is_member_punct(1) {
510            self.advance();
511            kind = MemberKind::Set;
512        } else {
513            if self.is_kw("async") && !self.peek_is_member_punct(1) && !self.peek_newline(1) {
514                self.advance();
515                is_async = true;
516            }
517            if self.eat_punct("*") {
518                is_generator = true;
519            }
520        }
521        // The member key (computed `[expr]`, string, number, or identifier).
522        let (key, computed) = self.parse_property_key()?;
523        // A field (no parentheses) vs a method.
524        if kind == MemberKind::Method && !self.is_punct("(") {
525            let field_init = if self.eat_punct("=") {
526                Some(self.parse_assign()?)
527            } else {
528                None
529            };
530            self.semicolon()?;
531            return Ok(ClassMember {
532                key,
533                computed,
534                kind: MemberKind::Field,
535                is_static,
536                is_generator: false,
537                is_async: false,
538                params: Vec::new(),
539                body: Vec::new(),
540                field_init,
541                span: (0, 0),
542            });
543        }
544        // A method / accessor / constructor.
545        let is_ctor = !is_static
546            && !computed
547            && matches!(&key, Expr::Str(s) if s == "constructor")
548            && kind == MemberKind::Method;
549        let params = self.parse_params()?;
550        self.expect_punct("{")?;
551        let body = self.parse_fn_body_block(is_generator, is_async)?;
552        Ok(ClassMember {
553            key,
554            computed,
555            kind: if is_ctor {
556                MemberKind::Constructor
557            } else {
558                kind
559            },
560            is_static,
561            is_generator,
562            is_async,
563            params,
564            body,
565            field_init: None,
566            span: self.span_from(start),
567        })
568    }
569
570    /// Whether the token `n` ahead is `(`, `=`, `;`, `}`, or a newline-boundary —
571    /// i.e. the current word is itself the member name, not a modifier prefix.
572    fn peek_is_member_punct(&self, n: usize) -> bool {
573        matches!(
574            self.toks.get(self.pos + n).map(|t| &t.tok),
575            Some(Tok::Punct(p)) if p == "(" || p == "=" || p == ";" || p == "}"
576        )
577    }
578
579    /// Parse a property key for a class member / object method: `[expr]` (computed),
580    /// a string, a number, or an identifier (returned as an `Expr::Str`).
581    fn parse_property_key(&mut self) -> Result<(Expr, bool), String> {
582        if self.is_punct("[") {
583            self.advance();
584            let k = self.parse_assign()?;
585            self.expect_punct("]")?;
586            Ok((k, true))
587        } else {
588            match self.tok().clone() {
589                Tok::Str(s) => {
590                    self.advance();
591                    Ok((Expr::Str(s), false))
592                }
593                Tok::Num(n) => {
594                    self.advance();
595                    Ok((Expr::Str(crate::host::fmt_number(n)), false))
596                }
597                Tok::Ident(s) => {
598                    self.advance();
599                    Ok((Expr::Str(s), false))
600                }
601                other => Err(format!(
602                    "SyntaxError: bad member key {other:?} (line {})",
603                    self.line()
604                )),
605            }
606        }
607    }
608
609    /// An optional non-newline label after break/continue.
610    fn opt_label(&mut self) -> Option<String> {
611        if self.newline_before() {
612            return None;
613        }
614        if let Tok::Ident(s) = self.tok() {
615            if !is_keyword(s) {
616                let s = s.clone();
617                self.advance();
618                return Some(s);
619            }
620        }
621        None
622    }
623
624    /// Parse statements up to (and consuming) the closing `}`.
625    fn parse_block_body(&mut self) -> Result<Vec<Stmt>, String> {
626        let mut out = Vec::new();
627        while !self.is_punct("}") && !self.at_eof() {
628            out.push(self.parse_stmt()?);
629        }
630        self.expect_punct("}")?;
631        Ok(out)
632    }
633
634    fn parse_decl_kind(&mut self) -> DeclKind {
635        let k = match self.tok() {
636            Tok::Ident(s) if s == "let" => DeclKind::Let,
637            Tok::Ident(s) if s == "const" => DeclKind::Const,
638            _ => DeclKind::Var,
639        };
640        self.advance();
641        k
642    }
643
644    fn parse_declarators(&mut self) -> Result<Vec<Declarator>, String> {
645        let mut decls = Vec::new();
646        loop {
647            let target = self.parse_binding_target()?;
648            let init = if self.eat_punct("=") {
649                Some(self.parse_assign()?)
650            } else {
651                None
652            };
653            decls.push(Declarator { target, init });
654            if !self.eat_punct(",") {
655                break;
656            }
657        }
658        Ok(decls)
659    }
660
661    /// A binding target: identifier or array/object destructuring pattern.
662    fn parse_binding_target(&mut self) -> Result<Expr, String> {
663        if self.is_punct("[") {
664            self.parse_array_literal()
665        } else if self.is_punct("{") {
666            self.parse_object_literal()
667        } else {
668            Ok(Expr::Ident(self.ident_name()?))
669        }
670    }
671
672    fn parse_if(&mut self) -> Result<StmtKind, String> {
673        self.advance(); // if
674        self.expect_punct("(")?;
675        let test = self.parse_expr()?;
676        self.expect_punct(")")?;
677        let cons = Box::new(self.parse_stmt()?);
678        let alt = if self.eat_kw("else") {
679            Some(Box::new(self.parse_stmt()?))
680        } else {
681            None
682        };
683        Ok(StmtKind::If { test, cons, alt })
684    }
685
686    fn parse_while(&mut self) -> Result<StmtKind, String> {
687        self.advance();
688        self.expect_punct("(")?;
689        let test = self.parse_expr()?;
690        self.expect_punct(")")?;
691        let body = Box::new(self.parse_stmt()?);
692        Ok(StmtKind::While { test, body })
693    }
694
695    fn parse_do_while(&mut self) -> Result<StmtKind, String> {
696        self.advance();
697        let body = Box::new(self.parse_stmt()?);
698        if !self.eat_kw("while") {
699            return Err(format!(
700                "SyntaxError: expected 'while' (line {})",
701                self.line()
702            ));
703        }
704        self.expect_punct("(")?;
705        let test = self.parse_expr()?;
706        self.expect_punct(")")?;
707        self.semicolon()?;
708        Ok(StmtKind::DoWhile { body, test })
709    }
710
711    fn parse_for(&mut self) -> Result<StmtKind, String> {
712        self.advance();
713        // `for await (… of …)` — the async-iteration form (valid in an async body).
714        let is_await = self.eat_kw("await");
715        self.expect_punct("(")?;
716        // Optional declaration or expression init.
717        let decl_kind = match self.tok() {
718            Tok::Ident(s) if s == "var" || s == "let" || s == "const" => {
719                Some(self.parse_decl_kind())
720            }
721            _ => None,
722        };
723        // Empty init: `for (;;)`.
724        if decl_kind.is_none() && self.is_punct(";") {
725            return self.parse_c_for(None);
726        }
727        // Parse the first binding/expression, then decide of/in vs C-style.
728        let first_target = if decl_kind.is_some() {
729            self.parse_binding_target()?
730        } else {
731            self.parse_expr_no_in()?
732        };
733        if self.eat_kw("of") {
734            let iter = self.parse_assign()?;
735            self.expect_punct(")")?;
736            let body = Box::new(self.parse_stmt()?);
737            return Ok(StmtKind::ForOf {
738                decl_kind,
739                target: first_target,
740                iter,
741                body,
742                is_await,
743            });
744        }
745        if self.eat_kw("in") {
746            let object = self.parse_assign()?;
747            self.expect_punct(")")?;
748            let body = Box::new(self.parse_stmt()?);
749            return Ok(StmtKind::ForIn {
750                decl_kind,
751                target: first_target,
752                object,
753                body,
754            });
755        }
756        // C-style: reconstruct the init statement.
757        let init_stmt = if let Some(k) = decl_kind {
758            let init = if self.eat_punct("=") {
759                Some(self.parse_assign()?)
760            } else {
761                None
762            };
763            let mut decls = vec![Declarator {
764                target: first_target,
765                init,
766            }];
767            while self.eat_punct(",") {
768                let target = self.parse_binding_target()?;
769                let init = if self.eat_punct("=") {
770                    Some(self.parse_assign()?)
771                } else {
772                    None
773                };
774                decls.push(Declarator { target, init });
775            }
776            StmtKind::Decl { kind: k, decls }
777        } else {
778            // A non-declaration C-style init may be a comma sequence
779            // (`for (i = 0, n = a.length; …)`) — extend past the first assignment.
780            let init = if self.is_punct(",") {
781                let mut items = vec![first_target];
782                while self.eat_punct(",") {
783                    items.push(self.parse_expr_no_in()?);
784                }
785                Expr::Sequence(items)
786            } else {
787                first_target
788            };
789            StmtKind::Expr(init)
790        };
791        self.parse_c_for(Some(Stmt::from(init_stmt)))
792    }
793
794    fn parse_c_for(&mut self, init: Option<Stmt>) -> Result<StmtKind, String> {
795        self.expect_punct(";")?;
796        let test = if self.is_punct(";") {
797            None
798        } else {
799            Some(self.parse_expr()?)
800        };
801        self.expect_punct(";")?;
802        let update = if self.is_punct(")") {
803            None
804        } else {
805            Some(self.parse_expr()?)
806        };
807        self.expect_punct(")")?;
808        let body = Box::new(self.parse_stmt()?);
809        Ok(StmtKind::For {
810            init: init.map(Box::new),
811            test,
812            update,
813            body,
814        })
815    }
816
817    fn parse_switch(&mut self) -> Result<StmtKind, String> {
818        self.advance();
819        self.expect_punct("(")?;
820        let disc = self.parse_expr()?;
821        self.expect_punct(")")?;
822        self.expect_punct("{")?;
823        let mut cases = Vec::new();
824        while !self.is_punct("}") && !self.at_eof() {
825            let test = if self.eat_kw("case") {
826                let e = self.parse_expr()?;
827                Some(e)
828            } else if self.eat_kw("default") {
829                None
830            } else {
831                return Err(format!(
832                    "SyntaxError: expected 'case' or 'default' (line {})",
833                    self.line()
834                ));
835            };
836            self.expect_punct(":")?;
837            let mut body = Vec::new();
838            while !self.is_punct("}")
839                && !self.is_kw("case")
840                && !self.is_kw("default")
841                && !self.at_eof()
842            {
843                body.push(self.parse_stmt()?);
844            }
845            cases.push(SwitchCase { test, body });
846        }
847        self.expect_punct("}")?;
848        Ok(StmtKind::Switch { disc, cases })
849    }
850
851    fn parse_try(&mut self) -> Result<StmtKind, String> {
852        self.advance();
853        self.expect_punct("{")?;
854        let block = self.parse_block_body()?;
855        let handler = if self.eat_kw("catch") {
856            let param = if self.eat_punct("(") {
857                let p = self.parse_binding_target()?;
858                self.expect_punct(")")?;
859                Some(p)
860            } else {
861                None
862            };
863            self.expect_punct("{")?;
864            let body = self.parse_block_body()?;
865            Some((param, body))
866        } else {
867            None
868        };
869        let finalizer = if self.eat_kw("finally") {
870            self.expect_punct("{")?;
871            Some(self.parse_block_body()?)
872        } else {
873            None
874        };
875        Ok(StmtKind::Try {
876            block,
877            handler,
878            finalizer,
879        })
880    }
881
882    // ── expressions ──────────────────────────────────────────────────────
883    /// Full expression, including the comma sequence operator.
884    fn parse_expr(&mut self) -> Result<Expr, String> {
885        let first = self.parse_assign()?;
886        if self.is_punct(",") {
887            let mut items = vec![first];
888            while self.eat_punct(",") {
889                items.push(self.parse_assign()?);
890            }
891            Ok(Expr::Sequence(items))
892        } else {
893            Ok(first)
894        }
895    }
896
897    /// Like `parse_expr` but stops before `in` (used in `for` init position).
898    fn parse_expr_no_in(&mut self) -> Result<Expr, String> {
899        // For simplicity the no-in variant only parses an assignment/LHS chain,
900        // which is sufficient for `for (x in ...)` / `for (x of ...)` heads.
901        let saved = self.no_in;
902        self.no_in = true;
903        let r = self.parse_assign();
904        self.no_in = saved;
905        r
906    }
907
908    /// Whether `e` is an optional chain — i.e. its member/call SPINE carries a
909    /// `?.` link. Mirrors the compiler's spine walk; used only to decide
910    /// whether a set of parentheses is a chain boundary worth recording.
911    fn has_optional_link(e: &Expr) -> bool {
912        match e {
913            Expr::Member {
914                object, optional, ..
915            } => *optional || Self::has_optional_link(object),
916            Expr::Index {
917                object, optional, ..
918            } => *optional || Self::has_optional_link(object),
919            Expr::Call { func, optional, .. } => *optional || Self::has_optional_link(func),
920            _ => false,
921        }
922    }
923
924    /// Run `f` with `in` re-enabled (inside a parenthesised/bracketed sub-
925    /// expression of a `for` LHS, where the no-in restriction does not apply).
926    fn allow_in<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, String>) -> Result<T, String> {
927        let saved = self.no_in;
928        self.no_in = false;
929        let r = f(self);
930        self.no_in = saved;
931        r
932    }
933
934    fn parse_assign(&mut self) -> Result<Expr, String> {
935        // Arrow function detection.
936        if let Some(arrow) = self.try_parse_arrow()? {
937            return Ok(arrow);
938        }
939        let left = self.parse_conditional()?;
940        // Assignment operators (right-associative).
941        let op = match self.tok() {
942            Tok::Punct(p) => p.clone(),
943            _ => return Ok(left),
944        };
945        let compound = match op.as_str() {
946            "=" => None,
947            "+=" => Some(BinOp::Add),
948            "-=" => Some(BinOp::Sub),
949            "*=" => Some(BinOp::Mul),
950            "/=" => Some(BinOp::Div),
951            "%=" => Some(BinOp::Mod),
952            "**=" => Some(BinOp::Pow),
953            "&=" => Some(BinOp::BitAnd),
954            "|=" => Some(BinOp::BitOr),
955            "^=" => Some(BinOp::BitXor),
956            "<<=" => Some(BinOp::Shl),
957            ">>=" => Some(BinOp::Shr),
958            ">>>=" => Some(BinOp::UShr),
959            "&&=" | "||=" | "??=" => {
960                // Logical assignment. The operator is CARRIED, not desugared:
961                // building `Logical(op, left.clone(), value)` here duplicated
962                // the target, so `o[k()] ||= 1` evaluated `k` twice — once for
963                // the read and once for the write — where node evaluates it
964                // once and may not write at all.
965                self.advance();
966                let value = self.parse_assign()?;
967                let lop = match op.as_str() {
968                    "&&=" => LogicalOp::And,
969                    "||=" => LogicalOp::Or,
970                    _ => LogicalOp::Nullish,
971                };
972                return Ok(Expr::Assign {
973                    target: Box::new(left),
974                    op: Some(AssignOp::Logical(lop)),
975                    value: Box::new(value),
976                });
977            }
978            _ => return Ok(left),
979        };
980        self.advance();
981        let value = self.parse_assign()?;
982        Ok(Expr::Assign {
983            target: Box::new(left),
984            op: compound.map(AssignOp::Binary),
985            value: Box::new(value),
986        })
987    }
988
989    fn parse_conditional(&mut self) -> Result<Expr, String> {
990        let test = self.parse_binary(0)?;
991        if self.eat_punct("?") {
992            let cons = self.parse_assign()?;
993            self.expect_punct(":")?;
994            let alt = self.parse_assign()?;
995            Ok(Expr::Conditional {
996                test: Box::new(test),
997                cons: Box::new(cons),
998                alt: Box::new(alt),
999            })
1000        } else {
1001            Ok(test)
1002        }
1003    }
1004
1005    /// Precedence-climbing binary parser. Handles `&& || ??` as logical nodes.
1006    fn parse_binary(&mut self, min_prec: u8) -> Result<Expr, String> {
1007        let mut left = self.parse_unary()?;
1008        while let Some((prec, right_assoc, logical, bin)) = self.bin_info() {
1009            if prec < min_prec {
1010                break;
1011            }
1012            self.advance();
1013            let next_min = if right_assoc { prec } else { prec + 1 };
1014            let right = self.parse_binary(next_min)?;
1015            left = if let Some(lop) = logical {
1016                Expr::Logical(lop, Box::new(left), Box::new(right))
1017            } else {
1018                Expr::Binary(bin.unwrap(), Box::new(left), Box::new(right))
1019            };
1020        }
1021        Ok(left)
1022    }
1023
1024    /// `(precedence, right_assoc, logical_op, bin_op)` for the current token.
1025    fn bin_info(&self) -> Option<(u8, bool, Option<LogicalOp>, Option<BinOp>)> {
1026        let p = match self.tok() {
1027            Tok::Punct(p) => p.as_str(),
1028            // In a `for` LHS (no-in) context, `in` is the loop separator, not a
1029            // relational operator.
1030            Tok::Ident(s) if s == "in" => {
1031                if self.no_in {
1032                    return None;
1033                }
1034                "in"
1035            }
1036            Tok::Ident(s) if s == "instanceof" => "instanceof",
1037            _ => return None,
1038        };
1039        let (prec, ra, log, bin) = match p {
1040            "??" => (1, false, Some(LogicalOp::Nullish), None),
1041            "||" => (2, false, Some(LogicalOp::Or), None),
1042            "&&" => (3, false, Some(LogicalOp::And), None),
1043            "|" => (4, false, None, Some(BinOp::BitOr)),
1044            "^" => (5, false, None, Some(BinOp::BitXor)),
1045            "&" => (6, false, None, Some(BinOp::BitAnd)),
1046            "==" => (7, false, None, Some(BinOp::EqEq)),
1047            "!=" => (7, false, None, Some(BinOp::NeEq)),
1048            "===" => (7, false, None, Some(BinOp::EqEqEq)),
1049            "!==" => (7, false, None, Some(BinOp::NeEqEq)),
1050            "<" => (8, false, None, Some(BinOp::Lt)),
1051            "<=" => (8, false, None, Some(BinOp::Le)),
1052            ">" => (8, false, None, Some(BinOp::Gt)),
1053            ">=" => (8, false, None, Some(BinOp::Ge)),
1054            "in" => (8, false, None, Some(BinOp::In)),
1055            "instanceof" => (8, false, None, Some(BinOp::InstanceOf)),
1056            "<<" => (9, false, None, Some(BinOp::Shl)),
1057            ">>" => (9, false, None, Some(BinOp::Shr)),
1058            ">>>" => (9, false, None, Some(BinOp::UShr)),
1059            "+" => (10, false, None, Some(BinOp::Add)),
1060            "-" => (10, false, None, Some(BinOp::Sub)),
1061            "*" => (11, false, None, Some(BinOp::Mul)),
1062            "/" => (11, false, None, Some(BinOp::Div)),
1063            "%" => (11, false, None, Some(BinOp::Mod)),
1064            "**" => (12, true, None, Some(BinOp::Pow)),
1065            _ => return None,
1066        };
1067        Some((prec, ra, log, bin))
1068    }
1069
1070    /// Reject a `**` directly after a just-parsed UnaryExpression. JS only
1071    /// allows an UpdateExpression there (`x++ ** y` and `++x ** y` are fine),
1072    /// so an unparenthesized `-x ** y` / `typeof x ** y` / `await x ** y` is a
1073    /// SyntaxError rather than a silently-reassociated `-(x ** y)`.
1074    fn reject_unary_before_pow(&mut self) -> Result<(), String> {
1075        if self.is_punct("**") {
1076            return Err(format!(
1077                "SyntaxError: Unary operator used immediately before exponentiation \
1078                 expression. Parenthesis must be used to disambiguate operator \
1079                 precedence (line {})",
1080                self.line()
1081            ));
1082        }
1083        Ok(())
1084    }
1085
1086    fn parse_unary(&mut self) -> Result<Expr, String> {
1087        let op = match self.tok() {
1088            Tok::Punct(p) if p == "!" => Some(UnOp::Not),
1089            Tok::Punct(p) if p == "~" => Some(UnOp::BitNot),
1090            Tok::Punct(p) if p == "+" => Some(UnOp::Pos),
1091            Tok::Punct(p) if p == "-" => Some(UnOp::Neg),
1092            Tok::Ident(s) if s == "typeof" => Some(UnOp::TypeOf),
1093            Tok::Ident(s) if s == "void" => Some(UnOp::Void),
1094            Tok::Ident(s) if s == "delete" => Some(UnOp::Delete),
1095            _ => None,
1096        };
1097        if let Some(op) = op {
1098            self.advance();
1099            let e = self.parse_unary()?;
1100            // `ExponentiationExpression : UpdateExpression ** …` — a
1101            // UnaryExpression on the left of `**` is a SyntaxError, so
1102            // `-x ** y` must be written `(-x) ** y` or `-(x ** y)`.
1103            self.reject_unary_before_pow()?;
1104            return Ok(Expr::Unary(op, Box::new(e)));
1105        }
1106        // Prefix ++/--.
1107        if self.is_punct("++") || self.is_punct("--") {
1108            let op = if self.is_punct("++") {
1109                UpdateOp::Inc
1110            } else {
1111                UpdateOp::Dec
1112            };
1113            self.advance();
1114            let e = self.parse_unary()?;
1115            return Ok(Expr::Update {
1116                op,
1117                prefix: true,
1118                target: Box::new(e),
1119            });
1120        }
1121        self.parse_postfix()
1122    }
1123
1124    fn parse_postfix(&mut self) -> Result<Expr, String> {
1125        let mut e = self.parse_call_member()?;
1126        // Postfix ++/-- (no line break before).
1127        if (self.is_punct("++") || self.is_punct("--")) && !self.newline_before() {
1128            let op = if self.is_punct("++") {
1129                UpdateOp::Inc
1130            } else {
1131                UpdateOp::Dec
1132            };
1133            self.advance();
1134            e = Expr::Update {
1135                op,
1136                prefix: false,
1137                target: Box::new(e),
1138            };
1139        }
1140        Ok(e)
1141    }
1142
1143    fn parse_call_member(&mut self) -> Result<Expr, String> {
1144        let mut e = if self.eat_kw("new") {
1145            // `new.target` meta-property.
1146            if self.is_punct(".") {
1147                self.advance();
1148                let prop = self.ident_name()?;
1149                if prop != "target" {
1150                    return Err(format!(
1151                        "SyntaxError: expected 'target' (line {})",
1152                        self.line()
1153                    ));
1154                }
1155                Expr::NewTarget
1156            } else {
1157                let callee = self.parse_call_member_no_call()?;
1158                // An optional chain cannot be the callee of `new` (13.3.5.1):
1159                // `new a?.b()` and `new C?.()` are early errors. Parenthesising
1160                // the chain ends it, so `new (a?.b)()` is legal — which is why
1161                // the check is on the callee's own spine, and the parenthesised
1162                // form records a chain boundary that clears `optional`.
1163                if Self::has_optional_link(&callee) || self.is_punct("?.") {
1164                    return Err(
1165                        "SyntaxError: Invalid optional chain from new expression".to_string()
1166                    );
1167                }
1168                let args = if self.is_punct("(") {
1169                    self.parse_args()?
1170                } else {
1171                    Vec::new()
1172                };
1173                Expr::New {
1174                    callee: Box::new(callee),
1175                    args,
1176                }
1177            }
1178        } else {
1179            self.parse_primary()?
1180        };
1181        loop {
1182            if self.eat_punct(".") {
1183                let property = self.ident_name()?;
1184                self.check_private_in_scope(&property)?;
1185                e = Expr::Member {
1186                    object: Box::new(e),
1187                    property,
1188                    optional: false,
1189                };
1190            } else if self.eat_punct("?.") {
1191                if self.is_punct("(") {
1192                    let args = self.parse_args()?;
1193                    e = Expr::Call {
1194                        func: Box::new(e),
1195                        args,
1196                        optional: true,
1197                    };
1198                } else if self.is_punct("[") {
1199                    self.advance();
1200                    let index = self.allow_in(|p| p.parse_expr())?;
1201                    self.expect_punct("]")?;
1202                    e = Expr::Index {
1203                        object: Box::new(e),
1204                        index: Box::new(index),
1205                        optional: true,
1206                    };
1207                } else {
1208                    let property = self.ident_name()?;
1209                    e = Expr::Member {
1210                        object: Box::new(e),
1211                        property,
1212                        optional: true,
1213                    };
1214                }
1215            } else if self.is_punct("[") {
1216                self.advance();
1217                let index = self.allow_in(|p| p.parse_expr())?;
1218                self.expect_punct("]")?;
1219                e = Expr::Index {
1220                    object: Box::new(e),
1221                    index: Box::new(index),
1222                    optional: false,
1223                };
1224            } else if self.is_punct("(") {
1225                let args = self.parse_args()?;
1226                e = Expr::Call {
1227                    func: Box::new(e),
1228                    args,
1229                    optional: false,
1230                };
1231            } else if matches!(self.tok(), Tok::Template { .. }) {
1232                // A template literal immediately after a callee is a *tagged*
1233                // template: `` tag`...` `` → `tag(strings, ...values)`.
1234                // A tagged template cannot sit on an optional chain (13.3.11.1):
1235                // `a?.b`t`` is an early error, because the tag would have to be
1236                // called even when the chain short-circuited.
1237                if Self::has_optional_link(&e) {
1238                    return Err(
1239                        "SyntaxError: Invalid tagged template on optional chain".to_string()
1240                    );
1241                }
1242                e = self.parse_tagged_template(e)?;
1243            } else {
1244                break;
1245            }
1246        }
1247        Ok(e)
1248    }
1249
1250    /// Parse `` tag`a${x}b` `` into a `TaggedTemplate` node (the tag expression is
1251    /// already parsed as `tag`, and the current token is the template).
1252    fn parse_tagged_template(&mut self, tag: Expr) -> Result<Expr, String> {
1253        let (quasis, raws, exprs_src, expr_at) = match self.tok().clone() {
1254            Tok::Template {
1255                quasis,
1256                raws,
1257                exprs,
1258                expr_at,
1259            } => (quasis, raws, exprs, expr_at),
1260            _ => unreachable!(),
1261        };
1262        self.advance();
1263        let mut exprs = Vec::new();
1264        for (src, at) in exprs_src.iter().zip(expr_at) {
1265            exprs.push(self.parse_field(src, at)?);
1266        }
1267        Ok(Expr::TaggedTemplate {
1268            tag: Box::new(tag),
1269            quasis,
1270            raws,
1271            exprs,
1272        })
1273    }
1274
1275    /// Member chain without a trailing call — the `new X.Y` callee grammar.
1276    fn parse_call_member_no_call(&mut self) -> Result<Expr, String> {
1277        let mut e = self.parse_primary()?;
1278        loop {
1279            if self.eat_punct(".") {
1280                let property = self.ident_name()?;
1281                self.check_private_in_scope(&property)?;
1282                e = Expr::Member {
1283                    object: Box::new(e),
1284                    property,
1285                    optional: false,
1286                };
1287            } else if self.is_punct("[") {
1288                self.advance();
1289                let index = self.allow_in(|p| p.parse_expr())?;
1290                self.expect_punct("]")?;
1291                e = Expr::Index {
1292                    object: Box::new(e),
1293                    index: Box::new(index),
1294                    optional: false,
1295                };
1296            } else {
1297                break;
1298            }
1299        }
1300        Ok(e)
1301    }
1302
1303    fn parse_args(&mut self) -> Result<Vec<Expr>, String> {
1304        self.expect_punct("(")?;
1305        // Inside a call-argument list `in` is always a relational operator, even
1306        // in a `for` LHS.
1307        let args = self.allow_in(|p| {
1308            let mut args = Vec::new();
1309            while !p.is_punct(")") {
1310                if p.eat_punct("...") {
1311                    let e = p.parse_assign()?;
1312                    args.push(Expr::Spread(Box::new(e)));
1313                } else {
1314                    args.push(p.parse_assign()?);
1315                }
1316                if !p.eat_punct(",") {
1317                    break;
1318                }
1319            }
1320            Ok(args)
1321        })?;
1322        self.expect_punct(")")?;
1323        Ok(args)
1324    }
1325
1326    fn parse_primary(&mut self) -> Result<Expr, String> {
1327        match self.tok().clone() {
1328            Tok::Num(n) => {
1329                self.advance();
1330                Ok(Expr::Number(n))
1331            }
1332            Tok::BigInt(s) => {
1333                self.advance();
1334                Ok(Expr::BigInt(s))
1335            }
1336            Tok::Regex(pat, flags) => {
1337                self.advance();
1338                Ok(Expr::Regex(pat, flags))
1339            }
1340            Tok::Str(s) => {
1341                self.advance();
1342                Ok(Expr::Str(s))
1343            }
1344            Tok::Template {
1345                quasis,
1346                raws: _,
1347                exprs,
1348                expr_at,
1349            } => {
1350                self.advance();
1351                let mut parsed = Vec::new();
1352                for (src, at) in exprs.iter().zip(expr_at) {
1353                    parsed.push(self.parse_field(src, at)?);
1354                }
1355                Ok(Expr::Template {
1356                    quasis,
1357                    exprs: parsed,
1358                })
1359            }
1360            Tok::Punct(p) if p == "(" => {
1361                self.advance();
1362                let e = self.parse_expr()?;
1363                self.expect_punct(")")?;
1364                // Parentheses are otherwise erased, but around an OPTIONAL CHAIN
1365                // they are load-bearing: they END the chain, so a `?.` inside
1366                // them cannot short-circuit an access written outside them.
1367                // `o?.a.b` is `undefined` when `o` is nullish; `(o?.a).b` reads
1368                // `.b` off that `undefined` and THROWS (ECMA-262 13.3.1 —
1369                // `ParenthesizedExpression` is not an `OptionalExpression`).
1370                // With the parentheses dropped the two parsed to the same tree
1371                // and both answered `undefined`.
1372                //
1373                // The boundary is spelled as a one-element sequence rather than
1374                // a new node type: `(e)` and `e,` evaluate identically, every
1375                // existing pass already walks `Sequence` correctly, and the
1376                // chain-spine walk does not descend through it — which is the
1377                // whole point. Only chains are wrapped, so no other expression's
1378                // tree shape changes.
1379                if Self::has_optional_link(&e) {
1380                    return Ok(Expr::Sequence(vec![e]));
1381                }
1382                Ok(e)
1383            }
1384            Tok::Punct(p) if p == "[" => self.parse_array_literal(),
1385            Tok::Punct(p) if p == "{" => self.parse_object_literal(),
1386            Tok::Ident(s) => {
1387                match s.as_str() {
1388                    "true" => {
1389                        self.advance();
1390                        Ok(Expr::True)
1391                    }
1392                    "false" => {
1393                        self.advance();
1394                        Ok(Expr::False)
1395                    }
1396                    "null" => {
1397                        self.advance();
1398                        Ok(Expr::Null)
1399                    }
1400                    "this" => {
1401                        self.advance();
1402                        Ok(Expr::This)
1403                    }
1404                    "super" => {
1405                        self.advance();
1406                        Ok(Expr::Super)
1407                    }
1408                    "class" => Ok(Expr::Class(Box::new(self.parse_class(false)?))),
1409                    "function" => self.parse_function_expr(false),
1410                    "async" if self.peek_kw(1, "function") && !self.peek_newline(1) => {
1411                        self.advance(); // async
1412                        self.parse_function_expr(true)
1413                    }
1414                    "yield" if self.in_generator => {
1415                        self.advance();
1416                        let delegate = self.eat_punct("*");
1417                        // `yield` with no argument (before `)`, `]`, `}`, `,`, `;`,
1418                        // newline, or EOF).
1419                        let arg = if delegate
1420                            || !(self.is_punct(")")
1421                                || self.is_punct("]")
1422                                || self.is_punct("}")
1423                                || self.is_punct(",")
1424                                || self.is_punct(";")
1425                                || self.is_punct(":")
1426                                || self.newline_before()
1427                                || self.at_eof())
1428                        {
1429                            Some(Box::new(self.parse_assign()?))
1430                        } else {
1431                            None
1432                        };
1433                        Ok(Expr::Yield { arg, delegate })
1434                    }
1435                    "await" if self.in_async => {
1436                        self.advance();
1437                        let e = self.parse_unary()?;
1438                        // An AwaitExpression is a UnaryExpression, so it too
1439                        // cannot sit directly left of `**`.
1440                        self.reject_unary_before_pow()?;
1441                        Ok(Expr::Await(Box::new(e)))
1442                    }
1443                    _ if is_keyword(&s) => Err(format!(
1444                        "SyntaxError: unexpected keyword '{s}' (line {})",
1445                        self.line()
1446                    )),
1447                    _ => {
1448                        self.advance();
1449                        Ok(Expr::Ident(s))
1450                    }
1451                }
1452            }
1453            other => Err(format!(
1454                "SyntaxError: unexpected token {other:?} (line {})",
1455                self.line()
1456            )),
1457        }
1458    }
1459
1460    fn parse_array_literal(&mut self) -> Result<Expr, String> {
1461        self.expect_punct("[")?;
1462        let mut items = Vec::new();
1463        while !self.is_punct("]") {
1464            if self.is_punct(",") {
1465                // Elision: the element is a HOLE, not a stored `undefined`.
1466                items.push(Expr::Hole);
1467                self.advance();
1468                continue;
1469            }
1470            if self.eat_punct("...") {
1471                let e = self.parse_assign()?;
1472                items.push(Expr::Spread(Box::new(e)));
1473            } else {
1474                items.push(self.parse_assign()?);
1475            }
1476            if !self.eat_punct(",") {
1477                break;
1478            }
1479        }
1480        self.expect_punct("]")?;
1481        Ok(Expr::Array(items))
1482    }
1483
1484    fn parse_object_literal(&mut self) -> Result<Expr, String> {
1485        self.expect_punct("{")?;
1486        let mut props = Vec::new();
1487        while !self.is_punct("}") {
1488            if self.eat_punct("...") {
1489                let e = self.parse_assign()?;
1490                props.push(Prop::Spread(e));
1491                if !self.eat_punct(",") {
1492                    break;
1493                }
1494                continue;
1495            }
1496            // `get key() {}` / `set key(v) {}` accessor (contextual: `get`/`set`
1497            // is a modifier only when followed by another key, not `:`/`(`/`,`).
1498            if (self.is_kw("get") || self.is_kw("set"))
1499                && !self.peek_is_member_punct(1)
1500                && !matches!(self.toks.get(self.pos + 1).map(|t| &t.tok), Some(Tok::Punct(p)) if p == ":" || p == ",")
1501            {
1502                let is_getter = self.is_kw("get");
1503                let start = self.start_at(self.pos);
1504                self.advance();
1505                let (key, computed) = self.parse_property_key()?;
1506                let params = self.parse_params()?;
1507                self.expect_punct("{")?;
1508                let body = self.parse_block_body()?;
1509                let func = Expr::Function {
1510                    params,
1511                    body: FnBody::Block(body),
1512                    is_arrow: false,
1513                    name: None,
1514                    is_generator: false,
1515                    is_async: false,
1516                    is_method: true,
1517                    span: self.span_from(start),
1518                };
1519                props.push(Prop::Accessor {
1520                    key,
1521                    computed,
1522                    is_getter,
1523                    func,
1524                });
1525                if !self.eat_punct(",") {
1526                    break;
1527                }
1528                continue;
1529            }
1530            // Concise-method modifiers: `async` and/or `*` before the key.
1531            let mut m_async = false;
1532            let mut m_gen = false;
1533            let start = self.start_at(self.pos);
1534            if self.is_kw("async")
1535                && !self.peek_is_member_punct(1)
1536                && !self.peek_newline(1)
1537                && !matches!(self.toks.get(self.pos + 1).map(|t| &t.tok), Some(Tok::Punct(p)) if p == ":" || p == ",")
1538            {
1539                self.advance();
1540                m_async = true;
1541            }
1542            if self.is_punct("*") {
1543                self.advance();
1544                m_gen = true;
1545            }
1546            let (key, computed) = self.parse_property_key()?;
1547            // Method shorthand `key(params) { }` (incl. `*gen(){}`, `async m(){}`).
1548            if self.is_punct("(") {
1549                let params = self.parse_params()?;
1550                self.expect_punct("{")?;
1551                let body = self.parse_fn_body_block(m_gen, m_async)?;
1552                let f = Expr::Function {
1553                    params,
1554                    body: FnBody::Block(body),
1555                    is_arrow: false,
1556                    name: None,
1557                    is_generator: m_gen,
1558                    is_async: m_async,
1559                    is_method: true,
1560                    span: self.span_from(start),
1561                };
1562                props.push(Prop::KeyValue {
1563                    key,
1564                    value: f,
1565                    computed,
1566                });
1567            } else if self.eat_punct(":") {
1568                let value = self.parse_assign()?;
1569                props.push(Prop::KeyValue {
1570                    key,
1571                    value,
1572                    computed,
1573                });
1574            } else {
1575                // Shorthand `{ x }` -> key "x", value ident x. Or with default
1576                // in a destructuring pattern: `{ x = 1 }`.
1577                let name = match &key {
1578                    Expr::Str(s) => s.clone(),
1579                    _ => return Err(format!("SyntaxError: bad shorthand (line {})", self.line())),
1580                };
1581                let value = if self.eat_punct("=") {
1582                    // Pattern default; represent as Assign so destructuring reads it.
1583                    let d = self.parse_assign()?;
1584                    Expr::Assign {
1585                        target: Box::new(Expr::Ident(name.clone())),
1586                        op: None,
1587                        value: Box::new(d),
1588                    }
1589                } else {
1590                    Expr::Ident(name)
1591                };
1592                props.push(Prop::KeyValue {
1593                    key,
1594                    value,
1595                    computed,
1596                });
1597            }
1598            if !self.eat_punct(",") {
1599                break;
1600            }
1601        }
1602        self.expect_punct("}")?;
1603        Ok(Expr::Object(props))
1604    }
1605
1606    // ── functions / arrows ───────────────────────────────────────────────
1607    fn parse_params(&mut self) -> Result<Vec<Param>, String> {
1608        self.expect_punct("(")?;
1609        let mut params = Vec::new();
1610        while !self.is_punct(")") {
1611            let rest = self.eat_punct("...");
1612            let pattern = self.parse_binding_target()?;
1613            let default = if !rest && self.eat_punct("=") {
1614                Some(self.parse_assign()?)
1615            } else {
1616                None
1617            };
1618            params.push(Param {
1619                pattern,
1620                default,
1621                rest,
1622            });
1623            if !self.eat_punct(",") {
1624                break;
1625            }
1626        }
1627        self.expect_punct(")")?;
1628        Ok(params)
1629    }
1630
1631    /// Try to parse an arrow function starting at the current position. Returns
1632    /// `None` (without consuming) if the head is not an arrow.
1633    fn try_parse_arrow(&mut self) -> Result<Option<Expr>, String> {
1634        let start = self.start_at(self.pos);
1635        // `async` prefix on an arrow (`async x => …` / `async (…) => …`), only
1636        // when `async` is not itself the parameter and no newline intervenes.
1637        let mut is_async = false;
1638        let mut base = self.pos;
1639        if self.is_kw("async") && !self.peek_newline(1) {
1640            let next = self.toks.get(self.pos + 1).map(|t| &t.tok);
1641            let looks_async_arrow = matches!(next, Some(Tok::Punct(p)) if p == "(")
1642                || matches!(next, Some(Tok::Ident(n)) if !is_keyword(n) && self.peek_is_arrow_after(2));
1643            if looks_async_arrow {
1644                is_async = true;
1645                base += 1;
1646            }
1647        }
1648        // `ident => ...`
1649        if let Some(Tok::Ident(name)) = self.toks.get(base).map(|t| &t.tok) {
1650            if !is_keyword(name)
1651                && matches!(self.toks.get(base + 1).map(|t| &t.tok), Some(Tok::Punct(p)) if p == "=>")
1652            {
1653                let name = name.clone();
1654                if is_async {
1655                    self.advance(); // async
1656                }
1657                self.advance(); // ident
1658                self.advance(); // =>
1659                let body = self.parse_arrow_body(is_async)?;
1660                return Ok(Some(Expr::Function {
1661                    params: vec![Param {
1662                        pattern: Expr::Ident(name),
1663                        default: None,
1664                        rest: false,
1665                    }],
1666                    body,
1667                    is_arrow: true,
1668                    name: None,
1669                    is_generator: false,
1670                    is_async,
1671                    is_method: false,
1672                    span: self.span_from(start),
1673                }));
1674            }
1675        }
1676        // `( ... ) => ...`
1677        if matches!(self.toks.get(base).map(|t| &t.tok), Some(Tok::Punct(p)) if p == "(") {
1678            if let Some(close) = self.matching_paren(base) {
1679                let after = close + 1;
1680                if matches!(self.toks.get(after).map(|t| &t.tok), Some(Tok::Punct(p)) if p == "=>")
1681                {
1682                    if is_async {
1683                        self.advance(); // async
1684                    }
1685                    let params = self.parse_params()?;
1686                    self.expect_punct("=>")?;
1687                    let body = self.parse_arrow_body(is_async)?;
1688                    return Ok(Some(Expr::Function {
1689                        params,
1690                        body,
1691                        is_arrow: true,
1692                        name: None,
1693                        is_generator: false,
1694                        is_async,
1695                        is_method: false,
1696                        span: self.span_from(start),
1697                    }));
1698                }
1699            }
1700        }
1701        Ok(None)
1702    }
1703
1704    fn parse_arrow_body(&mut self, is_async: bool) -> Result<FnBody, String> {
1705        let (pg, pa) = (self.in_generator, self.in_async);
1706        self.in_generator = false;
1707        self.in_async = is_async;
1708        let r = if self.is_punct("{") {
1709            self.advance();
1710            self.parse_block_body().map(FnBody::Block)
1711        } else {
1712            self.parse_assign().map(|e| FnBody::Expr(Box::new(e)))
1713        };
1714        self.in_generator = pg;
1715        self.in_async = pa;
1716        r
1717    }
1718
1719    /// Whether the token `n` positions ahead is `=>`.
1720    fn peek_is_arrow_after(&self, n: usize) -> bool {
1721        matches!(self.toks.get(self.pos + n).map(|t| &t.tok), Some(Tok::Punct(p)) if p == "=>")
1722    }
1723
1724    /// Index of the `)` matching the `(` at `open`, skipping nested brackets.
1725    fn matching_paren(&self, open: usize) -> Option<usize> {
1726        let mut depth = 0i32;
1727        let mut i = open;
1728        while i < self.toks.len() {
1729            match &self.toks[i].tok {
1730                Tok::Punct(p) if p == "(" || p == "[" || p == "{" => depth += 1,
1731                Tok::Punct(p) if p == ")" || p == "]" || p == "}" => {
1732                    depth -= 1;
1733                    if depth == 0 {
1734                        return Some(i);
1735                    }
1736                }
1737                Tok::Eof => return None,
1738                _ => {}
1739            }
1740            i += 1;
1741        }
1742        None
1743    }
1744}
1745
1746/// Parse a template-literal `${...}` field's raw source into an expression.
1747/// `base` is the field's byte offset in the enclosing script, so the spans
1748/// recorded inside it index that script; `None` records none.
1749fn parse_expr_source(src: &str, base: Option<u32>) -> Result<Expr, String> {
1750    let mut toks = lex(src)?;
1751    for t in &mut toks {
1752        t.start += base.unwrap_or(0);
1753        t.end += base.unwrap_or(0);
1754        if let Tok::Template { expr_at, .. } = &mut t.tok {
1755            for at in expr_at {
1756                *at += base.unwrap_or(0);
1757            }
1758        }
1759    }
1760    let mut p = Parser {
1761        toks,
1762        pos: 0,
1763        in_generator: false,
1764        in_async: false,
1765        no_in: false,
1766        class_scopes: Vec::new(),
1767        spans: base.is_some(),
1768    };
1769    let e = p.parse_expr()?;
1770    Ok(e)
1771}