Same name in other branches
  1. 5.0.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
  2. 6.0.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
  3. 7.x-2.x advagg_js_compress/jsminplus.inc \JSParser::Expression()
  4. 8.x-2.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
  5. 8.x-3.x advagg_js_minify/jsminplus.inc \JSParser::Expression()
  6. 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

JSParser

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);
}