Passed
Pull Request — development (#3829)
by Spuds
09:03
created

JavaScriptMinifier::augmentDebugContext()   B

Complexity

Conditions 8
Paths 25

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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