/*
Statement :: (tokenizer, compiler context) -> node

Parses a Statement.
function Statement(t, x) {
    var i, label, n, n2, p, c, ss, tt = t.get(true), tt2, x2, x3;

    // Cases for statements ending in a right curly return early, avoiding the
    // common semicolon insertion magic after this switch.
    switch (tt) {
      case FUNCTION:
        // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't.
        return FunctionDefinition(t, x, true,
                                  (x.nesting !== NESTING_TOP)
                                  ? STATEMENT_FORM
                                  : DECLARED_FORM);

      case LEFT_CURLY:
        n = new Node(t, blockInit());
        Statements(t, x.update({ parentBlock: n }).pushTarget(n).nest(NESTING_SHALLOW), n);
        t.mustMatch(RIGHT_CURLY);
        return n;

      case IF:
        n = new Node(t);
        n.condition = HeadExpression(t, x);
        x2 = x.pushTarget(n).nest(NESTING_DEEP);
        n.thenPart = Statement(t, x2);
        n.elsePart = t.match(ELSE) ? Statement(t, x2) : null;
        return n;

      case SWITCH:
        // This allows CASEs after a DEFAULT, which is in the standard.
        n = new Node(t, { cases: [], defaultIndex: -1 });
        n.discriminant = HeadExpression(t, x);
        x2 = x.pushTarget(n).nest(NESTING_DEEP);
        t.mustMatch(LEFT_CURLY);
        while ((tt = t.get()) !== RIGHT_CURLY) {
            switch (tt) {
              case DEFAULT:
                if (n.defaultIndex >= 0)
                    throw t.newSyntaxError("More than one switch default");
                // FALL THROUGH
              case CASE:
                n2 = new Node(t);
                if (tt === DEFAULT)
                    n.defaultIndex = n.cases.length;
                else
                    n2.caseLabel = Expression(t, x2, COLON);
                break;

              default:
                throw t.newSyntaxError("Invalid switch case");
            }
            t.mustMatch(COLON);
            n2.statements = new Node(t, blockInit());
            while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT &&
                    tt !== RIGHT_CURLY)
                n2.statements.push(Statement(t, x2));
            n.cases.push(n2);
        }
        return n;

      case FOR:
        n = new Node(t, LOOP_INIT);
        if (t.match(IDENTIFIER)) {
            if (t.token.value === "each")
                n.isEach = true;
            else
                t.unget();
        }
        if (!x.parenFreeMode)
            t.mustMatch(LEFT_PAREN);
        x2 = x.pushTarget(n).nest(NESTING_DEEP);
        x3 = x.update({ inForLoopInit: true });
        if ((tt = t.peek()) !== SEMICOLON) {
            if (tt === VAR || tt === CONST) {
                t.get();
                n2 = Variables(t, x3);
            } else if (tt === LET) {
                t.get();
                if (t.peek() === LEFT_PAREN) {
                    n2 = LetBlock(t, x3, false);
                } else {
                    // Let in for head, we need to add an implicit block
                    // around the rest of the for.
                    x3.parentBlock = n;
                    n.varDecls = [];
                    n2 = Variables(t, x3);
                }
            } else {
                n2 = Expression(t, x3);
            }
        }
        if (n2 && t.match(IN)) {
            n.type = FOR_IN;
            n.object = Expression(t, x3);
            if (n2.type === VAR || n2.type === LET) {
                c = n2.children;

                // Destructuring turns one decl into multiples, so either
                // there must be only one destructuring or only one
                // decl.
                if (c.length !== 1 && n2.destructurings.length !== 1) {
                    throw new SyntaxError("Invalid for..in left-hand side",
                                          t.filename, n2.lineno);
                }
                if (n2.destructurings.length > 0) {
                    n.iterator = n2.destructurings[0];
                } else {
                    n.iterator = c[0];
                }
                n.varDecl = n2;
            } else {
                if (n2.type === ARRAY_INIT || n2.type === OBJECT_INIT) {
                    n2.destructuredNames = checkDestructuring(t, x3, n2);
                }
                n.iterator = n2;
            }
        } else {
            n.setup = n2;
            t.mustMatch(SEMICOLON);
            if (n.isEach)
                throw t.newSyntaxError("Invalid for each..in loop");
            n.condition = (t.peek() === SEMICOLON)
                          ? null
                          : Expression(t, x3);
            t.mustMatch(SEMICOLON);
            tt2 = t.peek();
            n.update = (x.parenFreeMode
                        ? tt2 === LEFT_CURLY || definitions.isStatementStartCode[tt2]
                        : tt2 === RIGHT_PAREN)
                       ? null
                       : Expression(t, x3);
        }
        if (!x.parenFreeMode)
            t.mustMatch(RIGHT_PAREN);
        n.body = Statement(t, x2);
        return n;

      case WHILE:
        n = new Node(t, { isLoop: true });
        n.condition = HeadExpression(t, x);
        n.body = Statement(t, x.pushTarget(n).nest(NESTING_DEEP));
        return n;

      case DO:
        n = new Node(t, { isLoop: true });
        n.body = Statement(t, x.pushTarget(n).nest(NESTING_DEEP));
        t.mustMatch(WHILE);
        n.condition = HeadExpression(t, 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.
            t.match(SEMICOLON);
            return n;
        }
        break;

      case BREAK:
      case CONTINUE:
        n = new Node(t);

        // handle the |foo: break foo;| corner case
        x2 = x.pushTarget(n);

        if (t.peekOnSameLine() === IDENTIFIER) {
            t.get();
            n.label = t.token.value;
        }

        n.target = n.label
                 ? x2.labeledTargets.find(function(target) { return target.labels.has(n.label) })
                 : x2.defaultTarget;

        if (!n.target)
            throw t.newSyntaxError("Invalid " + ((tt === BREAK) ? "break" : "continue"));
        if (!n.target.isLoop && tt === CONTINUE)
            throw t.newSyntaxError("Invalid continue");

        break;

      case TRY:
        n = new Node(t, { catchClauses: [] });
        n.tryBlock = Block(t, x);
        while (t.match(CATCH)) {
            n2 = new Node(t);
            p = MaybeLeftParen(t, x);
            switch (t.get()) {
              case LEFT_BRACKET:
              case LEFT_CURLY:
                // Destructured catch identifiers.
                t.unget();
                n2.varName = DestructuringExpression(t, x, true);
                break;
              case IDENTIFIER:
                n2.varName = t.token.value;
                break;
              default:
                throw t.newSyntaxError("missing identifier in catch");
                break;
            }
            if (t.match(IF)) {
                if (x.ecma3OnlyMode)
                    throw t.newSyntaxError("Illegal catch guard");
                if (n.catchClauses.length && !n.catchClauses.top().guard)
                    throw t.newSyntaxError("Guarded catch after unguarded");
                n2.guard = Expression(t, x);
            }
            MaybeRightParen(t, p);
            n2.block = Block(t, x);
            n.catchClauses.push(n2);
        }
        if (t.match(FINALLY))
            n.finallyBlock = Block(t, x);
        if (!n.catchClauses.length && !n.finallyBlock)
            throw t.newSyntaxError("Invalid try statement");
        return n;

      case CATCH:
      case FINALLY:
        throw t.newSyntaxError(definitions.tokens[tt] + " without preceding try");

      case THROW:
        n = new Node(t);
        n.exception = Expression(t, x);
        break;

      case RETURN:
        n = ReturnOrYield(t, x);
        break;

      case WITH:
        n = new Node(t);
        n.object = HeadExpression(t, x);
        n.body = Statement(t, x.pushTarget(n).nest(NESTING_DEEP));
        return n;

      case VAR:
      case CONST:
        n = Variables(t, x);
        break;

      case LET:
        if (t.peek() === LEFT_PAREN)
            n = LetBlock(t, x, true);
        else
            n = Variables(t, x);
        break;

      case DEBUGGER:
        n = new Node(t);
        break;

      case NEWLINE:
      case SEMICOLON:
        n = new Node(t, { type: SEMICOLON });
        n.expression = null;
        return n;

      default:
        if (tt === IDENTIFIER) {
            tt = t.peek();
            // Labeled statement.
            if (tt === COLON) {
                label = t.token.value;
                if (x.allLabels.has(label))
                    throw t.newSyntaxError("Duplicate label");
                t.get();
                n = new Node(t, { type: LABEL, label: label });
                n.statement = Statement(t, x.pushLabel(label).nest(NESTING_SHALLOW));
                n.target = (n.statement.type === LABEL) ? n.statement.target : n.statement;
                return n;
            }
        }

        // Expression statement.
        // We unget the current token to parse the expression as a whole.
        n = new Node(t, { type: SEMICOLON });
        t.unget();
        n.expression = Expression(t, x);
        n.end = n.expression.end;
        break;
    }

    MagicalSemicolon(t);
    return n;
}