function() {
    switch (this.token) {
      case "{":
        return this.read_code_block(false);

      case this.tok.T_IF:
        return this.read_if();

      case this.tok.T_SWITCH:
        return this.read_switch();

      case this.tok.T_FOR:
        return this.read_for();

      case this.tok.T_FOREACH:
        return this.read_foreach();

      case this.tok.T_WHILE:
        return this.read_while();

      case this.tok.T_DO:
        return this.read_do();

      case this.tok.T_COMMENT:
        return this.read_comment();

      case this.tok.T_DOC_COMMENT:
        return this.read_doc_comment();

      case this.tok.T_RETURN: {
        const result = this.node("return");
        let expr = null;
        if (!this.next().is("EOS")) {
          expr = this.read_expr();
        }
        this.expectEndOfStatement();
        return result(expr);
      }

      // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L429
      case this.tok.T_BREAK:
      case this.tok.T_CONTINUE: {
        const result = this.node(
          this.token === this.tok.T_CONTINUE ? "continue" : "break"
        );
        let level = null;
        this.next(); // look ahead
        if (this.token !== ";") {
          level = this.read_expr();
        }
        this.expectEndOfStatement();
        return result(level);
      }

      case this.tok.T_GLOBAL: {
        const result = this.node("global");
        const items = this.next().read_list(this.read_simple_variable, ",");
        this.expectEndOfStatement();
        return result(items);
      }

      case this.tok.T_STATIC: {
        const current = [this.token, this.lexer.getState()];
        const result = this.node();
        if (this.next().token === this.tok.T_DOUBLE_COLON) {
          // static keyword for a class
          this.lexer.tokens.push(current);
          const expr = this.next().read_expr();
          this.expectEndOfStatement(expr);
          return result("expressionstatement", expr);
        }
        if (this.token === this.tok.T_FUNCTION) {
          return this.read_function(true, [0, 1, 0]);
        }
        const items = this.read_variable_declarations();
        this.expectEndOfStatement();
        return result("static", items);
      }

      case this.tok.T_ECHO: {
        const result = this.node("echo");
        const text = this.text();
        const shortForm = text === "<?=" || text === "<%=";
        const expressions = this.next().read_list(this.read_expr, ",");
        this.expectEndOfStatement();
        return result(expressions, shortForm);
      }

      case this.tok.T_INLINE_HTML: {
        const value = this.text();
        let prevChar =
          this.lexer.yylloc.first_offset > 0
            ? this.lexer._input[this.lexer.yylloc.first_offset - 1]
            : null;
        const fixFirstLine = prevChar === "\r" || prevChar === "\n";
        // revert back the first stripped line
        if (fixFirstLine) {
          if (
            prevChar === "\n" &&
            this.lexer.yylloc.first_offset > 1 &&
            this.lexer._input[this.lexer.yylloc.first_offset - 2] === "\r"
          ) {
            prevChar = "\r\n";
          }
        }
        const result = this.node("inline");
        this.next();
        return result(value, fixFirstLine ? prevChar + value : value);
      }

      case this.tok.T_UNSET: {
        const result = this.node("unset");
        this.next().expect("(") && this.next();
        const variables = this.read_list(this.read_variable, ",");
        this.expect(")") && this.next();
        this.expect(";") && this.next();
        return result(variables);
      }

      case this.tok.T_DECLARE: {
        const result = this.node("declare");
        const body = [];
        let mode;
        this.next().expect("(") && this.next();
        const directives = this.read_declare_list();
        this.expect(")") && this.next();
        if (this.token === ":") {
          this.next();
          while (
            this.token != this.EOF &&
            this.token !== this.tok.T_ENDDECLARE
          ) {
            // @todo : check declare_statement from php / not valid
            body.push(this.read_top_statement());
          }
          this.expect(this.tok.T_ENDDECLARE) && this.next();
          this.expectEndOfStatement();
          mode = this.ast.declare.MODE_SHORT;
        } else if (this.token === "{") {
          this.next();
          while (this.token != this.EOF && this.token !== "}") {
            // @todo : check declare_statement from php / not valid
            body.push(this.read_top_statement());
          }
          this.expect("}") && this.next();
          mode = this.ast.declare.MODE_BLOCK;
        } else {
          this.expect(";") && this.next();
          mode = this.ast.declare.MODE_NONE;
        }
        return result(directives, body, mode);
      }

      case this.tok.T_TRY:
        return this.read_try();

      case this.tok.T_THROW: {
        const result = this.node("throw");
        const expr = this.next().read_expr();
        this.expectEndOfStatement();
        return result(expr);
      }

      // ignore this (extra ponctuation)
      case ";": {
        this.next();
        return null;
      }

      case this.tok.T_STRING: {
        const result = this.node();
        const current = [this.token, this.lexer.getState()];
        const label = this.text();
        // AST : https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L457
        if (this.next().token === ":") {
          this.next();
          return result("label", label);
        }

        // default fallback expr / T_STRING '::' (etc...)
        result.destroy();
        this.lexer.tokens.push(current);
        const statement = this.node("expressionstatement");
        const expr = this.next().read_expr();
        this.expectEndOfStatement(expr);
        return statement(expr);
      }

      case this.tok.T_GOTO: {
        const result = this.node("goto");
        let label = null;
        if (this.next().expect(this.tok.T_STRING)) {
          label = this.text();
          this.next().expectEndOfStatement();
        }
        return result(label);
      }

      default: {
        // default fallback expr
        const statement = this.node("expressionstatement");
        const expr = this.read_expr();
        this.expectEndOfStatement(expr);
        return statement(expr);
      }
    }
  }
