function parseStatement() {
    if (tokType === _slash || tokType === _assign && tokVal === "/=") {
      readToken(true);
    }

    var starttype = tokType;
    var node = startNode();

    // 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) {
          var init = startNode();
          next();
          parseVar(init, true);
          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);
          // JS-Interpreter change:
          // Obsolete unused property; commenting out.
          // -- Neil Fraser, January 2023.
          // clause.guard = null;
          clause.body = parseBlock();
          node.handler = finishNode(clause, "CatchClause");
        }
        // JS-Interpreter change:
        // Obsolete unused property; commenting out.
        // -- Neil Fraser, January 2023.
        // 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 _var:
        next();
        parseVar(node);
        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
      // 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;
        var 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");
        }
    }
  }
Parse a single statement.

If expecting a statement and finding a slash operator, parse a
regular expression literal. This is to handle cases like
`if (foo) /blah/.exec(foo);`, where looking at the previous token
does not help.

@returns {!node_t}