* slow, because they construct a complete parse tree before outputting the code minified.
* So this class is meant to allow arbitrary (but syntactically correct) input, while being
* fast enough to be used for on-the-fly minifying.
+ *
+ * This class was written with ECMA-262 Edition 3 in mind ("ECMAScript 3"). Parsing features
+ * new to ECMAScript 5 or later might not be supported. However, Edition 5.1 better reflects
+ * how actual JS engines worked and work and is simpler and more readable prose. As such,
+ * the below code will refer to sections of the 5.1 specification.
+ *
+ * See <https://www.ecma-international.org/ecma-262/5.1/>.
*/
class JavaScriptMinifier {
const PROPERTY_EXPRESSION_FUNC = 15;
/* Token types */
- const TYPE_UN_OP = 1; // unary operators
- const TYPE_INCR_OP = 2; // ++ and --
- const TYPE_BIN_OP = 3; // binary operators
- const TYPE_ADD_OP = 4; // + and - which can be either unary or binary ops
- const TYPE_HOOK = 5; // ?
- const TYPE_COLON = 6; // :
- const TYPE_COMMA = 7; // ,
- const TYPE_SEMICOLON = 8; // ;
- const TYPE_BRACE_OPEN = 9; // {
- const TYPE_BRACE_CLOSE = 10; // }
- const TYPE_PAREN_OPEN = 11; // ( and [
- const TYPE_PAREN_CLOSE = 12; // ) and ]
- const TYPE_RETURN = 13; // keywords: break, continue, return, throw
- const TYPE_IF = 14; // keywords: catch, for, with, switch, while, if
- const TYPE_DO = 15; // keywords: case, var, finally, else, do, try
- const TYPE_FUNC = 16; // keywords: function
- const TYPE_LITERAL = 17; // all literals, identifiers and unrecognised tokens
+ const TYPE_UN_OP = 101; // unary operators
+ const TYPE_INCR_OP = 102; // ++ and --
+ const TYPE_BIN_OP = 103; // binary operators
+ const TYPE_ADD_OP = 104; // + and - which can be either unary or binary ops
+ const TYPE_HOOK = 105; // ?
+ const TYPE_COLON = 106; // :
+ const TYPE_COMMA = 107; // ,
+ const TYPE_SEMICOLON = 108; // ;
+ const TYPE_BRACE_OPEN = 109; // {
+ const TYPE_BRACE_CLOSE = 110; // }
+ const TYPE_PAREN_OPEN = 111; // ( and [
+ const TYPE_PAREN_CLOSE = 112; // ) and ]
+ const TYPE_RETURN = 113; // keywords: break, continue, return, throw
+ const TYPE_IF = 114; // keywords: catch, for, with, switch, while, if
+ const TYPE_DO = 115; // keywords: case, var, finally, else, do, try
+ const TYPE_FUNC = 116; // keywords: function
+ const TYPE_LITERAL = 117; // all literals, identifiers and unrecognised tokens
+
+ const ACTION_GOTO = 201;
+ const ACTION_PUSH = 202;
+ const ACTION_POP = 203;
// Sanity limit to avoid excessive memory usage
const STACK_LIMIT = 1000;
/**
- * NOTE: This isn't a strict maximum. Longer lines will be produced when
- * literals (e.g. quoted strings) longer than this are encountered
- * or when required to guard against semicolon insertion.
+ * Maximum line length
+ *
+ * This is not a strict maximum, but a guideline. Longer lines will be
+ * produced when literals (e.g. quoted strings) longer than this are
+ * encountered, or when required to guard against semicolon insertion.
+ *
+ * This is a private member (instead of constant) to allow tests to
+ * set it to 1, to verify ASI and line-breaking behaviour.
*/
- const MAX_LINE_LENGTH = 1000;
+ private static $maxLineLength = 1000;
/**
* Returns minified JavaScript code.
*
* @param string $s JavaScript code to minify
- * @return String Minified code
+ * @return string Minified code
*/
public static function minify( $s ) {
// First we declare a few tables that contain our parsing rules
- // $opChars : characters, which can be combined without whitespace in between them
+ // $opChars : Characters which can be combined without whitespace between them.
$opChars = [
- '!' => true,
- '"' => true,
- '%' => true,
- '&' => true,
- "'" => true,
+ // ECMAScript 5.1 § 7.7 Punctuators
+ // Unlike the spec, these are individual symbols, not sequences.
+ '{' => true,
+ '}' => true,
'(' => true,
')' => true,
- '*' => true,
- '+' => true,
- ',' => true,
- '-' => true,
+ '[' => true,
+ ']' => true,
'.' => true,
- '/' => true,
- ':' => true,
';' => true,
+ ',' => true,
'<' => true,
- '=' => true,
'>' => true,
- '?' => true,
- '[' => true,
- ']' => true,
- '^' => true,
- '{' => true,
+ '=' => true,
+ '!' => true,
+ '+' => true,
+ '-' => true,
+ '*' => true,
+ '%' => true,
+ '&' => true,
'|' => true,
- '}' => true,
- '~' => true
+ '^' => true,
+ '~' => true,
+ '?' => true,
+ ':' => true,
+ '/' => true,
+ // ECMAScript 5.1 § 7.8.4 String Literals
+ '"' => true,
+ "'" => true,
];
- // $tokenTypes : maps keywords and operators to their corresponding token type
+ // $tokenTypes : Map keywords and operators to their corresponding token type
$tokenTypes = [
- '!' => self::TYPE_UN_OP,
- '~' => self::TYPE_UN_OP,
- 'delete' => self::TYPE_UN_OP,
+ // ECMAScript 5.1 § 11.4 Unary Operators
+ // ECMAScript 5.1 § 11.6 Additive Operators
+ // UnaryExpression includes PostfixExpression, which includes 'new'.
'new' => self::TYPE_UN_OP,
- 'typeof' => self::TYPE_UN_OP,
+ 'delete' => self::TYPE_UN_OP,
'void' => self::TYPE_UN_OP,
+ 'typeof' => self::TYPE_UN_OP,
'++' => self::TYPE_INCR_OP,
'--' => self::TYPE_INCR_OP,
+ '+' => self::TYPE_ADD_OP,
+ '-' => self::TYPE_ADD_OP,
+ '~' => self::TYPE_UN_OP,
+ '!' => self::TYPE_UN_OP,
+ // ECMAScript 5.1 § 11.5 Multiplicative Operators
+ '*' => self::TYPE_BIN_OP,
+ '/' => self::TYPE_BIN_OP,
+ '%' => self::TYPE_BIN_OP,
+ // ECMAScript 5.1 § 11.7 Bitwise Shift Operators
+ '<<' => self::TYPE_BIN_OP,
+ '>>' => self::TYPE_BIN_OP,
+ '>>>' => self::TYPE_BIN_OP,
+ // ECMAScript 5.1 § 11.8 Relational Operators
+ '<' => self::TYPE_BIN_OP,
+ '>' => self::TYPE_BIN_OP,
+ '<=' => self::TYPE_BIN_OP,
+ '>=' => self::TYPE_BIN_OP,
+ // ECMAScript 5.1 § 11.9 Equality Operators
+ '==' => self::TYPE_BIN_OP,
'!=' => self::TYPE_BIN_OP,
+ '===' => self::TYPE_BIN_OP,
'!==' => self::TYPE_BIN_OP,
- '%' => self::TYPE_BIN_OP,
- '%=' => self::TYPE_BIN_OP,
+ 'instanceof' => self::TYPE_BIN_OP,
+ 'in' => self::TYPE_BIN_OP,
+ // ECMAScript 5.1 § 11.10 Binary Bitwise Operators
'&' => self::TYPE_BIN_OP,
+ '^' => self::TYPE_BIN_OP,
+ '|' => self::TYPE_BIN_OP,
+ // ECMAScript 5.1 § 11.11 Binary Logical Operators
'&&' => self::TYPE_BIN_OP,
- '&=' => self::TYPE_BIN_OP,
- '*' => self::TYPE_BIN_OP,
+ '||' => self::TYPE_BIN_OP,
+ // ECMAScript 5.1 § 11.12 Conditional Operator
+ // Also known as ternary.
+ '?' => self::TYPE_HOOK,
+ ':' => self::TYPE_COLON,
+ // ECMAScript 5.1 § 11.13 Assignment Operators
+ '=' => self::TYPE_BIN_OP,
'*=' => self::TYPE_BIN_OP,
+ '/=' => self::TYPE_BIN_OP,
+ '%=' => self::TYPE_BIN_OP,
'+=' => self::TYPE_BIN_OP,
'-=' => self::TYPE_BIN_OP,
- '.' => self::TYPE_BIN_OP,
- '/' => self::TYPE_BIN_OP,
- '/=' => self::TYPE_BIN_OP,
- '<' => self::TYPE_BIN_OP,
- '<<' => self::TYPE_BIN_OP,
'<<=' => self::TYPE_BIN_OP,
- '<=' => self::TYPE_BIN_OP,
- '=' => self::TYPE_BIN_OP,
- '==' => self::TYPE_BIN_OP,
- '===' => self::TYPE_BIN_OP,
- '>' => self::TYPE_BIN_OP,
- '>=' => self::TYPE_BIN_OP,
- '>>' => self::TYPE_BIN_OP,
'>>=' => self::TYPE_BIN_OP,
- '>>>' => self::TYPE_BIN_OP,
'>>>=' => self::TYPE_BIN_OP,
- '^' => self::TYPE_BIN_OP,
+ '&=' => self::TYPE_BIN_OP,
'^=' => self::TYPE_BIN_OP,
- '|' => self::TYPE_BIN_OP,
'|=' => self::TYPE_BIN_OP,
- '||' => self::TYPE_BIN_OP,
- 'in' => self::TYPE_BIN_OP,
- 'instanceof' => self::TYPE_BIN_OP,
- '+' => self::TYPE_ADD_OP,
- '-' => self::TYPE_ADD_OP,
- '?' => self::TYPE_HOOK,
- ':' => self::TYPE_COLON,
+ // ECMAScript 5.1 § 11.14 Comma Operator
',' => self::TYPE_COMMA,
- ';' => self::TYPE_SEMICOLON,
- '{' => self::TYPE_BRACE_OPEN,
- '}' => self::TYPE_BRACE_CLOSE,
- '(' => self::TYPE_PAREN_OPEN,
- '[' => self::TYPE_PAREN_OPEN,
- ')' => self::TYPE_PAREN_CLOSE,
- ']' => self::TYPE_PAREN_CLOSE,
- 'break' => self::TYPE_RETURN,
+
+ // The keywords that disallow LineTerminator before their
+ // (sometimes optional) Expression or Identifier.
+ //
+ // keyword ;
+ // keyword [no LineTerminator here] Identifier ;
+ // keyword [no LineTerminator here] Expression ;
+ //
+ // See also ECMAScript 5.1:
+ // - § 12.7 The continue Statement
+ // - $ 12.8 The break Statement
+ // - § 12.9 The return Statement
+ // - § 12.13 The throw Statement
'continue' => self::TYPE_RETURN,
+ 'break' => self::TYPE_RETURN,
'return' => self::TYPE_RETURN,
'throw' => self::TYPE_RETURN,
+
+ // The keywords require a parenthesised Expression or Identifier
+ // before the next Statement.
+ //
+ // keyword ( Expression ) Statement
+ // keyword ( Identifier ) Statement
+ //
+ // See also ECMAScript 5.1:
+ // - § 12.5 The if Statement
+ // - § 12.6 Iteration Statements (do, while, for)
+ // - § 12.10 The with Statement
+ // - § 12.11 The switch Statement
+ // - § 12.13 The throw Statement
+ 'if' => self::TYPE_IF,
'catch' => self::TYPE_IF,
+ 'while' => self::TYPE_IF,
'for' => self::TYPE_IF,
- 'if' => self::TYPE_IF,
'switch' => self::TYPE_IF,
- 'while' => self::TYPE_IF,
'with' => self::TYPE_IF,
- 'case' => self::TYPE_DO,
- 'do' => self::TYPE_DO,
+
+ // The keywords followed by an Identifier, Statement,
+ // Expression, or Block.
+ //
+ // var Identifier
+ // else Statement
+ // do Statement
+ // case Expression
+ // try Block
+ // finally Block
+ //
+ // See also ECMAScript 5.1:
+ // - § 12.2 Variable Statement
+ // - § 12.5 The if Statement (else)
+ // - § 12.6 Iteration Statements (do, while, for)
+ // - § 12.11 The switch Statement (case)
+ // - § 12.14 The try Statement
+ 'var' => self::TYPE_DO,
'else' => self::TYPE_DO,
- 'finally' => self::TYPE_DO,
+ 'do' => self::TYPE_DO,
+ 'case' => self::TYPE_DO,
'try' => self::TYPE_DO,
- 'var' => self::TYPE_DO,
- 'function' => self::TYPE_FUNC
- ];
+ 'finally' => self::TYPE_DO,
- // $goto : This is the main table for our state machine. For every state/token pair
- // the following state is defined. When no rule exists for a given pair,
- // the state is left unchanged.
- $goto = [
- self::STATEMENT => [
- self::TYPE_UN_OP => self::EXPRESSION,
- self::TYPE_INCR_OP => self::EXPRESSION,
- self::TYPE_ADD_OP => self::EXPRESSION,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
- self::TYPE_RETURN => self::EXPRESSION_NO_NL,
- self::TYPE_IF => self::CONDITION,
- self::TYPE_FUNC => self::CONDITION,
- self::TYPE_LITERAL => self::EXPRESSION_OP
- ],
- self::CONDITION => [
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
- ],
- self::PROPERTY_ASSIGNMENT => [
- self::TYPE_COLON => self::PROPERTY_EXPRESSION,
- self::TYPE_BRACE_OPEN => self::STATEMENT
- ],
- self::EXPRESSION => [
- self::TYPE_SEMICOLON => self::STATEMENT,
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
- self::TYPE_FUNC => self::EXPRESSION_FUNC,
- self::TYPE_LITERAL => self::EXPRESSION_OP
- ],
- self::EXPRESSION_NO_NL => [
- self::TYPE_SEMICOLON => self::STATEMENT,
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
- self::TYPE_FUNC => self::EXPRESSION_FUNC,
- self::TYPE_LITERAL => self::EXPRESSION_OP
- ],
- self::EXPRESSION_OP => [
- self::TYPE_BIN_OP => self::EXPRESSION,
- self::TYPE_ADD_OP => self::EXPRESSION,
- self::TYPE_HOOK => self::EXPRESSION_TERNARY,
- self::TYPE_COLON => self::STATEMENT,
- self::TYPE_COMMA => self::EXPRESSION,
- self::TYPE_SEMICOLON => self::STATEMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
- ],
- self::EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::STATEMENT
- ],
- self::EXPRESSION_TERNARY => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
- self::TYPE_FUNC => self::EXPRESSION_TERNARY_FUNC,
- self::TYPE_LITERAL => self::EXPRESSION_TERNARY_OP
- ],
- self::EXPRESSION_TERNARY_OP => [
- self::TYPE_BIN_OP => self::EXPRESSION_TERNARY,
- self::TYPE_ADD_OP => self::EXPRESSION_TERNARY,
- self::TYPE_HOOK => self::EXPRESSION_TERNARY,
- self::TYPE_COMMA => self::EXPRESSION_TERNARY,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
- ],
- self::EXPRESSION_TERNARY_FUNC => [
- self::TYPE_BRACE_OPEN => self::STATEMENT
- ],
- self::PAREN_EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
- self::TYPE_FUNC => self::PAREN_EXPRESSION_FUNC,
- self::TYPE_LITERAL => self::PAREN_EXPRESSION_OP
- ],
- self::PAREN_EXPRESSION_OP => [
- self::TYPE_BIN_OP => self::PAREN_EXPRESSION,
- self::TYPE_ADD_OP => self::PAREN_EXPRESSION,
- self::TYPE_HOOK => self::PAREN_EXPRESSION,
- self::TYPE_COLON => self::PAREN_EXPRESSION,
- self::TYPE_COMMA => self::PAREN_EXPRESSION,
- self::TYPE_SEMICOLON => self::PAREN_EXPRESSION,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
- ],
- self::PAREN_EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::STATEMENT
- ],
- self::PROPERTY_EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
- self::TYPE_FUNC => self::PROPERTY_EXPRESSION_FUNC,
- self::TYPE_LITERAL => self::PROPERTY_EXPRESSION_OP
- ],
- self::PROPERTY_EXPRESSION_OP => [
- self::TYPE_BIN_OP => self::PROPERTY_EXPRESSION,
- self::TYPE_ADD_OP => self::PROPERTY_EXPRESSION,
- self::TYPE_HOOK => self::PROPERTY_EXPRESSION,
- self::TYPE_COMMA => self::PROPERTY_ASSIGNMENT,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
- ],
- self::PROPERTY_EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::STATEMENT
- ]
+ // ECMAScript 5.1 § 13 Function Definition
+ 'function' => self::TYPE_FUNC,
+
+ // Can be one of:
+ // - DecimalLiteral (ECMAScript 5.1 § 7.8.3 Numeric Literals)
+ // - MemberExpression (ECMAScript 5.1 § 11.2 Left-Hand-Side Expressions)
+ '.' => self::TYPE_BIN_OP,
+
+ // Can be one of:
+ // - Block (ECMAScript 5.1 § 12.1 Block)
+ // - ObjectLiteral (ECMAScript 5.1 § 11.1 Primary Expressions)
+ '{' => self::TYPE_BRACE_OPEN,
+ '}' => self::TYPE_BRACE_CLOSE,
+
+ // Can be one of:
+ // - Parenthesised Identifier or Expression after a
+ // TYPE_IF or TYPE_FUNC keyword.
+ // - PrimaryExpression (ECMAScript 5.1 § 11.1 Primary Expressions)
+ // - CallExpression (ECMAScript 5.1 § 11.2 Left-Hand-Side Expressions)
+ '(' => self::TYPE_PAREN_OPEN,
+ ')' => self::TYPE_PAREN_CLOSE,
+
+ // Can be one of:
+ // - ArrayLiteral (ECMAScript 5.1 § 11.1 Primary Expressions)
+ '[' => self::TYPE_PAREN_OPEN,
+ ']' => self::TYPE_PAREN_CLOSE,
+
+ // Can be one of:
+ // - End of any statement
+ // - EmptyStatement (ECMAScript 5.1 § 12.3 Empty Statement)
+ ';' => self::TYPE_SEMICOLON,
];
- // $push : This table contains the rules for when to push a state onto the stack.
- // The pushed state is the state to return to when the corresponding
- // closing token is found
- $push = [
+ // $model : This is the main table for our state machine. For every state/token pair
+ // the desired action is defined.
+ //
+ // The state pushed onto the stack by ACTION_PUSH will be returned to by ACTION_POP.
+ //
+ // A given state/token pair MAY NOT specify both ACTION_POP and ACTION_GOTO.
+ // In the event of such mistake, ACTION_POP is used instead of ACTION_GOTO.
+ $model = [
+ // Statement - This is the initial state.
self::STATEMENT => [
- self::TYPE_BRACE_OPEN => self::STATEMENT,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
+ self::TYPE_UN_OP => [
+ self::ACTION_GOTO => self::EXPRESSION,
+ ],
+ self::TYPE_INCR_OP => [
+ self::ACTION_GOTO => self::EXPRESSION,
+ ],
+ self::TYPE_ADD_OP => [
+ self::ACTION_GOTO => self::EXPRESSION,
+ ],
+ self::TYPE_BRACE_OPEN => [
+ // Use of '{' in statement context, creates a Block.
+ self::ACTION_PUSH => self::STATEMENT,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ // Ends a Block
+ self::ACTION_POP => true,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_RETURN => [
+ self::ACTION_GOTO => self::EXPRESSION_NO_NL,
+ ],
+ self::TYPE_IF => [
+ self::ACTION_GOTO => self::CONDITION,
+ ],
+ self::TYPE_FUNC => [
+ self::ACTION_GOTO => self::CONDITION,
+ ],
+ self::TYPE_LITERAL => [
+ self::ACTION_GOTO => self::EXPRESSION_OP,
+ ],
],
self::CONDITION => [
- self::TYPE_PAREN_OPEN => self::STATEMENT
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::STATEMENT,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
],
+ // Property assignment - This is an object literal declaration.
+ // For example: `{ key: value }`
self::PROPERTY_ASSIGNMENT => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT
+ self::TYPE_COLON => [
+ self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
+ ],
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_ASSIGNMENT,
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
],
self::EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
+ self::TYPE_SEMICOLON => [
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_FUNC => [
+ self::ACTION_GOTO => self::EXPRESSION_FUNC,
+ ],
+ self::TYPE_LITERAL => [
+ self::ACTION_GOTO => self::EXPRESSION_OP,
+ ],
],
self::EXPRESSION_NO_NL => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
+ self::TYPE_SEMICOLON => [
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_FUNC => [
+ self::ACTION_GOTO => self::EXPRESSION_FUNC,
+ ],
+ self::TYPE_LITERAL => [
+ self::ACTION_GOTO => self::EXPRESSION_OP,
+ ],
],
self::EXPRESSION_OP => [
- self::TYPE_HOOK => self::EXPRESSION,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
+ self::TYPE_BIN_OP => [
+ self::ACTION_GOTO => self::EXPRESSION,
+ ],
+ self::TYPE_ADD_OP => [
+ self::ACTION_GOTO => self::EXPRESSION,
+ ],
+ self::TYPE_HOOK => [
+ self::ACTION_PUSH => self::EXPRESSION,
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY,
+ ],
+ self::TYPE_COLON => [
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
+ self::TYPE_COMMA => [
+ self::ACTION_GOTO => self::EXPRESSION,
+ ],
+ self::TYPE_SEMICOLON => [
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
],
self::EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_OP
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_OP,
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
],
self::EXPRESSION_TERNARY => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
+ self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_FUNC => [
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY_FUNC,
+ ],
+ self::TYPE_LITERAL => [
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY_OP,
+ ],
],
self::EXPRESSION_TERNARY_OP => [
- self::TYPE_HOOK => self::EXPRESSION_TERNARY,
- self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
+ self::TYPE_BIN_OP => [
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY,
+ ],
+ self::TYPE_ADD_OP => [
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY,
+ ],
+ self::TYPE_HOOK => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY,
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY,
+ ],
+ self::TYPE_COMMA => [
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_COLON => [
+ self::ACTION_POP => true,
+ ],
],
self::EXPRESSION_TERNARY_FUNC => [
- self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::EXPRESSION_TERNARY_OP,
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
],
self::PAREN_EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
+ self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_PAREN_CLOSE => [
+ self::ACTION_POP => true,
+ ],
+ self::TYPE_FUNC => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION_FUNC,
+ ],
+ self::TYPE_LITERAL => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION_OP,
+ ],
],
self::PAREN_EXPRESSION_OP => [
- self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
+ self::TYPE_BIN_OP => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_ADD_OP => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_HOOK => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_COLON => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_COMMA => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_SEMICOLON => [
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_PAREN_CLOSE => [
+ self::ACTION_POP => true,
+ ],
],
self::PAREN_EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PAREN_EXPRESSION_OP,
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
],
+ // Property expression - The value of a key in an object literal.
self::PROPERTY_EXPRESSION => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP,
- self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
+ self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
+ self::TYPE_FUNC => [
+ self::ACTION_GOTO => self::PROPERTY_EXPRESSION_FUNC,
+ ],
+ self::TYPE_LITERAL => [
+ self::ACTION_GOTO => self::PROPERTY_EXPRESSION_OP,
+ ],
],
self::PROPERTY_EXPRESSION_OP => [
- self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
+ self::TYPE_BIN_OP => [
+ self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
+ ],
+ self::TYPE_ADD_OP => [
+ self::ACTION_GOTO => self::PROPERTY_EXPRESSION,
+ ],
+ self::TYPE_HOOK => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION,
+ self::ACTION_GOTO => self::EXPRESSION_TERNARY,
+ ],
+ self::TYPE_COMMA => [
+ self::ACTION_GOTO => self::PROPERTY_ASSIGNMENT,
+ ],
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
+ ],
+ self::TYPE_BRACE_CLOSE => [
+ self::ACTION_POP => true,
+ ],
+ self::TYPE_PAREN_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
+ self::ACTION_GOTO => self::PAREN_EXPRESSION,
+ ],
],
self::PROPERTY_EXPRESSION_FUNC => [
- self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP
- ]
- ];
-
- // $pop : Rules for when to pop a state from the stack
- $pop = [
- self::STATEMENT => [ self::TYPE_BRACE_CLOSE => true ],
- self::PROPERTY_ASSIGNMENT => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION_NO_NL => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION_OP => [ self::TYPE_BRACE_CLOSE => true ],
- self::EXPRESSION_TERNARY_OP => [ self::TYPE_COLON => true ],
- self::PAREN_EXPRESSION => [ self::TYPE_PAREN_CLOSE => true ],
- self::PAREN_EXPRESSION_OP => [ self::TYPE_PAREN_CLOSE => true ],
- self::PROPERTY_EXPRESSION => [ self::TYPE_BRACE_CLOSE => true ],
- self::PROPERTY_EXPRESSION_OP => [ self::TYPE_BRACE_CLOSE => true ]
+ self::TYPE_BRACE_OPEN => [
+ self::ACTION_PUSH => self::PROPERTY_EXPRESSION_OP,
+ self::ACTION_GOTO => self::STATEMENT,
+ ],
+ ],
];
// $semicolon : Rules for when a semicolon insertion is appropriate
$semicolon = [
self::EXPRESSION_NO_NL => [
- self::TYPE_UN_OP => true,
- self::TYPE_INCR_OP => true,
- self::TYPE_ADD_OP => true,
+ self::TYPE_UN_OP => true,
+ self::TYPE_INCR_OP => true,
+ self::TYPE_ADD_OP => true,
self::TYPE_BRACE_OPEN => true,
self::TYPE_PAREN_OPEN => true,
- self::TYPE_RETURN => true,
- self::TYPE_IF => true,
- self::TYPE_DO => true,
- self::TYPE_FUNC => true,
- self::TYPE_LITERAL => true
+ self::TYPE_RETURN => true,
+ self::TYPE_IF => true,
+ self::TYPE_DO => true,
+ self::TYPE_FUNC => true,
+ self::TYPE_LITERAL => true
],
self::EXPRESSION_OP => [
- self::TYPE_UN_OP => true,
- self::TYPE_INCR_OP => true,
+ self::TYPE_UN_OP => true,
+ self::TYPE_INCR_OP => true,
self::TYPE_BRACE_OPEN => true,
- self::TYPE_RETURN => true,
- self::TYPE_IF => true,
- self::TYPE_DO => true,
- self::TYPE_FUNC => true,
- self::TYPE_LITERAL => true
+ self::TYPE_RETURN => true,
+ self::TYPE_IF => true,
+ self::TYPE_DO => true,
+ self::TYPE_FUNC => true,
+ self::TYPE_LITERAL => true
]
];
$out .= "\n";
$state = self::STATEMENT;
$lineLength = 0;
- } elseif ( $lineLength + $end - $pos > self::MAX_LINE_LENGTH &&
+ } elseif ( $lineLength + $end - $pos > self::$maxLineLength &&
!isset( $semicolon[$state][$type] ) && $type !== self::TYPE_INCR_OP ) {
// This line would get too long if we added $token, so add a newline first.
// Only do this if it won't trigger semicolon insertion and if it won't
$newlineFound = false;
// Now that we have output our token, transition into the new state.
- if ( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) {
- $stack[] = $push[$state][$type];
+ if ( isset( $model[$state][$type][self::ACTION_PUSH] ) &&
+ count( $stack ) < self::STACK_LIMIT
+ ) {
+ $stack[] = $model[$state][$type][self::ACTION_PUSH];
}
- if ( $stack && isset( $pop[$state][$type] ) ) {
+ if ( $stack && isset( $model[$state][$type][self::ACTION_POP] ) ) {
$state = array_pop( $stack );
- } elseif ( isset( $goto[$state][$type] ) ) {
- $state = $goto[$state][$type];
+ } elseif ( isset( $model[$state][$type][self::ACTION_GOTO] ) ) {
+ $state = $model[$state][$type][self::ACTION_GOTO];
}
}
return $out;