1/***********************************************************************
2
3  A JavaScript tokenizer / parser / beautifier / compressor.
4  https://github.com/mishoo/UglifyJS
5
6  -------------------------------- (C) ---------------------------------
7
8                           Author: Mihai Bazon
9                         <mihai.bazon@gmail.com>
10                       http://mihai.bazon.net/blog
11
12  Distributed under the BSD license:
13
14    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15    Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
16
17    Redistribution and use in source and binary forms, with or without
18    modification, are permitted provided that the following conditions
19    are met:
20
21        * Redistributions of source code must retain the above
22          copyright notice, this list of conditions and the following
23          disclaimer.
24
25        * Redistributions in binary form must reproduce the above
26          copyright notice, this list of conditions and the following
27          disclaimer in the documentation and/or other materials
28          provided with the distribution.
29
30    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
31    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
34    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
35    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
37    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
39    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
40    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41    SUCH DAMAGE.
42
43 ***********************************************************************/
44
45"use strict";
46
47var KEYWORDS = "break case catch class const continue debugger default delete do else extends finally for function if in instanceof new return switch throw try typeof var void while with";
48var KEYWORDS_ATOM = "false null true";
49var RESERVED_WORDS = [
50    "abstract async await boolean byte char double enum export final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield",
51    KEYWORDS_ATOM,
52    KEYWORDS,
53].join(" ");
54var KEYWORDS_BEFORE_EXPRESSION = "return new delete throw else case";
55
56KEYWORDS = makePredicate(KEYWORDS);
57RESERVED_WORDS = makePredicate(RESERVED_WORDS);
58KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
59KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
60
61var RE_BIN_NUMBER = /^0b([01]+)$/i;
62var RE_HEX_NUMBER = /^0x([0-9a-f]+)$/i;
63var RE_OCT_NUMBER = /^0o?([0-7]+)$/i;
64
65var OPERATORS = makePredicate([
66    "in",
67    "instanceof",
68    "typeof",
69    "new",
70    "void",
71    "delete",
72    "++",
73    "--",
74    "+",
75    "-",
76    "!",
77    "~",
78    "&",
79    "|",
80    "^",
81    "*",
82    "/",
83    "%",
84    "**",
85    ">>",
86    "<<",
87    ">>>",
88    "<",
89    ">",
90    "<=",
91    ">=",
92    "==",
93    "===",
94    "!=",
95    "!==",
96    "?",
97    "=",
98    "+=",
99    "-=",
100    "/=",
101    "*=",
102    "%=",
103    "**=",
104    ">>=",
105    "<<=",
106    ">>>=",
107    "&=",
108    "|=",
109    "^=",
110    "&&",
111    "||",
112    "??",
113    "&&=",
114    "||=",
115    "??=",
116]);
117
118var NEWLINE_CHARS = "\n\r\u2028\u2029";
119var OPERATOR_CHARS = "+-*&%=<>!?|~^";
120var PUNC_OPENERS = "[{(";
121var PUNC_SEPARATORS = ",;:";
122var PUNC_CLOSERS = ")}]";
123var PUNC_AFTER_EXPRESSION = PUNC_SEPARATORS + PUNC_CLOSERS;
124var PUNC_BEFORE_EXPRESSION = PUNC_OPENERS + PUNC_SEPARATORS;
125var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`" + PUNC_CLOSERS;
126var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
127var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"#" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
128
129NEWLINE_CHARS = makePredicate(characters(NEWLINE_CHARS));
130OPERATOR_CHARS = makePredicate(characters(OPERATOR_CHARS));
131PUNC_AFTER_EXPRESSION = makePredicate(characters(PUNC_AFTER_EXPRESSION));
132PUNC_BEFORE_EXPRESSION = makePredicate(characters(PUNC_BEFORE_EXPRESSION));
133PUNC_CHARS = makePredicate(characters(PUNC_CHARS));
134WHITESPACE_CHARS = makePredicate(characters(WHITESPACE_CHARS));
135
136/* -----[ Tokenizer ]----- */
137
138function is_surrogate_pair_head(code) {
139    return code >= 0xd800 && code <= 0xdbff;
140}
141
142function is_surrogate_pair_tail(code) {
143    return code >= 0xdc00 && code <= 0xdfff;
144}
145
146function is_digit(code) {
147    return code >= 48 && code <= 57;
148}
149
150function is_identifier_char(ch) {
151    return !NON_IDENTIFIER_CHARS[ch];
152}
153
154function is_identifier_string(str) {
155    return /^[a-z_$][a-z0-9_$]*$/i.test(str);
156}
157
158function decode_escape_sequence(seq) {
159    switch (seq[0]) {
160      case "b": return "\b";
161      case "f": return "\f";
162      case "n": return "\n";
163      case "r": return "\r";
164      case "t": return "\t";
165      case "u":
166        var code;
167        if (seq[1] == "{" && seq.slice(-1) == "}") {
168            code = seq.slice(2, -1);
169        } else if (seq.length == 5) {
170            code = seq.slice(1);
171        } else {
172            return;
173        }
174        var num = parseInt(code, 16);
175        if (num < 0 || isNaN(num)) return;
176        if (num < 0x10000) return String.fromCharCode(num);
177        if (num > 0x10ffff) return;
178        return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
179      case "v": return "\u000b";
180      case "x":
181        if (seq.length != 3) return;
182        var num = parseInt(seq.slice(1), 16);
183        if (num < 0 || isNaN(num)) return;
184        return String.fromCharCode(num);
185      case "\r":
186      case "\n":
187        return "";
188      default:
189        if (seq == "0") return "\0";
190        if (seq[0] >= "0" && seq[0] <= "9") return;
191        return seq;
192    }
193}
194
195function parse_js_number(num) {
196    var match;
197    if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
198    if (match = RE_HEX_NUMBER.exec(num)) return parseInt(match[1], 16);
199    if (match = RE_OCT_NUMBER.exec(num)) return parseInt(match[1], 8);
200    var val = parseFloat(num);
201    if (val == num) return val;
202}
203
204function JS_Parse_Error(message, filename, line, col, pos) {
205    this.message = message;
206    this.filename = filename;
207    this.line = line;
208    this.col = col;
209    this.pos = pos;
210}
211JS_Parse_Error.prototype = Object.create(Error.prototype);
212JS_Parse_Error.prototype.constructor = JS_Parse_Error;
213JS_Parse_Error.prototype.name = "SyntaxError";
214configure_error_stack(JS_Parse_Error);
215
216function js_error(message, filename, line, col, pos) {
217    throw new JS_Parse_Error(message, filename, line, col, pos);
218}
219
220function is_token(token, type, val) {
221    return token.type == type && (val == null || token.value == val);
222}
223
224var EX_EOF = {};
225
226function tokenizer($TEXT, filename, html5_comments, shebang) {
227
228    var S = {
229        text            : $TEXT,
230        filename        : filename,
231        pos             : 0,
232        tokpos          : 0,
233        line            : 1,
234        tokline         : 0,
235        col             : 0,
236        tokcol          : 0,
237        newline_before  : false,
238        regex_allowed   : false,
239        comments_before : [],
240        directives      : Object.create(null),
241        read_template   : with_eof_error("Unterminated template literal", function(strings) {
242            var s = "";
243            for (;;) {
244                var ch = read();
245                switch (ch) {
246                  case "\\":
247                    ch += read();
248                    break;
249                  case "`":
250                    strings.push(s);
251                    return;
252                  case "$":
253                    if (peek() == "{") {
254                        next();
255                        strings.push(s);
256                        S.regex_allowed = true;
257                        return true;
258                    }
259                }
260                s += ch;
261            }
262
263            function read() {
264                var ch = next(true, true);
265                return ch == "\r" ? "\n" : ch;
266            }
267        }),
268    };
269    var prev_was_dot = false;
270
271    function peek() {
272        return S.text.charAt(S.pos);
273    }
274
275    function next(signal_eof, in_string) {
276        var ch = S.text.charAt(S.pos++);
277        if (signal_eof && !ch)
278            throw EX_EOF;
279        if (NEWLINE_CHARS[ch]) {
280            S.col = 0;
281            S.line++;
282            if (!in_string) S.newline_before = true;
283            if (ch == "\r" && peek() == "\n") {
284                // treat `\r\n` as `\n`
285                S.pos++;
286                ch = "\n";
287            }
288        } else {
289            S.col++;
290        }
291        return ch;
292    }
293
294    function forward(i) {
295        while (i-- > 0) next();
296    }
297
298    function looking_at(str) {
299        return S.text.substr(S.pos, str.length) == str;
300    }
301
302    function find_eol() {
303        var text = S.text;
304        for (var i = S.pos; i < S.text.length; ++i) {
305            if (NEWLINE_CHARS[text[i]]) return i;
306        }
307        return -1;
308    }
309
310    function find(what, signal_eof) {
311        var pos = S.text.indexOf(what, S.pos);
312        if (signal_eof && pos == -1) throw EX_EOF;
313        return pos;
314    }
315
316    function start_token() {
317        S.tokline = S.line;
318        S.tokcol = S.col;
319        S.tokpos = S.pos;
320    }
321
322    function token(type, value, is_comment) {
323        S.regex_allowed = type == "operator" && !UNARY_POSTFIX[value]
324            || type == "keyword" && KEYWORDS_BEFORE_EXPRESSION[value]
325            || type == "punc" && PUNC_BEFORE_EXPRESSION[value];
326        if (type == "punc" && value == ".") prev_was_dot = true;
327        else if (!is_comment) prev_was_dot = false;
328        var ret = {
329            type    : type,
330            value   : value,
331            line    : S.tokline,
332            col     : S.tokcol,
333            pos     : S.tokpos,
334            endline : S.line,
335            endcol  : S.col,
336            endpos  : S.pos,
337            nlb     : S.newline_before,
338            file    : filename
339        };
340        if (/^(?:num|string|regexp)$/i.test(type)) {
341            ret.raw = $TEXT.substring(ret.pos, ret.endpos);
342        }
343        if (!is_comment) {
344            ret.comments_before = S.comments_before;
345            ret.comments_after = S.comments_before = [];
346        }
347        S.newline_before = false;
348        return new AST_Token(ret);
349    }
350
351    function skip_whitespace() {
352        while (WHITESPACE_CHARS[peek()])
353            next();
354    }
355
356    function read_while(pred) {
357        var ret = "", ch;
358        while ((ch = peek()) && pred(ch, ret)) ret += next();
359        return ret;
360    }
361
362    function parse_error(err) {
363        js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
364    }
365
366    function is_octal(num) {
367        return /^0[0-7_]+$/.test(num);
368    }
369
370    function read_num(prefix) {
371        var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
372        var num = read_while(function(ch, str) {
373            switch (ch) {
374              case "x": case "X":
375                return has_x ? false : (has_x = true);
376              case "e": case "E":
377                return has_x ? true : has_e ? false : (has_e = after_e = true);
378              case "+": case "-":
379                return after_e;
380              case (after_e = false, "."):
381                return has_dot || has_e || has_x || is_octal(str) ? false : (has_dot = true);
382            }
383            return /[_0-9a-dfo]/i.test(ch);
384        });
385        if (prefix) num = prefix + num;
386        if (is_octal(num)) {
387            if (next_token.has_directive("use strict")) parse_error("Legacy octal literals are not allowed in strict mode");
388        } else {
389            num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
390        }
391        var valid = parse_js_number(num);
392        if (isNaN(valid)) parse_error("Invalid syntax: " + num);
393        if (has_dot || has_e || peek() != "n") return token("num", valid);
394        return token("bigint", num.toLowerCase() + next());
395    }
396
397    function read_escaped_char(in_string) {
398        var seq = next(true, in_string);
399        if (seq >= "0" && seq <= "7") return read_octal_escape_sequence(seq);
400        if (seq == "u") {
401            var ch = next(true, in_string);
402            seq += ch;
403            if (ch != "{") {
404                seq += next(true, in_string) + next(true, in_string) + next(true, in_string);
405            } else do {
406                ch = next(true, in_string);
407                seq += ch;
408            } while (ch != "}");
409        } else if (seq == "x") {
410            seq += next(true, in_string) + next(true, in_string);
411        }
412        var str = decode_escape_sequence(seq);
413        if (typeof str != "string") parse_error("Invalid escape sequence: \\" + seq);
414        return str;
415    }
416
417    function read_octal_escape_sequence(ch) {
418        // Read
419        var p = peek();
420        if (p >= "0" && p <= "7") {
421            ch += next(true);
422            if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7")
423                ch += next(true);
424        }
425
426        // Parse
427        if (ch === "0") return "\0";
428        if (ch.length > 0 && next_token.has_directive("use strict"))
429            parse_error("Legacy octal escape sequences are not allowed in strict mode");
430        return String.fromCharCode(parseInt(ch, 8));
431    }
432
433    var read_string = with_eof_error("Unterminated string constant", function(quote_char) {
434        var quote = next(), ret = "";
435        for (;;) {
436            var ch = next(true, true);
437            if (ch == "\\") ch = read_escaped_char(true);
438            else if (NEWLINE_CHARS[ch]) parse_error("Unterminated string constant");
439            else if (ch == quote) break;
440            ret += ch;
441        }
442        var tok = token("string", ret);
443        tok.quote = quote_char;
444        return tok;
445    });
446
447    function skip_line_comment(type) {
448        var regex_allowed = S.regex_allowed;
449        var i = find_eol(), ret;
450        if (i == -1) {
451            ret = S.text.substr(S.pos);
452            S.pos = S.text.length;
453        } else {
454            ret = S.text.substring(S.pos, i);
455            S.pos = i;
456        }
457        S.col = S.tokcol + (S.pos - S.tokpos);
458        S.comments_before.push(token(type, ret, true));
459        S.regex_allowed = regex_allowed;
460        return next_token;
461    }
462
463    var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() {
464        var regex_allowed = S.regex_allowed;
465        var i = find("*/", true);
466        var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, "\n");
467        // update stream position
468        forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
469        S.comments_before.push(token("comment2", text, true));
470        S.regex_allowed = regex_allowed;
471        return next_token;
472    });
473
474    function read_name() {
475        var backslash = false, ch, escaped = false, name = peek() == "#" ? next() : "";
476        while (ch = peek()) {
477            if (!backslash) {
478                if (ch == "\\") escaped = backslash = true, next();
479                else if (is_identifier_char(ch)) name += next();
480                else break;
481            } else {
482                if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
483                ch = read_escaped_char();
484                if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
485                name += ch;
486                backslash = false;
487            }
488        }
489        if (KEYWORDS[name] && escaped) {
490            var hex = name.charCodeAt(0).toString(16).toUpperCase();
491            name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
492        }
493        return name;
494    }
495
496    var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
497        var prev_backslash = false, ch, in_class = false;
498        while ((ch = next(true))) if (NEWLINE_CHARS[ch]) {
499            parse_error("Unexpected line terminator");
500        } else if (prev_backslash) {
501            source += "\\" + ch;
502            prev_backslash = false;
503        } else if (ch == "[") {
504            in_class = true;
505            source += ch;
506        } else if (ch == "]" && in_class) {
507            in_class = false;
508            source += ch;
509        } else if (ch == "/" && !in_class) {
510            break;
511        } else if (ch == "\\") {
512            prev_backslash = true;
513        } else {
514            source += ch;
515        }
516        var mods = read_name();
517        try {
518            var regexp = new RegExp(source, mods);
519            regexp.raw_source = source;
520            return token("regexp", regexp);
521        } catch (e) {
522            parse_error(e.message);
523        }
524    });
525
526    function read_operator(prefix) {
527        function grow(op) {
528            if (!peek()) return op;
529            var bigger = op + peek();
530            if (OPERATORS[bigger]) {
531                next();
532                return grow(bigger);
533            } else {
534                return op;
535            }
536        }
537        return token("operator", grow(prefix || next()));
538    }
539
540    function handle_slash() {
541        next();
542        switch (peek()) {
543          case "/":
544            next();
545            return skip_line_comment("comment1");
546          case "*":
547            next();
548            return skip_multiline_comment();
549        }
550        return S.regex_allowed ? read_regexp("") : read_operator("/");
551    }
552
553    function handle_dot() {
554        next();
555        if (looking_at("..")) return token("operator", "." + next() + next());
556        return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", ".");
557    }
558
559    function read_word() {
560        var word = read_name();
561        if (prev_was_dot) return token("name", word);
562        return KEYWORDS_ATOM[word] ? token("atom", word)
563            : !KEYWORDS[word] ? token("name", word)
564            : OPERATORS[word] ? token("operator", word)
565            : token("keyword", word);
566    }
567
568    function with_eof_error(eof_error, cont) {
569        return function(x) {
570            try {
571                return cont(x);
572            } catch (ex) {
573                if (ex === EX_EOF) parse_error(eof_error);
574                else throw ex;
575            }
576        };
577    }
578
579    function next_token(force_regexp) {
580        if (force_regexp != null)
581            return read_regexp(force_regexp);
582        if (shebang && S.pos == 0 && looking_at("#!")) {
583            start_token();
584            forward(2);
585            skip_line_comment("comment5");
586        }
587        for (;;) {
588            skip_whitespace();
589            start_token();
590            if (html5_comments) {
591                if (looking_at("<!--")) {
592                    forward(4);
593                    skip_line_comment("comment3");
594                    continue;
595                }
596                if (looking_at("-->") && S.newline_before) {
597                    forward(3);
598                    skip_line_comment("comment4");
599                    continue;
600                }
601            }
602            var ch = peek();
603            if (!ch) return token("eof");
604            var code = ch.charCodeAt(0);
605            switch (code) {
606              case 34: case 39: return read_string(ch);
607              case 46: return handle_dot();
608              case 47:
609                var tok = handle_slash();
610                if (tok === next_token) continue;
611                return tok;
612            }
613            if (is_digit(code)) return read_num();
614            if (PUNC_CHARS[ch]) return token("punc", next());
615            if (looking_at("=>")) return token("punc", next() + next());
616            if (OPERATOR_CHARS[ch]) return read_operator();
617            if (code == 35 || code == 92 || !NON_IDENTIFIER_CHARS[ch]) return read_word();
618            break;
619        }
620        parse_error("Unexpected character '" + ch + "'");
621    }
622
623    next_token.context = function(nc) {
624        if (nc) S = nc;
625        return S;
626    };
627
628    next_token.add_directive = function(directive) {
629        S.directives[directive] = true;
630    }
631
632    next_token.push_directives_stack = function() {
633        S.directives = Object.create(S.directives);
634    }
635
636    next_token.pop_directives_stack = function() {
637        S.directives = Object.getPrototypeOf(S.directives);
638    }
639
640    next_token.has_directive = function(directive) {
641        return !!S.directives[directive];
642    }
643
644    return next_token;
645}
646
647/* -----[ Parser (constants) ]----- */
648
649var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
650
651var UNARY_POSTFIX = makePredicate("-- ++");
652
653var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??=");
654
655var PRECEDENCE = function(a, ret) {
656    for (var i = 0; i < a.length;) {
657        var b = a[i++];
658        for (var j = 0; j < b.length; j++) {
659            ret[b[j]] = i;
660        }
661    }
662    return ret;
663}([
664    ["??"],
665    ["||"],
666    ["&&"],
667    ["|"],
668    ["^"],
669    ["&"],
670    ["==", "===", "!=", "!=="],
671    ["<", ">", "<=", ">=", "in", "instanceof"],
672    [">>", "<<", ">>>"],
673    ["+", "-"],
674    ["*", "/", "%"],
675    ["**"],
676], {});
677
678var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string");
679
680/* -----[ Parser ]----- */
681
682function parse($TEXT, options) {
683    options = defaults(options, {
684        bare_returns   : false,
685        expression     : false,
686        filename       : null,
687        html5_comments : true,
688        module         : false,
689        shebang        : true,
690        strict         : false,
691        toplevel       : null,
692    }, true);
693
694    var S = {
695        input         : typeof $TEXT == "string"
696                        ? tokenizer($TEXT, options.filename, options.html5_comments, options.shebang)
697                        : $TEXT,
698        in_async      : false,
699        in_directives : true,
700        in_funarg     : -1,
701        in_function   : 0,
702        in_generator  : false,
703        in_loop       : 0,
704        labels        : [],
705        peeked        : null,
706        prev          : null,
707        token         : null,
708    };
709
710    S.token = next();
711
712    function is(type, value) {
713        return is_token(S.token, type, value);
714    }
715
716    function peek() {
717        return S.peeked || (S.peeked = S.input());
718    }
719
720    function next() {
721        S.prev = S.token;
722        if (S.peeked) {
723            S.token = S.peeked;
724            S.peeked = null;
725        } else {
726            S.token = S.input();
727        }
728        S.in_directives = S.in_directives && (
729            S.token.type == "string" || is("punc", ";")
730        );
731        return S.token;
732    }
733
734    function prev() {
735        return S.prev;
736    }
737
738    function croak(msg, line, col, pos) {
739        var ctx = S.input.context();
740        js_error(msg,
741                 ctx.filename,
742                 line != null ? line : ctx.tokline,
743                 col != null ? col : ctx.tokcol,
744                 pos != null ? pos : ctx.tokpos);
745    }
746
747    function token_error(token, msg) {
748        croak(msg, token.line, token.col);
749    }
750
751    function token_to_string(type, value) {
752        return type + (value === undefined ? "" : " «" + value + "»");
753    }
754
755    function unexpected(token) {
756        if (token == null) token = S.token;
757        token_error(token, "Unexpected token: " + token_to_string(token.type, token.value));
758    }
759
760    function expect_token(type, val) {
761        if (is(type, val)) return next();
762        token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val));
763    }
764
765    function expect(punc) {
766        return expect_token("punc", punc);
767    }
768
769    function has_newline_before(token) {
770        return token.nlb || !all(token.comments_before, function(comment) {
771            return !comment.nlb;
772        });
773    }
774
775    function can_insert_semicolon() {
776        return !options.strict
777            && (is("eof") || is("punc", "}") || has_newline_before(S.token));
778    }
779
780    function semicolon(optional) {
781        if (is("punc", ";")) next();
782        else if (!optional && !can_insert_semicolon()) expect(";");
783    }
784
785    function parenthesized() {
786        expect("(");
787        var exp = expression();
788        expect(")");
789        return exp;
790    }
791
792    function embed_tokens(parser) {
793        return function() {
794            var start = S.token;
795            var expr = parser.apply(null, arguments);
796            var end = prev();
797            expr.start = start;
798            expr.end = end;
799            return expr;
800        };
801    }
802
803    function handle_regexp() {
804        if (is("operator", "/") || is("operator", "/=")) {
805            S.peeked = null;
806            S.token = S.input(S.token.value.substr(1)); // force regexp
807        }
808    }
809
810    var statement = embed_tokens(function(toplevel) {
811        handle_regexp();
812        switch (S.token.type) {
813          case "string":
814            var dir = S.in_directives;
815            var body = expression();
816            if (dir) {
817                if (body instanceof AST_String) {
818                    var value = body.start.raw.slice(1, -1);
819                    S.input.add_directive(value);
820                    body.value = value;
821                } else {
822                    S.in_directives = dir = false;
823                }
824            }
825            semicolon();
826            return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
827          case "num":
828          case "bigint":
829          case "regexp":
830          case "operator":
831          case "atom":
832            return simple_statement();
833
834          case "name":
835            switch (S.token.value) {
836              case "async":
837                if (is_token(peek(), "keyword", "function")) {
838                    next();
839                    next();
840                    if (!is("operator", "*")) return function_(AST_AsyncDefun);
841                    next();
842                    return function_(AST_AsyncGeneratorDefun);
843                }
844                break;
845              case "await":
846                if (S.in_async) return simple_statement();
847                break;
848              case "export":
849                if (!toplevel && options.module !== "") unexpected();
850                next();
851                return export_();
852              case "import":
853                var token = peek();
854                if (token.type == "punc" && /^[(.]$/.test(token.value)) break;
855                if (!toplevel && options.module !== "") unexpected();
856                next();
857                return import_();
858              case "let":
859                if (is_vardefs()) {
860                    next();
861                    var node = let_();
862                    semicolon();
863                    return node;
864                }
865                break;
866              case "yield":
867                if (S.in_generator) return simple_statement();
868                break;
869            }
870            return is_token(peek(), "punc", ":")
871                ? labeled_statement()
872                : simple_statement();
873
874          case "punc":
875            switch (S.token.value) {
876              case "{":
877                return new AST_BlockStatement({
878                    start : S.token,
879                    body  : block_(),
880                    end   : prev()
881                });
882              case "[":
883              case "(":
884              case "`":
885                return simple_statement();
886              case ";":
887                S.in_directives = false;
888                next();
889                return new AST_EmptyStatement();
890              default:
891                unexpected();
892            }
893
894          case "keyword":
895            switch (S.token.value) {
896              case "break":
897                next();
898                return break_cont(AST_Break);
899
900              case "class":
901                next();
902                return class_(AST_DefClass);
903
904              case "const":
905                next();
906                var node = const_();
907                semicolon();
908                return node;
909
910              case "continue":
911                next();
912                return break_cont(AST_Continue);
913
914              case "debugger":
915                next();
916                semicolon();
917                return new AST_Debugger();
918
919              case "do":
920                next();
921                var body = in_loop(statement);
922                expect_token("keyword", "while");
923                var condition = parenthesized();
924                semicolon(true);
925                return new AST_Do({
926                    body      : body,
927                    condition : condition,
928                });
929
930              case "while":
931                next();
932                return new AST_While({
933                    condition : parenthesized(),
934                    body      : in_loop(statement),
935                });
936
937              case "for":
938                next();
939                return for_();
940
941              case "function":
942                next();
943                if (!is("operator", "*")) return function_(AST_Defun);
944                next();
945                return function_(AST_GeneratorDefun);
946
947              case "if":
948                next();
949                return if_();
950
951              case "return":
952                if (S.in_function == 0 && !options.bare_returns)
953                    croak("'return' outside of function");
954                next();
955                var value = null;
956                if (is("punc", ";")) {
957                    next();
958                } else if (!can_insert_semicolon()) {
959                    value = expression();
960                    semicolon();
961                }
962                return new AST_Return({ value: value });
963
964              case "switch":
965                next();
966                return new AST_Switch({
967                    expression : parenthesized(),
968                    body       : in_loop(switch_body_),
969                });
970
971              case "throw":
972                next();
973                if (has_newline_before(S.token))
974                    croak("Illegal newline after 'throw'");
975                var value = expression();
976                semicolon();
977                return new AST_Throw({ value: value });
978
979              case "try":
980                next();
981                return try_();
982
983              case "var":
984                next();
985                var node = var_();
986                semicolon();
987                return node;
988
989              case "with":
990                if (S.input.has_directive("use strict")) {
991                    croak("Strict mode may not include a with statement");
992                }
993                next();
994                return new AST_With({
995                    expression : parenthesized(),
996                    body       : statement(),
997                });
998            }
999        }
1000        unexpected();
1001    });
1002
1003    function labeled_statement() {
1004        var label = as_symbol(AST_Label);
1005        if (!all(S.labels, function(l) {
1006            return l.name != label.name;
1007        })) {
1008            // ECMA-262, 12.12: An ECMAScript program is considered
1009            // syntactically incorrect if it contains a
1010            // LabelledStatement that is enclosed by a
1011            // LabelledStatement with the same Identifier as label.
1012            croak("Label " + label.name + " defined twice");
1013        }
1014        expect(":");
1015        S.labels.push(label);
1016        var stat = statement();
1017        S.labels.pop();
1018        if (!(stat instanceof AST_IterationStatement)) {
1019            // check for `continue` that refers to this label.
1020            // those should be reported as syntax errors.
1021            // https://github.com/mishoo/UglifyJS/issues/287
1022            label.references.forEach(function(ref) {
1023                if (ref instanceof AST_Continue) {
1024                    token_error(ref.label.start, "Continue label `" + label.name + "` must refer to IterationStatement");
1025                }
1026            });
1027        }
1028        return new AST_LabeledStatement({ body: stat, label: label });
1029    }
1030
1031    function simple_statement() {
1032        var body = expression();
1033        semicolon();
1034        return new AST_SimpleStatement({ body: body });
1035    }
1036
1037    function break_cont(type) {
1038        var label = null, ldef;
1039        if (!can_insert_semicolon()) {
1040            label = as_symbol(AST_LabelRef, true);
1041        }
1042        if (label != null) {
1043            ldef = find_if(function(l) {
1044                return l.name == label.name;
1045            }, S.labels);
1046            if (!ldef) token_error(label.start, "Undefined label " + label.name);
1047            label.thedef = ldef;
1048        } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
1049        semicolon();
1050        var stat = new type({ label: label });
1051        if (ldef) ldef.references.push(stat);
1052        return stat;
1053    }
1054
1055    function has_modifier(name, no_nlb) {
1056        if (!is("name", name)) return;
1057        var token = peek();
1058        if (!token) return;
1059        if (is_token(token, "operator", "=")) return;
1060        if (token.type == "punc" && /^[(;}]$/.test(token.value)) return;
1061        if (no_nlb && has_newline_before(token)) return;
1062        return next();
1063    }
1064
1065    function class_(ctor) {
1066        var was_async = S.in_async;
1067        var was_gen = S.in_generator;
1068        S.input.push_directives_stack();
1069        S.input.add_directive("use strict");
1070        var name;
1071        if (ctor === AST_DefClass) {
1072            name = as_symbol(AST_SymbolDefClass);
1073        } else {
1074            name = as_symbol(AST_SymbolClass, true);
1075        }
1076        var parent = null;
1077        if (is("keyword", "extends")) {
1078            next();
1079            handle_regexp();
1080            parent = expr_atom(true);
1081        }
1082        expect("{");
1083        var props = [];
1084        while (!is("punc", "}")) {
1085            if (is("punc", ";")) {
1086                next();
1087                continue;
1088            }
1089            var start = S.token;
1090            var fixed = !!has_modifier("static");
1091            var async = has_modifier("async", true);
1092            if (is("operator", "*")) {
1093                next();
1094                var internal = is("name") && /^#/.test(S.token.value);
1095                var key = as_property_key();
1096                var gen_start = S.token;
1097                var gen = function_(async ? AST_AsyncGeneratorFunction : AST_GeneratorFunction);
1098                gen.start = gen_start;
1099                gen.end = prev();
1100                props.push(new AST_ClassMethod({
1101                    start: start,
1102                    static: fixed,
1103                    private: internal,
1104                    key: key,
1105                    value: gen,
1106                    end: prev(),
1107                }));
1108                continue;
1109            }
1110            if (fixed && is("punc", "{")) {
1111                props.push(new AST_ClassInit({
1112                    start: start,
1113                    value: new AST_ClassInitBlock({
1114                        start: start,
1115                        body: block_(),
1116                        end: prev(),
1117                    }),
1118                    end: prev(),
1119                }));
1120                continue;
1121            }
1122            var internal = is("name") && /^#/.test(S.token.value);
1123            var key = as_property_key();
1124            if (is("punc", "(")) {
1125                var func_start = S.token;
1126                var func = function_(async ? AST_AsyncFunction : AST_Function);
1127                func.start = func_start;
1128                func.end = prev();
1129                props.push(new AST_ClassMethod({
1130                    start: start,
1131                    static: fixed,
1132                    private: internal,
1133                    key: key,
1134                    value: func,
1135                    end: prev(),
1136                }));
1137                continue;
1138            }
1139            if (async) unexpected(async);
1140            var value = null;
1141            if (is("operator", "=")) {
1142                next();
1143                S.in_async = false;
1144                S.in_generator = false;
1145                value = maybe_assign();
1146                S.in_generator = was_gen;
1147                S.in_async = was_async;
1148            } else if (!(is("punc", ";") || is("punc", "}"))) {
1149                var type = null;
1150                switch (key) {
1151                  case "get":
1152                    type = AST_ClassGetter;
1153                    break;
1154                  case "set":
1155                    type = AST_ClassSetter;
1156                    break;
1157                }
1158                if (type) {
1159                    props.push(new type({
1160                        start: start,
1161                        static: fixed,
1162                        private: is("name") && /^#/.test(S.token.value),
1163                        key: as_property_key(),
1164                        value: create_accessor(),
1165                        end: prev(),
1166                    }));
1167                    continue;
1168                }
1169            }
1170            semicolon();
1171            props.push(new AST_ClassField({
1172                start: start,
1173                static: fixed,
1174                private: internal,
1175                key: key,
1176                value: value,
1177                end: prev(),
1178            }));
1179        }
1180        next();
1181        S.input.pop_directives_stack();
1182        S.in_generator = was_gen;
1183        S.in_async = was_async;
1184        return new ctor({
1185            extends: parent,
1186            name: name,
1187            properties: props,
1188        });
1189    }
1190
1191    function for_() {
1192        var await_token = is("name", "await") && next();
1193        expect("(");
1194        var init = null;
1195        if (await_token || !is("punc", ";")) {
1196            init = is("keyword", "const")
1197                ? (next(), const_(true))
1198                : is("name", "let") && is_vardefs()
1199                ? (next(), let_(true))
1200                : is("keyword", "var")
1201                ? (next(), var_(true))
1202                : expression(true);
1203            var ctor;
1204            if (await_token) {
1205                expect_token("name", "of");
1206                ctor = AST_ForAwaitOf;
1207            } else if (is("operator", "in")) {
1208                next();
1209                ctor = AST_ForIn;
1210            } else if (is("name", "of")) {
1211                next();
1212                ctor = AST_ForOf;
1213            }
1214            if (ctor) {
1215                if (init instanceof AST_Definitions) {
1216                    if (init.definitions.length > 1) {
1217                        token_error(init.start, "Only one variable declaration allowed in for..in/of loop");
1218                    }
1219                    if (ctor !== AST_ForIn && init.definitions[0].value) {
1220                        token_error(init.definitions[0].value.start, "No initializers allowed in for..of loop");
1221                    }
1222                } else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
1223                    token_error(init.start, "Invalid left-hand side in for..in/of loop");
1224                }
1225                return for_enum(ctor, init);
1226            }
1227        }
1228        return regular_for(init);
1229    }
1230
1231    function regular_for(init) {
1232        expect(";");
1233        var test = is("punc", ";") ? null : expression();
1234        expect(";");
1235        var step = is("punc", ")") ? null : expression();
1236        expect(")");
1237        return new AST_For({
1238            init      : init,
1239            condition : test,
1240            step      : step,
1241            body      : in_loop(statement)
1242        });
1243    }
1244
1245    function for_enum(ctor, init) {
1246        handle_regexp();
1247        var obj = expression();
1248        expect(")");
1249        return new ctor({
1250            init   : init,
1251            object : obj,
1252            body   : in_loop(statement)
1253        });
1254    }
1255
1256    function to_funarg(node) {
1257        if (node instanceof AST_Array) {
1258            var rest = null;
1259            if (node.elements[node.elements.length - 1] instanceof AST_Spread) {
1260                rest = to_funarg(node.elements.pop().expression);
1261            }
1262            return new AST_DestructuredArray({
1263                start: node.start,
1264                elements: node.elements.map(to_funarg),
1265                rest: rest,
1266                end: node.end,
1267            });
1268        }
1269        if (node instanceof AST_Assign) return new AST_DefaultValue({
1270            start: node.start,
1271            name: to_funarg(node.left),
1272            value: node.right,
1273            end: node.end,
1274        });
1275        if (node instanceof AST_DefaultValue) {
1276            node.name = to_funarg(node.name);
1277            return node;
1278        }
1279        if (node instanceof AST_DestructuredArray) {
1280            node.elements = node.elements.map(to_funarg);
1281            if (node.rest) node.rest = to_funarg(node.rest);
1282            return node;
1283        }
1284        if (node instanceof AST_DestructuredObject) {
1285            node.properties.forEach(function(prop) {
1286                prop.value = to_funarg(prop.value);
1287            });
1288            if (node.rest) node.rest = to_funarg(node.rest);
1289            return node;
1290        }
1291        if (node instanceof AST_Hole) return node;
1292        if (node instanceof AST_Object) {
1293            var rest = null;
1294            if (node.properties[node.properties.length - 1] instanceof AST_Spread) {
1295                rest = to_funarg(node.properties.pop().expression);
1296            }
1297            return new AST_DestructuredObject({
1298                start: node.start,
1299                properties: node.properties.map(function(prop) {
1300                    if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment");
1301                    return new AST_DestructuredKeyVal({
1302                        start: prop.start,
1303                        key: prop.key,
1304                        value: to_funarg(prop.value),
1305                        end: prop.end,
1306                    });
1307                }),
1308                rest: rest,
1309                end: node.end,
1310            });
1311        }
1312        if (node instanceof AST_SymbolFunarg) return node;
1313        if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
1314        if (node instanceof AST_Yield) return new AST_SymbolFunarg({
1315            start: node.start,
1316            name: "yield",
1317            end: node.end,
1318        });
1319        token_error(node.start, "Invalid arrow parameter");
1320    }
1321
1322    function arrow(exprs, start, async) {
1323        var was_async = S.in_async;
1324        var was_gen = S.in_generator;
1325        S.in_async = async;
1326        S.in_generator = false;
1327        var was_funarg = S.in_funarg;
1328        S.in_funarg = S.in_function;
1329        var argnames = exprs.map(to_funarg);
1330        var rest = exprs.rest || null;
1331        if (rest) rest = to_funarg(rest);
1332        S.in_funarg = was_funarg;
1333        expect("=>");
1334        var body, value;
1335        var loop = S.in_loop;
1336        var labels = S.labels;
1337        ++S.in_function;
1338        S.input.push_directives_stack();
1339        S.in_loop = 0;
1340        S.labels = [];
1341        if (is("punc", "{")) {
1342            S.in_directives = true;
1343            body = block_();
1344            value = null;
1345        } else {
1346            body = [];
1347            handle_regexp();
1348            value = maybe_assign();
1349        }
1350        var is_strict = S.input.has_directive("use strict");
1351        S.input.pop_directives_stack();
1352        --S.in_function;
1353        S.in_loop = loop;
1354        S.labels = labels;
1355        S.in_generator = was_gen;
1356        S.in_async = was_async;
1357        var node = new (async ? AST_AsyncArrow : AST_Arrow)({
1358            start: start,
1359            argnames: argnames,
1360            rest: rest,
1361            body: body,
1362            value: value,
1363            end: prev(),
1364        });
1365        if (is_strict) node.each_argname(strict_verify_symbol);
1366        return node;
1367    }
1368
1369    var function_ = function(ctor) {
1370        var was_async = S.in_async;
1371        var was_gen = S.in_generator;
1372        var name;
1373        if (/Defun$/.test(ctor.TYPE)) {
1374            name = as_symbol(AST_SymbolDefun);
1375            S.in_async = /^Async/.test(ctor.TYPE);
1376            S.in_generator = /Generator/.test(ctor.TYPE);
1377        } else {
1378            S.in_async = /^Async/.test(ctor.TYPE);
1379            S.in_generator = /Generator/.test(ctor.TYPE);
1380            name = as_symbol(AST_SymbolLambda, true);
1381        }
1382        if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
1383            unexpected(prev());
1384        expect("(");
1385        var was_funarg = S.in_funarg;
1386        S.in_funarg = S.in_function;
1387        var argnames = expr_list(")", !options.strict, false, function() {
1388            return maybe_default(AST_SymbolFunarg);
1389        });
1390        S.in_funarg = was_funarg;
1391        var loop = S.in_loop;
1392        var labels = S.labels;
1393        ++S.in_function;
1394        S.in_directives = true;
1395        S.input.push_directives_stack();
1396        S.in_loop = 0;
1397        S.labels = [];
1398        var body = block_();
1399        var is_strict = S.input.has_directive("use strict");
1400        S.input.pop_directives_stack();
1401        --S.in_function;
1402        S.in_loop = loop;
1403        S.labels = labels;
1404        S.in_generator = was_gen;
1405        S.in_async = was_async;
1406        var node = new ctor({
1407            name: name,
1408            argnames: argnames,
1409            rest: argnames.rest || null,
1410            body: body,
1411        });
1412        if (is_strict) {
1413            if (name) strict_verify_symbol(name);
1414            node.each_argname(strict_verify_symbol);
1415        }
1416        return node;
1417    };
1418
1419    function if_() {
1420        var cond = parenthesized(), body = statement(), alt = null;
1421        if (is("keyword", "else")) {
1422            next();
1423            alt = statement();
1424        }
1425        return new AST_If({
1426            condition   : cond,
1427            body        : body,
1428            alternative : alt,
1429        });
1430    }
1431
1432    function is_alias() {
1433        return is("name") || is("string") || is_identifier_string(S.token.value);
1434    }
1435
1436    function make_string(token) {
1437        return new AST_String({
1438            start: token,
1439            quote: token.quote,
1440            value: token.value,
1441            end: token,
1442        });
1443    }
1444
1445    function as_path() {
1446        var path = S.token;
1447        expect_token("string");
1448        semicolon();
1449        return make_string(path);
1450    }
1451
1452    function export_() {
1453        if (is("operator", "*")) {
1454            var key = S.token;
1455            var alias = key;
1456            next();
1457            if (is("name", "as")) {
1458                next();
1459                if (!is_alias()) expect_token("name");
1460                alias = S.token;
1461                next();
1462            }
1463            expect_token("name", "from");
1464            return new AST_ExportForeign({
1465                aliases: [ make_string(alias) ],
1466                keys: [ make_string(key) ],
1467                path: as_path(),
1468            });
1469        }
1470        if (is("punc", "{")) {
1471            next();
1472            var aliases = [];
1473            var keys = [];
1474            while (is_alias()) {
1475                var key = S.token;
1476                next();
1477                keys.push(key);
1478                if (is("name", "as")) {
1479                    next();
1480                    if (!is_alias()) expect_token("name");
1481                    aliases.push(S.token);
1482                    next();
1483                } else {
1484                    aliases.push(key);
1485                }
1486                if (!is("punc", "}")) expect(",");
1487            }
1488            expect("}");
1489            if (is("name", "from")) {
1490                next();
1491                return new AST_ExportForeign({
1492                    aliases: aliases.map(make_string),
1493                    keys: keys.map(make_string),
1494                    path: as_path(),
1495                });
1496            }
1497            semicolon();
1498            return new AST_ExportReferences({
1499                properties: keys.map(function(token, index) {
1500                    if (!is_token(token, "name")) token_error(token, "Name expected");
1501                    var sym = _make_symbol(AST_SymbolExport, token);
1502                    sym.alias = make_string(aliases[index]);
1503                    return sym;
1504                }),
1505            });
1506        }
1507        if (is("keyword", "default")) {
1508            next();
1509            var start = S.token;
1510            var body = export_default_decl();
1511            if (body) {
1512                body.start = start;
1513                body.end = prev();
1514            } else {
1515                handle_regexp();
1516                body = expression();
1517                semicolon();
1518            }
1519            return new AST_ExportDefault({ body: body });
1520        }
1521        return new AST_ExportDeclaration({ body: export_decl() });
1522    }
1523
1524    function maybe_named(def, expr) {
1525        if (expr.name) {
1526            expr = new def(expr);
1527            expr.name = new (def === AST_DefClass ? AST_SymbolDefClass : AST_SymbolDefun)(expr.name);
1528        }
1529        return expr;
1530    }
1531
1532    function export_default_decl() {
1533        if (is("name", "async")) {
1534            if (!is_token(peek(), "keyword", "function")) return;
1535            next();
1536            next();
1537            if (!is("operator", "*")) return maybe_named(AST_AsyncDefun, function_(AST_AsyncFunction));
1538            next();
1539            return maybe_named(AST_AsyncGeneratorDefun, function_(AST_AsyncGeneratorFunction));
1540        } else if (is("keyword")) switch (S.token.value) {
1541          case "class":
1542            next();
1543            return maybe_named(AST_DefClass, class_(AST_ClassExpression));
1544          case "function":
1545            next();
1546            if (!is("operator", "*")) return maybe_named(AST_Defun, function_(AST_Function));
1547            next();
1548            return maybe_named(AST_GeneratorDefun, function_(AST_GeneratorFunction));
1549        }
1550    }
1551
1552    var export_decl = embed_tokens(function() {
1553        if (is("name")) switch (S.token.value) {
1554          case "async":
1555            next();
1556            expect_token("keyword", "function");
1557            if (!is("operator", "*")) return function_(AST_AsyncDefun);
1558            next();
1559            return function_(AST_AsyncGeneratorDefun);
1560          case "let":
1561            next();
1562            var node = let_();
1563            semicolon();
1564            return node;
1565        } else if (is("keyword")) switch (S.token.value) {
1566          case "class":
1567            next();
1568            return class_(AST_DefClass);
1569          case "const":
1570            next();
1571            var node = const_();
1572            semicolon();
1573            return node;
1574          case "function":
1575            next();
1576            if (!is("operator", "*")) return function_(AST_Defun);
1577            next();
1578            return function_(AST_GeneratorDefun);
1579          case "var":
1580            next();
1581            var node = var_();
1582            semicolon();
1583            return node;
1584        }
1585        unexpected();
1586    });
1587
1588    function import_() {
1589        var all = null;
1590        var def = as_symbol(AST_SymbolImport, true);
1591        var props = null;
1592        var cont;
1593        if (def) {
1594            def.key = new AST_String({
1595                start: def.start,
1596                value: "",
1597                end: def.end,
1598            });
1599            if (cont = is("punc", ",")) next();
1600        } else {
1601            cont = !is("string");
1602        }
1603        if (cont) {
1604            if (is("operator", "*")) {
1605                var key = S.token;
1606                next();
1607                expect_token("name", "as");
1608                all = as_symbol(AST_SymbolImport);
1609                all.key = make_string(key);
1610            } else {
1611                expect("{");
1612                props = [];
1613                while (is_alias()) {
1614                    var alias;
1615                    if (is_token(peek(), "name", "as")) {
1616                        var key = S.token;
1617                        next();
1618                        next();
1619                        alias = as_symbol(AST_SymbolImport);
1620                        alias.key = make_string(key);
1621                    } else {
1622                        alias = as_symbol(AST_SymbolImport);
1623                        alias.key = new AST_String({
1624                            start: alias.start,
1625                            value: alias.name,
1626                            end: alias.end,
1627                        });
1628                    }
1629                    props.push(alias);
1630                    if (!is("punc", "}")) expect(",");
1631                }
1632                expect("}");
1633            }
1634        }
1635        if (all || def || props) expect_token("name", "from");
1636        return new AST_Import({
1637            all: all,
1638            default: def,
1639            path: as_path(),
1640            properties: props,
1641        });
1642    }
1643
1644    function block_() {
1645        expect("{");
1646        var a = [];
1647        while (!is("punc", "}")) {
1648            if (is("eof")) expect("}");
1649            a.push(statement());
1650        }
1651        next();
1652        return a;
1653    }
1654
1655    function switch_body_() {
1656        expect("{");
1657        var a = [], branch, cur, default_branch, tmp;
1658        while (!is("punc", "}")) {
1659            if (is("eof")) expect("}");
1660            if (is("keyword", "case")) {
1661                if (branch) branch.end = prev();
1662                cur = [];
1663                branch = new AST_Case({
1664                    start      : (tmp = S.token, next(), tmp),
1665                    expression : expression(),
1666                    body       : cur
1667                });
1668                a.push(branch);
1669                expect(":");
1670            } else if (is("keyword", "default")) {
1671                if (branch) branch.end = prev();
1672                if (default_branch) croak("More than one default clause in switch statement");
1673                cur = [];
1674                branch = new AST_Default({
1675                    start : (tmp = S.token, next(), expect(":"), tmp),
1676                    body  : cur
1677                });
1678                a.push(branch);
1679                default_branch = branch;
1680            } else {
1681                if (!cur) unexpected();
1682                cur.push(statement());
1683            }
1684        }
1685        if (branch) branch.end = prev();
1686        next();
1687        return a;
1688    }
1689
1690    function try_() {
1691        var body = block_(), bcatch = null, bfinally = null;
1692        if (is("keyword", "catch")) {
1693            var start = S.token;
1694            next();
1695            var name = null;
1696            if (is("punc", "(")) {
1697                next();
1698                name = maybe_destructured(AST_SymbolCatch);
1699                expect(")");
1700            }
1701            bcatch = new AST_Catch({
1702                start   : start,
1703                argname : name,
1704                body    : block_(),
1705                end     : prev()
1706            });
1707        }
1708        if (is("keyword", "finally")) {
1709            var start = S.token;
1710            next();
1711            bfinally = new AST_Finally({
1712                start : start,
1713                body  : block_(),
1714                end   : prev()
1715            });
1716        }
1717        if (!bcatch && !bfinally)
1718            croak("Missing catch/finally blocks");
1719        return new AST_Try({
1720            body     : body,
1721            bcatch   : bcatch,
1722            bfinally : bfinally
1723        });
1724    }
1725
1726    function vardefs(type, no_in) {
1727        var a = [];
1728        for (;;) {
1729            var start = S.token;
1730            var name = maybe_destructured(type);
1731            var value = null;
1732            if (is("operator", "=")) {
1733                next();
1734                value = maybe_assign(no_in);
1735            } else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
1736                croak("Missing initializer in declaration");
1737            }
1738            a.push(new AST_VarDef({
1739                start : start,
1740                name  : name,
1741                value : value,
1742                end   : prev()
1743            }));
1744            if (!is("punc", ","))
1745                break;
1746            next();
1747        }
1748        return a;
1749    }
1750
1751    function is_vardefs() {
1752        var token = peek();
1753        return is_token(token, "name") || is_token(token, "punc", "[") || is_token(token, "punc", "{");
1754    }
1755
1756    var const_ = function(no_in) {
1757        return new AST_Const({
1758            start       : prev(),
1759            definitions : vardefs(AST_SymbolConst, no_in),
1760            end         : prev()
1761        });
1762    };
1763
1764    var let_ = function(no_in) {
1765        return new AST_Let({
1766            start       : prev(),
1767            definitions : vardefs(AST_SymbolLet, no_in),
1768            end         : prev()
1769        });
1770    };
1771
1772    var var_ = function(no_in) {
1773        return new AST_Var({
1774            start       : prev(),
1775            definitions : vardefs(AST_SymbolVar, no_in),
1776            end         : prev()
1777        });
1778    };
1779
1780    var new_ = function(allow_calls) {
1781        var start = S.token;
1782        expect_token("operator", "new");
1783        var call;
1784        if (is("punc", ".") && is_token(peek(), "name", "target")) {
1785            next();
1786            next();
1787            call = new AST_NewTarget();
1788        } else {
1789            var exp = expr_atom(false), args;
1790            if (is("punc", "(")) {
1791                next();
1792                args = expr_list(")", !options.strict);
1793            } else {
1794                args = [];
1795            }
1796            call = new AST_New({ expression: exp, args: args });
1797        }
1798        call.start = start;
1799        call.end = prev();
1800        return subscripts(call, allow_calls);
1801    };
1802
1803    function as_atom_node() {
1804        var ret, tok = S.token, value = tok.value;
1805        switch (tok.type) {
1806          case "num":
1807            if (isFinite(value)) {
1808                ret = new AST_Number({ value: value });
1809            } else {
1810                ret = new AST_Infinity();
1811                if (value < 0) ret = new AST_UnaryPrefix({ operator: "-", expression: ret });
1812            }
1813            break;
1814          case "bigint":
1815            ret = new AST_BigInt({ value: value });
1816            break;
1817          case "string":
1818            ret = new AST_String({ value: value, quote: tok.quote });
1819            break;
1820          case "regexp":
1821            ret = new AST_RegExp({ value: value });
1822            break;
1823          case "atom":
1824            switch (value) {
1825              case "false":
1826                ret = new AST_False();
1827                break;
1828              case "true":
1829                ret = new AST_True();
1830                break;
1831              case "null":
1832                ret = new AST_Null();
1833                break;
1834              default:
1835                unexpected();
1836            }
1837            break;
1838          default:
1839            unexpected();
1840        }
1841        next();
1842        ret.start = ret.end = tok;
1843        return ret;
1844    }
1845
1846    var expr_atom = function(allow_calls) {
1847        if (is("operator", "new")) {
1848            return new_(allow_calls);
1849        }
1850        var start = S.token;
1851        if (is("punc")) {
1852            switch (start.value) {
1853              case "`":
1854                return subscripts(template(null), allow_calls);
1855              case "(":
1856                next();
1857                if (is("punc", ")")) {
1858                    next();
1859                    return arrow([], start);
1860                }
1861                var ex = expression(false, true);
1862                var len = start.comments_before.length;
1863                [].unshift.apply(ex.start.comments_before, start.comments_before);
1864                start.comments_before.length = 0;
1865                start.comments_before = ex.start.comments_before;
1866                start.comments_before_length = len;
1867                if (len == 0 && start.comments_before.length > 0) {
1868                    var comment = start.comments_before[0];
1869                    if (!comment.nlb) {
1870                        comment.nlb = start.nlb;
1871                        start.nlb = false;
1872                    }
1873                }
1874                start.comments_after = ex.start.comments_after;
1875                ex.start = start;
1876                expect(")");
1877                var end = prev();
1878                end.comments_before = ex.end.comments_before;
1879                end.comments_after.forEach(function(comment) {
1880                    ex.end.comments_after.push(comment);
1881                    if (comment.nlb) S.token.nlb = true;
1882                });
1883                end.comments_after.length = 0;
1884                end.comments_after = ex.end.comments_after;
1885                ex.end = end;
1886                if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start);
1887                return subscripts(ex, allow_calls);
1888              case "[":
1889                return subscripts(array_(), allow_calls);
1890              case "{":
1891                return subscripts(object_(), allow_calls);
1892            }
1893            unexpected();
1894        }
1895        if (is("keyword")) switch (start.value) {
1896          case "class":
1897            next();
1898            var clazz = class_(AST_ClassExpression);
1899            clazz.start = start;
1900            clazz.end = prev();
1901            return subscripts(clazz, allow_calls);
1902          case "function":
1903            next();
1904            var func;
1905            if (is("operator", "*")) {
1906                next();
1907                func = function_(AST_GeneratorFunction);
1908            } else {
1909                func = function_(AST_Function);
1910            }
1911            func.start = start;
1912            func.end = prev();
1913            return subscripts(func, allow_calls);
1914        }
1915        if (is("name")) {
1916            var sym = _make_symbol(AST_SymbolRef, start);
1917            next();
1918            if (sym.name == "async") {
1919                if (is("keyword", "function")) {
1920                    next();
1921                    var func;
1922                    if (is("operator", "*")) {
1923                        next();
1924                        func = function_(AST_AsyncGeneratorFunction);
1925                    } else {
1926                        func = function_(AST_AsyncFunction);
1927                    }
1928                    func.start = start;
1929                    func.end = prev();
1930                    return subscripts(func, allow_calls);
1931                }
1932                if (is("name") && is_token(peek(), "punc", "=>")) {
1933                    start = S.token;
1934                    sym = _make_symbol(AST_SymbolRef, start);
1935                    next();
1936                    return arrow([ sym ], start, true);
1937                }
1938                if (is("punc", "(")) {
1939                    var call = subscripts(sym, allow_calls);
1940                    if (!is("punc", "=>")) return call;
1941                    var args = call.args;
1942                    if (args[args.length - 1] instanceof AST_Spread) {
1943                        args.rest = args.pop().expression;
1944                    }
1945                    return arrow(args, start, true);
1946                }
1947            }
1948            return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls);
1949        }
1950        if (ATOMIC_START_TOKEN[S.token.type]) {
1951            return subscripts(as_atom_node(), allow_calls);
1952        }
1953        unexpected();
1954    };
1955
1956    function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
1957        if (!parser) parser = maybe_assign;
1958        var first = true, a = [];
1959        while (!is("punc", closing)) {
1960            if (first) first = false; else expect(",");
1961            if (allow_trailing_comma && is("punc", closing)) break;
1962            if (allow_empty && is("punc", ",")) {
1963                a.push(new AST_Hole({ start: S.token, end: S.token }));
1964            } else if (!is("operator", "...")) {
1965                a.push(parser());
1966            } else if (parser === maybe_assign) {
1967                a.push(new AST_Spread({
1968                    start: S.token,
1969                    expression: (next(), parser()),
1970                    end: prev(),
1971                }));
1972            } else {
1973                next();
1974                a.rest = parser();
1975                if (a.rest instanceof AST_DefaultValue) token_error(a.rest.start, "Invalid rest parameter");
1976                break;
1977            }
1978        }
1979        expect(closing);
1980        return a;
1981    }
1982
1983    var array_ = embed_tokens(function() {
1984        expect("[");
1985        return new AST_Array({
1986            elements: expr_list("]", !options.strict, true)
1987        });
1988    });
1989
1990    var create_accessor = embed_tokens(function() {
1991        return function_(AST_Accessor);
1992    });
1993
1994    var object_ = embed_tokens(function() {
1995        expect("{");
1996        var first = true, a = [];
1997        while (!is("punc", "}")) {
1998            if (first) first = false; else expect(",");
1999            // allow trailing comma
2000            if (!options.strict && is("punc", "}")) break;
2001            var start = S.token;
2002            if (is("operator", "*")) {
2003                next();
2004                var key = as_property_key();
2005                var gen_start = S.token;
2006                var gen = function_(AST_GeneratorFunction);
2007                gen.start = gen_start;
2008                gen.end = prev();
2009                a.push(new AST_ObjectMethod({
2010                    start: start,
2011                    key: key,
2012                    value: gen,
2013                    end: prev(),
2014                }));
2015                continue;
2016            }
2017            if (is("operator", "...")) {
2018                next();
2019                a.push(new AST_Spread({
2020                    start: start,
2021                    expression: maybe_assign(),
2022                    end: prev(),
2023                }));
2024                continue;
2025            }
2026            if (is_token(peek(), "operator", "=")) {
2027                var name = as_symbol(AST_SymbolRef);
2028                next();
2029                a.push(new AST_ObjectKeyVal({
2030                    start: start,
2031                    key: start.value,
2032                    value: new AST_Assign({
2033                        start: start,
2034                        left: name,
2035                        operator: "=",
2036                        right: maybe_assign(),
2037                        end: prev(),
2038                    }),
2039                    end: prev(),
2040                }));
2041                continue;
2042            }
2043            if (is_token(peek(), "punc", ",") || is_token(peek(), "punc", "}")) {
2044                a.push(new AST_ObjectKeyVal({
2045                    start: start,
2046                    key: start.value,
2047                    value: as_symbol(AST_SymbolRef),
2048                    end: prev(),
2049                }));
2050                continue;
2051            }
2052            var key = as_property_key();
2053            if (is("punc", "(")) {
2054                var func_start = S.token;
2055                var func = function_(AST_Function);
2056                func.start = func_start;
2057                func.end = prev();
2058                a.push(new AST_ObjectMethod({
2059                    start: start,
2060                    key: key,
2061                    value: func,
2062                    end: prev(),
2063                }));
2064                continue;
2065            }
2066            if (is("punc", ":")) {
2067                next();
2068                a.push(new AST_ObjectKeyVal({
2069                    start: start,
2070                    key: key,
2071                    value: maybe_assign(),
2072                    end: prev(),
2073                }));
2074                continue;
2075            }
2076            if (start.type == "name") switch (key) {
2077              case "async":
2078                var is_gen = is("operator", "*") && next();
2079                key = as_property_key();
2080                var func_start = S.token;
2081                var func = function_(is_gen ? AST_AsyncGeneratorFunction : AST_AsyncFunction);
2082                func.start = func_start;
2083                func.end = prev();
2084                a.push(new AST_ObjectMethod({
2085                    start: start,
2086                    key: key,
2087                    value: func,
2088                    end: prev(),
2089                }));
2090                continue;
2091              case "get":
2092                a.push(new AST_ObjectGetter({
2093                    start: start,
2094                    key: as_property_key(),
2095                    value: create_accessor(),
2096                    end: prev(),
2097                }));
2098                continue;
2099              case "set":
2100                a.push(new AST_ObjectSetter({
2101                    start: start,
2102                    key: as_property_key(),
2103                    value: create_accessor(),
2104                    end: prev(),
2105                }));
2106                continue;
2107            }
2108            unexpected();
2109        }
2110        next();
2111        return new AST_Object({ properties: a });
2112    });
2113
2114    function as_property_key() {
2115        var tmp = S.token;
2116        switch (tmp.type) {
2117          case "operator":
2118            if (!KEYWORDS[tmp.value]) unexpected();
2119          case "num":
2120          case "string":
2121          case "name":
2122          case "keyword":
2123          case "atom":
2124            next();
2125            return "" + tmp.value;
2126          case "punc":
2127            expect("[");
2128            var key = maybe_assign();
2129            expect("]");
2130            return key;
2131          default:
2132            unexpected();
2133        }
2134    }
2135
2136    function as_name() {
2137        var name = S.token.value;
2138        expect_token("name");
2139        return name;
2140    }
2141
2142    function _make_symbol(type, token) {
2143        var name = token.value;
2144        switch (name) {
2145          case "await":
2146            if (S.in_async) unexpected(token);
2147            break;
2148          case "super":
2149            type = AST_Super;
2150            break;
2151          case "this":
2152            type = AST_This;
2153            break;
2154          case "yield":
2155            if (S.in_generator) unexpected(token);
2156            break;
2157        }
2158        return new type({
2159            name: "" + name,
2160            start: token,
2161            end: token,
2162        });
2163    }
2164
2165    function strict_verify_symbol(sym) {
2166        if (sym.name == "arguments" || sym.name == "eval" || sym.name == "let")
2167            token_error(sym.start, "Unexpected " + sym.name + " in strict mode");
2168    }
2169
2170    function as_symbol(type, no_error) {
2171        if (!is("name")) {
2172            if (!no_error) croak("Name expected");
2173            return null;
2174        }
2175        var sym = _make_symbol(type, S.token);
2176        if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
2177            strict_verify_symbol(sym);
2178        }
2179        next();
2180        return sym;
2181    }
2182
2183    function maybe_destructured(type) {
2184        var start = S.token;
2185        if (is("punc", "[")) {
2186            next();
2187            var elements = expr_list("]", !options.strict, true, function() {
2188                return maybe_default(type);
2189            });
2190            return new AST_DestructuredArray({
2191                start: start,
2192                elements: elements,
2193                rest: elements.rest || null,
2194                end: prev(),
2195            });
2196        }
2197        if (is("punc", "{")) {
2198            next();
2199            var first = true, a = [], rest = null;
2200            while (!is("punc", "}")) {
2201                if (first) first = false; else expect(",");
2202                // allow trailing comma
2203                if (!options.strict && is("punc", "}")) break;
2204                var key_start = S.token;
2205                if (is("punc", "[") || is_token(peek(), "punc", ":")) {
2206                    var key = as_property_key();
2207                    expect(":");
2208                    a.push(new AST_DestructuredKeyVal({
2209                        start: key_start,
2210                        key: key,
2211                        value: maybe_default(type),
2212                        end: prev(),
2213                    }));
2214                    continue;
2215                }
2216                if (is("operator", "...")) {
2217                    next();
2218                    rest = maybe_destructured(type);
2219                    break;
2220                }
2221                var name = as_symbol(type);
2222                if (is("operator", "=")) {
2223                    next();
2224                    name = new AST_DefaultValue({
2225                        start: name.start,
2226                        name: name,
2227                        value: maybe_assign(),
2228                        end: prev(),
2229                    });
2230                }
2231                a.push(new AST_DestructuredKeyVal({
2232                    start: key_start,
2233                    key: key_start.value,
2234                    value: name,
2235                    end: prev(),
2236                }));
2237            }
2238            expect("}");
2239            return new AST_DestructuredObject({
2240                start: start,
2241                properties: a,
2242                rest: rest,
2243                end: prev(),
2244            });
2245        }
2246        return as_symbol(type);
2247    }
2248
2249    function maybe_default(type) {
2250        var start = S.token;
2251        var name = maybe_destructured(type);
2252        if (!is("operator", "=")) return name;
2253        next();
2254        return new AST_DefaultValue({
2255            start: start,
2256            name: name,
2257            value: maybe_assign(),
2258            end: prev(),
2259        });
2260    }
2261
2262    function template(tag) {
2263        var start = tag ? tag.start : S.token;
2264        var read = S.input.context().read_template;
2265        var strings = [];
2266        var expressions = [];
2267        while (read(strings)) {
2268            next();
2269            expressions.push(expression());
2270            if (!is("punc", "}")) unexpected();
2271        }
2272        next();
2273        return new AST_Template({
2274            start: start,
2275            expressions: expressions,
2276            strings: strings,
2277            tag: tag,
2278            end: prev(),
2279        });
2280    }
2281
2282    function subscripts(expr, allow_calls) {
2283        var start = expr.start;
2284        var optional = null;
2285        while (true) {
2286            if (is("operator", "?") && is_token(peek(), "punc", ".")) {
2287                next();
2288                next();
2289                optional = expr;
2290            }
2291            if (is("punc", "[")) {
2292                next();
2293                var prop = expression();
2294                expect("]");
2295                expr = new AST_Sub({
2296                    start: start,
2297                    optional: optional === expr,
2298                    expression: expr,
2299                    property: prop,
2300                    end: prev(),
2301                });
2302            } else if (allow_calls && is("punc", "(")) {
2303                next();
2304                expr = new AST_Call({
2305                    start: start,
2306                    optional: optional === expr,
2307                    expression: expr,
2308                    args: expr_list(")", !options.strict),
2309                    end: prev(),
2310                });
2311            } else if (optional === expr || is("punc", ".")) {
2312                if (optional !== expr) next();
2313                expr = new AST_Dot({
2314                    start: start,
2315                    optional: optional === expr,
2316                    expression: expr,
2317                    property: as_name(),
2318                    end: prev(),
2319                });
2320            } else if (is("punc", "`")) {
2321                if (optional) croak("Invalid template on optional chain");
2322                expr = template(expr);
2323            } else {
2324                break;
2325            }
2326        }
2327        if (optional) expr.terminal = true;
2328        if (expr instanceof AST_Call && !expr.pure) {
2329            var start = expr.start;
2330            var comments = start.comments_before;
2331            var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
2332            while (--i >= 0) {
2333                if (/[@#]__PURE__/.test(comments[i].value)) {
2334                    expr.pure = true;
2335                    break;
2336                }
2337            }
2338        }
2339        return expr;
2340    }
2341
2342    function maybe_unary(no_in) {
2343        var start = S.token;
2344        if (S.in_async && is("name", "await")) {
2345            if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
2346            S.input.context().regex_allowed = true;
2347            next();
2348            return new AST_Await({
2349                start: start,
2350                expression: maybe_unary(no_in),
2351                end: prev(),
2352            });
2353        }
2354        if (S.in_generator && is("name", "yield")) {
2355            if (S.in_funarg === S.in_function) croak("Invalid use of yield in function argument");
2356            S.input.context().regex_allowed = true;
2357            next();
2358            var exp = null;
2359            var nested = false;
2360            if (is("operator", "*")) {
2361                next();
2362                exp = maybe_assign(no_in);
2363                nested = true;
2364            } else if (is("punc") ? !PUNC_AFTER_EXPRESSION[S.token.value] : !can_insert_semicolon()) {
2365                exp = maybe_assign(no_in);
2366            }
2367            return new AST_Yield({
2368                start: start,
2369                expression: exp,
2370                nested: nested,
2371                end: prev(),
2372            });
2373        }
2374        if (is("operator") && UNARY_PREFIX[start.value]) {
2375            next();
2376            handle_regexp();
2377            var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(no_in));
2378            ex.start = start;
2379            ex.end = prev();
2380            return ex;
2381        }
2382        var val = expr_atom(true);
2383        while (is("operator") && UNARY_POSTFIX[S.token.value] && !has_newline_before(S.token)) {
2384            val = make_unary(AST_UnaryPostfix, S.token, val);
2385            val.start = start;
2386            val.end = S.token;
2387            next();
2388        }
2389        return val;
2390    }
2391
2392    function make_unary(ctor, token, expr) {
2393        var op = token.value;
2394        switch (op) {
2395          case "++":
2396          case "--":
2397            if (!is_assignable(expr))
2398                token_error(token, "Invalid use of " + op + " operator");
2399            break;
2400          case "delete":
2401            if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
2402                token_error(expr.start, "Calling delete on expression not allowed in strict mode");
2403            break;
2404        }
2405        return new ctor({ operator: op, expression: expr });
2406    }
2407
2408    var expr_op = function(left, min_precision, no_in) {
2409        var op = is("operator") ? S.token.value : null;
2410        if (op == "in" && no_in) op = null;
2411        var precision = op != null ? PRECEDENCE[op] : null;
2412        if (precision != null && precision > min_precision) {
2413            next();
2414            var right = expr_op(maybe_unary(no_in), op == "**" ? precision - 1 : precision, no_in);
2415            return expr_op(new AST_Binary({
2416                start    : left.start,
2417                left     : left,
2418                operator : op,
2419                right    : right,
2420                end      : right.end,
2421            }), min_precision, no_in);
2422        }
2423        return left;
2424    };
2425
2426    function expr_ops(no_in) {
2427        return expr_op(maybe_unary(no_in), 0, no_in);
2428    }
2429
2430    var maybe_conditional = function(no_in) {
2431        var start = S.token;
2432        var expr = expr_ops(no_in);
2433        if (is("operator", "?")) {
2434            next();
2435            var yes = maybe_assign();
2436            expect(":");
2437            return new AST_Conditional({
2438                start       : start,
2439                condition   : expr,
2440                consequent  : yes,
2441                alternative : maybe_assign(no_in),
2442                end         : prev()
2443            });
2444        }
2445        return expr;
2446    };
2447
2448    function is_assignable(expr) {
2449        return expr instanceof AST_PropAccess && !expr.optional || expr instanceof AST_SymbolRef;
2450    }
2451
2452    function to_destructured(node) {
2453        if (node instanceof AST_Array) {
2454            var rest = null;
2455            if (node.elements[node.elements.length - 1] instanceof AST_Spread) {
2456                rest = to_destructured(node.elements.pop().expression);
2457                if (!(rest instanceof AST_Destructured || is_assignable(rest))) return node;
2458            }
2459            var elements = node.elements.map(to_destructured);
2460            return all(elements, function(node) {
2461                return node instanceof AST_DefaultValue
2462                    || node instanceof AST_Destructured
2463                    || node instanceof AST_Hole
2464                    || is_assignable(node);
2465            }) ? new AST_DestructuredArray({
2466                start: node.start,
2467                elements: elements,
2468                rest: rest,
2469                end: node.end,
2470            }) : node;
2471        }
2472        if (node instanceof AST_Assign) {
2473            var name = to_destructured(node.left);
2474            return name instanceof AST_Destructured || is_assignable(name) ? new AST_DefaultValue({
2475                start: node.start,
2476                name: name,
2477                value: node.right,
2478                end: node.end,
2479            }) : node;
2480        }
2481        if (!(node instanceof AST_Object)) return node;
2482        var rest = null;
2483        if (node.properties[node.properties.length - 1] instanceof AST_Spread) {
2484            rest = to_destructured(node.properties.pop().expression);
2485            if (!(rest instanceof AST_Destructured || is_assignable(rest))) return node;
2486        }
2487        var props = [];
2488        for (var i = 0; i < node.properties.length; i++) {
2489            var prop = node.properties[i];
2490            if (!(prop instanceof AST_ObjectKeyVal)) return node;
2491            var value = to_destructured(prop.value);
2492            if (!(value instanceof AST_DefaultValue || value instanceof AST_Destructured || is_assignable(value))) {
2493                return node;
2494            }
2495            props.push(new AST_DestructuredKeyVal({
2496                start: prop.start,
2497                key: prop.key,
2498                value: value,
2499                end: prop.end,
2500            }));
2501        }
2502        return new AST_DestructuredObject({
2503            start: node.start,
2504            properties: props,
2505            rest: rest,
2506            end: node.end,
2507        });
2508    }
2509
2510    function maybe_assign(no_in) {
2511        var start = S.token;
2512        var left = maybe_conditional(no_in), val = S.token.value;
2513        if (is("operator") && ASSIGNMENT[val]) {
2514            if (is_assignable(left) || val == "=" && (left = to_destructured(left)) instanceof AST_Destructured) {
2515                next();
2516                return new AST_Assign({
2517                    start    : start,
2518                    left     : left,
2519                    operator : val,
2520                    right    : maybe_assign(no_in),
2521                    end      : prev()
2522                });
2523            }
2524            croak("Invalid assignment");
2525        }
2526        return left;
2527    }
2528
2529    function expression(no_in, maybe_arrow) {
2530        var start = S.token;
2531        var exprs = [];
2532        while (true) {
2533            if (maybe_arrow && is("operator", "...")) {
2534                next();
2535                exprs.rest = maybe_destructured(AST_SymbolFunarg);
2536                break;
2537            }
2538            exprs.push(maybe_assign(no_in));
2539            if (!is("punc", ",")) break;
2540            next();
2541            if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break;
2542        }
2543        return exprs.length == 1 && !exprs.rest ? exprs[0] : new AST_Sequence({
2544            start: start,
2545            expressions: exprs,
2546            end: prev(),
2547        });
2548    }
2549
2550    function in_loop(cont) {
2551        ++S.in_loop;
2552        var ret = cont();
2553        --S.in_loop;
2554        return ret;
2555    }
2556
2557    if (options.expression) {
2558        handle_regexp();
2559        var exp = expression();
2560        expect_token("eof");
2561        return exp;
2562    }
2563
2564    return function() {
2565        var start = S.token;
2566        var body = [];
2567        if (options.module) {
2568            S.in_async = true;
2569            S.input.add_directive("use strict");
2570        }
2571        S.input.push_directives_stack();
2572        while (!is("eof"))
2573            body.push(statement(true));
2574        S.input.pop_directives_stack();
2575        var end = prev() || start;
2576        var toplevel = options.toplevel;
2577        if (toplevel) {
2578            toplevel.body = toplevel.body.concat(body);
2579            toplevel.end = end;
2580        } else {
2581            toplevel = new AST_Toplevel({ start: start, body: body, end: end });
2582        }
2583        return toplevel;
2584    }();
2585}
2586