function() {
    let result, expr;
    if (this.token === "@") return this.node("silent")(this.next().read_expr());
    if (this.token === "+")
      return this.node("unary")("+", this.next().read_expr());
    if (this.token === "-")
      return this.node("unary")("-", this.next().read_expr());
    if (this.token === "!")
      return this.node("unary")("!", this.next().read_expr());
    if (this.token === "~")
      return this.node("unary")("~", this.next().read_expr());

    if (this.token === "(") {
      expr = this.next().read_expr();
      expr.parenthesizedExpression = true;
      this.expect(")") && this.next();
      return this.handleDereferencable(expr);
    }

    if (this.token === "`") {
      // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1048
      return this.read_encapsed_string("`");
    }

    if (this.token === this.tok.T_LIST) {
      let assign = null;
      const isInner = this.innerList;
      result = this.node("list");
      if (!isInner) {
        assign = this.node("assign");
      }
      if (this.next().expect("(")) {
        this.next();
      }

      if (!this.innerList) this.innerList = true;

      // reads inner items
      const assignList = this.read_array_pair_list(false);
      if (this.expect(")")) {
        this.next();
      }

      // check if contains at least one assignment statement
      let hasItem = false;
      for (let i = 0; i < assignList.length; i++) {
        if (assignList[i] !== null) {
          hasItem = true;
          break;
        }
      }
      if (!hasItem) {
        this.raiseError(
          "Fatal Error :  Cannot use empty list on line " +
            this.lexer.yylloc.first_line
        );
      }

      // handles the node resolution
      if (!isInner) {
        this.innerList = false;
        if (this.expect("=")) {
          return assign(
            result(assignList, false),
            this.next().read_expr(),
            "="
          );
        } else {
          // error fallback : list($a, $b);
          return result(assignList, false);
        }
      } else {
        return result(assignList, false);
      }
    }

    if (this.token === this.tok.T_CLONE)
      return this.node("clone")(this.next().read_expr());

    switch (this.token) {
      case this.tok.T_INC:
        return this.node("pre")(
          "+",
          this.next().read_variable(false, false, false)
        );

      case this.tok.T_DEC:
        return this.node("pre")(
          "-",
          this.next().read_variable(false, false, false)
        );

      case this.tok.T_NEW:
        return this.read_new_expr();

      case this.tok.T_ISSET: {
        result = this.node("isset");
        if (this.next().expect("(")) {
          this.next();
        }
        const variables = this.read_list(this.read_expr, ",");
        if (this.expect(")")) {
          this.next();
        }
        return result(variables);
      }
      case this.tok.T_EMPTY: {
        result = this.node("empty");
        if (this.next().expect("(")) {
          this.next();
        }
        const expression = this.read_expr();
        if (this.expect(")")) {
          this.next();
        }
        return result(expression);
      }
      case this.tok.T_INCLUDE:
        return this.node("include")(false, false, this.next().read_expr());

      case this.tok.T_INCLUDE_ONCE:
        return this.node("include")(true, false, this.next().read_expr());

      case this.tok.T_REQUIRE:
        return this.node("include")(false, true, this.next().read_expr());

      case this.tok.T_REQUIRE_ONCE:
        return this.node("include")(true, true, this.next().read_expr());

      case this.tok.T_EVAL:
        result = this.node("eval");
        if (this.next().expect("(")) {
          this.next();
        }
        expr = this.read_expr();
        if (this.expect(")")) {
          this.next();
        }
        return result(expr);

      case this.tok.T_INT_CAST:
        return this.read_expr_cast("int");

      case this.tok.T_DOUBLE_CAST:
        return this.read_expr_cast("float");

      case this.tok.T_STRING_CAST:
        return this.read_expr_cast(
          this.text().indexOf("binary") !== -1 ? "binary" : "string"
        );

      case this.tok.T_ARRAY_CAST:
        return this.read_expr_cast("array");

      case this.tok.T_OBJECT_CAST:
        return this.read_expr_cast("object");

      case this.tok.T_BOOL_CAST:
        return this.read_expr_cast("bool");

      case this.tok.T_UNSET_CAST:
        return this.read_expr_cast("unset");

      case this.tok.T_EXIT: {
        const useDie = this.lexer.yytext.toLowerCase() === "die";
        result = this.node("exit");
        let status = null;
        if (this.next().token === "(") {
          if (this.next().token !== ")") {
            status = this.read_expr();
            if (this.expect(")")) {
              this.next();
            }
          } else {
            this.next();
          }
        }
        return result(status, useDie);
      }

      case this.tok.T_PRINT:
        return this.node("print")(this.next().read_expr());

      // T_YIELD (expr (T_DOUBLE_ARROW expr)?)?
      case this.tok.T_YIELD: {
        let value = null;
        let key = null;
        result = this.node("yield");
        if (this.next().is("EXPR")) {
          // reads the yield return value
          value = this.read_expr();
          if (this.token === this.tok.T_DOUBLE_ARROW) {
            // reads the yield returned key
            key = value;
            value = this.next().read_expr();
          }
        }
        return result(value, key);
      }

      // T_YIELD_FROM expr
      case this.tok.T_YIELD_FROM:
        result = this.node("yieldfrom");
        expr = this.next().read_expr();
        return result(expr);

      case this.tok.T_FUNCTION:
        return this.read_function(true);

      case this.tok.T_STATIC: {
        const backup = [this.token, this.lexer.getState()];
        if (this.next().token === this.tok.T_FUNCTION) {
          // handles static function
          return this.read_function(true, [0, 1, 0]);
        } else {
          // rollback
          this.lexer.tokens.push(backup);
          this.next();
        }
      }
    }

    // SCALAR | VARIABLE
    if (this.is("VARIABLE")) {
      result = this.node();
      expr = this.read_variable(false, false, false);

      // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L877
      // should accept only a variable
      const isConst =
        expr.kind === "identifier" ||
        (expr.kind === "staticlookup" && expr.offset.kind === "identifier");

      // VARIABLES SPECIFIC OPERATIONS
      switch (this.token) {
        case "=": {
          if (isConst) this.error("VARIABLE");
          let right;
          if (this.next().token == "&") {
            if (this.next().token === this.tok.T_NEW) {
              right = this.read_new_expr();
            } else {
              right = this.read_variable(false, false, true);
            }
          } else {
            right = this.read_expr();
          }
          return result("assign", expr, right, "=");
        }

        // operations :
        case this.tok.T_PLUS_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "+=");

        case this.tok.T_MINUS_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "-=");

        case this.tok.T_MUL_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "*=");

        case this.tok.T_POW_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "**=");

        case this.tok.T_DIV_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "/=");

        case this.tok.T_CONCAT_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), ".=");

        case this.tok.T_MOD_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "%=");

        case this.tok.T_AND_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "&=");

        case this.tok.T_OR_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "|=");

        case this.tok.T_XOR_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "^=");

        case this.tok.T_SL_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "<<=");

        case this.tok.T_SR_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), ">>=");

        case this.tok.T_INC:
          if (isConst) this.error("VARIABLE");
          this.next();
          return result("post", "+", expr);
        case this.tok.T_DEC:
          if (isConst) this.error("VARIABLE");
          this.next();
          return result("post", "-", expr);
        default:
          // see #193
          result.destroy(expr);
      }
    } else if (this.is("SCALAR")) {
      result = this.node();
      expr = this.read_scalar();
      if (expr.kind === "array" && expr.shortForm && this.token === "=") {
        // list assign
        const list = this.node("list")(expr.items, true);
        if (expr.loc) list.loc = expr.loc;
        const right = this.next().read_expr();
        return result("assign", list, right, "=");
      } else {
        // see #189 - swap docs on nodes
        result.destroy(expr);
      }
      // classic array
      return this.handleDereferencable(expr);
    } else {
      this.error("EXPR");
      this.next();
    }

    // returns variable | scalar
    return expr;
  }
