diff --git a/acorn.js b/acorn.js index 7de370d593..9de7353950 100644 --- a/acorn.js +++ b/acorn.js @@ -1167,211 +1167,245 @@ // complexity. switch (starttype) { - case _break: case _continue: - next(); - var isBreak = starttype === _break; - if (eat(_semi) || canInsertSemicolon()) node.label = null; - else if (tokType !== _name) unexpected(); - else { - node.label = parseIdent(); - semicolon(); - } - - // Verify that there is an actual destination to break or - // continue to. - for (var i = 0; i < labels.length; ++i) { - var lab = labels[i]; - if (node.label == null || lab.name === node.label.name) { - if (lab.kind != null && (isBreak || lab.kind === "loop")) break; - if (node.label && isBreak) break; - } - } - if (i === labels.length) raise(node.start, "Unsyntactic " + starttype.keyword); - return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); - - case _debugger: - next(); - semicolon(); - return finishNode(node, "DebuggerStatement"); - - case _do: - next(); - labels.push(loopLabel); - node.body = parseStatement(); - labels.pop(); - expect(_while); - node.test = parseParenExpression(); - semicolon(); - return finishNode(node, "DoWhileStatement"); - - // Disambiguating between a `for` and a `for`/`in` loop is - // non-trivial. Basically, we have to parse the init `var` - // statement or expression, disallowing the `in` operator (see - // the second parameter to `parseExpression`), and then check - // whether the next token is `in`. When there is no init part - // (semicolon immediately after the opening parenthesis), it is - // a regular `for` loop. - - case _for: - next(); - labels.push(loopLabel); - expect(_parenL); - if (tokType === _semi) return parseFor(node, null); - if (tokType === _var || tokType === _let) { - var varKind = tokType.keyword; - var init = startNode(); - next(); - parseVar(init, true, varKind); - finishNode(init, "VariableDeclaration"); - if (init.declarations.length === 1 && eat(_in)) - return parseForIn(node, init); - return parseFor(node, init); - } - var init = parseExpression(false, true); - if (eat(_in)) {checkLVal(init); return parseForIn(node, init);} - return parseFor(node, init); - - case _function: - next(); - return parseFunction(node, true); - - case _if: - next(); - node.test = parseParenExpression(); - node.consequent = parseStatement(); - node.alternate = eat(_else) ? parseStatement() : null; - return finishNode(node, "IfStatement"); - - case _return: - if (!inFunction && !options.allowReturnOutsideFunction) - raise(tokStart, "'return' outside of function"); - next(); - - // In `return` (and `break`/`continue`), the keywords with - // optional arguments, we eagerly look for a semicolon or the - // possibility to insert one. - - if (eat(_semi) || canInsertSemicolon()) node.argument = null; - else { node.argument = parseExpression(); semicolon(); } - return finishNode(node, "ReturnStatement"); - - case _switch: - next(); - node.discriminant = parseParenExpression(); - node.cases = []; - expect(_braceL); - labels.push(switchLabel); - - // Statements under must be grouped (by label) in SwitchCase - // nodes. `cur` is used to keep the node that we are currently - // adding statements to. - - for (var cur, sawDefault; tokType != _braceR;) { - if (tokType === _case || tokType === _default) { - var isCase = tokType === _case; - if (cur) finishNode(cur, "SwitchCase"); - node.cases.push(cur = startNode()); - cur.consequent = []; - next(); - if (isCase) cur.test = parseExpression(); - else { - if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true; - cur.test = null; - } - expect(_colon); - } else { - if (!cur) unexpected(); - cur.consequent.push(parseStatement()); - } - } - if (cur) finishNode(cur, "SwitchCase"); - next(); // Closing brace - labels.pop(); - return finishNode(node, "SwitchStatement"); - - case _throw: - next(); - if (newline.test(input.slice(lastEnd, tokStart))) - raise(lastEnd, "Illegal newline after throw"); - node.argument = parseExpression(); - semicolon(); - return finishNode(node, "ThrowStatement"); - - case _try: - next(); - node.block = parseBlock(); - node.handler = null; - if (tokType === _catch) { - var clause = startNode(); - next(); - expect(_parenL); - clause.param = parseIdent(); - if (strict && isStrictBadIdWord(clause.param.name)) - raise(clause.param.start, "Binding " + clause.param.name + " in strict mode"); - expect(_parenR); - clause.guard = null; - clause.body = parseBlock(); - node.handler = finishNode(clause, "CatchClause"); - } - node.guardedHandlers = empty; - node.finalizer = eat(_finally) ? parseBlock() : null; - if (!node.handler && !node.finalizer) - raise(node.start, "Missing catch or finally clause"); - return finishNode(node, "TryStatement"); - - case _const: - case _let: - case _var: - next(); - parseVar(node, false, starttype.keyword); - semicolon(); - return finishNode(node, "VariableDeclaration"); - - case _while: - next(); - node.test = parseParenExpression(); - labels.push(loopLabel); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "WhileStatement"); - - case _with: - if (strict) raise(tokStart, "'with' in strict mode"); - next(); - node.object = parseParenExpression(); - node.body = parseStatement(); - return finishNode(node, "WithStatement"); - - case _braceL: - return parseBlock(); - - case _semi: - next(); - return finishNode(node, "EmptyStatement"); + case _break: case _continue: return parseBreakContinueStatement(node, starttype.keyword); + case _debugger: return parseDebuggerStatement(node); + case _do: return parseDoStatement(node); + case _for: return parseForStatement(node); + case _function: return parseFunctionStatement(node); + case _if: return parseIfStatement(node); + case _return: return parseReturnStatement(node); + case _switch: return parseSwitchStatement(node); + case _throw: return parseThrowStatement(node); + case _try: return parseTryStatement(node); + case _var: case _let: case _const: return parseVarStatement(node, starttype.keyword); + case _while: return parseWhileStatement(node); + case _with: return parseWithStatement(node); + case _braceL: return parseBlock(); // no point creating a function for this + case _semi: return parseEmptyStatement(node); // If the statement does not start with a statement keyword or a // brace, it's an ExpressionStatement or LabeledStatement. We // simply start parsing an expression, and afterwards, if the // next token is a colon and the expression was a simple // Identifier node, we switch to interpreting it as a label. - default: var maybeName = tokVal, expr = parseExpression(); - if (starttype === _name && expr.type === "Identifier" && eat(_colon)) { - for (var i = 0; i < labels.length; ++i) - if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared"); - var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null; - labels.push({name: maybeName, kind: kind}); - node.body = parseStatement(); - labels.pop(); - node.label = expr; - return finishNode(node, "LabeledStatement"); - } else { - node.expression = expr; - semicolon(); - return finishNode(node, "ExpressionStatement"); + if (starttype === _name && expr.type === "Identifier" && eat(_colon)) + return parseLabeledStatement(node, maybeName, expr); + else return parseExpressionStatement(node, expr); + } + } + + function parseBreakContinueStatement(node, keyword) { + var isBreak = keyword == "break"; + next(); + if (eat(_semi) || canInsertSemicolon()) node.label = null; + else if (tokType !== _name) unexpected(); + else { + node.label = parseIdent(); + semicolon(); + } + + // Verify that there is an actual destination to break or + // continue to. + for (var i = 0; i < labels.length; ++i) { + var lab = labels[i]; + if (node.label == null || lab.name === node.label.name) { + if (lab.kind != null && (isBreak || lab.kind === "loop")) break; + if (node.label && isBreak) break; } } + if (i === labels.length) raise(node.start, "Unsyntactic " + keyword); + return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); + } + + function parseDebuggerStatement(node) { + next(); + semicolon(); + return finishNode(node, "DebuggerStatement"); + } + + function parseDoStatement(node) { + next(); + labels.push(loopLabel); + node.body = parseStatement(); + labels.pop(); + expect(_while); + node.test = parseParenExpression(); + semicolon(); + return finishNode(node, "DoWhileStatement"); + } + + // Disambiguating between a `for` and a `for`/`in` loop is + // non-trivial. Basically, we have to parse the init `var` + // statement or expression, disallowing the `in` operator (see + // the second parameter to `parseExpression`), and then check + // whether the next token is `in`. When there is no init part + // (semicolon immediately after the opening parenthesis), it is + // a regular `for` loop. + + function parseForStatement(node) { + next(); + labels.push(loopLabel); + expect(_parenL); + if (tokType === _semi) return parseFor(node, null); + if (tokType === _var || tokType === _let) { + var init = startNode(), varKind = tokType.keyword; + next(); + parseVar(init, true, varKind); + finishNode(init, "VariableDeclaration"); + if (init.declarations.length === 1 && eat(_in)) + return parseForIn(node, init); + return parseFor(node, init); + } + var init = parseExpression(false, true); + if (eat(_in)) {checkLVal(init); return parseForIn(node, init);} + return parseFor(node, init); + } + + function parseFunctionStatement(node) { + next(); + return parseFunction(node, true); + } + + function parseIfStatement(node) { + next(); + node.test = parseParenExpression(); + node.consequent = parseStatement(); + node.alternate = eat(_else) ? parseStatement() : null; + return finishNode(node, "IfStatement"); + } + + function parseReturnStatement(node) { + if (!inFunction && !options.allowReturnOutsideFunction) + raise(tokStart, "'return' outside of function"); + next(); + + // In `return` (and `break`/`continue`), the keywords with + // optional arguments, we eagerly look for a semicolon or the + // possibility to insert one. + + if (eat(_semi) || canInsertSemicolon()) node.argument = null; + else { node.argument = parseExpression(); semicolon(); } + return finishNode(node, "ReturnStatement"); + } + + function parseSwitchStatement(node) { + next(); + node.discriminant = parseParenExpression(); + node.cases = []; + expect(_braceL); + labels.push(switchLabel); + + // Statements under must be grouped (by label) in SwitchCase + // nodes. `cur` is used to keep the node that we are currently + // adding statements to. + + for (var cur, sawDefault; tokType != _braceR;) { + if (tokType === _case || tokType === _default) { + var isCase = tokType === _case; + if (cur) finishNode(cur, "SwitchCase"); + node.cases.push(cur = startNode()); + cur.consequent = []; + next(); + if (isCase) cur.test = parseExpression(); + else { + if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true; + cur.test = null; + } + expect(_colon); + } else { + if (!cur) unexpected(); + cur.consequent.push(parseStatement()); + } + } + if (cur) finishNode(cur, "SwitchCase"); + next(); // Closing brace + labels.pop(); + return finishNode(node, "SwitchStatement"); + } + + function parseThrowStatement(node) { + next(); + if (newline.test(input.slice(lastEnd, tokStart))) + raise(lastEnd, "Illegal newline after throw"); + node.argument = parseExpression(); + semicolon(); + return finishNode(node, "ThrowStatement");next(); + if (newline.test(input.slice(lastEnd, tokStart))) + raise(lastEnd, "Illegal newline after throw"); + node.argument = parseExpression(); + semicolon(); + return finishNode(node, "ThrowStatement"); + } + + function parseTryStatement(node) { + next(); + node.block = parseBlock(); + node.handler = null; + if (tokType === _catch) { + var clause = startNode(); + next(); + expect(_parenL); + clause.param = parseIdent(); + if (strict && isStrictBadIdWord(clause.param.name)) + raise(clause.param.start, "Binding " + clause.param.name + " in strict mode"); + expect(_parenR); + clause.guard = null; + clause.body = parseBlock(); + node.handler = finishNode(clause, "CatchClause"); + } + node.guardedHandlers = empty; + node.finalizer = eat(_finally) ? parseBlock() : null; + if (!node.handler && !node.finalizer) + raise(node.start, "Missing catch or finally clause"); + return finishNode(node, "TryStatement"); + } + + function parseVarStatement(node, kind) { + next(); + parseVar(node, false, kind); + semicolon(); + return finishNode(node, "VariableDeclaration"); + } + + function parseWhileStatement(node) { + next(); + node.test = parseParenExpression(); + labels.push(loopLabel); + node.body = parseStatement(); + labels.pop(); + return finishNode(node, "WhileStatement"); + } + + function parseWithStatement(node) { + if (strict) raise(tokStart, "'with' in strict mode"); + next(); + node.object = parseParenExpression(); + node.body = parseStatement(); + return finishNode(node, "WithStatement"); + } + + function parseEmptyStatement(node) { + next(); + return finishNode(node, "EmptyStatement"); + } + + function parseLabeledStatement(node, maybeName, expr) { + for (var i = 0; i < labels.length; ++i) + if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared"); + var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null; + labels.push({name: maybeName, kind: kind}); + node.body = parseStatement(); + labels.pop(); + node.label = expr; + return finishNode(node, "LabeledStatement"); + } + + function parseExpressionStatement(node, expr) { + node.expression = expr; + semicolon(); + return finishNode(node, "ExpressionStatement"); } // Used for constructs like `switch` and `if` that insist on diff --git a/index.html b/index.html index 187bf39aaf..1092860041 100644 --- a/index.html +++ b/index.html @@ -832,192 +832,229 @@ does not help.
Most types of statements are recognized by the keyword they start with. Many are trivial to parse, some require a bit of complexity.
switch (starttype) {
- case _break: case _continue:
- next();
- var isBreak = starttype === _break;
- if (eat(_semi) || canInsertSemicolon()) node.label = null;
- else if (tokType !== _name) unexpected();
- else {
- node.label = parseIdent();
- semicolon();
- }Verify that there is an actual destination to break or -continue to.
for (var i = 0; i < labels.length; ++i) {
- var lab = labels[i];
- if (node.label == null || lab.name === node.label.name) {
- if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
- if (node.label && isBreak) break;
- }
- }
- if (i === labels.length) raise(node.start, "Unsyntactic " + starttype.keyword);
- return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
-
- case _debugger:
- next();
- semicolon();
- return finishNode(node, "DebuggerStatement");
-
- case _do:
- next();
- labels.push(loopLabel);
- node.body = parseStatement();
- labels.pop();
- expect(_while);
- node.test = parseParenExpression();
- semicolon();
- return finishNode(node, "DoWhileStatement");Disambiguating between a for and a for/in loop is
-non-trivial. Basically, we have to parse the init var
-statement or expression, disallowing the in operator (see
-the second parameter to parseExpression), and then check
-whether the next token is in. When there is no init part
-(semicolon immediately after the opening parenthesis), it is
-a regular for loop.
case _for:
- next();
- labels.push(loopLabel);
- expect(_parenL);
- if (tokType === _semi) return parseFor(node, null);
- if (tokType === _var || tokType === _let) {
- var varKind = tokType.keyword;
- var init = startNode();
- next();
- parseVar(init, true, varKind);
- finishNode(init, "VariableDeclaration");
- if (init.declarations.length === 1 && eat(_in))
- return parseForIn(node, init);
- return parseFor(node, init);
- }
- var init = parseExpression(false, true);
- if (eat(_in)) {checkLVal(init); return parseForIn(node, init);}
- return parseFor(node, init);
-
- case _function:
- next();
- return parseFunction(node, true);
-
- case _if:
- next();
- node.test = parseParenExpression();
- node.consequent = parseStatement();
- node.alternate = eat(_else) ? parseStatement() : null;
- return finishNode(node, "IfStatement");
-
- case _return:
- if (!inFunction && !options.allowReturnOutsideFunction)
- raise(tokStart, "'return' outside of function");
- next();In return (and break/continue), the keywords with
-optional arguments, we eagerly look for a semicolon or the
-possibility to insert one.
if (eat(_semi) || canInsertSemicolon()) node.argument = null;
- else { node.argument = parseExpression(); semicolon(); }
- return finishNode(node, "ReturnStatement");
-
- case _switch:
- next();
- node.discriminant = parseParenExpression();
- node.cases = [];
- expect(_braceL);
- labels.push(switchLabel);Statements under must be grouped (by label) in SwitchCase
-nodes. cur is used to keep the node that we are currently
-adding statements to.
for (var cur, sawDefault; tokType != _braceR;) {
- if (tokType === _case || tokType === _default) {
- var isCase = tokType === _case;
- if (cur) finishNode(cur, "SwitchCase");
- node.cases.push(cur = startNode());
- cur.consequent = [];
- next();
- if (isCase) cur.test = parseExpression();
- else {
- if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true;
- cur.test = null;
- }
- expect(_colon);
- } else {
- if (!cur) unexpected();
- cur.consequent.push(parseStatement());
- }
- }
- if (cur) finishNode(cur, "SwitchCase");
- next(); // Closing brace
- labels.pop();
- return finishNode(node, "SwitchStatement");
-
- case _throw:
- next();
- if (newline.test(input.slice(lastEnd, tokStart)))
- raise(lastEnd, "Illegal newline after throw");
- node.argument = parseExpression();
- semicolon();
- return finishNode(node, "ThrowStatement");
-
- case _try:
- next();
- node.block = parseBlock();
- node.handler = null;
- if (tokType === _catch) {
- var clause = startNode();
- next();
- expect(_parenL);
- clause.param = parseIdent();
- if (strict && isStrictBadIdWord(clause.param.name))
- raise(clause.param.start, "Binding " + clause.param.name + " in strict mode");
- expect(_parenR);
- clause.guard = null;
- clause.body = parseBlock();
- node.handler = finishNode(clause, "CatchClause");
- }
- node.guardedHandlers = empty;
- node.finalizer = eat(_finally) ? parseBlock() : null;
- if (!node.handler && !node.finalizer)
- raise(node.start, "Missing catch or finally clause");
- return finishNode(node, "TryStatement");
-
- case _const:
- case _let:
- case _var:
- next();
- parseVar(node, false, starttype.keyword);
- semicolon();
- return finishNode(node, "VariableDeclaration");
-
- case _while:
- next();
- node.test = parseParenExpression();
- labels.push(loopLabel);
- node.body = parseStatement();
- labels.pop();
- return finishNode(node, "WhileStatement");
-
- case _with:
- if (strict) raise(tokStart, "'with' in strict mode");
- next();
- node.object = parseParenExpression();
- node.body = parseStatement();
- return finishNode(node, "WithStatement");
-
- case _braceL:
- return parseBlock();
-
- case _semi:
- next();
- return finishNode(node, "EmptyStatement");If the statement does not start with a statement keyword or a + case _break: case _continue: return parseBreakContinueStatement(node, starttype.keyword); + case _debugger: return parseDebuggerStatement(node); + case _do: return parseDoStatement(node); + case _for: return parseForStatement(node); + case _function: return parseFunctionStatement(node); + case _if: return parseIfStatement(node); + case _return: return parseReturnStatement(node); + case _switch: return parseSwitchStatement(node); + case _throw: return parseThrowStatement(node); + case _try: return parseTryStatement(node); + case _var: case _let: case _const: return parseVarStatement(node, starttype.keyword); + case _while: return parseWhileStatement(node); + case _with: return parseWithStatement(node); + case _braceL: return parseBlock(); // no point creating a function for this + case _semi: return parseEmptyStatement(node);
If the statement does not start with a statement keyword or a brace, it's an ExpressionStatement or LabeledStatement. We simply start parsing an expression, and afterwards, if the next token is a colon and the expression was a simple Identifier node, we switch to interpreting it as a label.
default:
var maybeName = tokVal, expr = parseExpression();
- if (starttype === _name && expr.type === "Identifier" && eat(_colon)) {
- for (var i = 0; i < labels.length; ++i)
- if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared");
- var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null;
- labels.push({name: maybeName, kind: kind});
- node.body = parseStatement();
- labels.pop();
- node.label = expr;
- return finishNode(node, "LabeledStatement");
- } else {
- node.expression = expr;
- semicolon();
- return finishNode(node, "ExpressionStatement");
+ if (starttype === _name && expr.type === "Identifier" && eat(_colon))
+ return parseLabeledStatement(node, maybeName, expr);
+ else return parseExpressionStatement(node, expr);
+ }
+ }
+
+ function parseBreakContinueStatement(node, keyword) {
+ var isBreak = keyword == "break";
+ next();
+ if (eat(_semi) || canInsertSemicolon()) node.label = null;
+ else if (tokType !== _name) unexpected();
+ else {
+ node.label = parseIdent();
+ semicolon();
+ }Verify that there is an actual destination to break or +continue to.
for (var i = 0; i < labels.length; ++i) {
+ var lab = labels[i];
+ if (node.label == null || lab.name === node.label.name) {
+ if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
+ if (node.label && isBreak) break;
}
}
+ if (i === labels.length) raise(node.start, "Unsyntactic " + keyword);
+ return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+ }
+
+ function parseDebuggerStatement(node) {
+ next();
+ semicolon();
+ return finishNode(node, "DebuggerStatement");
+ }
+
+ function parseDoStatement(node) {
+ next();
+ labels.push(loopLabel);
+ node.body = parseStatement();
+ labels.pop();
+ expect(_while);
+ node.test = parseParenExpression();
+ semicolon();
+ return finishNode(node, "DoWhileStatement");
+ }
+ Disambiguating between a for and a for/in loop is
+non-trivial. Basically, we have to parse the init var
+statement or expression, disallowing the in operator (see
+the second parameter to parseExpression), and then check
+whether the next token is in. When there is no init part
+(semicolon immediately after the opening parenthesis), it is
+a regular for loop.
+ function parseForStatement(node) {
+ next();
+ labels.push(loopLabel);
+ expect(_parenL);
+ if (tokType === _semi) return parseFor(node, null);
+ if (tokType === _var || tokType === _let) {
+ var init = startNode(), varKind = tokType.keyword;
+ next();
+ parseVar(init, true, varKind);
+ finishNode(init, "VariableDeclaration");
+ if (init.declarations.length === 1 && eat(_in))
+ return parseForIn(node, init);
+ return parseFor(node, init);
+ }
+ var init = parseExpression(false, true);
+ if (eat(_in)) {checkLVal(init); return parseForIn(node, init);}
+ return parseFor(node, init);
+ }
+
+ function parseFunctionStatement(node) {
+ next();
+ return parseFunction(node, true);
+ }
+
+ function parseIfStatement(node) {
+ next();
+ node.test = parseParenExpression();
+ node.consequent = parseStatement();
+ node.alternate = eat(_else) ? parseStatement() : null;
+ return finishNode(node, "IfStatement");
+ }
+
+ function parseReturnStatement(node) {
+ if (!inFunction && !options.allowReturnOutsideFunction)
+ raise(tokStart, "'return' outside of function");
+ next();In return (and break/continue), the keywords with
+optional arguments, we eagerly look for a semicolon or the
+possibility to insert one.
if (eat(_semi) || canInsertSemicolon()) node.argument = null;
+ else { node.argument = parseExpression(); semicolon(); }
+ return finishNode(node, "ReturnStatement");
+ }
+
+ function parseSwitchStatement(node) {
+ next();
+ node.discriminant = parseParenExpression();
+ node.cases = [];
+ expect(_braceL);
+ labels.push(switchLabel);Statements under must be grouped (by label) in SwitchCase
+nodes. cur is used to keep the node that we are currently
+adding statements to.
for (var cur, sawDefault; tokType != _braceR;) {
+ if (tokType === _case || tokType === _default) {
+ var isCase = tokType === _case;
+ if (cur) finishNode(cur, "SwitchCase");
+ node.cases.push(cur = startNode());
+ cur.consequent = [];
+ next();
+ if (isCase) cur.test = parseExpression();
+ else {
+ if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true;
+ cur.test = null;
+ }
+ expect(_colon);
+ } else {
+ if (!cur) unexpected();
+ cur.consequent.push(parseStatement());
+ }
+ }
+ if (cur) finishNode(cur, "SwitchCase");
+ next(); // Closing brace
+ labels.pop();
+ return finishNode(node, "SwitchStatement");
+ }
+
+ function parseThrowStatement(node) {
+ next();
+ if (newline.test(input.slice(lastEnd, tokStart)))
+ raise(lastEnd, "Illegal newline after throw");
+ node.argument = parseExpression();
+ semicolon();
+ return finishNode(node, "ThrowStatement");next();
+ if (newline.test(input.slice(lastEnd, tokStart)))
+ raise(lastEnd, "Illegal newline after throw");
+ node.argument = parseExpression();
+ semicolon();
+ return finishNode(node, "ThrowStatement");
+ }
+
+ function parseTryStatement(node) {
+ next();
+ node.block = parseBlock();
+ node.handler = null;
+ if (tokType === _catch) {
+ var clause = startNode();
+ next();
+ expect(_parenL);
+ clause.param = parseIdent();
+ if (strict && isStrictBadIdWord(clause.param.name))
+ raise(clause.param.start, "Binding " + clause.param.name + " in strict mode");
+ expect(_parenR);
+ clause.guard = null;
+ clause.body = parseBlock();
+ node.handler = finishNode(clause, "CatchClause");
+ }
+ node.guardedHandlers = empty;
+ node.finalizer = eat(_finally) ? parseBlock() : null;
+ if (!node.handler && !node.finalizer)
+ raise(node.start, "Missing catch or finally clause");
+ return finishNode(node, "TryStatement");
+ }
+
+ function parseVarStatement(node, kind) {
+ next();
+ parseVar(node, false, kind);
+ semicolon();
+ return finishNode(node, "VariableDeclaration");
+ }
+
+ function parseWhileStatement(node) {
+ next();
+ node.test = parseParenExpression();
+ labels.push(loopLabel);
+ node.body = parseStatement();
+ labels.pop();
+ return finishNode(node, "WhileStatement");
+ }
+
+ function parseWithStatement(node) {
+ if (strict) raise(tokStart, "'with' in strict mode");
+ next();
+ node.object = parseParenExpression();
+ node.body = parseStatement();
+ return finishNode(node, "WithStatement");
+ }
+
+ function parseEmptyStatement(node) {
+ next();
+ return finishNode(node, "EmptyStatement");
+ }
+
+ function parseLabeledStatement(node, maybeName, expr) {
+ for (var i = 0; i < labels.length; ++i)
+ if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared");
+ var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null;
+ labels.push({name: maybeName, kind: kind});
+ node.body = parseStatement();
+ labels.pop();
+ node.label = expr;
+ return finishNode(node, "LabeledStatement");
+ }
+
+ function parseExpressionStatement(node, expr) {
+ node.expression = expr;
+ semicolon();
+ return finishNode(node, "ExpressionStatement");
}Used for constructs like switch and if that insist on
parentheses around their expression.
function parseParenExpression() {
expect(_parenL);