function(byref) {
    let result = this.node("variable");
    let name;
    if (
      this.expect([this.tok.T_VARIABLE, "$"]) &&
      this.token === this.tok.T_VARIABLE
    ) {
      // plain variable name
      name = this.text().substring(1);
      this.next();
      result = result(name, byref, false);
    } else {
      if (this.token === "$") this.next();
      // dynamic variable name
      switch (this.token) {
        case "{": {
          const expr = this.next().read_expr();
          this.expect("}") && this.next();
          result = result(expr, byref, true);
          break;
        }
        case "$": // $$$var
          // @fixme check coverage here
          result = result(this.read_simple_variable(false), byref, false);
          break;
        case this.tok.T_VARIABLE: {
          // $$var
          name = this.text().substring(1);
          const node = this.node("variable");
          this.next();
          result = result(node(name, false, false), byref, false);
          break;
        }
        default:
          this.error(["{", "$", this.tok.T_VARIABLE]);
          // graceful mode
          name = this.text();
          this.next();
          result = result(name, byref, false);
      }
    }
    return result;
  }
function(byref) {
    let result = this.node("variable");
    let name;
    if (
      this.expect([this.tok.T_VARIABLE, "$"]) &&
      this.token === this.tok.T_VARIABLE
    ) {
      
      name = this.text().substring(1);
      this.next();
      result = result(name, byref, false);
    } else {
      if (this.token === "$") this.next();
      
      switch (this.token) {
        case "{": {
          const expr = this.next().read_expr();
          this.expect("}") && this.next();
          result = result(expr, byref, true);
          break;
        }
        case "$": 
          
          result = result(this.read_simple_variable(false), byref, false);
          break;
        case this.tok.T_VARIABLE: {
          
          name = this.text().substring(1);
          const node = this.node("variable");
          this.next();
          result = result(node(name, false, false), byref, false);
          break;
        }
        default:
          this.error(["{", "$", this.tok.T_VARIABLE]);
          
          name = this.text();
          this.next();
          result = result(name, byref, false);
      }
    }
    return result;
  }
function ( byref ) { let result = this . node ( "variable" ) ; let name ; if ( this . expect ( [ this . tok . T_VARIABLE , "$" ] ) && this . token === this . tok . T_VARIABLE ) { // plain variable name name = this . text ( ) . substring ( 1 ) ; this . next ( ) ; result = result ( name , byref , false ) ; } else { if ( this . token === "$" ) this . next ( ) ; // dynamic variable name switch ( this . token ) { case "{" : { const expr = this . next ( ) . read_expr ( ) ; this . expect ( "}" ) && this . next ( ) ; result = result ( expr , byref , true ) ; break ; } case "$" : // $$$var // @fixme check coverage here result = result ( this . read_simple_variable ( false ) , byref , false ) ; break ; case this . tok . T_VARIABLE : { // $$var name = this . text ( ) . substring ( 1 ) ; const node = this . node ( "variable" ) ; this . next ( ) ; result = result ( node ( name , false , false ) , byref , false ) ; break ; } default : this . error ( [ "{" , "$" , this . tok . T_VARIABLE ] ) ; // graceful mode name = this . text ( ) ; this . next ( ) ; result = result ( name , byref , false ) ; } } return result ; }
```ebnf
simple_variable ::= T_VARIABLE | '$' '{' expr '}' | '$' simple_variable
```
ebnf simple_variable :: = T_VARIABLE | $ { expr } | $ simple_variable