Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.73% covered (warning)
77.73%
171 / 220
14.29% covered (danger)
14.29%
1 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
JavaScriptMinifier
77.73% covered (warning)
77.73%
171 / 220
14.29% covered (danger)
14.29%
1 / 7
282.77
0.00% covered (danger)
0.00%
0 / 1
 ensureExpandedStates
7.69% covered (danger)
7.69%
2 / 26
0.00% covered (danger)
0.00%
0 / 1
244.31
 minify
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createMinifier
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 createSourceMapState
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 createIdentityMinifier
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 minifyInternal
97.11% covered (success)
97.11%
168 / 173
0.00% covered (danger)
0.00%
0 / 1
94
 debug
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2/**
3 * Copyright 2011 Paul Copperman <paul.copperman@gmail.com>
4 * Copyright 2018 Timo Tijhof
5 * Copyright 2021 Roan Kattouw <roan.kattouw@gmail.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @file
20 * @license Apache-2.0
21 * @license MIT
22 * @license GPL-2.0-or-later
23 * @license LGPL-2.1-or-later
24 */
25
26namespace Wikimedia\Minify;
27
28use ReflectionClass;
29
30/**
31 * JavaScript Minifier
32 *
33 * This class is meant to safely minify JavaScript code, while leaving syntactically correct
34 * programs intact. Other libraries, such as JSMin require a certain coding style to work
35 * correctly. OTOH, libraries like jsminplus, that do parse the code correctly are rather
36 * slow, because they construct a complete parse tree before outputting the code minified.
37 * So this class is meant to allow arbitrary (but syntactically correct) input, while being
38 * fast enough to be used for on-the-fly minifying.
39 *
40 * This class was written with ECMA-262 7th Edition in mind ("ECMAScript 2016"). Parsing features
41 * new to later editions of ECMAScript might not be supported. It's assumed that the input is
42 * syntactically correct; if it's not, this class may not detect that, and may produce incorrect
43 * output.
44 *
45 * This class has limited support for 8.0 spec ("ECMAScript 2017"), specifically, the await
46 * keyword, and most kinds of async functions are implemented. Other new parsing features of ES2017
47 * are not yet supported.
48 *
49 * See also:
50 * - <https://262.ecma-international.org/7.0/>
51 * - <https://262.ecma-international.org/8.0/>
52 * - <https://262.ecma-international.org/11.0/>
53 */
54class JavaScriptMinifier {
55
56    /* Parsing states.
57     * The state machine is necessary to decide whether to parse a slash as division
58     * operator or as regexp literal, and to know where semicolon insertion is possible.
59     * States are generally named after the next expected item. We only distinguish states when the
60     * distinction is relevant for our purpose. The meaning of these states is documented
61     * in $model below.
62     *
63     * Negative numbers are used to indicate that the state is inside a generator function,
64     * which changes the behavior of 'yield'
65     */
66    private const STATEMENT                     = 1;
67    private const CONDITION                     = 2;
68    private const FUNC                          = 3;
69    private const GENFUNC                       = 4;
70    private const PROPERTY_ASSIGNMENT           = 5;
71    private const EXPRESSION                    = 6;
72    private const EXPRESSION_NO_NL              = 7;
73    private const EXPRESSION_OP                 = 8;
74    private const EXPRESSION_DOT                = 9;
75    private const EXPRESSION_END                = 10;
76    private const EXPRESSION_ARROWFUNC          = 11;
77    private const EXPRESSION_TERNARY            = 12;
78    private const EXPRESSION_TERNARY_OP         = 13;
79    private const EXPRESSION_TERNARY_DOT        = 14;
80    private const EXPRESSION_TERNARY_ARROWFUNC  = 15;
81    private const PAREN_EXPRESSION              = 16;
82    private const PAREN_EXPRESSION_OP           = 17;
83    private const PAREN_EXPRESSION_DOT          = 18;
84    private const PAREN_EXPRESSION_ARROWFUNC    = 19;
85    private const PROPERTY_EXPRESSION           = 20;
86    private const PROPERTY_EXPRESSION_OP        = 21;
87    private const PROPERTY_EXPRESSION_DOT       = 22;
88    private const PROPERTY_EXPRESSION_ARROWFUNC = 23;
89    private const CLASS_DEF                     = 24;
90    private const IMPORT_EXPORT                 = 25;
91    private const TEMPLATE_STRING_HEAD          = 26;
92    private const TEMPLATE_STRING_TAIL          = 27;
93    private const PAREN_EXPRESSION_OP_NO_NL     = 28;
94    private const EXPRESSION_TERNARY_NO_NL      = 29;
95    private const PAREN_EXPRESSION_NO_NL        = 30;
96    private const PROPERTY_EXPRESSION_NO_NL     = 31;
97
98    /* Token types */
99
100    /** @var int unary operators */
101    private const TYPE_UN_OP = 101;
102
103    /** @var int ++ and -- */
104    private const TYPE_INCR_OP = 102;
105
106    /** @var int binary operators (except .) */
107    private const TYPE_BIN_OP = 103;
108
109    /** @var int + and - which can be either unary or binary ops */
110    private const TYPE_ADD_OP = 104;
111
112    /** @var int . */
113    private const TYPE_DOT = 105;
114
115    /** @var int ? */
116    private const TYPE_HOOK = 106;
117
118    /** @var int : */
119    private const TYPE_COLON = 107;
120
121    /** @var int , */
122    private const TYPE_COMMA = 108;
123
124    /** @var int ; */
125    private const TYPE_SEMICOLON = 109;
126
127    /** @var int { */
128    private const TYPE_BRACE_OPEN = 110;
129
130    /** @var int } */
131    private const TYPE_BRACE_CLOSE = 111;
132
133    /** @var int ( and [ */
134    private const TYPE_PAREN_OPEN = 112;
135
136    /** @var int ) and ] */
137    private const TYPE_PAREN_CLOSE = 113;
138
139    /** @var int => */
140    private const TYPE_ARROW = 114;
141
142    /** @var int keywords: break, continue, return, throw (and yield, if we're in a generator) */
143    private const TYPE_RETURN = 115;
144
145    /** @var int keywords: catch, for, with, switch, while, if */
146    private const TYPE_IF = 116;
147
148    /** @var int keywords: case, finally, else, do, try */
149    private const TYPE_DO = 117;
150
151    /** @var int keywords: var, let, const */
152    private const TYPE_VAR = 118;
153
154    /** @var int keywords: yield */
155    private const TYPE_YIELD = 119;
156
157    /** @var int keywords: function */
158    private const TYPE_FUNC = 120;
159
160    /** @var int keywords: class */
161    private const TYPE_CLASS = 121;
162
163    /** @var int all literals, identifiers, unrecognised tokens, and other keywords */
164    private const TYPE_LITERAL = 122;
165
166    /** @var int For special treatment of tokens that usually mean something else */
167    private const TYPE_SPECIAL = 123;
168
169    /** @var int keywords: async */
170    private const TYPE_ASYNC = 124;
171
172    /** @var int keywords: await */
173    private const TYPE_AWAIT = 125;
174
175    /** @var int Go to another state */
176    private const ACTION_GOTO = 201;
177
178    /** @var int Push a state to the stack */
179    private const ACTION_PUSH = 202;
180
181    /** @var int Pop the state from the top of the stack, and go to that state */
182    private const ACTION_POP = 203;
183
184    /** @var int Limit to avoid excessive memory usage */
185    private const STACK_LIMIT = 1000;
186
187    /** Length of the longest token in $tokenTypes made of punctuation characters,
188     * as defined in $opChars. Update this if you add longer tokens to $tokenTypes.
189     *
190     * Currently, the longest punctuation token is `>>>=`, which is 4 characters.
191     */
192    private const LONGEST_PUNCTUATION_TOKEN = 4;
193
194    /**
195     * @var int $maxLineLength
196     *
197     * Maximum line length
198     *
199     * This is not a strict maximum, but a guideline. Longer lines will be
200     * produced when literals (e.g. quoted strings) longer than this are
201     * encountered, or when required to guard against semicolon insertion.
202     *
203     * This is a private member (instead of constant) to allow tests to
204     * set it to 1, to verify ASI and line-breaking behaviour.
205     */
206    private static $maxLineLength = 1000;
207
208    private static bool $expandedStates = false;
209
210    /**
211     * @var array $opChars
212     *
213     * Characters which can be combined without whitespace between them.
214     * Unlike the ECMAScript spec, we define these as individual symbols, not sequences.
215     */
216    private static $opChars = [
217        // ECMAScript 7.0 Â§ 11.7 Punctuators
218        //
219        //    Punctuator
220        //    DivPunctuator
221        //    RightBracePunctuator
222        //
223        '{' => true,
224        '(' => true,
225        ')' => true,
226        '[' => true,
227        ']' => true,
228        // Dots have a special case after $dotlessNum which require whitespace
229        '.' => true,
230        ';' => true,
231        ',' => true,
232        '<' => true,
233        '>' => true,
234        '=' => true,
235        '!' => true,
236        '+' => true,
237        '-' => true,
238        '*' => true,
239        '%' => true,
240        '&' => true,
241        '|' => true,
242        '^' => true,
243        '~' => true,
244        '?' => true,
245        ':' => true,
246        '/' => true,
247        '}' => true,
248
249        // ECMAScript 7.0 Â§ 11.8.4 String Literals
250        '"' => true,
251        "'" => true,
252
253        // ECMAScript 7.0 Â§ 11.8.6 Template Literal Lexical Components
254        '`' => true,
255    ];
256
257    /**
258     * @var array $tokenTypes
259     *
260     * Tokens and their types.
261     */
262    private static $tokenTypes = [
263        // ECMAScript 7.0 Â§ 12.2 Primary Expression
264        //
265        //    ...BindingIdentifier
266        //
267        '...'        => self::TYPE_UN_OP,
268
269        // ECMAScript 7.0 Â§ 12.3 Left-Hand-Side Expressions
270        //
271        //    MemberExpression
272        //
273        // A dot can also be part of a DecimalLiteral, but in that case we handle the entire
274        // DecimalLiteral as one token. A separate '.' token is always part of a MemberExpression.
275        '.'          => self::TYPE_DOT,
276
277        // ECMAScript 7.0 Â§ 12.4 Update Expressions
278        //
279        //    LeftHandSideExpression [no LineTerminator here] ++
280        //    LeftHandSideExpression [no LineTerminator here] --
281        //    ++ UnaryExpression
282        //    -- UnaryExpression
283        //
284        // This is given a separate type from TYPE_UN_OP,
285        // because `++` and `--` require special handling
286        // around new lines and semicolon insertion.
287        //
288        '++'         => self::TYPE_INCR_OP,
289        '--'         => self::TYPE_INCR_OP,
290
291        // ECMAScript 7.0 Â§ 12.5 Unary Operators
292        //
293        //    UnaryExpression
294        //        includes UpdateExpression
295        //            includes NewExpression, which defines 'new'
296        //
297        'new'        => self::TYPE_UN_OP,
298        'delete'     => self::TYPE_UN_OP,
299        'void'       => self::TYPE_UN_OP,
300        'typeof'     => self::TYPE_UN_OP,
301        '~'          => self::TYPE_UN_OP,
302        '!'          => self::TYPE_UN_OP,
303
304        // These operators can be either binary or unary depending on context,
305        // and thus require separate type from TYPE_UN_OP and TYPE_BIN_OP.
306        //
307        //     var z = +y;    // unary (convert to number)
308        //     var z = x + y; // binary (add operation)
309        //
310        // ECMAScript 7.0 Â§ 12.5 Unary Operators
311        //
312        //     + UnaryExpression
313        //     - UnaryExpression
314        //
315        // ECMAScript 7.0 Â§ 12.8 Additive Operators
316        //
317        //     Expression + Expression
318        //     Expression - Expression
319        //
320        '+'          => self::TYPE_ADD_OP,
321        '-'          => self::TYPE_ADD_OP,
322
323        // These operators can be treated the same as binary operators.
324        // They are all defined in one of these two forms, and do
325        // not require special handling for preserving whitespace or
326        // line breaks.
327        //
328        //     Expression operator Expression
329        //
330        // Defined in:
331        // - ECMAScript 7.0 Â§ 12.6 Exponentiation Operator
332        //   ExponentiationExpression
333        // - ECMAScript 7.0 Â§ 12.7 Multiplicative Operators
334        //   MultiplicativeOperator
335        // - ECMAScript 7.0 Â§ 12.9 Bitwise Shift Operators
336        //   ShiftExpression
337        // - ECMAScript 7.0 Â§ 12.10 Relational Operators
338        //   RelationalExpression
339        // - ECMAScript 7.0 Â§ 12.11 Equality Operators
340        //   EqualityExpression
341        '**'         => self::TYPE_BIN_OP,
342        '*'          => self::TYPE_BIN_OP,
343        '/'          => self::TYPE_BIN_OP,
344        '%'          => self::TYPE_BIN_OP,
345        '<<'         => self::TYPE_BIN_OP,
346        '>>'         => self::TYPE_BIN_OP,
347        '>>>'        => self::TYPE_BIN_OP,
348        '<'          => self::TYPE_BIN_OP,
349        '>'          => self::TYPE_BIN_OP,
350        '<='         => self::TYPE_BIN_OP,
351        '>='         => self::TYPE_BIN_OP,
352        'instanceof' => self::TYPE_BIN_OP,
353        'in'         => self::TYPE_BIN_OP,
354        '=='         => self::TYPE_BIN_OP,
355        '!='         => self::TYPE_BIN_OP,
356        '==='        => self::TYPE_BIN_OP,
357        '!=='        => self::TYPE_BIN_OP,
358
359        // ECMAScript 7.0 Â§ 12.12 Binary Bitwise Operators
360        //
361        //    BitwiseANDExpression
362        //    BitwiseXORExpression
363        //    BitwiseORExpression
364        //
365        '&'          => self::TYPE_BIN_OP,
366        '^'          => self::TYPE_BIN_OP,
367        '|'          => self::TYPE_BIN_OP,
368
369        // ECMAScript 7.0 Â§ 12.13 Binary Logical Operators
370        //
371        //    LogicalANDExpression
372        //    LogicalORExpression
373        //
374        '&&'         => self::TYPE_BIN_OP,
375        '||'         => self::TYPE_BIN_OP,
376
377        // ECMAScript 11.0 Â§ 12.13 Binary Logical Operators
378        '??'         => self::TYPE_BIN_OP,
379
380        // ECMAScript 7.0 Â§ 12.14 Conditional Operator
381        //
382        //    ConditionalExpression:
383        //        LogicalORExpression ? AssignmentExpression : AssignmentExpression
384        //
385        // Also known as ternary.
386        '?'          => self::TYPE_HOOK,
387        ':'          => self::TYPE_COLON,
388
389        // ECMAScript 7.0 Â§ 12.15 Assignment Operators
390        '='          => self::TYPE_BIN_OP,
391        '*='         => self::TYPE_BIN_OP,
392        '/='         => self::TYPE_BIN_OP,
393        '%='         => self::TYPE_BIN_OP,
394        '+='         => self::TYPE_BIN_OP,
395        '-='         => self::TYPE_BIN_OP,
396        '<<='        => self::TYPE_BIN_OP,
397        '>>='        => self::TYPE_BIN_OP,
398        '>>>='       => self::TYPE_BIN_OP,
399        '&='         => self::TYPE_BIN_OP,
400        '^='         => self::TYPE_BIN_OP,
401        '|='         => self::TYPE_BIN_OP,
402        '**='        => self::TYPE_BIN_OP,
403
404        // ECMAScript 7.0 Â§ 12.16 Comma Operator
405        ','          => self::TYPE_COMMA,
406
407        // ECMAScript 7.0 Â§ 11.9.1 Rules of Automatic Semicolon Insertion
408        //
409        // These keywords disallow LineTerminator before their (sometimes optional)
410        // Expression or Identifier. They are similar enough that we can treat
411        // them all the same way that we treat return, with regards to new line
412        // and semicolon insertion.
413        //
414        //    keyword ;
415        //    keyword [no LineTerminator here] Identifier ;
416        //    keyword [no LineTerminator here] Expression ;
417        //
418        // See also ECMAScript 7.0:
419        // - Â§ 13.8 The continue Statement
420        // - Â§ 13.9 The break Statement
421        // - Â§ 13.10 The return Statement
422        // - Â§ 13.14 The throw Statement
423        // - Â§ 14.4 Generator Function Definitions (yield)
424        'continue'   => self::TYPE_RETURN,
425        'break'      => self::TYPE_RETURN,
426        'return'     => self::TYPE_RETURN,
427        'throw'      => self::TYPE_RETURN,
428        // "yield" only counts as a keyword if when inside inside a generator functions,
429        // otherwise it is a regular identifier.
430        // This is handled with the negative states hack: if the state is negative, TYPE_YIELD
431        // is treated as TYPE_RETURN, if it's positive it's treated as TYPE_LITERAL
432        'yield'      => self::TYPE_YIELD,
433
434        // These keywords require a parenthesised Expression or Identifier before the
435        // next Statement. They are similar enough to all treat like "if".
436        //
437        //     keyword ( Expression ) Statement
438        //     keyword ( Identifier ) Statement
439        //
440        // See also ECMAScript 7.0:
441        // - Â§ 13.6 The if Statement
442        // - Â§ 13.7 Iteration Statements (while, for)
443        // - Â§ 13.11 The with Statement
444        // - Â§ 13.12 The switch Statement
445        // - Â§ 13.15 The try Statement (catch)
446        'if'         => self::TYPE_IF,
447        'while'      => self::TYPE_IF,
448        'for'        => self::TYPE_IF,
449        'with'       => self::TYPE_IF,
450        'switch'     => self::TYPE_IF,
451        'catch'      => self::TYPE_IF,
452
453        // The keywords followed by a Statement, Expression, or Block.
454        //
455        //     keyword Statement
456        //     keyword Expression
457        //     keyword Block
458        //
459        // See also ECMAScript 7.0:
460        // - Â§ 13.6 The if Statement (else)
461        // - Â§ 13.7 Iteration Statements (do)
462        // - Â§ 13.12 The switch Statement (case)
463        // - Â§ 13.15 The try Statement (try, finally)
464        'else'       => self::TYPE_DO,
465        'do'         => self::TYPE_DO,
466        'case'       => self::TYPE_DO,
467        'try'        => self::TYPE_DO,
468        'finally'    => self::TYPE_DO,
469
470        // ECMAScript 7.0 Â§ 13.3 Declarations and the Variable Statement
471        //
472        //    LetOrConst
473        //    VariableStatement
474        //
475        // These keywords are followed by a variable declaration statement.
476        // This needs to be treated differently from the TYPE_DO group,
477        // because for TYPE_VAR, when we see an "{" open curly brace, it
478        // begins object destructuring (ObjectBindingPattern), not a block.
479        'var'        => self::TYPE_VAR,
480        'let'        => self::TYPE_VAR,
481        'const'      => self::TYPE_VAR,
482
483        // ECMAScript 7.0 Â§ 14.1 Function Definitions
484        'function'   => self::TYPE_FUNC,
485
486        // ECMAScript 7.0 Â§ 14.2 Arrow Function Definitions
487        '=>'         => self::TYPE_ARROW,
488
489        // ECMAScript 7.0 Â§ 14.5 Class Definitions
490        //
491        //     class Identifier { ClassBody }
492        //     class { ClassBody }
493        //     class Identifier extends Expression { ClassBody }
494        //     class extends Expression { ClassBody }
495        //
496        'class'      => self::TYPE_CLASS,
497
498        // ECMAScript 8.0 Â§ 14.6 AwaitExpression
499        //
500        //    await UnaryExpression
501        //
502        'await'      => self::TYPE_AWAIT,
503
504        // Can be one of:
505        // - Block (ECMAScript 7.0 Â§ 13.2 Block)
506        // - ObjectLiteral (ECMAScript 7.0 Â§ 12.2 Primary Expression)
507        '{'          => self::TYPE_BRACE_OPEN,
508        '}'          => self::TYPE_BRACE_CLOSE,
509
510        // Can be one of:
511        // - Parenthesised Identifier or Expression after a
512        //   TYPE_IF or TYPE_FUNC keyword.
513        // - PrimaryExpression (ECMAScript 7.0 Â§ 12.2 Primary Expression)
514        // - CallExpression (ECMAScript 7.0 Â§ 12.3 Left-Hand-Side Expressions)
515        // - Beginning of an ArrowFunction (ECMAScript 7.0 Â§ 14.2 Arrow Function Definitions)
516        '('          => self::TYPE_PAREN_OPEN,
517        ')'          => self::TYPE_PAREN_CLOSE,
518
519        // Can be one of:
520        // - ArrayLiteral (ECMAScript 7.0 Â§ 12.2 Primary Expressions)
521        // - ComputedPropertyName (ECMAScript 7.0 Â§ 12.2.6 Object Initializer)
522        '['          => self::TYPE_PAREN_OPEN,
523        ']'          => self::TYPE_PAREN_CLOSE,
524
525        // Can be one of:
526        // - End of any statement
527        // - EmptyStatement (ECMAScript 7.0 Â§ 13.4 Empty Statement)
528        ';'          => self::TYPE_SEMICOLON,
529
530        // ECMAScript 8.0 Â§ 14.6 Async Function Definitions
531        // async [no LineTerminator here] function ...
532        // async [no LineTerminator here] propertyName() ...
533        'async'      => self::TYPE_ASYNC,
534    ];
535
536    /**
537     * @var array $model
538     *
539     * The main table for the state machine. Defines the desired action for every state/token pair.
540     *
541     * The state pushed onto the stack by ACTION_PUSH will be returned to by ACTION_POP.
542     * A state/token pair may not specify both ACTION_POP and ACTION_GOTO. If that does happen,
543     * ACTION_POP takes precedence.
544     *
545     * This table is augmented by self::ensureExpandedStates().
546     */
547    private static $model = [
548        // Statement - This is the initial state.
549        self::STATEMENT => [
550            self::TYPE_UN_OP => [
551                self::ACTION_GOTO => self::EXPRESSION,
552            ],
553            self::TYPE_INCR_OP => [
554                self::ACTION_GOTO => self::EXPRESSION,
555            ],
556            self::TYPE_ADD_OP => [
557                self::ACTION_GOTO => self::EXPRESSION,
558            ],
559            self::TYPE_BRACE_OPEN => [
560                // Use of '{' in statement context, creates a Block.
561                self::ACTION_PUSH => self::STATEMENT,
562            ],
563            self::TYPE_BRACE_CLOSE => [
564                // Ends a Block
565                self::ACTION_POP => true,
566            ],
567            self::TYPE_PAREN_OPEN => [
568                self::ACTION_PUSH => self::EXPRESSION_OP,
569                self::ACTION_GOTO => self::PAREN_EXPRESSION,
570            ],
571            self::TYPE_RETURN => [
572                self::ACTION_GOTO => self::EXPRESSION_NO_NL,
573            ],
574            self::TYPE_IF => [
575                self::ACTION_GOTO => self::CONDITION,
576            ],
577            self::TYPE_VAR => [
578                self::ACTION_GOTO => self::EXPRESSION,
579            ],
580            self::TYPE_FUNC => [
581                self::ACTION_PUSH => self::STATEMENT,
582                self::ACTION_GOTO => self::FUNC,
583            ],
584            self::TYPE_CLASS => [
585                self::ACTION_PUSH => self::STATEMENT,
586                self::ACTION_GOTO => self::CLASS_DEF,
587            ],
588            self::TYPE_SPECIAL => [
589                'import' => [
590                    self::ACTION_GOTO => self::IMPORT_EXPORT,
591                ],
592                'export' => [
593                    self::ACTION_GOTO => self::IMPORT_EXPORT,
594                ],
595            ],
596            self::TYPE_LITERAL => [
597                self::ACTION_GOTO => self::EXPRESSION_OP,
598            ],
599            self::TYPE_ASYNC => [
600                self::ACTION_GOTO => self::EXPRESSION_OP,
601            ],
602            self::TYPE_AWAIT => [
603                self::ACTION_GOTO => self::EXPRESSION,
604            ],
605        ],
606        // The state after if/catch/while/for/switch/with
607        // Waits for an expression in parentheses, then goes to STATEMENT
608        self::CONDITION => [
609            self::TYPE_PAREN_OPEN => [
610                self::ACTION_PUSH => self::STATEMENT,
611                self::ACTION_GOTO => self::PAREN_EXPRESSION,
612            ],
613        ],
614        // The state after the function keyword. Waits for {, then goes to STATEMENT.
615        // The function body's closing } will pop the stack, so the state to return to
616        // after the function should be pushed to the stack first
617        self::FUNC => [
618            // Needed to prevent * in an expression in the argument list from improperly
619            // triggering GENFUNC
620            self::TYPE_PAREN_OPEN => [
621                self::ACTION_PUSH => self::FUNC,
622                self::ACTION_GOTO => self::PAREN_EXPRESSION,
623            ],
624            self::TYPE_BRACE_OPEN => [
625                self::ACTION_GOTO => self::STATEMENT,
626            ],
627            self::TYPE_SPECIAL => [
628                '*' => [
629                    self::ACTION_GOTO => self::GENFUNC,
630                ],
631            ],
632        ],
633        // After function*. Waits for { , then goes to a generator function statement.
634        self::GENFUNC => [
635            self::TYPE_BRACE_OPEN => [
636                // Note negative value: generator function states are negative
637                self::ACTION_GOTO => -self::STATEMENT
638            ],
639        ],
640        // Property assignment - This is an object literal declaration.
641        // For example: `{ key: value, key2, [computedKey3]: value3, method4() { ... } }`
642        self::PROPERTY_ASSIGNMENT => [
643            // Note that keywords like if, class, var, delete, instanceof etc. can be used as keys,
644            // and should be treated as literals here, as they are in EXPRESSION_DOT. In this state,
645            // that is implicitly true because TYPE_LITERAL has no action, so it stays in this state.
646            // If we later add a state transition for TYPE_LITERAL, that same transition should
647            // also be applied to TYPE_RETURN, TYPE_IF, TYPE_DO, TYPE_VAR, TYPE_FUNC and TYPE_CLASS.
648            self::TYPE_COLON => [
649                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
650            ],
651            // For {, which begins a method
652            self::TYPE_BRACE_OPEN => [
653                self::ACTION_PUSH => self::PROPERTY_ASSIGNMENT,
654                // This is not flipped, see "Special cases" below
655                self::ACTION_GOTO => self::STATEMENT,
656            ],
657            self::TYPE_BRACE_CLOSE => [
658                self::ACTION_POP => true,
659            ],
660            // For [, which begins a computed key
661            self::TYPE_PAREN_OPEN => [
662                self::ACTION_PUSH => self::PROPERTY_ASSIGNMENT,
663                self::ACTION_GOTO => self::PAREN_EXPRESSION,
664            ],
665            self::TYPE_SPECIAL => [
666                '*' => [
667                    self::ACTION_PUSH => self::PROPERTY_ASSIGNMENT,
668                    self::ACTION_GOTO => self::GENFUNC,
669                ],
670            ],
671        ],
672        // Place in an expression where we expect an operand or a unary operator: the start
673        // of an expression or after an operator. Note that unary operators (including INCR_OP
674        // and ADD_OP) cause us to stay in this state, while operands take us to EXPRESSION_OP
675        self::EXPRESSION => [
676            self::TYPE_SEMICOLON => [
677                self::ACTION_GOTO => self::STATEMENT,
678            ],
679            self::TYPE_BRACE_OPEN => [
680                self::ACTION_PUSH => self::EXPRESSION_OP,
681                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
682            ],
683            self::TYPE_BRACE_CLOSE => [
684                self::ACTION_POP => true,
685            ],
686            self::TYPE_PAREN_OPEN => [
687                self::ACTION_PUSH => self::EXPRESSION_OP,
688                self::ACTION_GOTO => self::PAREN_EXPRESSION,
689            ],
690            self::TYPE_FUNC => [
691                self::ACTION_PUSH => self::EXPRESSION_OP,
692                self::ACTION_GOTO => self::FUNC,
693            ],
694            self::TYPE_CLASS => [
695                self::ACTION_PUSH => self::EXPRESSION_OP,
696                self::ACTION_GOTO => self::CLASS_DEF,
697            ],
698            self::TYPE_LITERAL => [
699                self::ACTION_GOTO => self::EXPRESSION_OP,
700            ],
701            self::TYPE_ASYNC => [
702                self::ACTION_GOTO => self::EXPRESSION_OP,
703            ],
704            // 'return' can't appear here, but 'yield' can
705            self::TYPE_RETURN => [
706                self::ACTION_GOTO => self::EXPRESSION_NO_NL,
707            ],
708        ],
709        // An expression immediately after return/throw/break/continue/yield, where a newline
710        // is not allowed. This state is identical to EXPRESSION, except that semicolon
711        // insertion can happen here, and we (almost) never stay here: in cases where EXPRESSION
712        // would do nothing, we go to EXPRESSION. We only stay here if there's a double yield,
713        // because 'yield yield foo' is a valid expression.
714        self::EXPRESSION_NO_NL => [
715            self::TYPE_UN_OP => [
716                self::ACTION_GOTO => self::EXPRESSION,
717            ],
718            self::TYPE_INCR_OP => [
719                self::ACTION_GOTO => self::EXPRESSION,
720            ],
721            // BIN_OP seems impossible at the start of an expression, but it can happen in
722            // yield *foo
723            self::TYPE_BIN_OP => [
724                self::ACTION_GOTO => self::EXPRESSION,
725            ],
726            self::TYPE_ADD_OP => [
727                self::ACTION_GOTO => self::EXPRESSION,
728            ],
729            self::TYPE_SEMICOLON => [
730                self::ACTION_GOTO => self::STATEMENT,
731            ],
732            self::TYPE_BRACE_OPEN => [
733                self::ACTION_PUSH => self::EXPRESSION_OP,
734                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
735            ],
736            self::TYPE_BRACE_CLOSE => [
737                self::ACTION_POP => true,
738            ],
739            self::TYPE_PAREN_OPEN => [
740                self::ACTION_PUSH => self::EXPRESSION_OP,
741                self::ACTION_GOTO => self::PAREN_EXPRESSION,
742            ],
743            self::TYPE_FUNC => [
744                self::ACTION_PUSH => self::EXPRESSION_OP,
745                self::ACTION_GOTO => self::FUNC,
746            ],
747            self::TYPE_CLASS => [
748                self::ACTION_PUSH => self::EXPRESSION_OP,
749                self::ACTION_GOTO => self::CLASS_DEF,
750            ],
751            self::TYPE_LITERAL => [
752                self::ACTION_GOTO => self::EXPRESSION_OP,
753            ],
754            self::TYPE_ASYNC => [
755                self::ACTION_GOTO => self::EXPRESSION_OP,
756            ],
757            self::TYPE_AWAIT => [
758                self::ACTION_GOTO => self::EXPRESSION,
759            ],
760            // 'return' can't appear here, because 'return return' isn't allowed
761            // But 'yield' can appear here, because 'yield yield' is allowed
762            self::TYPE_RETURN => [
763                self::ACTION_GOTO => self::EXPRESSION_NO_NL,
764            ],
765        ],
766        // Place in an expression after an operand, where we expect an operator
767        self::EXPRESSION_OP => [
768            self::TYPE_BIN_OP => [
769                self::ACTION_GOTO => self::EXPRESSION,
770            ],
771            self::TYPE_ADD_OP => [
772                self::ACTION_GOTO => self::EXPRESSION,
773            ],
774            self::TYPE_DOT => [
775                self::ACTION_GOTO => self::EXPRESSION_DOT,
776            ],
777            self::TYPE_HOOK => [
778                self::ACTION_PUSH => self::EXPRESSION,
779                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
780            ],
781            self::TYPE_COLON => [
782                self::ACTION_GOTO => self::STATEMENT,
783            ],
784            self::TYPE_COMMA => [
785                self::ACTION_GOTO => self::EXPRESSION,
786            ],
787            self::TYPE_SEMICOLON => [
788                self::ACTION_GOTO => self::STATEMENT,
789            ],
790            self::TYPE_ARROW => [
791                self::ACTION_GOTO => self::EXPRESSION_ARROWFUNC,
792            ],
793            self::TYPE_PAREN_OPEN => [
794                self::ACTION_PUSH => self::EXPRESSION_OP,
795                self::ACTION_GOTO => self::PAREN_EXPRESSION,
796            ],
797            self::TYPE_BRACE_CLOSE => [
798                self::ACTION_POP => true,
799            ],
800            self::TYPE_FUNC => [
801                self::ACTION_PUSH => self::EXPRESSION_OP,
802                self::ACTION_GOTO => self::FUNC,
803            ],
804        ],
805        // State after a dot (.). Like EXPRESSION, except that many keywords behave like literals
806        // (e.g. class, if, else, var, function) because they're not valid as identifiers but are
807        // valid as property names.
808        self::EXPRESSION_DOT => [
809            self::TYPE_LITERAL => [
810                self::ACTION_GOTO => self::EXPRESSION_OP,
811            ],
812            // The following are keywords behaving as literals
813            self::TYPE_RETURN => [
814                self::ACTION_GOTO => self::EXPRESSION_OP,
815            ],
816            self::TYPE_IF => [
817                self::ACTION_GOTO => self::EXPRESSION_OP,
818            ],
819            self::TYPE_DO => [
820                self::ACTION_GOTO => self::EXPRESSION_OP,
821            ],
822            self::TYPE_VAR => [
823                self::ACTION_GOTO => self::EXPRESSION_OP,
824            ],
825            self::TYPE_FUNC => [
826                self::ACTION_GOTO => self::EXPRESSION_OP,
827            ],
828            self::TYPE_CLASS => [
829                self::ACTION_GOTO => self::EXPRESSION_OP,
830            ],
831            // We don't expect real unary/binary operators here, but some keywords
832            // (new, delete, void, typeof, instanceof, in) are classified as such, and they can be
833            // used as property names
834            self::TYPE_UN_OP => [
835                self::ACTION_GOTO => self::EXPRESSION_OP,
836            ],
837            self::TYPE_BIN_OP => [
838                self::ACTION_GOTO => self::EXPRESSION_OP,
839            ],
840        ],
841        // State after the } closing an arrow function body: like STATEMENT except
842        // that it has semicolon insertion, COMMA can continue the expression, and after
843        // a function we go to STATEMENT instead of EXPRESSION_OP
844        self::EXPRESSION_END => [
845            self::TYPE_UN_OP => [
846                self::ACTION_GOTO => self::EXPRESSION,
847            ],
848            self::TYPE_INCR_OP => [
849                self::ACTION_GOTO => self::EXPRESSION,
850            ],
851            self::TYPE_ADD_OP => [
852                self::ACTION_GOTO => self::EXPRESSION,
853            ],
854            self::TYPE_COMMA => [
855                self::ACTION_GOTO => self::EXPRESSION,
856            ],
857            self::TYPE_SEMICOLON => [
858                self::ACTION_GOTO => self::STATEMENT,
859            ],
860            self::TYPE_BRACE_OPEN => [
861                self::ACTION_PUSH => self::STATEMENT,
862                self::ACTION_GOTO => self::STATEMENT,
863            ],
864            self::TYPE_BRACE_CLOSE => [
865                self::ACTION_POP => true,
866            ],
867            self::TYPE_PAREN_OPEN => [
868                self::ACTION_PUSH => self::EXPRESSION_OP,
869                self::ACTION_GOTO => self::PAREN_EXPRESSION,
870            ],
871            self::TYPE_RETURN => [
872                self::ACTION_GOTO => self::EXPRESSION_NO_NL,
873            ],
874            self::TYPE_IF => [
875                self::ACTION_GOTO => self::CONDITION,
876            ],
877            self::TYPE_VAR => [
878                self::ACTION_GOTO => self::EXPRESSION,
879            ],
880            self::TYPE_FUNC => [
881                self::ACTION_PUSH => self::STATEMENT,
882                self::ACTION_GOTO => self::FUNC,
883            ],
884            self::TYPE_CLASS => [
885                self::ACTION_PUSH => self::STATEMENT,
886                self::ACTION_GOTO => self::CLASS_DEF,
887            ],
888            self::TYPE_LITERAL => [
889                self::ACTION_GOTO => self::EXPRESSION_OP,
890            ],
891            self::TYPE_ASYNC => [
892                self::ACTION_GOTO => self::EXPRESSION_OP,
893            ],
894        ],
895        // State after =>. Like EXPRESSION, except that { begins an arrow function body
896        // rather than an object literal.
897        self::EXPRESSION_ARROWFUNC => [
898            self::TYPE_UN_OP => [
899                self::ACTION_GOTO => self::EXPRESSION,
900            ],
901            self::TYPE_INCR_OP => [
902                self::ACTION_GOTO => self::EXPRESSION,
903            ],
904            self::TYPE_ADD_OP => [
905                self::ACTION_GOTO => self::EXPRESSION,
906            ],
907            self::TYPE_BRACE_OPEN => [
908                self::ACTION_PUSH => self::EXPRESSION_END,
909                self::ACTION_GOTO => self::STATEMENT,
910            ],
911            self::TYPE_PAREN_OPEN => [
912                self::ACTION_PUSH => self::EXPRESSION_OP,
913                self::ACTION_GOTO => self::PAREN_EXPRESSION,
914            ],
915            self::TYPE_FUNC => [
916                self::ACTION_PUSH => self::EXPRESSION_OP,
917                self::ACTION_GOTO => self::FUNC,
918            ],
919            self::TYPE_CLASS => [
920                self::ACTION_PUSH => self::EXPRESSION_OP,
921                self::ACTION_GOTO => self::CLASS_DEF,
922            ],
923            self::TYPE_LITERAL => [
924                self::ACTION_GOTO => self::EXPRESSION_OP,
925            ],
926        ],
927        // Expression after a ? . This differs from EXPRESSION because a : ends the ternary
928        // rather than starting STATEMENT (outside a ternary, : comes after a goto label)
929        // The actual rule for : ending the ternary is in EXPRESSION_TERNARY_OP.
930        self::EXPRESSION_TERNARY => [
931            self::TYPE_BRACE_OPEN => [
932                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
933                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
934            ],
935            self::TYPE_PAREN_OPEN => [
936                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
937                self::ACTION_GOTO => self::PAREN_EXPRESSION,
938            ],
939            self::TYPE_FUNC => [
940                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
941                self::ACTION_GOTO => self::FUNC,
942            ],
943            self::TYPE_CLASS => [
944                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
945                self::ACTION_GOTO => self::CLASS_DEF,
946            ],
947            self::TYPE_LITERAL => [
948                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
949            ],
950            // 'return' can't appear here, but 'yield' can
951            self::TYPE_RETURN => [
952                self::ACTION_GOTO => self::EXPRESSION_TERNARY_NO_NL,
953            ],
954        ],
955        // Like EXPRESSION_TERNARY, except that semicolon insertion can happen
956        // See also EXPRESSION_NO_NL
957        self::EXPRESSION_TERNARY_NO_NL => [
958            self::TYPE_BRACE_OPEN => [
959                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
960                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
961            ],
962            self::TYPE_PAREN_OPEN => [
963                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
964                self::ACTION_GOTO => self::PAREN_EXPRESSION,
965            ],
966            self::TYPE_FUNC => [
967                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
968                self::ACTION_GOTO => self::FUNC,
969            ],
970            self::TYPE_CLASS => [
971                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
972                self::ACTION_GOTO => self::CLASS_DEF,
973            ],
974            self::TYPE_LITERAL => [
975                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
976            ],
977            // 'yield' can appear here, because 'yield yield' is allowed
978            self::TYPE_RETURN => [
979                self::ACTION_GOTO => self::EXPRESSION_TERNARY_NO_NL,
980            ],
981            self::TYPE_UN_OP => [
982                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
983            ],
984            self::TYPE_INCR_OP => [
985                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
986            ],
987            // BIN_OP seems impossible at the start of an expression, but it can happen in
988            // yield *foo
989            self::TYPE_BIN_OP => [
990                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
991            ],
992            self::TYPE_ADD_OP => [
993                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
994            ],
995        ],
996        // Like EXPRESSION_OP, but for ternaries, see EXPRESSION_TERNARY
997        self::EXPRESSION_TERNARY_OP => [
998            self::TYPE_BIN_OP => [
999                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1000            ],
1001            self::TYPE_ADD_OP => [
1002                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1003            ],
1004            self::TYPE_DOT => [
1005                self::ACTION_GOTO => self::EXPRESSION_TERNARY_DOT,
1006            ],
1007            self::TYPE_HOOK => [
1008                self::ACTION_PUSH => self::EXPRESSION_TERNARY,
1009                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1010            ],
1011            self::TYPE_COMMA => [
1012                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1013            ],
1014            self::TYPE_ARROW => [
1015                self::ACTION_GOTO => self::EXPRESSION_TERNARY_ARROWFUNC,
1016            ],
1017            self::TYPE_PAREN_OPEN => [
1018                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
1019                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1020            ],
1021            self::TYPE_COLON => [
1022                self::ACTION_POP => true,
1023            ],
1024        ],
1025        // Like EXPRESSION_DOT, but for ternaries, see EXPRESSION_TERNARY
1026        self::EXPRESSION_TERNARY_DOT => [
1027            self::TYPE_LITERAL => [
1028                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1029            ],
1030            // The following are keywords behaving as literals
1031            self::TYPE_RETURN => [
1032                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1033            ],
1034            self::TYPE_IF => [
1035                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1036            ],
1037            self::TYPE_DO => [
1038                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1039            ],
1040            self::TYPE_VAR => [
1041                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1042            ],
1043            self::TYPE_FUNC => [
1044                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1045            ],
1046            self::TYPE_CLASS => [
1047                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1048            ],
1049            // We don't expect real unary/binary operators here, but some keywords
1050            // (new, delete, void, typeof, instanceof, in) are classified as such, and they can be
1051            // used as property names
1052            self::TYPE_UN_OP => [
1053                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1054            ],
1055            self::TYPE_BIN_OP => [
1056                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1057            ],
1058        ],
1059        // Like EXPRESSION_ARROWFUNC, but for ternaries, see EXPRESSION_TERNARY
1060        self::EXPRESSION_TERNARY_ARROWFUNC => [
1061            self::TYPE_UN_OP => [
1062                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1063            ],
1064            self::TYPE_INCR_OP => [
1065                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1066            ],
1067            self::TYPE_ADD_OP => [
1068                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1069            ],
1070            self::TYPE_BRACE_OPEN => [
1071                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
1072                self::ACTION_GOTO => self::STATEMENT,
1073            ],
1074            self::TYPE_PAREN_OPEN => [
1075                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
1076                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1077            ],
1078            self::TYPE_FUNC => [
1079                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
1080                self::ACTION_GOTO => self::FUNC,
1081            ],
1082            self::TYPE_CLASS => [
1083                self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
1084                self::ACTION_GOTO => self::CLASS_DEF,
1085            ],
1086            self::TYPE_LITERAL => [
1087                self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
1088            ],
1089        ],
1090        // Expression inside parentheses. Like EXPRESSION, except that ) ends this state
1091        // This differs from EXPRESSION because semicolon insertion can't happen here
1092        self::PAREN_EXPRESSION => [
1093            self::TYPE_BRACE_OPEN => [
1094                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1095                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
1096            ],
1097            self::TYPE_PAREN_OPEN => [
1098                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1099                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1100            ],
1101            self::TYPE_PAREN_CLOSE => [
1102                self::ACTION_POP => true,
1103            ],
1104            self::TYPE_FUNC => [
1105                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1106                self::ACTION_GOTO => self::FUNC,
1107            ],
1108            self::TYPE_CLASS => [
1109                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1110                self::ACTION_GOTO => self::CLASS_DEF,
1111            ],
1112            self::TYPE_LITERAL => [
1113                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1114            ],
1115            self::TYPE_ASYNC => [
1116                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP_NO_NL,
1117            ],
1118            // 'return' can't appear here, but 'yield' can
1119            self::TYPE_RETURN => [
1120                self::ACTION_GOTO => self::PAREN_EXPRESSION_NO_NL,
1121            ],
1122        ],
1123        // Like PAREN_EXPRESSION, except that semicolon insertion can happen
1124        // See also EXPRESSION_NO_NL
1125        self::PAREN_EXPRESSION_NO_NL => [
1126            self::TYPE_BRACE_OPEN => [
1127                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1128                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
1129            ],
1130            self::TYPE_PAREN_OPEN => [
1131                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1132                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1133            ],
1134            self::TYPE_PAREN_CLOSE => [
1135                self::ACTION_POP => true,
1136            ],
1137            self::TYPE_FUNC => [
1138                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1139                self::ACTION_GOTO => self::FUNC,
1140            ],
1141            self::TYPE_CLASS => [
1142                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1143                self::ACTION_GOTO => self::CLASS_DEF,
1144            ],
1145            self::TYPE_LITERAL => [
1146                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1147            ],
1148            self::TYPE_ASYNC => [
1149                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP_NO_NL,
1150            ],
1151            // 'yield' can appear here, because 'yield yield' is allowed
1152            self::TYPE_RETURN => [
1153                self::ACTION_GOTO => self::PAREN_EXPRESSION_NO_NL,
1154            ],
1155            self::TYPE_UN_OP => [
1156                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1157            ],
1158            self::TYPE_INCR_OP => [
1159                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1160            ],
1161            // BIN_OP seems impossible at the start of an expression, but it can happen in
1162            // yield *foo
1163            self::TYPE_BIN_OP => [
1164                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1165            ],
1166            self::TYPE_ADD_OP => [
1167                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1168            ],
1169            self::TYPE_AWAIT => [
1170                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1171            ],
1172        ],
1173        // Like EXPRESSION_OP, but in parentheses, see PAREN_EXPRESSION
1174        self::PAREN_EXPRESSION_OP => [
1175            self::TYPE_BIN_OP => [
1176                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1177            ],
1178            self::TYPE_ADD_OP => [
1179                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1180            ],
1181            self::TYPE_DOT => [
1182                self::ACTION_GOTO => self::PAREN_EXPRESSION_DOT,
1183            ],
1184            self::TYPE_HOOK => [
1185                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1186            ],
1187            self::TYPE_COLON => [
1188                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1189            ],
1190            self::TYPE_COMMA => [
1191                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1192            ],
1193            self::TYPE_SEMICOLON => [
1194                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1195            ],
1196            self::TYPE_ARROW => [
1197                self::ACTION_GOTO => self::PAREN_EXPRESSION_ARROWFUNC,
1198            ],
1199            self::TYPE_PAREN_OPEN => [
1200                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1201                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1202            ],
1203            self::TYPE_PAREN_CLOSE => [
1204                self::ACTION_POP => true,
1205            ],
1206        ],
1207        // Like EXPRESSION_DOT, but in parentheses, see PAREN_EXPRESSION
1208        self::PAREN_EXPRESSION_DOT => [
1209            self::TYPE_LITERAL => [
1210                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1211            ],
1212            // The following are keywords behaving as literals
1213            self::TYPE_RETURN => [
1214                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1215            ],
1216            self::TYPE_IF => [
1217                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1218            ],
1219            self::TYPE_DO => [
1220                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1221            ],
1222            self::TYPE_VAR => [
1223                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1224            ],
1225            self::TYPE_FUNC => [
1226                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1227            ],
1228            self::TYPE_CLASS => [
1229                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1230            ],
1231            // We don't expect real unary/binary operators here, but some keywords
1232            // (new, delete, void, typeof, instanceof, in) are classified as such, and they can be
1233            // used as property names
1234            self::TYPE_UN_OP => [
1235                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1236            ],
1237            self::TYPE_BIN_OP => [
1238                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1239            ],
1240        ],
1241        // Like EXPRESSION_ARROWFUNC, but in parentheses, see PAREN_EXPRESSION
1242        self::PAREN_EXPRESSION_ARROWFUNC => [
1243            self::TYPE_UN_OP => [
1244                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1245            ],
1246            self::TYPE_INCR_OP => [
1247                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1248            ],
1249            self::TYPE_ADD_OP => [
1250                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1251            ],
1252            self::TYPE_BRACE_OPEN => [
1253                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1254                self::ACTION_GOTO => self::STATEMENT,
1255            ],
1256            self::TYPE_PAREN_OPEN => [
1257                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1258                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1259            ],
1260            self::TYPE_FUNC => [
1261                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1262                self::ACTION_GOTO => self::FUNC,
1263            ],
1264            self::TYPE_CLASS => [
1265                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1266                self::ACTION_GOTO => self::CLASS_DEF,
1267            ],
1268            self::TYPE_LITERAL => [
1269                self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
1270            ],
1271        ],
1272
1273        // Like PAREN_EXPRESSION_OP, for the state after "async" in a PAREN_EXPRESSION,
1274        // for use by the $semicolon model.
1275        self::PAREN_EXPRESSION_OP_NO_NL => [
1276            self::TYPE_BIN_OP => [
1277                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1278            ],
1279            self::TYPE_ADD_OP => [
1280                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1281            ],
1282            self::TYPE_DOT => [
1283                self::ACTION_GOTO => self::PAREN_EXPRESSION_DOT,
1284            ],
1285            self::TYPE_HOOK => [
1286                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1287            ],
1288            self::TYPE_COLON => [
1289                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1290            ],
1291            self::TYPE_COMMA => [
1292                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1293            ],
1294            self::TYPE_SEMICOLON => [
1295                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1296            ],
1297            self::TYPE_ARROW => [
1298                self::ACTION_GOTO => self::PAREN_EXPRESSION_ARROWFUNC,
1299            ],
1300            self::TYPE_PAREN_OPEN => [
1301                self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
1302                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1303            ],
1304            self::TYPE_PAREN_CLOSE => [
1305                self::ACTION_POP => true,
1306            ],
1307        ],
1308        // Expression as the value of a key in an object literal. Like EXPRESSION, except that
1309        // a comma (in PROPERTY_EXPRESSION_OP) goes to PROPERTY_ASSIGNMENT instead
1310        self::PROPERTY_EXPRESSION => [
1311            self::TYPE_BRACE_OPEN => [
1312                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1313                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
1314            ],
1315            self::TYPE_BRACE_CLOSE => [
1316                self::ACTION_POP => true,
1317            ],
1318            self::TYPE_PAREN_OPEN => [
1319                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1320                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1321            ],
1322            self::TYPE_FUNC => [
1323                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1324                self::ACTION_GOTO => self::FUNC,
1325            ],
1326            self::TYPE_CLASS => [
1327                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1328                self::ACTION_GOTO => self::CLASS_DEF,
1329            ],
1330            self::TYPE_LITERAL => [
1331                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1332            ],
1333            // 'return' can't appear here, but 'yield' can
1334            self::TYPE_RETURN => [
1335                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_NO_NL,
1336            ],
1337        ],
1338        // Like PROPERTY_EXPRESSION, except that semicolon insertion can happen
1339        // See also EXPRESSION_NO_NL
1340        self::PROPERTY_EXPRESSION_NO_NL => [
1341            self::TYPE_BRACE_OPEN => [
1342                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1343                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
1344            ],
1345            self::TYPE_BRACE_CLOSE => [
1346                self::ACTION_POP => true,
1347            ],
1348            self::TYPE_PAREN_OPEN => [
1349                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1350                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1351            ],
1352            self::TYPE_FUNC => [
1353                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1354                self::ACTION_GOTO => self::FUNC,
1355            ],
1356            self::TYPE_CLASS => [
1357                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1358                self::ACTION_GOTO => self::CLASS_DEF,
1359            ],
1360            self::TYPE_LITERAL => [
1361                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1362            ],
1363            // 'yield' can appear here, because 'yield yield' is allowed
1364            self::TYPE_RETURN => [
1365                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_NO_NL,
1366            ],
1367            self::TYPE_UN_OP => [
1368                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1369            ],
1370            self::TYPE_INCR_OP => [
1371                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1372            ],
1373            // BIN_OP seems impossible at the start of an expression, but it can happen in
1374            // yield *foo
1375            self::TYPE_BIN_OP => [
1376                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1377            ],
1378            self::TYPE_ADD_OP => [
1379                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1380            ],
1381        ],
1382        // Like EXPRESSION_OP, but in a property expression, see PROPERTY_EXPRESSION
1383        self::PROPERTY_EXPRESSION_OP => [
1384            self::TYPE_BIN_OP => [
1385                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1386            ],
1387            self::TYPE_ADD_OP => [
1388                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1389            ],
1390            self::TYPE_DOT => [
1391                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_DOT,
1392            ],
1393            self::TYPE_HOOK => [
1394                self::ACTION_PUSH => self::PROPERTY_EXPRESSION,
1395                self::ACTION_GOTO => self::EXPRESSION_TERNARY,
1396            ],
1397            self::TYPE_COMMA => [
1398                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
1399            ],
1400            self::TYPE_ARROW => [
1401                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_ARROWFUNC,
1402            ],
1403            self::TYPE_BRACE_OPEN => [
1404                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1405            ],
1406            self::TYPE_BRACE_CLOSE => [
1407                self::ACTION_POP => true,
1408            ],
1409            self::TYPE_PAREN_OPEN => [
1410                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1411                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1412            ],
1413        ],
1414        // Like EXPRESSION_DOT, but in a property expression, see PROPERTY_EXPRESSION
1415        self::PROPERTY_EXPRESSION_DOT => [
1416            self::TYPE_LITERAL => [
1417                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1418            ],
1419            // The following are keywords behaving as literals
1420            self::TYPE_RETURN => [
1421                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1422            ],
1423            self::TYPE_IF => [
1424                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1425            ],
1426            self::TYPE_DO => [
1427                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1428            ],
1429            self::TYPE_VAR => [
1430                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1431            ],
1432            self::TYPE_FUNC => [
1433                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1434            ],
1435            self::TYPE_CLASS => [
1436                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1437            ],
1438            // We don't expect real unary/binary operators here, but some keywords
1439            // (new, delete, void, typeof, instanceof, in) are classified as such, and they can be
1440            // used as property names
1441            self::TYPE_UN_OP => [
1442                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1443            ],
1444            self::TYPE_BIN_OP => [
1445                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1446            ],
1447        ],
1448        // Like EXPRESSION_ARROWFUNC, but in a property expression, see PROPERTY_EXPRESSION
1449        self::PROPERTY_EXPRESSION_ARROWFUNC => [
1450            self::TYPE_UN_OP => [
1451                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1452            ],
1453            self::TYPE_INCR_OP => [
1454                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1455            ],
1456            self::TYPE_ADD_OP => [
1457                self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
1458            ],
1459            self::TYPE_BRACE_OPEN => [
1460                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1461                self::ACTION_GOTO => self::STATEMENT,
1462            ],
1463            self::TYPE_PAREN_OPEN => [
1464                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1465                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1466            ],
1467            self::TYPE_FUNC => [
1468                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1469                self::ACTION_GOTO => self::FUNC,
1470            ],
1471            self::TYPE_CLASS => [
1472                self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
1473                self::ACTION_GOTO => self::CLASS_DEF,
1474            ],
1475            self::TYPE_LITERAL => [
1476                self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
1477            ],
1478        ],
1479        // Class definition (after the class keyword). Expects an identifier, or the extends
1480        // keyword followed by an expression (or both), followed by {, which starts an object
1481        // literal. The object literal's closing } will pop the stack, so the state to return
1482        // to after the class definition should be pushed to the stack first.
1483        self::CLASS_DEF => [
1484            self::TYPE_BRACE_OPEN => [
1485                self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
1486            ],
1487            self::TYPE_PAREN_OPEN => [
1488                self::ACTION_PUSH => self::CLASS_DEF,
1489                self::ACTION_GOTO => self::PAREN_EXPRESSION,
1490            ],
1491            self::TYPE_FUNC => [
1492                self::ACTION_PUSH => self::CLASS_DEF,
1493                self::ACTION_GOTO => self::FUNC,
1494            ],
1495        ],
1496        // Import or export declaration
1497        self::IMPORT_EXPORT => [
1498            self::TYPE_SEMICOLON => [
1499                self::ACTION_GOTO => self::STATEMENT,
1500            ],
1501            self::TYPE_VAR => [
1502                self::ACTION_GOTO => self::EXPRESSION,
1503            ],
1504            self::TYPE_FUNC => [
1505                self::ACTION_PUSH => self::EXPRESSION_OP,
1506                self::ACTION_GOTO => self::FUNC,
1507            ],
1508            self::TYPE_CLASS => [
1509                self::ACTION_PUSH => self::EXPRESSION_OP,
1510                self::ACTION_GOTO => self::CLASS_DEF,
1511            ],
1512            self::TYPE_SPECIAL => [
1513                'default' => [
1514                    self::ACTION_GOTO => self::EXPRESSION,
1515                ],
1516                // Stay in this state for *, as, from
1517                '*' => [],
1518                'as' => [],
1519                'from' => [],
1520            ],
1521        ],
1522        // Used in template string-specific code below
1523        self::TEMPLATE_STRING_HEAD => [
1524            self::TYPE_LITERAL => [
1525                self::ACTION_PUSH => self::TEMPLATE_STRING_TAIL,
1526                self::ACTION_GOTO => self::EXPRESSION,
1527            ],
1528        ],
1529    ];
1530
1531    /**
1532     * @var array $semicolon
1533     *
1534     * Rules for when semicolon insertion is appropriate. Semicolon insertion happens if we are
1535     * in one of these states, and encounter one of these tokens preceded by a newline.
1536     *
1537     * This array is augmented by ensureExpandedStates().
1538     */
1539    private static $semicolon = [
1540        self::EXPRESSION_NO_NL => [
1541            self::TYPE_UN_OP => true,
1542            // BIN_OP seems impossible at the start of an expression, but it can happen in
1543            // yield *foo
1544            self::TYPE_BIN_OP => true,
1545            self::TYPE_INCR_OP => true,
1546            self::TYPE_ADD_OP => true,
1547            self::TYPE_BRACE_OPEN => true,
1548            self::TYPE_PAREN_OPEN => true,
1549            self::TYPE_RETURN => true,
1550            self::TYPE_IF => true,
1551            self::TYPE_DO => true,
1552            self::TYPE_VAR => true,
1553            self::TYPE_FUNC => true,
1554            self::TYPE_CLASS => true,
1555            self::TYPE_LITERAL => true,
1556            self::TYPE_ASYNC => true,
1557        ],
1558        self::EXPRESSION_TERNARY_NO_NL => [
1559            self::TYPE_UN_OP => true,
1560            // BIN_OP seems impossible at the start of an expression, but it can happen in
1561            // yield *foo
1562            self::TYPE_BIN_OP => true,
1563            self::TYPE_INCR_OP => true,
1564            self::TYPE_ADD_OP => true,
1565            self::TYPE_BRACE_OPEN => true,
1566            self::TYPE_PAREN_OPEN => true,
1567            self::TYPE_RETURN => true,
1568            self::TYPE_IF => true,
1569            self::TYPE_DO => true,
1570            self::TYPE_VAR => true,
1571            self::TYPE_FUNC => true,
1572            self::TYPE_CLASS => true,
1573            self::TYPE_LITERAL => true,
1574            self::TYPE_ASYNC => true,
1575        ],
1576        self::PAREN_EXPRESSION_NO_NL => [
1577            self::TYPE_UN_OP => true,
1578            // BIN_OP seems impossible at the start of an expression, but it can happen in
1579            // yield *foo
1580            self::TYPE_BIN_OP => true,
1581            self::TYPE_INCR_OP => true,
1582            self::TYPE_ADD_OP => true,
1583            self::TYPE_BRACE_OPEN => true,
1584            self::TYPE_PAREN_OPEN => true,
1585            self::TYPE_RETURN => true,
1586            self::TYPE_IF => true,
1587            self::TYPE_DO => true,
1588            self::TYPE_VAR => true,
1589            self::TYPE_FUNC => true,
1590            self::TYPE_CLASS => true,
1591            self::TYPE_LITERAL => true,
1592            self::TYPE_ASYNC => true,
1593        ],
1594        self::PROPERTY_EXPRESSION_NO_NL => [
1595            self::TYPE_UN_OP => true,
1596            // BIN_OP seems impossible at the start of an expression, but it can happen in
1597            // yield *foo
1598            self::TYPE_BIN_OP => true,
1599            self::TYPE_INCR_OP => true,
1600            self::TYPE_ADD_OP => true,
1601            self::TYPE_BRACE_OPEN => true,
1602            self::TYPE_PAREN_OPEN => true,
1603            self::TYPE_RETURN => true,
1604            self::TYPE_IF => true,
1605            self::TYPE_DO => true,
1606            self::TYPE_VAR => true,
1607            self::TYPE_FUNC => true,
1608            self::TYPE_CLASS => true,
1609            self::TYPE_LITERAL => true,
1610            self::TYPE_ASYNC => true,
1611        ],
1612        self::EXPRESSION_OP => [
1613            self::TYPE_UN_OP => true,
1614            self::TYPE_INCR_OP => true,
1615            self::TYPE_BRACE_OPEN => true,
1616            self::TYPE_RETURN => true,
1617            self::TYPE_IF => true,
1618            self::TYPE_DO => true,
1619            self::TYPE_VAR => true,
1620            self::TYPE_FUNC => true,
1621            self::TYPE_CLASS => true,
1622            self::TYPE_LITERAL => true,
1623            self::TYPE_ASYNC => true,
1624        ],
1625        self::EXPRESSION_END => [
1626            self::TYPE_UN_OP => true,
1627            self::TYPE_INCR_OP => true,
1628            self::TYPE_ADD_OP => true,
1629            self::TYPE_BRACE_OPEN => true,
1630            self::TYPE_PAREN_OPEN => true,
1631            self::TYPE_RETURN => true,
1632            self::TYPE_IF => true,
1633            self::TYPE_DO => true,
1634            self::TYPE_VAR => true,
1635            self::TYPE_FUNC => true,
1636            self::TYPE_CLASS => true,
1637            self::TYPE_LITERAL => true,
1638            self::TYPE_ASYNC => true,
1639        ],
1640        self::PAREN_EXPRESSION_OP_NO_NL => [
1641            self::TYPE_FUNC => true,
1642        ]
1643    ];
1644
1645    /**
1646     * @var array $divStates
1647     *
1648     * States in which a / is a division operator. In all other states, it's the start of a regex.
1649     *
1650     * This array is augmented by self::ensureExpandedStates().
1651     */
1652    private static $divStates = [
1653        self::EXPRESSION_OP          => true,
1654        self::EXPRESSION_TERNARY_OP  => true,
1655        self::PAREN_EXPRESSION_OP    => true,
1656        self::PROPERTY_EXPRESSION_OP => true
1657    ];
1658
1659    /**
1660     * Add copies of all states but with negative numbers to self::$model (if not already present),
1661     * to represent generator function states.
1662     */
1663    private static function ensureExpandedStates() {
1664        // Already done?
1665        if ( self::$expandedStates ) {
1666            return;
1667        }
1668        self::$expandedStates = true;
1669
1670        // Add copies of all states (except FUNC and GENFUNC) with negative numbers.
1671        // These negative states represent states inside generator functions. When in these states,
1672        // TYPE_YIELD is treated as TYPE_RETURN, otherwise as TYPE_LITERAL
1673        foreach ( self::$model as $state => $transitions ) {
1674            if ( $state === self::FUNC || $state === self::GENFUNC ) {
1675                continue;
1676            }
1677            foreach ( $transitions as $tokenType => $actions ) {
1678                foreach ( $actions as $action => $target ) {
1679                    if ( !is_array( $target ) ) {
1680                        self::$model[-$state][$tokenType][$action] = (
1681                            $target === self::FUNC ||
1682                            $target === true ||
1683                            $target === self::GENFUNC
1684                        ) ? $target : -$target;
1685                        continue;
1686                    }
1687
1688                    foreach ( $target as $subaction => $subtarget ) {
1689                        self::$model[-$state][$tokenType][$action][$subaction] = (
1690                            $subtarget === self::FUNC ||
1691                            $subtarget === true ||
1692                            $subtarget === self::GENFUNC
1693                        ) ? $subtarget : -$subtarget;
1694                    }
1695                }
1696            }
1697        }
1698        // Special cases:
1699        // '{' in a property assignment starts a method, so it shouldn't be flipped
1700        self::$model[-self::PROPERTY_ASSIGNMENT][self::TYPE_BRACE_OPEN][self::ACTION_GOTO] = self::STATEMENT;
1701
1702        // Also add negative versions of states to the other arrays
1703        foreach ( self::$semicolon as $state => $value ) {
1704            self::$semicolon[-$state] = $value;
1705        }
1706        foreach ( self::$divStates as $state => $value ) {
1707            self::$divStates[-$state] = $value;
1708        }
1709    }
1710
1711    /**
1712     * Returns minified JavaScript code.
1713     *
1714     * @see MinifierState::setErrorHandler
1715     * @param string $s JavaScript code to minify
1716     * @param callable|null $onError Called with a ParseError object
1717     * @return string Minified code
1718     */
1719    public static function minify( $s, $onError = null ) {
1720        return self::minifyInternal( $s, null, $onError );
1721    }
1722
1723    /**
1724     * Create a minifier state object without source map capabilities
1725     *
1726     * Example:
1727     *
1728     *   JavaScriptMinifier::createMinifier()
1729     *     ->addSourceFile( 'file.js', $source )
1730     *     ->getMinifiedOutput();
1731     *
1732     * @return JavaScriptMinifierState
1733     */
1734    public static function createMinifier() {
1735        return new JavaScriptMinifierState;
1736    }
1737
1738    /**
1739     * Create a minifier state object with source map capabilities
1740     *
1741     * Example:
1742     *
1743     *   $mapper = JavaScriptMinifier::createSourceMapState()
1744     *     ->addSourceFile( 'file1.js', $source1 )
1745     *     ->addOutput( "\n\n" )
1746     *     ->addSourceFile( 'file2.js', $source2 );
1747     *   $out = $mapper->getMinifiedOutput();
1748     *   $map = $mapper->getSourceMap()
1749     *
1750     * @return JavaScriptMapperState
1751     */
1752    public static function createSourceMapState() {
1753        return new JavaScriptMapperState;
1754    }
1755
1756    /**
1757     * Create a MinifierState that doesn't actually minify
1758     *
1759     * @return IdentityMinifierState
1760     */
1761    public static function createIdentityMinifier() {
1762        return new IdentityMinifierState;
1763    }
1764
1765    /**
1766     * Minify with optional source map.
1767     *
1768     * @internal
1769     *
1770     * @param string $s
1771     * @param MappingsGenerator|null $mapGenerator
1772     * @param callable|null $onError
1773     * @return string
1774     */
1775    public static function minifyInternal( $s, $mapGenerator = null, $onError = null ) {
1776        self::ensureExpandedStates();
1777
1778        // Here's where the minifying takes place: Loop through the input, looking for tokens
1779        // and output them to $out, taking actions to the above defined rules when appropriate.
1780        $error = null;
1781        $out = '';
1782        $pos = 0;
1783        $length = strlen( $s );
1784        $lineLength = 0;
1785        $dotlessNum = false;
1786        $lastDotlessNum = false;
1787        $newlineFound = true;
1788        $state = self::STATEMENT;
1789        $stack = [];
1790        // Optimization: calling end( $stack ) repeatedly is expensive
1791        $topOfStack = null;
1792        // Pretend that we have seen a semicolon yet
1793        $last = ';';
1794        while ( $pos < $length ) {
1795            // First, skip over any whitespace and multiline comments, recording whether we
1796            // found any newline character
1797            $skip = strspn( $s, " \t\n\r\xb\xc", $pos );
1798            if ( !$skip ) {
1799                $ch = $s[$pos];
1800                if ( $ch === '/' && substr( $s, $pos, 2 ) === '/*' ) {
1801                    // Multiline comment. Search for the end token or EOT.
1802                    $end = strpos( $s, '*/', $pos + 2 );
1803                    $skip = $end === false ? $length - $pos : $end - $pos + 2;
1804                }
1805            }
1806            if ( $skip ) {
1807                // The semicolon insertion mechanism needs to know whether there was a newline
1808                // between two tokens, so record it now.
1809                if ( !$newlineFound && strcspn( $s, "\r\n", $pos, $skip ) !== $skip ) {
1810                    $newlineFound = true;
1811                }
1812                if ( $mapGenerator ) {
1813                    $mapGenerator->consumeSource( $skip );
1814                }
1815                $pos += $skip;
1816                continue;
1817            }
1818            // Handle C++-style comments and html comments, which are treated as single line
1819            // comments by the browser, regardless of whether the end tag is on the same line.
1820            // Handle --> the same way, but only if it's at the beginning of the line
1821            // @phan-suppress-next-line PhanPossiblyUndeclaredVariable
1822            if ( ( $ch === '/' && substr( $s, $pos, 2 ) === '//' )
1823                || ( $ch === '<' && substr( $s, $pos, 4 ) === '<!--' )
1824                || ( $ch === '-' && $newlineFound && substr( $s, $pos, 3 ) === '-->' )
1825            ) {
1826                $skip = strcspn( $s, "\r\n", $pos );
1827                if ( $mapGenerator ) {
1828                    $mapGenerator->consumeSource( $skip );
1829                }
1830                $pos += $skip;
1831                continue;
1832            }
1833
1834            // Find out which kind of token we're handling.
1835            // Note: $end must point past the end of the current token
1836            // so that `substr($s, $pos, $end - $pos)` would be the entire token.
1837            // In order words, $end will be the offset of the last relevant character
1838            // in the stream + 1, or simply put: The offset of the first character
1839            // of any next token in the stream.
1840            $end = $pos + 1;
1841            // Handle string literals
1842            if ( $ch === "'" || $ch === '"' ) {
1843                // Search to the end of the string literal, skipping over backslash escapes
1844                $search = $ch . '\\';
1845                do {
1846                    // Speculatively add 2 to the end so that if we see a backslash,
1847                    // the next iteration will start 2 characters further (one for the
1848                    // backslash, one for the escaped character).
1849                    // We'll correct this outside the loop.
1850                    $end += strcspn( $s, $search, $end ) + 2;
1851                    // If the last character in our search for a quote or a backlash
1852                    // matched a backslash and we haven't reached the end, keep searching..
1853                } while ( $end - 2 < $length && $s[$end - 2] === '\\' );
1854                // Correction (1): Undo speculative add, keep only one (end of string literal)
1855                $end--;
1856                if ( $end > $length ) {
1857                    // Correction (2): Loop wrongly assumed an end quote ended the search,
1858                    // but search ended because we've reached the end. Correct $end.
1859                    // TODO: This is invalid and should throw.
1860                    $end--;
1861                }
1862
1863            // Handle template strings, either from "`" to begin a new string,
1864            // or continuation after the "}" that ends a "${"-expression.
1865            } elseif ( $ch === '`' || ( $ch === '}' && $topOfStack === self::TEMPLATE_STRING_TAIL ) ) {
1866                if ( $ch === '}' ) {
1867                    // Pop the TEMPLATE_STRING_TAIL state off the stack
1868                    // We don't let it get popped off the stack the normal way, to avoid the newline
1869                    // and comment stripping code above running on the continuation of the literal
1870                    array_pop( $stack );
1871                    // Also pop the previous state off the stack
1872                    $state = array_pop( $stack );
1873                    $topOfStack = end( $stack );
1874                }
1875                // Search until we reach either a closing ` or a ${, skipping over backslash escapes
1876                // and $ characters followed by something other than { or `
1877                do {
1878                    $end += strcspn( $s, '`$\\', $end ) + 1;
1879                    if ( $end - 1 < $length && $s[$end - 1] === '`' ) {
1880                        // End of the string, stop
1881                        // We don't do this in the while() condition because the $end++ in the
1882                        // backslash escape branch makes it difficult to do so without incorrectly
1883                        // considering an escaped backtick (\`) the end of the string
1884                        break;
1885                    }
1886                    if ( $end - 1 < $length && $s[$end - 1] === '\\' ) {
1887                        // Backslash escape. Skip the next character, and keep going
1888                        $end++;
1889                        continue;
1890                    }
1891                    if ( $end < $length && $s[$end - 1] === '$' && $s[$end] === '{' ) {
1892                        // Beginning of an expression in ${ ... }. Skip the {, and stop
1893                        $end++;
1894                        // Push the current state to the stack. We'll pop this off later when hitting
1895                        // the end of this template string
1896                        $stack[] = $state;
1897                        $topOfStack = $state;
1898                        // Change the state to TEMPLATE_STRING_HEAD. The token type will be detected
1899                        // as TYPE_LITERAL, and this will cause the state machine to expect an
1900                        // expression, then go to the TEMPLATE_STRING_TAIL state when it hits the }
1901                        $state = self::TEMPLATE_STRING_HEAD;
1902                        break;
1903                    }
1904                } while ( $end - 1 < $length );
1905                if ( $end > $length ) {
1906                    // Loop wrongly assumed an end quote or ${ ended the search,
1907                    // but search ended because we've reached the end. Correct $end.
1908                    // TODO: This is invalid and should throw.
1909                    $end--;
1910                }
1911
1912            // We have to distinguish between regexp literals and division operators
1913            // A division operator is only possible in certain states
1914            } elseif ( $ch === '/' && !isset( self::$divStates[$state] ) ) {
1915                // Regexp literal
1916                for ( ; ; ) {
1917                    // Search until we find "/" (end of regexp), "\" (backslash escapes),
1918                    // or "[" (start of character classes).
1919                    do {
1920                        // Speculatively add 2 to ensure next iteration skips
1921                        // over backslash and escaped character.
1922                        // We'll correct this outside the loop.
1923                        $end += strcspn( $s, '/[\\', $end ) + 2;
1924                        // If backslash escape, keep searching...
1925                    } while ( $end - 2 < $length && $s[$end - 2] === '\\' );
1926                    // Correction (1): Undo speculative add, keep only one (end of regexp)
1927                    $end--;
1928                    if ( $end > $length ) {
1929                        // Correction (2): Loop wrongly assumed end slash was seen
1930                        // String ended without end of regexp. Correct $end.
1931                        // TODO: This is invalid and should throw.
1932                        $end--;
1933                        break;
1934                    }
1935                    if ( $s[$end - 1] === '/' ) {
1936                        break;
1937                    }
1938                    // (Implicit else), we must've found the start of a char class,
1939                    // skip until we find "]" (end of char class), or "\" (backslash escape)
1940                    do {
1941                        // Speculatively add 2 for backslash escape.
1942                        // We'll substract one outside the loop.
1943                        $end += strcspn( $s, ']\\', $end ) + 2;
1944                        // If backslash escape, keep searching...
1945                    } while ( $end - 2 < $length && $s[$end - 2] === '\\' );
1946                    // Correction (1): Undo speculative add, keep only one (end of regexp)
1947                    $end--;
1948                    if ( $end > $length ) {
1949                        // Correction (2): Loop wrongly assumed "]" was seen
1950                        // String ended without ending char class or regexp. Correct $end.
1951                        // TODO: This is invalid and should throw.
1952                        $end--;
1953                        break;
1954                    }
1955                }
1956                // Search past the regexp modifiers (gi)
1957                while ( $end < $length && ctype_alpha( $s[$end] ) ) {
1958                    $end++;
1959                }
1960            } elseif (
1961                $ch === '0'
1962                && ( $pos + 1 < $length ) && ( $s[$pos + 1] === 'x' || $s[$pos + 1] === 'X' )
1963            ) {
1964                // Hex numeric literal
1965                // x or X
1966                $end++;
1967                $len = strspn( $s, '0123456789ABCDEFabcdef', $end );
1968                if ( !$len && !$error ) {
1969                    $error = new ParseError(
1970                        'Expected a hexadecimal number but found ' . substr( $s, $pos, 5 ),
1971                        $pos,
1972                    );
1973                }
1974                $end += $len;
1975            } elseif (
1976                // Optimisation: This check must accept only ASCII digits 0-9.
1977                // Avoid ctype_digit() because it is slower and also accepts locale-specific digits.
1978                // Using is_numeric() might seem wrong also as it accepts negative numbers, decimal
1979                // numbers, and exponents (e.g. strings like "+012.34e6"). But, it is fine here
1980                // because we know $ch is a single character, and we believe the only single
1981                // characters that is_numeric() accepts are ASCII digits 0-9.
1982                is_numeric( $ch )
1983                || ( $ch === '.' && $pos + 1 < $length && is_numeric( $s[$pos + 1] ) )
1984            ) {
1985                $end += strspn( $s, '0123456789', $end );
1986                $decimal = strspn( $s, '.', $end );
1987                if ( $decimal ) {
1988                    if ( $decimal > 2 && !$error ) {
1989                        $error = new ParseError( 'Too many decimal points', $end );
1990                    }
1991                    $end += strspn( $s, '0123456789', $end + 1 ) + $decimal;
1992                } else {
1993                    $dotlessNum = true;
1994                }
1995                $exponent = strspn( $s, 'eE', $end );
1996                if ( $exponent ) {
1997                    if ( $exponent > 1 && !$error ) {
1998                        $error = new ParseError( 'Number with several E', $end );
1999                    }
2000                    $end += $exponent;
2001
2002                    // + sign is optional; - sign is required.
2003                    $end += strspn( $s, '-+', $end );
2004                    $len = strspn( $s, '0123456789', $end );
2005                    if ( !$len && !$error ) {
2006                        $error = new ParseError(
2007                            'Missing decimal digits after exponent',
2008                            $pos
2009                        );
2010                    }
2011                    $end += $len;
2012                }
2013            } elseif ( isset( self::$opChars[$ch] ) ) {
2014                // Punctuation character. Search for the longest matching operator.
2015                for ( $tokenLength = self::LONGEST_PUNCTUATION_TOKEN; $tokenLength > 1; $tokenLength-- ) {
2016                    if (
2017                        $pos + $tokenLength <= $length &&
2018                        isset( self::$tokenTypes[ substr( $s, $pos, $tokenLength ) ] )
2019                    ) {
2020                        $end = $pos + $tokenLength;
2021                        break;
2022                    }
2023                }
2024            } else {
2025                // Identifier or reserved word. Search for the end by excluding whitespace and
2026                // punctuation.
2027                $end += strcspn( $s, " \t\n.;,=<>+-{}()[]?:*/%'\"`!&|^~\xb\xc\r", $end );
2028            }
2029
2030            // Now get the token type from our type array
2031            // so $end - $pos == strlen( $token )
2032            $token = substr( $s, $pos, $end - $pos );
2033            $type = isset( self::$model[$state][self::TYPE_SPECIAL][$token] )
2034                ? self::TYPE_SPECIAL
2035                : self::$tokenTypes[$token] ?? self::TYPE_LITERAL;
2036            if ( $type === self::TYPE_YIELD ) {
2037                // yield is treated as TYPE_RETURN inside a generator function (negative state)
2038                // but as TYPE_LITERAL when not in a generator function (positive state)
2039                $type = $state < 0 ? self::TYPE_RETURN : self::TYPE_LITERAL;
2040            }
2041
2042            $pad = '';
2043
2044            if ( $newlineFound && isset( self::$semicolon[$state][$type] ) ) {
2045                // This token triggers the semicolon insertion mechanism of javascript. While we
2046                // could add the ; token here ourselves, keeping the newline has a few advantages.
2047                $pad = "\n";
2048                $state = $state < 0 ? -self::STATEMENT : self::STATEMENT;
2049                $lineLength = 0;
2050            // This check adds a new line if we have exceeded the max length and only does this if
2051            // a newline was found in this this position, if it wasn't, it uses the next available
2052            // line break
2053            } elseif ( $newlineFound &&
2054                $lineLength + $end - $pos > self::$maxLineLength &&
2055                !isset( self::$semicolon[$state][$type] ) &&
2056                $type !== self::TYPE_INCR_OP &&
2057                $type !== self::TYPE_ARROW
2058            ) {
2059                $pad = "\n";
2060                $lineLength = 0;
2061            // Check, whether we have to separate the token from the last one with whitespace
2062            } elseif ( !isset( self::$opChars[$last] ) && !isset( self::$opChars[$ch] ) ) {
2063                $pad = ' ';
2064                $lineLength++;
2065            // Don't accidentally create ++, -- or // tokens
2066            } elseif ( $last === $ch && ( $ch === '+' || $ch === '-' || $ch === '/' ) ) {
2067                $pad = ' ';
2068                $lineLength++;
2069            // Don't create invalid dot notation after number literal (T303827).
2070            // Keep whitespace in "42. foo".
2071            // But keep minifying "foo.bar", "42..foo", and "42.0.foo" per $opChars.
2072            } elseif ( $lastDotlessNum && $type === self::TYPE_DOT ) {
2073                $pad = ' ';
2074                $lineLength++;
2075            }
2076
2077            // self::debug( $topOfStack, $last, $state, $ch, $token, $type );
2078
2079            if ( $mapGenerator ) {
2080                $mapGenerator->outputSpace( $pad );
2081                $mapGenerator->outputToken( $token );
2082                $mapGenerator->consumeSource( $end - $pos );
2083            }
2084            $out .= $pad;
2085            $out .= $token;
2086            $lineLength += $end - $pos;
2087            $last = $s[$end - 1];
2088            $pos = $end;
2089            $newlineFound = false;
2090            $lastDotlessNum = $dotlessNum;
2091            $dotlessNum = false;
2092
2093            // Now that we have output our token, transition into the new state.
2094            $actions = $type === self::TYPE_SPECIAL ?
2095                self::$model[$state][$type][$token] :
2096                self::$model[$state][$type] ?? [];
2097            if ( isset( $actions[self::ACTION_PUSH] ) &&
2098                count( $stack ) < self::STACK_LIMIT
2099            ) {
2100                $topOfStack = $actions[self::ACTION_PUSH];
2101                $stack[] = $topOfStack;
2102            }
2103            if ( $stack && isset( $actions[self::ACTION_POP] ) ) {
2104                $state = array_pop( $stack );
2105                $topOfStack = end( $stack );
2106            } elseif ( isset( $actions[self::ACTION_GOTO] ) ) {
2107                $state = $actions[self::ACTION_GOTO];
2108            }
2109        }
2110        if ( $onError && $error ) {
2111            $onError( $error );
2112        }
2113        return $out;
2114    }
2115
2116    /**
2117     * @param null|false|int $top
2118     * @param string $last
2119     * @param int $state
2120     * @param string $ch
2121     * @param string $token
2122     * @param int $type
2123     */
2124    private static function debug(
2125        $top, string $last,
2126        int $state, string $ch, string $token, int $type
2127    ) {
2128        static $first = true;
2129        $self = new ReflectionClass( self::class );
2130
2131        foreach ( $self->getConstants() as $name => $value ) {
2132            if ( $value === $top ) {
2133                $top = $name;
2134            }
2135            if ( $value === $state ) {
2136                $state = $name;
2137            }
2138            if ( $value === $type ) {
2139                $type = $name;
2140            }
2141        }
2142
2143        if ( $first ) {
2144            print sprintf( "| %-29s | %-4s | %-29s | %-2s | %-10s | %-29s\n",
2145                'topOfStack', 'last', 'state', 'ch', 'token', 'type' );
2146            print sprintf( "| %'-29s | %'-4s | %'-29s | %'-2s | %'-10s | %'-29s\n",
2147                '', '', '', '', '', '' );
2148            $first = false;
2149        }
2150        print sprintf( "| %-29s | %-4s | %-29s | %-2s | %-10s | %-29s\n",
2151            (string)$top, $last, $state, $ch, $token, $type );
2152    }
2153}