Same name in other branches
- 5.0.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
- 6.0.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
- 7.x-2.x advagg_js_compress/jsminplus.inc \JSParser::Expression()
- 8.x-2.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
- 8.x-3.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
- 8.x-4.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
3 calls to JSParser::Expression()
- JSParser::ParenExpression in advagg_js_compress/
jsminplus.inc - JSParser::Statement in advagg_js_compress/
jsminplus.inc - JSParser::Variables in advagg_js_compress/
jsminplus.inc
File
-
advagg_js_compress/
jsminplus.inc, line 1288
Class
Code
private function Expression($x, $stop = false) {
$operators = array();
$operands = array();
$n = false;
$bl = $x->bracketLevel;
$cl = $x->curlyLevel;
$pl = $x->parenLevel;
$hl = $x->hookLevel;
while (($tt = $this->t
->get()) != TOKEN_END) {
if ($tt == $stop && $x->bracketLevel == $bl && $x->curlyLevel == $cl && $x->parenLevel == $pl && $x->hookLevel == $hl) {
// Stop only if tt matches the optional stop parameter, and that
// token is not quoted by some kind of bracket.
break;
}
switch ($tt) {
case OP_SEMICOLON:
// NB: cannot be empty, Statement handled that.
break 2;
case OP_HOOK:
if ($this->t->scanOperand) {
break 2;
}
while (!empty($operators) && $this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]) {
$this->reduce($operators, $operands);
}
array_push($operators, new JSNode($this->t));
++$x->hookLevel;
$this->t->scanOperand = true;
$n = $this->Expression($x);
if (!$this->t
->match(OP_COLON)) {
break 2;
}
--$x->hookLevel;
array_push($operands, $n);
break;
case OP_COLON:
if ($x->hookLevel) {
break 2;
}
throw $this->t
->newSyntaxError('Invalid label');
break;
case OP_ASSIGN:
if ($this->t->scanOperand) {
break 2;
}
// Use >, not >=, for right-associative ASSIGN
while (!empty($operators) && $this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]) {
$this->reduce($operators, $operands);
}
array_push($operators, new JSNode($this->t));
end($operands)->assignOp = $this->t
->currentToken()->assignOp;
$this->t->scanOperand = true;
break;
case KEYWORD_IN:
// An in operator should not be parsed if we're parsing the head of
// a for (...) loop, unless it is in the then part of a conditional
// expression, or parenthesized somehow.
if ($x->inForLoopInit && !$x->hookLevel && !$x->bracketLevel && !$x->curlyLevel && !$x->parenLevel) {
break 2;
}
// FALL THROUGH
case OP_COMMA:
// A comma operator should not be parsed if we're parsing the then part
// of a conditional expression unless it's parenthesized somehow.
if ($tt == OP_COMMA && $x->hookLevel && !$x->bracketLevel && !$x->curlyLevel && !$x->parenLevel) {
break 2;
}
// Treat comma as left-associative so reduce can fold left-heavy
// COMMA trees into a single array.
// FALL THROUGH
case OP_OR:
case OP_AND:
case OP_BITWISE_OR:
case OP_BITWISE_XOR:
case OP_BITWISE_AND:
case OP_EQ:
case OP_NE:
case OP_STRICT_EQ:
case OP_STRICT_NE:
case OP_LT:
case OP_LE:
case OP_GE:
case OP_GT:
case KEYWORD_INSTANCEOF:
case OP_LSH:
case OP_RSH:
case OP_URSH:
case OP_PLUS:
case OP_MINUS:
case OP_MUL:
case OP_DIV:
case OP_MOD:
case OP_DOT:
if ($this->t->scanOperand) {
break 2;
}
while (!empty($operators) && $this->opPrecedence[end($operators)->type] >= $this->opPrecedence[$tt]) {
$this->reduce($operators, $operands);
}
if ($tt == OP_DOT) {
$this->t
->mustMatch(TOKEN_IDENTIFIER);
array_push($operands, new JSNode($this->t, OP_DOT, array_pop($operands), new JSNode($this->t)));
}
else {
array_push($operators, new JSNode($this->t));
$this->t->scanOperand = true;
}
break;
case KEYWORD_DELETE:
case KEYWORD_VOID:
case KEYWORD_TYPEOF:
case OP_NOT:
case OP_BITWISE_NOT:
case OP_UNARY_PLUS:
case OP_UNARY_MINUS:
case KEYWORD_NEW:
if (!$this->t->scanOperand) {
break 2;
}
array_push($operators, new JSNode($this->t));
break;
case OP_INCREMENT:
case OP_DECREMENT:
if ($this->t->scanOperand) {
array_push($operators, new JSNode($this->t));
// prefix increment or decrement
}
else {
// Don't cross a line boundary for postfix {in,de}crement.
$t = $this->t->tokens[$this->t->tokenIndex + $this->t->lookahead - 1 & 3];
if ($t && $t->lineno != $this->t->lineno) {
break 2;
}
if (!empty($operators)) {
// Use >, not >=, so postfix has higher precedence than prefix.
while ($this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]) {
$this->reduce($operators, $operands);
}
}
$n = new JSNode($this->t, $tt, array_pop($operands));
$n->postfix = true;
array_push($operands, $n);
}
break;
case KEYWORD_FUNCTION:
if (!$this->t->scanOperand) {
break 2;
}
array_push($operands, $this->FunctionDefinition($x, false, EXPRESSED_FORM));
$this->t->scanOperand = false;
break;
case KEYWORD_NULL:
case KEYWORD_THIS:
case KEYWORD_TRUE:
case KEYWORD_FALSE:
case TOKEN_IDENTIFIER:
case TOKEN_NUMBER:
case TOKEN_STRING:
case TOKEN_REGEXP:
if (!$this->t->scanOperand) {
break 2;
}
array_push($operands, new JSNode($this->t));
$this->t->scanOperand = false;
break;
case TOKEN_CONDCOMMENT_START:
case TOKEN_CONDCOMMENT_END:
if ($this->t->scanOperand) {
array_push($operators, new JSNode($this->t));
}
else {
array_push($operands, new JSNode($this->t));
}
break;
case OP_LEFT_BRACKET:
if ($this->t->scanOperand) {
// Array initialiser. Parse using recursive descent, as the
// sub-grammar here is not an operator grammar.
$n = new JSNode($this->t, JS_ARRAY_INIT);
while (($tt = $this->t
->peek()) != OP_RIGHT_BRACKET) {
if ($tt == OP_COMMA) {
$this->t
->get();
$n->addNode(null);
continue;
}
$n->addNode($this->Expression($x, OP_COMMA));
if (!$this->t
->match(OP_COMMA)) {
break;
}
}
$this->t
->mustMatch(OP_RIGHT_BRACKET);
array_push($operands, $n);
$this->t->scanOperand = false;
}
else {
// Property indexing operator.
array_push($operators, new JSNode($this->t, JS_INDEX));
$this->t->scanOperand = true;
++$x->bracketLevel;
}
break;
case OP_RIGHT_BRACKET:
if ($this->t->scanOperand || $x->bracketLevel == $bl) {
break 2;
}
while ($this->reduce($operators, $operands)->type != JS_INDEX) {
continue;
}
--$x->bracketLevel;
break;
case OP_LEFT_CURLY:
if (!$this->t->scanOperand) {
break 2;
}
// Object initialiser. As for array initialisers (see above),
// parse using recursive descent.
++$x->curlyLevel;
$n = new JSNode($this->t, JS_OBJECT_INIT);
while (!$this->t
->match(OP_RIGHT_CURLY)) {
do {
$tt = $this->t
->get();
$tv = $this->t
->currentToken()->value;
if (($tv == 'get' || $tv == 'set') && $this->t
->peek() == TOKEN_IDENTIFIER) {
if ($x->ecmaStrictMode) {
throw $this->t
->newSyntaxError('Illegal property accessor');
}
$n->addNode($this->FunctionDefinition($x, true, EXPRESSED_FORM));
}
else {
switch ($tt) {
case TOKEN_IDENTIFIER:
case TOKEN_NUMBER:
case TOKEN_STRING:
$id = new JSNode($this->t);
break;
case OP_RIGHT_CURLY:
if ($x->ecmaStrictMode) {
throw $this->t
->newSyntaxError('Illegal trailing ,');
}
break 3;
default:
throw $this->t
->newSyntaxError('Invalid property name');
}
$this->t
->mustMatch(OP_COLON);
$n->addNode(new JSNode($this->t, JS_PROPERTY_INIT, $id, $this->Expression($x, OP_COMMA)));
}
} while ($this->t
->match(OP_COMMA));
$this->t
->mustMatch(OP_RIGHT_CURLY);
break;
}
array_push($operands, $n);
$this->t->scanOperand = false;
--$x->curlyLevel;
break;
case OP_RIGHT_CURLY:
if (!$this->t->scanOperand && $x->curlyLevel != $cl) {
throw new Exception('PANIC: right curly botch');
}
break 2;
case OP_LEFT_PAREN:
if ($this->t->scanOperand) {
array_push($operators, new JSNode($this->t, JS_GROUP));
}
else {
while (!empty($operators) && $this->opPrecedence[end($operators)->type] > $this->opPrecedence[KEYWORD_NEW]) {
$this->reduce($operators, $operands);
}
// Handle () now, to regularize the n-ary case for n > 0.
// We must set scanOperand in case there are arguments and
// the first one is a regexp or unary+/-.
$n = end($operators);
$this->t->scanOperand = true;
if ($this->t
->match(OP_RIGHT_PAREN)) {
if ($n && $n->type == KEYWORD_NEW) {
array_pop($operators);
$n->addNode(array_pop($operands));
}
else {
$n = new JSNode($this->t, JS_CALL, array_pop($operands), new JSNode($this->t, JS_LIST));
}
array_push($operands, $n);
$this->t->scanOperand = false;
break;
}
if ($n && $n->type == KEYWORD_NEW) {
$n->type = JS_NEW_WITH_ARGS;
}
else {
array_push($operators, new JSNode($this->t, JS_CALL));
}
}
++$x->parenLevel;
break;
case OP_RIGHT_PAREN:
if ($this->t->scanOperand || $x->parenLevel == $pl) {
break 2;
}
while (($tt = $this->reduce($operators, $operands)->type) != JS_GROUP && $tt != JS_CALL && $tt != JS_NEW_WITH_ARGS) {
continue;
}
if ($tt != JS_GROUP) {
$n = end($operands);
if ($n->treeNodes[1]->type != OP_COMMA) {
$n->treeNodes[1] = new JSNode($this->t, JS_LIST, $n->treeNodes[1]);
}
else {
$n->treeNodes[1]->type = JS_LIST;
}
}
--$x->parenLevel;
break;
// Automatic semicolon insertion means we may scan across a newline
// and into the beginning of another statement. If so, break out of
// the while loop and let the t.scanOperand logic handle errors.
default:
break 2;
}
}
if ($x->hookLevel != $hl) {
throw $this->t
->newSyntaxError('Missing : in conditional expression');
}
if ($x->parenLevel != $pl) {
throw $this->t
->newSyntaxError('Missing ) in parenthetical');
}
if ($x->bracketLevel != $bl) {
throw $this->t
->newSyntaxError('Missing ] in index expression');
}
if ($this->t->scanOperand) {
throw $this->t
->newSyntaxError('Missing operand');
}
// Resume default mode, scanning for operands, not operators.
$this->t->scanOperand = true;
$this->t
->unget();
while (count($operators)) {
$this->reduce($operators, $operands);
}
return array_pop($operands);
}