function() {
    switch (this.token) {
      case "{":
        return this.read_code_block(false);

      case this.tok.T_IF:
        return this.read_if();

      case this.tok.T_SWITCH:
        return this.read_switch();

      case this.tok.T_FOR:
        return this.read_for();

      case this.tok.T_FOREACH:
        return this.read_foreach();

      case this.tok.T_WHILE:
        return this.read_while();

      case this.tok.T_DO:
        return this.read_do();

      case this.tok.T_COMMENT:
        return this.read_comment();

      case this.tok.T_DOC_COMMENT:
        return this.read_doc_comment();

      case this.tok.T_RETURN: {
        const result = this.node("return");
        let expr = null;
        if (!this.next().is("EOS")) {
          expr = this.read_expr();
        }
        this.expectEndOfStatement();
        return result(expr);
      }

      
      case this.tok.T_BREAK:
      case this.tok.T_CONTINUE: {
        const result = this.node(
          this.token === this.tok.T_CONTINUE ? "continue" : "break"
        );
        let level = null;
        this.next(); 
        if (this.token !== ";") {
          level = this.read_expr();
        }
        this.expectEndOfStatement();
        return result(level);
      }

      case this.tok.T_GLOBAL: {
        const result = this.node("global");
        const items = this.next().read_list(this.read_simple_variable, ",");
        this.expectEndOfStatement();
        return result(items);
      }

      case this.tok.T_STATIC: {
        const current = [this.token, this.lexer.getState()];
        const result = this.node();
        if (this.next().token === this.tok.T_DOUBLE_COLON) {
          
          this.lexer.tokens.push(current);
          const expr = this.next().read_expr();
          this.expectEndOfStatement(expr);
          return result("expressionstatement", expr);
        }
        if (this.token === this.tok.T_FUNCTION) {
          return this.read_function(true, [0, 1, 0]);
        }
        const items = this.read_variable_declarations();
        this.expectEndOfStatement();
        return result("static", items);
      }

      case this.tok.T_ECHO: {
        const result = this.node("echo");
        const text = this.text();
        const shortForm = text === "<?=" || text === "<%=";
        const expressions = this.next().read_list(this.read_expr, ",");
        this.expectEndOfStatement();
        return result(expressions, shortForm);
      }

      case this.tok.T_INLINE_HTML: {
        const value = this.text();
        let prevChar =
          this.lexer.yylloc.first_offset > 0
            ? this.lexer._input[this.lexer.yylloc.first_offset - 1]
            : null;
        const fixFirstLine = prevChar === "\r" || prevChar === "\n";
        
        if (fixFirstLine) {
          if (
            prevChar === "\n" &&
            this.lexer.yylloc.first_offset > 1 &&
            this.lexer._input[this.lexer.yylloc.first_offset - 2] === "\r"
          ) {
            prevChar = "\r\n";
          }
        }
        const result = this.node("inline");
        this.next();
        return result(value, fixFirstLine ? prevChar + value : value);
      }

      case this.tok.T_UNSET: {
        const result = this.node("unset");
        this.next().expect("(") && this.next();
        const variables = this.read_list(this.read_variable, ",");
        this.expect(")") && this.next();
        this.expect(";") && this.next();
        return result(variables);
      }

      case this.tok.T_DECLARE: {
        const result = this.node("declare");
        const body = [];
        let mode;
        this.next().expect("(") && this.next();
        const directives = this.read_declare_list();
        this.expect(")") && this.next();
        if (this.token === ":") {
          this.next();
          while (
            this.token != this.EOF &&
            this.token !== this.tok.T_ENDDECLARE
          ) {
            
            body.push(this.read_top_statement());
          }
          this.expect(this.tok.T_ENDDECLARE) && this.next();
          this.expectEndOfStatement();
          mode = this.ast.declare.MODE_SHORT;
        } else if (this.token === "{") {
          this.next();
          while (this.token != this.EOF && this.token !== "}") {
            
            body.push(this.read_top_statement());
          }
          this.expect("}") && this.next();
          mode = this.ast.declare.MODE_BLOCK;
        } else {
          this.expect(";") && this.next();
          mode = this.ast.declare.MODE_NONE;
        }
        return result(directives, body, mode);
      }

      case this.tok.T_TRY:
        return this.read_try();

      case this.tok.T_THROW: {
        const result = this.node("throw");
        const expr = this.next().read_expr();
        this.expectEndOfStatement();
        return result(expr);
      }

      
      case ";": {
        this.next();
        return null;
      }

      case this.tok.T_STRING: {
        const result = this.node();
        const current = [this.token, this.lexer.getState()];
        const label = this.text();
        
        if (this.next().token === ":") {
          this.next();
          return result("label", label);
        }

        
        result.destroy();
        this.lexer.tokens.push(current);
        const statement = this.node("expressionstatement");
        const expr = this.next().read_expr();
        this.expectEndOfStatement(expr);
        return statement(expr);
      }

      case this.tok.T_GOTO: {
        const result = this.node("goto");
        let label = null;
        if (this.next().expect(this.tok.T_STRING)) {
          label = this.text();
          this.next().expectEndOfStatement();
        }
        return result(label);
      }

      default: {
        
        const statement = this.node("expressionstatement");
        const expr = this.read_expr();
        this.expectEndOfStatement(expr);
        return statement(expr);
      }
    }
  }