function() {
    let result, expr;
    if (this.token === "@") return this.node("silent")(this.next().read_expr());
    if (this.token === "+")
      return this.node("unary")("+", this.next().read_expr());
    if (this.token === "-")
      return this.node("unary")("-", this.next().read_expr());
    if (this.token === "!")
      return this.node("unary")("!", this.next().read_expr());
    if (this.token === "~")
      return this.node("unary")("~", this.next().read_expr());

    if (this.token === "(") {
      expr = this.next().read_expr();
      expr.parenthesizedExpression = true;
      this.expect(")") && this.next();
      return this.handleDereferencable(expr);
    }

    if (this.token === "`") {
      
      return this.read_encapsed_string("`");
    }

    if (this.token === this.tok.T_LIST) {
      let assign = null;
      const isInner = this.innerList;
      result = this.node("list");
      if (!isInner) {
        assign = this.node("assign");
      }
      if (this.next().expect("(")) {
        this.next();
      }

      if (!this.innerList) this.innerList = true;

      
      const assignList = this.read_array_pair_list(false);
      if (this.expect(")")) {
        this.next();
      }

      
      let hasItem = false;
      for (let i = 0; i < assignList.length; i++) {
        if (assignList[i] !== null) {
          hasItem = true;
          break;
        }
      }
      if (!hasItem) {
        this.raiseError(
          "Fatal Error :  Cannot use empty list on line " +
            this.lexer.yylloc.first_line
        );
      }

      
      if (!isInner) {
        this.innerList = false;
        if (this.expect("=")) {
          return assign(
            result(assignList, false),
            this.next().read_expr(),
            "="
          );
        } else {
          
          return result(assignList, false);
        }
      } else {
        return result(assignList, false);
      }
    }

    if (this.token === this.tok.T_CLONE)
      return this.node("clone")(this.next().read_expr());

    switch (this.token) {
      case this.tok.T_INC:
        return this.node("pre")(
          "+",
          this.next().read_variable(false, false, false)
        );

      case this.tok.T_DEC:
        return this.node("pre")(
          "-",
          this.next().read_variable(false, false, false)
        );

      case this.tok.T_NEW:
        return this.read_new_expr();

      case this.tok.T_ISSET: {
        result = this.node("isset");
        if (this.next().expect("(")) {
          this.next();
        }
        const variables = this.read_list(this.read_expr, ",");
        if (this.expect(")")) {
          this.next();
        }
        return result(variables);
      }
      case this.tok.T_EMPTY: {
        result = this.node("empty");
        if (this.next().expect("(")) {
          this.next();
        }
        const expression = this.read_expr();
        if (this.expect(")")) {
          this.next();
        }
        return result(expression);
      }
      case this.tok.T_INCLUDE:
        return this.node("include")(false, false, this.next().read_expr());

      case this.tok.T_INCLUDE_ONCE:
        return this.node("include")(true, false, this.next().read_expr());

      case this.tok.T_REQUIRE:
        return this.node("include")(false, true, this.next().read_expr());

      case this.tok.T_REQUIRE_ONCE:
        return this.node("include")(true, true, this.next().read_expr());

      case this.tok.T_EVAL:
        result = this.node("eval");
        if (this.next().expect("(")) {
          this.next();
        }
        expr = this.read_expr();
        if (this.expect(")")) {
          this.next();
        }
        return result(expr);

      case this.tok.T_INT_CAST:
        return this.read_expr_cast("int");

      case this.tok.T_DOUBLE_CAST:
        return this.read_expr_cast("float");

      case this.tok.T_STRING_CAST:
        return this.read_expr_cast(
          this.text().indexOf("binary") !== -1 ? "binary" : "string"
        );

      case this.tok.T_ARRAY_CAST:
        return this.read_expr_cast("array");

      case this.tok.T_OBJECT_CAST:
        return this.read_expr_cast("object");

      case this.tok.T_BOOL_CAST:
        return this.read_expr_cast("bool");

      case this.tok.T_UNSET_CAST:
        return this.read_expr_cast("unset");

      case this.tok.T_EXIT: {
        const useDie = this.lexer.yytext.toLowerCase() === "die";
        result = this.node("exit");
        let status = null;
        if (this.next().token === "(") {
          if (this.next().token !== ")") {
            status = this.read_expr();
            if (this.expect(")")) {
              this.next();
            }
          } else {
            this.next();
          }
        }
        return result(status, useDie);
      }

      case this.tok.T_PRINT:
        return this.node("print")(this.next().read_expr());

      
      case this.tok.T_YIELD: {
        let value = null;
        let key = null;
        result = this.node("yield");
        if (this.next().is("EXPR")) {
          
          value = this.read_expr();
          if (this.token === this.tok.T_DOUBLE_ARROW) {
            
            key = value;
            value = this.next().read_expr();
          }
        }
        return result(value, key);
      }

      
      case this.tok.T_YIELD_FROM:
        result = this.node("yieldfrom");
        expr = this.next().read_expr();
        return result(expr);

      case this.tok.T_FUNCTION:
        return this.read_function(true);

      case this.tok.T_STATIC: {
        const backup = [this.token, this.lexer.getState()];
        if (this.next().token === this.tok.T_FUNCTION) {
          
          return this.read_function(true, [0, 1, 0]);
        } else {
          
          this.lexer.tokens.push(backup);
          this.next();
        }
      }
    }

    
    if (this.is("VARIABLE")) {
      result = this.node();
      expr = this.read_variable(false, false, false);

      
      
      const isConst =
        expr.kind === "identifier" ||
        (expr.kind === "staticlookup" && expr.offset.kind === "identifier");

      
      switch (this.token) {
        case "=": {
          if (isConst) this.error("VARIABLE");
          let right;
          if (this.next().token == "&") {
            if (this.next().token === this.tok.T_NEW) {
              right = this.read_new_expr();
            } else {
              right = this.read_variable(false, false, true);
            }
          } else {
            right = this.read_expr();
          }
          return result("assign", expr, right, "=");
        }

        
        case this.tok.T_PLUS_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "+=");

        case this.tok.T_MINUS_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "-=");

        case this.tok.T_MUL_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "*=");

        case this.tok.T_POW_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "**=");

        case this.tok.T_DIV_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "/=");

        case this.tok.T_CONCAT_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), ".=");

        case this.tok.T_MOD_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "%=");

        case this.tok.T_AND_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "&=");

        case this.tok.T_OR_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "|=");

        case this.tok.T_XOR_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "^=");

        case this.tok.T_SL_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), "<<=");

        case this.tok.T_SR_EQUAL:
          if (isConst) this.error("VARIABLE");
          return result("assign", expr, this.next().read_expr(), ">>=");

        case this.tok.T_INC:
          if (isConst) this.error("VARIABLE");
          this.next();
          return result("post", "+", expr);
        case this.tok.T_DEC:
          if (isConst) this.error("VARIABLE");
          this.next();
          return result("post", "-", expr);
        default:
          
          result.destroy(expr);
      }
    } else if (this.is("SCALAR")) {
      result = this.node();
      expr = this.read_scalar();
      if (expr.kind === "array" && expr.shortForm && this.token === "=") {
        
        const list = this.node("list")(expr.items, true);
        if (expr.loc) list.loc = expr.loc;
        const right = this.next().read_expr();
        return result("assign", list, right, "=");
      } else {
        
        result.destroy(expr);
      }
      
      return this.handleDereferencable(expr);
    } else {
      this.error("EXPR");
      this.next();
    }

    
    return expr;
  }
