68 define(
'TOKEN_END', 1);
69 define(
'TOKEN_NUMBER', 2);
70 define(
'TOKEN_IDENTIFIER', 3);
71 define(
'TOKEN_STRING', 4);
72 define(
'TOKEN_REGEXP', 5);
73 define(
'TOKEN_NEWLINE', 6);
74 define(
'TOKEN_CONDCOMMENT_START', 7);
75 define(
'TOKEN_CONDCOMMENT_END', 8);
77 define(
'JS_SCRIPT', 100);
78 define(
'JS_BLOCK', 101);
79 define(
'JS_LABEL', 102);
80 define(
'JS_FOR_IN', 103);
81 define(
'JS_CALL', 104);
82 define(
'JS_NEW_WITH_ARGS', 105);
83 define(
'JS_INDEX', 106);
84 define(
'JS_ARRAY_INIT', 107);
85 define(
'JS_OBJECT_INIT', 108);
86 define(
'JS_PROPERTY_INIT', 109);
87 define(
'JS_GETTER', 110);
88 define(
'JS_SETTER', 111);
89 define(
'JS_GROUP', 112);
90 define(
'JS_LIST', 113);
92 define(
'JS_MINIFIED', 999);
94 define(
'DECLARED_FORM', 0);
95 define(
'EXPRESSED_FORM', 1);
96 define(
'STATEMENT_FORM', 2);
99 define(
'OP_SEMICOLON',
';');
100 define(
'OP_COMMA',
',');
101 define(
'OP_HOOK',
'?');
102 define(
'OP_COLON',
':');
103 define(
'OP_OR',
'||');
104 define(
'OP_AND',
'&&');
105 define(
'OP_BITWISE_OR',
'|');
106 define(
'OP_BITWISE_XOR',
'^');
107 define(
'OP_BITWISE_AND',
'&');
108 define(
'OP_STRICT_EQ',
'===');
109 define(
'OP_EQ',
'==');
110 define(
'OP_ASSIGN',
'=');
111 define(
'OP_STRICT_NE',
'!==');
112 define(
'OP_NE',
'!=');
113 define(
'OP_LSH',
'<<');
114 define(
'OP_LE',
'<=');
115 define(
'OP_LT',
'<');
116 define(
'OP_URSH',
'>>>');
117 define(
'OP_RSH',
'>>');
118 define(
'OP_GE',
'>=');
119 define(
'OP_GT',
'>');
120 define(
'OP_INCREMENT',
'++');
121 define(
'OP_DECREMENT',
'--');
122 define(
'OP_PLUS',
'+');
123 define(
'OP_MINUS',
'-');
124 define(
'OP_MUL',
'*');
125 define(
'OP_DIV',
'/');
126 define(
'OP_MOD',
'%');
127 define(
'OP_NOT',
'!');
128 define(
'OP_BITWISE_NOT',
'~');
129 define(
'OP_DOT',
'.');
130 define(
'OP_LEFT_BRACKET',
'[');
131 define(
'OP_RIGHT_BRACKET',
']');
132 define(
'OP_LEFT_CURLY',
'{');
133 define(
'OP_RIGHT_CURLY',
'}');
134 define(
'OP_LEFT_PAREN',
'(');
135 define(
'OP_RIGHT_PAREN',
')');
136 define(
'OP_CONDCOMMENT_END',
'@*/');
138 define(
'OP_UNARY_PLUS',
'U+');
139 define(
'OP_UNARY_MINUS',
'U-');
142 define(
'KEYWORD_BREAK',
'break');
143 define(
'KEYWORD_CASE',
'case');
144 define(
'KEYWORD_CATCH',
'catch');
145 define(
'KEYWORD_CONST',
'const');
146 define(
'KEYWORD_CONTINUE',
'continue');
147 define(
'KEYWORD_DEBUGGER',
'debugger');
148 define(
'KEYWORD_DEFAULT',
'default');
149 define(
'KEYWORD_DELETE',
'delete');
150 define(
'KEYWORD_DO',
'do');
151 define(
'KEYWORD_ELSE',
'else');
152 define(
'KEYWORD_ENUM',
'enum');
153 define(
'KEYWORD_FALSE',
'false');
154 define(
'KEYWORD_FINALLY',
'finally');
155 define(
'KEYWORD_FOR',
'for');
156 define(
'KEYWORD_FUNCTION',
'function');
157 define(
'KEYWORD_IF',
'if');
158 define(
'KEYWORD_IN',
'in');
159 define(
'KEYWORD_INSTANCEOF',
'instanceof');
160 define(
'KEYWORD_NEW',
'new');
161 define(
'KEYWORD_NULL',
'null');
162 define(
'KEYWORD_RETURN',
'return');
163 define(
'KEYWORD_SWITCH',
'switch');
164 define(
'KEYWORD_THIS',
'this');
165 define(
'KEYWORD_THROW',
'throw');
166 define(
'KEYWORD_TRUE',
'true');
167 define(
'KEYWORD_TRY',
'try');
168 define(
'KEYWORD_TYPEOF',
'typeof');
169 define(
'KEYWORD_VAR',
'var');
170 define(
'KEYWORD_VOID',
'void');
171 define(
'KEYWORD_WHILE',
'while');
172 define(
'KEYWORD_WITH',
'with');
178 private $reserved = array(
179 'break',
'case',
'catch',
'continue',
'default',
'delete',
'do',
180 'else',
'finally',
'for',
'function',
'if',
'in',
'instanceof',
181 'new',
'return',
'switch',
'this',
'throw',
'try',
'typeof',
'var',
182 'void',
'while',
'with',
184 'abstract',
'boolean',
'byte',
'char',
'class',
'const',
'debugger',
185 'double',
'enum',
'export',
'extends',
'final',
'float',
'goto',
186 'implements',
'import',
'int',
'interface',
'long',
'native',
187 'package',
'private',
'protected',
'public',
'short',
'static',
188 'super',
'synchronized',
'throws',
'transient',
'volatile',
191 'arguments',
'eval',
'true',
'false',
'Infinity',
'NaN',
'null',
'undefined'
194 private function __construct()
196 $this->parser =
new JSParser($this);
199 public static function minify($js, $filename=
'')
207 return $instance->min($js, $filename);
210 private function min($js, $filename)
214 $n = $this->parser->parse($js, $filename, 1);
219 echo $e->getMessage() .
"\n";
237 $noBlockGrouping =
true;
241 $childs = $n->treeNodes;
243 for ($c = 0, $i = 0, $j = count($childs); $i < $j; $i++)
245 $type = $childs[$i]->type;
261 $t =
',' . substr(
$t, 4);
277 if ($c > 1 && !$noBlockGrouping)
284 $s .=
'function' . ($n->name ?
' ' . $n->name :
'') .
'(';
285 $params = $n->params;
286 for ($i = 0, $j = count($params); $i < $j; $i++)
287 $s .= ($i ?
',' :
'') . $params[$i];
288 $s .=
'){' . $this->
parseTree($n->body,
true) .
'}';
292 $s =
'if(' . $this->
parseTree($n->condition) .
')';
293 $thenPart = $this->
parseTree($n->thenPart);
294 $elsePart = $n->elsePart ? $this->
parseTree($n->elsePart) :
null;
303 if ($thenPart !=
';' && $thenPart[0] !=
'{')
304 $thenPart =
'{' . $thenPart .
'}';
306 $s .= $thenPart .
'else';
309 if ($elsePart[0] !=
'{')
321 $s =
'switch(' . $this->
parseTree($n->discriminant) .
'){';
323 for ($i = 0, $j = count($cases); $i < $j; $i++)
327 $s .=
'case' . ($case->caseLabel->type !=
TOKEN_STRING ?
' ' :
'') . $this->
parseTree($case->caseLabel) .
':';
331 $statement = $this->
parseTree($case->statements,
true);
344 $s =
'for(' . ($n->setup ? $this->
parseTree($n->setup) :
'')
345 .
';' . ($n->condition ? $this->parseTree($n->condition) :
'')
346 .
';' . ($n->update ? $this->parseTree($n->update) :
'') .
')';
356 $s =
'while(' . $this->
parseTree($n->condition) .
')';
366 $s =
'for(' . ($n->varDecl ? $this->
parseTree($n->varDecl) : $this->
parseTree($n->iterator)) .
' in ' . $this->
parseTree($n->object) .
')';
376 $s =
'do{' . $this->
parseTree($n->body,
true) .
'}while(' . $this->
parseTree($n->condition) .
')';
381 $s = $n->value . ($n->label ?
' ' . $n->label :
'');
385 $s =
'try{' . $this->
parseTree($n->tryBlock,
true) .
'}';
386 $catchClauses = $n->catchClauses;
387 for ($i = 0, $j = count($catchClauses); $i < $j; $i++)
389 $t = $catchClauses[$i];
390 $s .=
'catch(' .
$t->varName . (
$t->guard ?
' if ' . $this->
parseTree(
$t->guard) :
'') .
'){' . $this->
parseTree(
$t->block,
true) .
'}';
392 if ($n->finallyBlock)
393 $s .=
'finally{' . $this->
parseTree($n->finallyBlock,
true) .
'}';
404 if ($this->isWordChar(
$t[0]) ||
$t[0] ==
'\\')
418 $s = $n->value .
' ';
419 $childs = $n->treeNodes;
420 for ($i = 0, $j = count($childs); $i < $j; $i++)
423 $s .= ($i ?
',' :
'') .
$t->name;
424 $u =
$t->initializer;
432 $left = $this->
parseTree($n->treeNodes[0]);
433 $right = $this->
parseTree($n->treeNodes[1]);
437 if ($this->isWordChar(substr($left, -1)))
442 if ($this->isWordChar($right[0]) || $right[0] ==
'\\')
450 $right = $this->
parseTree($n->treeNodes[0]);
454 if ($this->isWordChar($right[0]) || $right[0] ==
'\\')
461 $s =
'void(' . $this->
parseTree($n->treeNodes[0]) .
')';
465 throw new Exception(
'NOT IMPLEMENTED: DEBUGGER');
471 $childs = $n->treeNodes;
472 for ($i = 0, $j = count($childs); $i < $j; $i++)
477 if ($expression = $n->expression)
482 $s = $n->label .
':' . $this->
parseTree($n->statement);
486 $childs = $n->treeNodes;
487 for ($i = 0, $j = count($childs); $i < $j; $i++)
488 $s .= ($i ?
',' :
'') . $this->
parseTree($childs[$i]);
492 $s = $this->
parseTree($n->treeNodes[0]) . $n->value . $this->
parseTree($n->treeNodes[1]);
505 $s = $this->
parseTree($n->treeNodes[0]) . $n->type . $this->
parseTree($n->treeNodes[1]);
510 $left = $this->
parseTree($n->treeNodes[0]);
511 $right = $this->
parseTree($n->treeNodes[1]);
513 switch ($n->treeNodes[1]->type)
521 $s = $left . $n->type .
' ' . $right;
526 if ($n->type ==
OP_PLUS && substr($left, -1) == $right[0])
528 $s = substr($left, 0, -1) . substr($right, 1);
534 $s = $left . $n->type . $right;
542 $s = $n->value . $this->
parseTree($n->treeNodes[0]);
548 $s = $this->
parseTree($n->treeNodes[0]) . $n->value;
550 $s = $n->value . $this->
parseTree($n->treeNodes[0]);
562 $this->isValidIdentifier(substr($n->treeNodes[1]->value, 1, -1))
564 $s .=
'.' . substr($n->treeNodes[1]->value, 1, -1);
566 $s .=
'[' . $this->
parseTree($n->treeNodes[1]) .
']';
570 $childs = $n->treeNodes;
571 for ($i = 0, $j = count($childs); $i < $j; $i++)
572 $s .= ($i ?
',' :
'') . $this->
parseTree($childs[$i]);
576 $s = $this->
parseTree($n->treeNodes[0]) .
'(' . $this->
parseTree($n->treeNodes[1]) .
')';
586 $childs = $n->treeNodes;
587 for ($i = 0, $j = count($childs); $i < $j; $i++)
589 $s .= ($i ?
',' :
'') . $this->
parseTree($childs[$i]);
596 $childs = $n->treeNodes;
597 for ($i = 0, $j = count($childs); $i < $j; $i++)
606 $this->isValidIdentifier(substr(
$t->treeNodes[0]->value, 1, -1))
608 $s .= substr(
$t->treeNodes[0]->value, 1, -1);
610 $s .=
$t->treeNodes[0]->value;
617 $s .=
' ' .
$t->name .
'(';
618 $params =
$t->params;
619 for ($i = 0, $j = count($params); $i < $j; $i++)
620 $s .= ($i ?
',' :
'') . $params[$i];
621 $s .=
'){' . $this->
parseTree(
$t->body,
true) .
'}';
629 if (preg_match(
'/^([1-9]+)(0{3,})$/', $s, $m))
630 $s = $m[1] .
'e' . strlen($m[2]);
640 $n->treeNodes[0]->type,
652 $s =
'(' . $this->
parseTree($n->treeNodes[0]) .
')';
657 throw new Exception(
'UNKNOWN TOKEN TYPE: ' . $n->type);
663 private function isValidIdentifier($string)
665 return preg_match(
'/^[a-zA-Z_][a-zA-Z0-9_]*$/', $string) && !in_array($string, $this->reserved);
668 private function isWordChar($char)
670 return $char ==
'_' || $char ==
'$' || ctype_alnum($char);
679 private $opPrecedence = array(
682 '=' => 2,
'?' => 2,
':' => 2,
689 '==' => 9,
'!=' => 9,
'===' => 9,
'!==' => 9,
690 '<' => 10,
'<=' => 10,
'>=' => 10,
'>' => 10,
'in' => 10,
'instanceof' => 10,
691 '<<' => 11,
'>>' => 11,
'>>>' => 11,
692 '+' => 12,
'-' => 12,
693 '*' => 13,
'/' => 13,
'%' => 13,
694 'delete' => 14,
'void' => 14,
'typeof' => 14,
695 '!' => 14,
'~' => 14,
'U+' => 14,
'U-' => 14,
696 '++' => 15,
'--' => 15,
703 private $opArity = array(
712 '==' => 2,
'!=' => 2,
'===' => 2,
'!==' => 2,
713 '<' => 2,
'<=' => 2,
'>=' => 2,
'>' => 2,
'in' => 2,
'instanceof' => 2,
714 '<<' => 2,
'>>' => 2,
'>>>' => 2,
716 '*' => 2,
'/' => 2,
'%' => 2,
717 'delete' => 1,
'void' => 1,
'typeof' => 1,
718 '!' => 1,
'~' => 1,
'U+' => 1,
'U-' => 1,
719 '++' => 1,
'--' => 1,
729 $this->minifier = $minifier;
736 $this->t->init($s, $f, $l);
739 $n = $this->Script($x);
740 if (!$this->t->isDone())
741 throw $this->t->newSyntaxError(
'Syntax error');
746 private function Script($x)
748 $n = $this->Statements($x);
750 $n->funDecls = $x->funDecls;
751 $n->varDecls = $x->varDecls;
756 $n->value = $this->minifier->parseTree($n);
759 $n->treeNodes =
null;
769 private function Statements($x)
772 array_push($x->stmtStack, $n);
775 $n->addNode($this->Statement($x));
777 array_pop($x->stmtStack);
782 private function Block($x)
785 $n = $this->Statements($x);
791 private function Statement($x)
793 $tt = $this->t->get();
801 return $this->FunctionDefinition(
809 $n = $this->Statements($x);
814 $n =
new JSNode($this->t);
815 $n->condition = $this->ParenExpression($x);
816 array_push($x->stmtStack, $n);
817 $n->thenPart = $this->Statement($x);
818 $n->elsePart = $this->t->match(
KEYWORD_ELSE) ? $this->Statement($x) : null;
819 array_pop($x->stmtStack);
823 $n =
new JSNode($this->t);
825 $n->discriminant = $this->Expression($x);
828 $n->defaultIndex = -1;
830 array_push($x->stmtStack, $n);
839 if ($n->defaultIndex >= 0)
840 throw $this->t->newSyntaxError(
'More than one switch default');
843 $n2 =
new JSNode($this->t);
845 $n->defaultIndex = count($n->cases);
847 $n2->caseLabel = $this->Expression($x,
OP_COLON);
850 throw $this->t->newSyntaxError(
'Invalid switch case');
856 $n2->statements->addNode($this->Statement($x));
858 array_push($n->cases, $n2);
861 array_pop($x->stmtStack);
865 $n =
new JSNode($this->t);
871 $x->inForLoopInit =
true;
875 $n2 = $this->Variables($x);
879 $n2 = $this->Expression($x);
881 $x->inForLoopInit =
false;
889 if (count($n2->treeNodes) != 1)
891 throw $this->t->SyntaxError(
892 'Invalid for..in left-hand side',
899 $n->iterator = $n2->treeNodes[0];
908 $n->object = $this->Expression($x);
912 $n->setup = $n2 ?:
null;
914 $n->condition = $this->t->peek() ==
OP_SEMICOLON ? null : $this->Expression($x);
916 $n->update = $this->t->peek() ==
OP_RIGHT_PAREN ? null : $this->Expression($x);
920 $n->body = $this->nest($x, $n);
924 $n =
new JSNode($this->t);
926 $n->condition = $this->ParenExpression($x);
927 $n->body = $this->nest($x, $n);
931 $n =
new JSNode($this->t);
934 $n->condition = $this->ParenExpression($x);
935 if (!$x->ecmaStrictMode)
947 $n =
new JSNode($this->t);
952 $n->label = $this->t->currentToken()->value;
963 throw $this->t->newSyntaxError(
'Label not found');
965 while ($ss[$i]->label != $label);
972 throw $this->t->newSyntaxError(
'Invalid ' . $tt);
979 $n =
new JSNode($this->t);
980 $n->tryBlock = $this->Block($x);
981 $n->catchClauses = array();
985 $n2 =
new JSNode($this->t);
991 if ($x->ecmaStrictMode)
992 throw $this->t->newSyntaxError(
'Illegal catch guard');
994 if (count($n->catchClauses) && !end($n->catchClauses)->guard)
995 throw $this->t->newSyntaxError(
'Guarded catch after unguarded');
997 $n2->guard = $this->Expression($x);
1005 $n2->block = $this->Block($x);
1006 array_push($n->catchClauses, $n2);
1010 $n->finallyBlock = $this->Block($x);
1012 if (!count($n->catchClauses) && !$n->finallyBlock)
1013 throw $this->t->newSyntaxError(
'Invalid try statement');
1018 throw $this->t->newSyntaxError($tt .
' without preceding try');
1021 $n =
new JSNode($this->t);
1022 $n->value = $this->Expression($x);
1026 if (!$x->inFunction)
1027 throw $this->t->newSyntaxError(
'Invalid return');
1029 $n =
new JSNode($this->t);
1030 $tt = $this->t->peekOnSameLine();
1032 $n->value = $this->Expression($x);
1038 $n =
new JSNode($this->t);
1039 $n->object = $this->ParenExpression($x);
1040 $n->body = $this->nest($x, $n);
1045 $n = $this->Variables($x);
1050 $n =
new JSNode($this->t);
1054 $n =
new JSNode($this->t);
1065 $this->t->scanOperand =
false;
1066 $tt = $this->t->peek();
1067 $this->t->scanOperand =
true;
1070 $label = $this->t->currentToken()->value;
1071 $ss = $x->stmtStack;
1072 for ($i = count($ss) - 1; $i >= 0; --$i)
1074 if ($ss[$i]->label == $label)
1075 throw $this->t->newSyntaxError(
'Duplicate label');
1081 $n->statement = $this->nest($x, $n);
1089 $n->expression = $this->Expression($x);
1090 $n->end = $n->expression->end;
1094 if ($this->t->lineno == $this->t->currentToken()->lineno)
1096 $tt = $this->t->peekOnSameLine();
1098 throw $this->t->newSyntaxError(
'Missing ; before statement');
1106 private function FunctionDefinition($x, $requireName, $functionForm)
1108 $f =
new JSNode($this->t);
1114 $f->name = $this->t->currentToken()->value;
1115 elseif ($requireName)
1116 throw $this->t->newSyntaxError(
'Missing function identifier');
1119 $f->params = array();
1124 throw $this->t->newSyntaxError(
'Missing formal parameter');
1126 array_push($f->params, $this->t->currentToken()->value);
1135 $f->body = $this->Script($x2);
1138 $f->end = $this->t->currentToken()->end;
1140 $f->functionForm = $functionForm;
1142 array_push($x->funDecls, $f);
1147 private function Variables($x)
1149 $n =
new JSNode($this->t);
1155 $n2 =
new JSNode($this->t);
1156 $n2->name = $n2->value;
1160 if ($this->t->currentToken()->assignOp)
1161 throw $this->t->newSyntaxError(
'Invalid variable initialization');
1163 $n2->initializer = $this->Expression($x,
OP_COMMA);
1169 array_push($x->varDecls, $n2);
1176 private function Expression($x, $stop=
false)
1178 $operators = array();
1179 $operands = array();
1182 $bl = $x->bracketLevel;
1183 $cl = $x->curlyLevel;
1184 $pl = $x->parenLevel;
1185 $hl = $x->hookLevel;
1187 while (($tt = $this->t->get()) !=
TOKEN_END)
1190 $x->bracketLevel == $bl &&
1191 $x->curlyLevel == $cl &&
1192 $x->parenLevel == $pl &&
1193 $x->hookLevel == $hl
1208 if ($this->t->scanOperand)
1211 while ( !empty($operators) &&
1212 $this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]
1214 $this->reduce($operators, $operands);
1216 array_push($operators,
new JSNode($this->t));
1219 $this->t->scanOperand =
true;
1220 $n = $this->Expression($x);
1226 array_push($operands, $n);
1233 throw $this->t->newSyntaxError(
'Invalid label');
1237 if ($this->t->scanOperand)
1241 while ( !empty($operators) &&
1242 $this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]
1244 $this->reduce($operators, $operands);
1246 array_push($operators,
new JSNode($this->t));
1247 end($operands)->assignOp = $this->t->currentToken()->assignOp;
1248 $this->t->scanOperand =
true;
1255 if ($x->inForLoopInit && !$x->hookLevel &&
1256 !$x->bracketLevel && !$x->curlyLevel &&
1264 if ($tt ==
OP_COMMA && $x->hookLevel &&
1265 !$x->bracketLevel && !$x->curlyLevel &&
1284 if ($this->t->scanOperand)
1287 while ( !empty($operators) &&
1288 $this->opPrecedence[end($operators)->type] >= $this->opPrecedence[$tt]
1290 $this->reduce($operators, $operands);
1294 $tt = $this->t->get();
1296 throw $this->t->newSyntaxError(
"Unexpected token; token identifier or keyword expected.");
1298 array_push($operands,
new JSNode($this->t,
OP_DOT, array_pop($operands),
new JSNode($this->t)));
1302 array_push($operators,
new JSNode($this->t));
1303 $this->t->scanOperand =
true;
1310 if (!$this->t->scanOperand)
1313 array_push($operators,
new JSNode($this->t));
1317 if ($this->t->scanOperand)
1319 array_push($operators,
new JSNode($this->t));
1324 $t = $this->t->tokens[($this->t->tokenIndex + $this->t->lookahead - 1) & 3];
1325 if ($t && $t->lineno != $this->t->lineno)
1328 if (!empty($operators))
1331 while ($this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt])
1332 $this->reduce($operators, $operands);
1335 $n =
new JSNode($this->t, $tt, array_pop($operands));
1337 array_push($operands, $n);
1342 if (!$this->t->scanOperand)
1345 array_push($operands, $this->FunctionDefinition($x,
false,
EXPRESSED_FORM));
1346 $this->t->scanOperand =
false;
1351 if (!$this->t->scanOperand)
1354 array_push($operands,
new JSNode($this->t));
1355 $this->t->scanOperand =
false;
1360 if ($this->t->scanOperand)
1361 array_push($operators,
new JSNode($this->t));
1363 array_push($operands,
new JSNode($this->t));
1367 if ($this->t->scanOperand)
1381 $n->addNode($this->Expression($x,
OP_COMMA));
1387 array_push($operands, $n);
1388 $this->t->scanOperand =
false;
1394 $this->t->scanOperand =
true;
1400 if ($this->t->scanOperand || $x->bracketLevel == $bl)
1403 while ($this->reduce($operators, $operands)->type !=
JS_INDEX)
1410 if (!$this->t->scanOperand)
1421 $tt = $this->t->get();
1422 $tv = $this->t->currentToken()->value;
1423 if (($tv ==
'get' || $tv ==
'set') && $this->t->peek() ==
TOKEN_IDENTIFIER)
1425 if ($x->ecmaStrictMode)
1426 throw $this->t->newSyntaxError(
'Illegal property accessor');
1428 $n->addNode($this->FunctionDefinition($x,
true,
EXPRESSED_FORM));
1434 if ($this->isKeyword($tt))
1442 $id =
new JSNode($this->t);
1446 if ($x->ecmaStrictMode)
1447 throw $this->t->newSyntaxError(
'Illegal trailing ,');
1451 throw $this->t->newSyntaxError(
'Invalid property name');
1464 array_push($operands, $n);
1465 $this->t->scanOperand =
false;
1470 if (!$this->t->scanOperand && $x->curlyLevel != $cl)
1471 throw new Exception(
'PANIC: right curly botch');
1475 if ($this->t->scanOperand)
1481 while ( !empty($operators) &&
1482 $this->opPrecedence[end($operators)->type] > $this->opPrecedence[
KEYWORD_NEW]
1484 $this->reduce($operators, $operands);
1489 $n = end($operators);
1490 $this->t->scanOperand =
true;
1495 array_pop($operators);
1496 $n->addNode(array_pop($operands));
1503 array_push($operands, $n);
1504 $this->t->scanOperand =
false;
1518 if ($this->t->scanOperand || $x->parenLevel == $pl)
1521 while (($tt = $this->reduce($operators, $operands)->type) !=
JS_GROUP &&
1530 $n = end($operands);
1531 if ($n->treeNodes[1]->type !=
OP_COMMA)
1532 $n->treeNodes[1] =
new JSNode($this->t,
JS_LIST, $n->treeNodes[1]);
1534 $n->treeNodes[1]->type =
JS_LIST;
1548 if ($x->hookLevel != $hl)
1549 throw $this->t->newSyntaxError(
'Missing : in conditional expression');
1551 if ($x->parenLevel != $pl)
1552 throw $this->t->newSyntaxError(
'Missing ) in parenthetical');
1554 if ($x->bracketLevel != $bl)
1555 throw $this->t->newSyntaxError(
'Missing ] in index expression');
1557 if ($this->t->scanOperand)
1558 throw $this->t->newSyntaxError(
'Missing operand');
1561 $this->t->scanOperand =
true;
1564 while (count($operators))
1565 $this->reduce($operators, $operands);
1567 return array_pop($operands);
1570 private function ParenExpression($x)
1573 $n = $this->Expression($x);
1580 private function nest($x, $node, $end =
false)
1582 array_push($x->stmtStack, $node);
1583 $n = $this->Statement($x);
1584 array_pop($x->stmtStack);
1587 $this->t->mustMatch($end);
1592 private function reduce(&$operators, &$operands)
1594 $n = array_pop($operators);
1596 $arity = $this->opArity[$op];
1597 $c = count($operands);
1603 $left = $operands[$c - 2];
1604 if ($left->type == $op)
1606 $right = array_pop($operands);
1607 $left->addNode($right);
1615 $a = array_splice($operands, $c - $arity);
1616 for ($i = 0; $i < $arity; $i++)
1617 $n->addNode($a[$i]);
1620 $te = $this->t->currentToken()->end;
1624 array_push($operands, $n);
1629 private function isKeyword($tt)
1705 if ($token =
$t->currentToken())
1707 $this->type = $type ?: $token->type;
1708 $this->value = $token->value;
1709 $this->lineno = $token->lineno;
1710 $this->start = $token->start;
1711 $this->end = $token->end;
1715 $this->type = $type;
1716 $this->lineno =
$t->lineno;
1719 foreach($nodes as $node)
1728 $this->$name = $value;
1733 if (isset($this->$name))
1734 return $this->$name;
1743 if ($node->start < $this->start)
1744 $this->start = $node->start;
1745 if ($this->end < $node->end)
1746 $this->end = $node->end;
1749 $this->treeNodes[] = $node;
1755 private $cursor = 0;
1767 private $keywords = array(
1769 'case',
'catch',
'const',
'continue',
1770 'debugger',
'default',
'delete',
'do',
1772 'false',
'finally',
'for',
'function',
1773 'if',
'in',
'instanceof',
1777 'this',
'throw',
'true',
'try',
'typeof',
1782 private $opTypeNames = array(
1783 ';',
',',
'?',
':',
'||',
'&&',
'|',
'^',
1784 '&',
'===',
'==',
'=',
'!==',
'!=',
'<<',
'<=',
1785 '<',
'>>>',
'>>',
'>=',
'>',
'++',
'--',
'+',
1786 '-',
'*',
'/',
'%',
'!',
'~',
'.',
'[',
1787 ']',
'{',
'}',
'(',
')',
'@*/'
1790 private $assignOps = array(
'|',
'^',
'&',
'<<',
'>>',
'>>>',
'+',
'-',
'*',
'/',
'%');
1795 $this->opRegExp =
'#^(' . implode(
'|', array_map(
'preg_quote', $this->opTypeNames)) .
')#';
1800 $this->source = $source;
1801 $this->filename =
$filename ?:
'[inline]';
1805 $this->tokens = array();
1806 $this->tokenIndex = 0;
1807 $this->lookahead = 0;
1808 $this->scanNewlines =
false;
1809 $this->scanOperand =
true;
1815 return substr($this->source, $this->cursor, $chunksize);
1817 return substr($this->source, $this->cursor);
1827 return $this->
get() == $tt || $this->
unget();
1832 if (!$this->
match($tt))
1833 throw $this->
newSyntaxError(
'Unexpected token; token ' . $tt .
' expected');
1840 if ($this->lookahead)
1843 if ($this->scanNewlines && $next->lineno != $this->lineno)
1859 $this->scanNewlines =
true;
1860 $tt = $this->
peek();
1861 $this->scanNewlines =
false;
1868 if (!empty($this->tokens))
1872 public function get($chunksize = 1000)
1874 while($this->lookahead)
1877 $this->tokenIndex = ($this->tokenIndex + 1) & 3;
1880 return $token->type;
1883 $conditional_comment =
false;
1888 $input = $this->
getInput($chunksize);
1891 $re = $this->scanNewlines ?
'/^[ \r\t]+/' :
'/^\s+/';
1892 if (preg_match($re, $input, $match))
1894 $spaces = $match[0];
1895 $spacelen = strlen($spaces);
1896 $this->cursor += $spacelen;
1897 if (!$this->scanNewlines)
1898 $this->lineno += substr_count($spaces,
"\n");
1900 if ($spacelen == $chunksize)
1903 $input = $this->
getInput($chunksize);
1904 if ($input ==
'' || $input[0] !=
'/')
1909 if (!preg_match(
'/^\/(?:\*(@(?:cc_on|if|elif|else|end))?.*?\*\/|\/[^\n]*)/s', $input, $match))
1920 if (!empty($match[1]))
1922 $match[0] =
'/*' . $match[1];
1923 $conditional_comment =
true;
1928 $this->cursor += strlen($match[0]);
1929 $this->lineno += substr_count($match[0],
"\n");
1938 elseif ($conditional_comment)
1948 if (($input[1] ==
'x' || $input[1] ==
'X') && preg_match(
'/^0x[0-9a-f]+/i', $input, $match))
1955 case '1':
case '2':
case '3':
case '4':
case '5':
1956 case '6':
case '7':
case '8':
case '9':
1958 preg_match(
'/^\d+(?:\.\d*)?(?:[eE][-+]?\d+)?/', $input, $match);
1963 if (preg_match(
'/^\'(?:[^\\\\\'\r\n]++|\\\\(?:.|\r?\n))*+\'/', $input, $match))
1970 return $this->
get(
null);
1977 if (preg_match(
'/^"(?:[^\\\\"\r\n]++|\\\\(?:.|\r?\n))*+"/', $input, $match))
1984 return $this->
get(
null);
1991 if ($this->scanOperand && preg_match(
'/^\/((?:\\\\.|\[(?:\\\\.|[^\]])*\]|[^\/])++)\/([gimy]*)/', $input, $match))
2010 preg_match($this->opRegExp, $input, $match);
2012 if (in_array($op, $this->assignOps) && $input[strlen($op)] ==
'=')
2020 if ($this->scanOperand)
2032 if (preg_match(
'/^\.\d+(?:[eE][-+]?\d+)?/', $input, $match))
2051 $match = array($input[0]);
2057 if (substr($input, 0, 3) ==
'@*/')
2059 $match = array(
'@*/');
2067 if ($this->scanNewlines)
2069 $match = array(
"\n");
2080 if (!preg_match(
'/^[$\w]+(?=[\s\/\|\^\&<>\+\-\*%=!.;,\?:~\[\]\{\}\(\)@])/', $input, $match))
2085 $identifierStartChars =
2086 "\\p{L}\\p{Nl}" . # UnicodeLetter
2089 $identifierPartChars =
2090 $identifierStartChars .
2091 "\\p{Mn}\\p{Mc}" . # UnicodeCombiningMark
2092 "\\p{Nd}" . # UnicodeDigit
2093 "\\p{Pc}"; # UnicodeConnectorPunctuation
2094 $unicodeEscape =
"\\\\u[0-9A-F-a-f]{4}";
2097 "(?:[$identifierStartChars]|$unicodeEscape)" .
2098 "(?:[$identifierPartChars]|$unicodeEscape)*" .
2100 if (preg_match($identifierRegex, $input, $match))
2102 if (strpos($match[0],
'\\') !==
false) {
2105 $decoded = preg_replace_callback(
'/\\\\u([0-9A-Fa-f]{4})/',
2106 array(__CLASS__,
'unicodeEscapeCallback'),
2111 if (!preg_match(
"/^[$identifierStartChars][$identifierPartChars]*$/u", $decoded)) {
2118 if (in_array($decoded, $this->keywords)) {
2132 $this->tokenIndex = ($this->tokenIndex + 1) & 3;
2134 if (!isset($this->tokens[$this->tokenIndex]))
2135 $this->tokens[$this->tokenIndex] =
new JSToken();
2141 $token->assignOp = $op;
2143 $token->start = $this->cursor;
2145 $token->value = $match[0];
2146 $this->cursor += strlen($match[0]);
2148 $token->end = $this->cursor;
2156 if (++$this->lookahead == 4)
2159 $this->tokenIndex = ($this->tokenIndex - 1) & 3;
2164 return new Exception(
'Parse error: ' . $m .
' in file \'' . $this->filename .
'\' on line
' . $this->lineno);
2167 public static function unicodeEscapeCallback($m)
2169 return html_entity_decode('&#x
' . $m[1]. ';
', ENT_QUOTES, 'UTF-8
');
if(!defined('MW_SETUP_CALLBACK'))
parseTree($n, $noBlockGrouping=false)
static minify($js, $filename='')
__construct($t, $type=0,... $nodes)
__construct($minifier=null)
init($source, $filename='', $lineno=1)
const TOKEN_CONDCOMMENT_START
const TOKEN_CONDCOMMENT_END