function ( ) { switch ( this . token ) { case "{" : return this . read_code_block ( false ) ; case this . tok . T_IF : return this . read_if ( ) ; case this . tok . T_SWITCH : return this . read_switch ( ) ; case this . tok . T_FOR : return this . read_for ( ) ; case this . tok . T_FOREACH : return this . read_foreach ( ) ; case this . tok . T_WHILE : return this . read_while ( ) ; case this . tok . T_DO : return this . read_do ( ) ; case this . tok . T_COMMENT : return this . read_comment ( ) ; case this . tok . T_DOC_COMMENT : return this . read_doc_comment ( ) ; case this . tok . T_RETURN : { const result = this . node ( "return" ) ; let expr = null ; if ( ! this . next ( ) . is ( "EOS" ) ) { expr = this . read_expr ( ) ; } this . expectEndOfStatement ( ) ; return result ( expr ) ; } // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L429 case this . tok . T_BREAK : case this . tok . T_CONTINUE : { const result = this . node ( this . token === this . tok . T_CONTINUE ? "continue" : "break" ) ; let level = null ; this . next ( ) ; // look ahead if ( this . token !== ";" ) { level = this . read_expr ( ) ; } this . expectEndOfStatement ( ) ; return result ( level ) ; } case this . tok . T_GLOBAL : { const result = this . node ( "global" ) ; const items = this . next ( ) . read_list ( this . read_simple_variable , "," ) ; this . expectEndOfStatement ( ) ; return result ( items ) ; } case this . tok . T_STATIC : { const current = [ this . token , this . lexer . getState ( ) ] ; const result = this . node ( ) ; if ( this . next ( ) . token === this . tok . T_DOUBLE_COLON ) { // static keyword for a class this . lexer . tokens . push ( current ) ; const expr = this . next ( ) . read_expr ( ) ; this . expectEndOfStatement ( expr ) ; return result ( "expressionstatement" , expr ) ; } if ( this . token === this . tok . T_FUNCTION ) { return this . read_function ( true , [ 0 , 1 , 0 ] ) ; } const items = this . read_variable_declarations ( ) ; this . expectEndOfStatement ( ) ; return result ( "static" , items ) ; } case this . tok . T_ECHO : { const result = this . node ( "echo" ) ; const text = this . text ( ) ; const shortForm = text === "<?=" || text === "<%=" ; const expressions = this . next ( ) . read_list ( this . read_expr , "," ) ; this . expectEndOfStatement ( ) ; return result ( expressions , shortForm ) ; } case this . tok . T_INLINE_HTML : { const value = this . text ( ) ; let prevChar = this . lexer . yylloc . first_offset > 0 ? this . lexer . _input [ this . lexer . yylloc . first_offset - 1 ] : null ; const fixFirstLine = prevChar === "\r" || prevChar === "\n" ; // revert back the first stripped line if ( fixFirstLine ) { if ( prevChar === "\n" && this . lexer . yylloc . first_offset > 1 && this . lexer . _input [ this . lexer . yylloc . first_offset - 2 ] === "\r" ) { prevChar = "\r\n" ; } } const result = this . node ( "inline" ) ; this . next ( ) ; return result ( value , fixFirstLine ? prevChar + value : value ) ; } case this . tok . T_UNSET : { const result = this . node ( "unset" ) ; this . next ( ) . expect ( "(" ) && this . next ( ) ; const variables = this . read_list ( this . read_variable , "," ) ; this . expect ( ")" ) && this . next ( ) ; this . expect ( ";" ) && this . next ( ) ; return result ( variables ) ; } case this . tok . T_DECLARE : { const result = this . node ( "declare" ) ; const body = [ ] ; let mode ; this . next ( ) . expect ( "(" ) && this . next ( ) ; const directives = this . read_declare_list ( ) ; this . expect ( ")" ) && this . next ( ) ; if ( this . token === ":" ) { this . next ( ) ; while ( this . token != this . EOF && this . token !== this . tok . T_ENDDECLARE ) { // @todo : check declare_statement from php / not valid body . push ( this . read_top_statement ( ) ) ; } this . expect ( this . tok . T_ENDDECLARE ) && this . next ( ) ; this . expectEndOfStatement ( ) ; mode = this . ast . declare . MODE_SHORT ; } else if ( this . token === "{" ) { this . next ( ) ; while ( this . token != this . EOF && this . token !== "}" ) { // @todo : check declare_statement from php / not valid body . push ( this . read_top_statement ( ) ) ; } this . expect ( "}" ) && this . next ( ) ; mode = this . ast . declare . MODE_BLOCK ; } else { this . expect ( ";" ) && this . next ( ) ; mode = this . ast . declare . MODE_NONE ; } return result ( directives , body , mode ) ; } case this . tok . T_TRY : return this . read_try ( ) ; case this . tok . T_THROW : { const result = this . node ( "throw" ) ; const expr = this . next ( ) . read_expr ( ) ; this . expectEndOfStatement ( ) ; return result ( expr ) ; } // ignore this (extra ponctuation) case ";" : { this . next ( ) ; return null ; } case this . tok . T_STRING : { const result = this . node ( ) ; const current = [ this . token , this . lexer . getState ( ) ] ; const label = this . text ( ) ; // AST : https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L457 if ( this . next ( ) . token === ":" ) { this . next ( ) ; return result ( "label" , label ) ; } // default fallback expr / T_STRING '::' (etc...) result . destroy ( ) ; this . lexer . tokens . push ( current ) ; const statement = this . node ( "expressionstatement" ) ; const expr = this . next ( ) . read_expr ( ) ; this . expectEndOfStatement ( expr ) ; return statement ( expr ) ; } case this . tok . T_GOTO : { const result = this . node ( "goto" ) ; let label = null ; if ( this . next ( ) . expect ( this . tok . T_STRING ) ) { label = this . text ( ) ; this . next ( ) . expectEndOfStatement ( ) ; } return result ( label ) ; } default : { // default fallback expr const statement = this . node ( "expressionstatement" ) ; const expr = this . read_expr ( ) ; this . expectEndOfStatement ( expr ) ; return statement ( expr ) ; } } }
Reads statements
Reads statements