You are here

private function JSParser::Statement in Javascript Aggregator 6

1 call to JSParser::Statement()
JSParser::Statements in ./jsminplus.php

File

./jsminplus.php, line 790

Class

JSParser

Code

private function Statement($x) {
  $tt = $this->t
    ->get();
  $n2 = null;

  // Cases for statements ending in a right curly return early, avoiding the
  // common semicolon insertion magic after this switch.
  switch ($tt) {
    case KEYWORD_FUNCTION:
      return $this
        ->FunctionDefinition($x, true, count($x->stmtStack) > 1 ? STATEMENT_FORM : DECLARED_FORM);
      break;
    case OP_LEFT_CURLY:
      $n = $this
        ->Statements($x);
      $this->t
        ->mustMatch(OP_RIGHT_CURLY);
      return $n;
    case KEYWORD_IF:
      $n = new JSNode($this->t);
      $n->condition = $this
        ->ParenExpression($x);
      array_push($x->stmtStack, $n);
      $n->thenPart = $this
        ->Statement($x);
      $n->elsePart = $this->t
        ->match(KEYWORD_ELSE) ? $this
        ->Statement($x) : null;
      array_pop($x->stmtStack);
      return $n;
    case KEYWORD_SWITCH:
      $n = new JSNode($this->t);
      $this->t
        ->mustMatch(OP_LEFT_PAREN);
      $n->discriminant = $this
        ->Expression($x);
      $this->t
        ->mustMatch(OP_RIGHT_PAREN);
      $n->cases = array();
      $n->defaultIndex = -1;
      array_push($x->stmtStack, $n);
      $this->t
        ->mustMatch(OP_LEFT_CURLY);
      while (($tt = $this->t
        ->get()) != OP_RIGHT_CURLY) {
        switch ($tt) {
          case KEYWORD_DEFAULT:
            if ($n->defaultIndex >= 0) {
              throw $this->t
                ->newSyntaxError('More than one switch default');
            }

          // FALL THROUGH
          case KEYWORD_CASE:
            $n2 = new JSNode($this->t);
            if ($tt == KEYWORD_DEFAULT) {
              $n->defaultIndex = count($n->cases);
            }
            else {
              $n2->caseLabel = $this
                ->Expression($x, OP_COLON);
            }
            break;
          default:
            throw $this->t
              ->newSyntaxError('Invalid switch case');
        }
        $this->t
          ->mustMatch(OP_COLON);
        $n2->statements = new JSNode($this->t, JS_BLOCK);
        while (($tt = $this->t
          ->peek()) != KEYWORD_CASE && $tt != KEYWORD_DEFAULT && $tt != OP_RIGHT_CURLY) {
          $n2->statements
            ->addNode($this
            ->Statement($x));
        }
        array_push($n->cases, $n2);
      }
      array_pop($x->stmtStack);
      return $n;
    case KEYWORD_FOR:
      $n = new JSNode($this->t);
      $n->isLoop = true;
      $this->t
        ->mustMatch(OP_LEFT_PAREN);
      if (($tt = $this->t
        ->peek()) != OP_SEMICOLON) {
        $x->inForLoopInit = true;
        if ($tt == KEYWORD_VAR || $tt == KEYWORD_CONST) {
          $this->t
            ->get();
          $n2 = $this
            ->Variables($x);
        }
        else {
          $n2 = $this
            ->Expression($x);
        }
        $x->inForLoopInit = false;
      }
      if ($n2 && $this->t
        ->match(KEYWORD_IN)) {
        $n->type = JS_FOR_IN;
        if ($n2->type == KEYWORD_VAR) {
          if (count($n2->treeNodes) != 1) {
            throw $this->t
              ->SyntaxError('Invalid for..in left-hand side', $this->t->filename, $n2->lineno);
          }

          // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
          $n->iterator = $n2->treeNodes[0];
          $n->varDecl = $n2;
        }
        else {
          $n->iterator = $n2;
          $n->varDecl = null;
        }
        $n->object = $this
          ->Expression($x);
      }
      else {
        $n->setup = $n2 ? $n2 : null;
        $this->t
          ->mustMatch(OP_SEMICOLON);
        $n->condition = $this->t
          ->peek() == OP_SEMICOLON ? null : $this
          ->Expression($x);
        $this->t
          ->mustMatch(OP_SEMICOLON);
        $n->update = $this->t
          ->peek() == OP_RIGHT_PAREN ? null : $this
          ->Expression($x);
      }
      $this->t
        ->mustMatch(OP_RIGHT_PAREN);
      $n->body = $this
        ->nest($x, $n);
      return $n;
    case KEYWORD_WHILE:
      $n = new JSNode($this->t);
      $n->isLoop = true;
      $n->condition = $this
        ->ParenExpression($x);
      $n->body = $this
        ->nest($x, $n);
      return $n;
    case KEYWORD_DO:
      $n = new JSNode($this->t);
      $n->isLoop = true;
      $n->body = $this
        ->nest($x, $n, KEYWORD_WHILE);
      $n->condition = $this
        ->ParenExpression($x);
      if (!$x->ecmaStrictMode) {

        // <script language="JavaScript"> (without version hints) may need
        // automatic semicolon insertion without a newline after do-while.
        // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
        $this->t
          ->match(OP_SEMICOLON);
        return $n;
      }
      break;
    case KEYWORD_BREAK:
    case KEYWORD_CONTINUE:
      $n = new JSNode($this->t);
      if ($this->t
        ->peekOnSameLine() == TOKEN_IDENTIFIER) {
        $this->t
          ->get();
        $n->label = $this->t
          ->currentToken()->value;
      }
      $ss = $x->stmtStack;
      $i = count($ss);
      $label = $n->label;
      if ($label) {
        do {
          if (--$i < 0) {
            throw $this->t
              ->newSyntaxError('Label not found');
          }
        } while ($ss[$i]->label != $label);
      }
      else {
        do {
          if (--$i < 0) {
            throw $this->t
              ->newSyntaxError('Invalid ' . $tt);
          }
        } while (!$ss[$i]->isLoop && ($tt != KEYWORD_BREAK || $ss[$i]->type != KEYWORD_SWITCH));
      }
      $n->target = $ss[$i];
      break;
    case KEYWORD_TRY:
      $n = new JSNode($this->t);
      $n->tryBlock = $this
        ->Block($x);
      $n->catchClauses = array();
      while ($this->t
        ->match(KEYWORD_CATCH)) {
        $n2 = new JSNode($this->t);
        $this->t
          ->mustMatch(OP_LEFT_PAREN);
        $n2->varName = $this->t
          ->mustMatch(TOKEN_IDENTIFIER)->value;
        if ($this->t
          ->match(KEYWORD_IF)) {
          if ($x->ecmaStrictMode) {
            throw $this->t
              ->newSyntaxError('Illegal catch guard');
          }
          if (count($n->catchClauses) && !end($n->catchClauses)->guard) {
            throw $this->t
              ->newSyntaxError('Guarded catch after unguarded');
          }
          $n2->guard = $this
            ->Expression($x);
        }
        else {
          $n2->guard = null;
        }
        $this->t
          ->mustMatch(OP_RIGHT_PAREN);
        $n2->block = $this
          ->Block($x);
        array_push($n->catchClauses, $n2);
      }
      if ($this->t
        ->match(KEYWORD_FINALLY)) {
        $n->finallyBlock = $this
          ->Block($x);
      }
      if (!count($n->catchClauses) && !$n->finallyBlock) {
        throw $this->t
          ->newSyntaxError('Invalid try statement');
      }
      return $n;
    case KEYWORD_CATCH:
    case KEYWORD_FINALLY:
      throw $this->t
        ->newSyntaxError($tt + ' without preceding try');
    case KEYWORD_THROW:
      $n = new JSNode($this->t);
      $n->value = $this
        ->Expression($x);
      break;
    case KEYWORD_RETURN:
      if (!$x->inFunction) {
        throw $this->t
          ->newSyntaxError('Invalid return');
      }
      $n = new JSNode($this->t);
      $tt = $this->t
        ->peekOnSameLine();
      if ($tt != TOKEN_END && $tt != TOKEN_NEWLINE && $tt != OP_SEMICOLON && $tt != OP_RIGHT_CURLY) {
        $n->value = $this
          ->Expression($x);
      }
      else {
        $n->value = null;
      }
      break;
    case KEYWORD_WITH:
      $n = new JSNode($this->t);
      $n->object = $this
        ->ParenExpression($x);
      $n->body = $this
        ->nest($x, $n);
      return $n;
    case KEYWORD_VAR:
    case KEYWORD_CONST:
      $n = $this
        ->Variables($x);
      break;
    case TOKEN_CONDCOMMENT_START:
    case TOKEN_CONDCOMMENT_END:
      $n = new JSNode($this->t);
      return $n;
    case KEYWORD_DEBUGGER:
      $n = new JSNode($this->t);
      break;
    case TOKEN_NEWLINE:
    case OP_SEMICOLON:
      $n = new JSNode($this->t, OP_SEMICOLON);
      $n->expression = null;
      return $n;
    default:
      if ($tt == TOKEN_IDENTIFIER) {
        $this->t->scanOperand = false;
        $tt = $this->t
          ->peek();
        $this->t->scanOperand = true;
        if ($tt == OP_COLON) {
          $label = $this->t
            ->currentToken()->value;
          $ss = $x->stmtStack;
          for ($i = count($ss) - 1; $i >= 0; --$i) {
            if ($ss[$i]->label == $label) {
              throw $this->t
                ->newSyntaxError('Duplicate label');
            }
          }
          $this->t
            ->get();
          $n = new JSNode($this->t, JS_LABEL);
          $n->label = $label;
          $n->statement = $this
            ->nest($x, $n);
          return $n;
        }
      }
      $n = new JSNode($this->t, OP_SEMICOLON);
      $this->t
        ->unget();
      $n->expression = $this
        ->Expression($x);
      $n->end = $n->expression->end;
      break;
  }
  if ($this->t->lineno == $this->t
    ->currentToken()->lineno) {
    $tt = $this->t
      ->peekOnSameLine();
    if ($tt != TOKEN_END && $tt != TOKEN_NEWLINE && $tt != OP_SEMICOLON && $tt != OP_RIGHT_CURLY) {
      throw $this->t
        ->newSyntaxError('Missing ; before statement');
    }
  }
  $this->t
    ->match(OP_SEMICOLON);
  return $n;
}