function ( ) { let result , expr ; if ( this . token === "@" ) return this . node ( "silent" ) ( this . next ( ) . read_expr ( ) ) ; if ( this . token === "+" ) return this . node ( "unary" ) ( "+" , this . next ( ) . read_expr ( ) ) ; if ( this . token === "-" ) return this . node ( "unary" ) ( "-" , this . next ( ) . read_expr ( ) ) ; if ( this . token === "!" ) return this . node ( "unary" ) ( "!" , this . next ( ) . read_expr ( ) ) ; if ( this . token === "~" ) return this . node ( "unary" ) ( "~" , this . next ( ) . read_expr ( ) ) ; if ( this . token === "(" ) { expr = this . next ( ) . read_expr ( ) ; expr . parenthesizedExpression = true ; this . expect ( ")" ) && this . next ( ) ; return this . handleDereferencable ( expr ) ; } if ( this . token === "`" ) { // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1048 return this . read_encapsed_string ( "`" ) ; } if ( this . token === this . tok . T_LIST ) { let assign = null ; const isInner = this . innerList ; result = this . node ( "list" ) ; if ( ! isInner ) { assign = this . node ( "assign" ) ; } if ( this . next ( ) . expect ( "(" ) ) { this . next ( ) ; } if ( ! this . innerList ) this . innerList = true ; // reads inner items const assignList = this . read_array_pair_list ( false ) ; if ( this . expect ( ")" ) ) { this . next ( ) ; } // check if contains at least one assignment statement let hasItem = false ; for ( let i = 0 ; i < assignList . length ; i ++ ) { if ( assignList [ i ] !== null ) { hasItem = true ; break ; } } if ( ! hasItem ) { this . raiseError ( "Fatal Error :  Cannot use empty list on line " + this . lexer . yylloc . first_line ) ; } // handles the node resolution if ( ! isInner ) { this . innerList = false ; if ( this . expect ( "=" ) ) { return assign ( result ( assignList , false ) , this . next ( ) . read_expr ( ) , "=" ) ; } else { // error fallback : list($a, $b); return result ( assignList , false ) ; } } else { return result ( assignList , false ) ; } } if ( this . token === this . tok . T_CLONE ) return this . node ( "clone" ) ( this . next ( ) . read_expr ( ) ) ; switch ( this . token ) { case this . tok . T_INC : return this . node ( "pre" ) ( "+" , this . next ( ) . read_variable ( false , false , false ) ) ; case this . tok . T_DEC : return this . node ( "pre" ) ( "-" , this . next ( ) . read_variable ( false , false , false ) ) ; case this . tok . T_NEW : return this . read_new_expr ( ) ; case this . tok . T_ISSET : { result = this . node ( "isset" ) ; if ( this . next ( ) . expect ( "(" ) ) { this . next ( ) ; } const variables = this . read_list ( this . read_expr , "," ) ; if ( this . expect ( ")" ) ) { this . next ( ) ; } return result ( variables ) ; } case this . tok . T_EMPTY : { result = this . node ( "empty" ) ; if ( this . next ( ) . expect ( "(" ) ) { this . next ( ) ; } const expression = this . read_expr ( ) ; if ( this . expect ( ")" ) ) { this . next ( ) ; } return result ( expression ) ; } case this . tok . T_INCLUDE : return this . node ( "include" ) ( false , false , this . next ( ) . read_expr ( ) ) ; case this . tok . T_INCLUDE_ONCE : return this . node ( "include" ) ( true , false , this . next ( ) . read_expr ( ) ) ; case this . tok . T_REQUIRE : return this . node ( "include" ) ( false , true , this . next ( ) . read_expr ( ) ) ; case this . tok . T_REQUIRE_ONCE : return this . node ( "include" ) ( true , true , this . next ( ) . read_expr ( ) ) ; case this . tok . T_EVAL : result = this . node ( "eval" ) ; if ( this . next ( ) . expect ( "(" ) ) { this . next ( ) ; } expr = this . read_expr ( ) ; if ( this . expect ( ")" ) ) { this . next ( ) ; } return result ( expr ) ; case this . tok . T_INT_CAST : return this . read_expr_cast ( "int" ) ; case this . tok . T_DOUBLE_CAST : return this . read_expr_cast ( "float" ) ; case this . tok . T_STRING_CAST : return this . read_expr_cast ( this . text ( ) . indexOf ( "binary" ) !== - 1 ? "binary" : "string" ) ; case this . tok . T_ARRAY_CAST : return this . read_expr_cast ( "array" ) ; case this . tok . T_OBJECT_CAST : return this . read_expr_cast ( "object" ) ; case this . tok . T_BOOL_CAST : return this . read_expr_cast ( "bool" ) ; case this . tok . T_UNSET_CAST : return this . read_expr_cast ( "unset" ) ; case this . tok . T_EXIT : { const useDie = this . lexer . yytext . toLowerCase ( ) === "die" ; result = this . node ( "exit" ) ; let status = null ; if ( this . next ( ) . token === "(" ) { if ( this . next ( ) . token !== ")" ) { status = this . read_expr ( ) ; if ( this . expect ( ")" ) ) { this . next ( ) ; } } else { this . next ( ) ; } } return result ( status , useDie ) ; } case this . tok . T_PRINT : return this . node ( "print" ) ( this . next ( ) . read_expr ( ) ) ; // T_YIELD (expr (T_DOUBLE_ARROW expr)?)? case this . tok . T_YIELD : { let value = null ; let key = null ; result = this . node ( "yield" ) ; if ( this . next ( ) . is ( "EXPR" ) ) { // reads the yield return value value = this . read_expr ( ) ; if ( this . token === this . tok . T_DOUBLE_ARROW ) { // reads the yield returned key key = value ; value = this . next ( ) . read_expr ( ) ; } } return result ( value , key ) ; } // T_YIELD_FROM expr case this . tok . T_YIELD_FROM : result = this . node ( "yieldfrom" ) ; expr = this . next ( ) . read_expr ( ) ; return result ( expr ) ; case this . tok . T_FUNCTION : return this . read_function ( true ) ; case this . tok . T_STATIC : { const backup = [ this . token , this . lexer . getState ( ) ] ; if ( this . next ( ) . token === this . tok . T_FUNCTION ) { // handles static function return this . read_function ( true , [ 0 , 1 , 0 ] ) ; } else { // rollback this . lexer . tokens . push ( backup ) ; this . next ( ) ; } } } // SCALAR | VARIABLE if ( this . is ( "VARIABLE" ) ) { result = this . node ( ) ; expr = this . read_variable ( false , false , false ) ; // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L877 // should accept only a variable const isConst = expr . kind === "identifier" || ( expr . kind === "staticlookup" && expr . offset . kind === "identifier" ) ; // VARIABLES SPECIFIC OPERATIONS switch ( this . token ) { case "=" : { if ( isConst ) this . error ( "VARIABLE" ) ; let right ; if ( this . next ( ) . token == "&" ) { if ( this . next ( ) . token === this . tok . T_NEW ) { right = this . read_new_expr ( ) ; } else { right = this . read_variable ( false , false , true ) ; } } else { right = this . read_expr ( ) ; } return result ( "assign" , expr , right , "=" ) ; } // operations : case this . tok . T_PLUS_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "+=" ) ; case this . tok . T_MINUS_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "-=" ) ; case this . tok . T_MUL_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "*=" ) ; case this . tok . T_POW_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "**=" ) ; case this . tok . T_DIV_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "/=" ) ; case this . tok . T_CONCAT_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , ".=" ) ; case this . tok . T_MOD_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "%=" ) ; case this . tok . T_AND_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "&=" ) ; case this . tok . T_OR_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "|=" ) ; case this . tok . T_XOR_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "^=" ) ; case this . tok . T_SL_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , "<<=" ) ; case this . tok . T_SR_EQUAL : if ( isConst ) this . error ( "VARIABLE" ) ; return result ( "assign" , expr , this . next ( ) . read_expr ( ) , ">>=" ) ; case this . tok . T_INC : if ( isConst ) this . error ( "VARIABLE" ) ; this . next ( ) ; return result ( "post" , "+" , expr ) ; case this . tok . T_DEC : if ( isConst ) this . error ( "VARIABLE" ) ; this . next ( ) ; return result ( "post" , "-" , expr ) ; default : // see #193 result . destroy ( expr ) ; } } else if ( this . is ( "SCALAR" ) ) { result = this . node ( ) ; expr = this . read_scalar ( ) ; if ( expr . kind === "array" && expr . shortForm && this . token === "=" ) { // list assign const list = this . node ( "list" ) ( expr . items , true ) ; if ( expr . loc ) list . loc = expr . loc ; const right = this . next ( ) . read_expr ( ) ; return result ( "assign" , list , right , "=" ) ; } else { // see #189 - swap docs on nodes result . destroy ( expr ) ; } // classic array return this . handleDereferencable ( expr ) ; } else { this . error ( "EXPR" ) ; this . next ( ) ; } // returns variable | scalar return expr ; }
```ebnf
Reads an expression
expr ::= @todo
```
ebnf Reads an expression expr :: =