Package org.luaj.vm2.compiler

Source Code of org.luaj.vm2.compiler.LexState$ConsControl

/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;

import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;

import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.FuncState.BlockCnt;


public class LexState {
 
  protected static final String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)";
    protected static final String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)";
    protected static final String RESERVED_LOCAL_VAR_FOR_GENERATOR = "(for generator)";
    protected static final String RESERVED_LOCAL_VAR_FOR_STEP = "(for step)";
    protected static final String RESERVED_LOCAL_VAR_FOR_LIMIT = "(for limit)";
    protected static final String RESERVED_LOCAL_VAR_FOR_INDEX = "(for index)";
   
    // keywords array
    protected static final String[] RESERVED_LOCAL_VAR_KEYWORDS = new String[] {
        RESERVED_LOCAL_VAR_FOR_CONTROL,
        RESERVED_LOCAL_VAR_FOR_GENERATOR,
        RESERVED_LOCAL_VAR_FOR_INDEX,
        RESERVED_LOCAL_VAR_FOR_LIMIT,
        RESERVED_LOCAL_VAR_FOR_STATE,
        RESERVED_LOCAL_VAR_FOR_STEP
    };
    private static final Hashtable RESERVED_LOCAL_VAR_KEYWORDS_TABLE = new Hashtable();
    static {
      for ( int i=0; i<RESERVED_LOCAL_VAR_KEYWORDS.length; i++ )
          RESERVED_LOCAL_VAR_KEYWORDS_TABLE.put( RESERVED_LOCAL_VAR_KEYWORDS[i], Boolean.TRUE );
    }
                              
    private static final int EOZ    = (-1);
  private static final int MAXSRC = 80;
  private static final int MAX_INT = Integer.MAX_VALUE-2;
  private static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX?
  private static final int LUAI_MAXCCALLS = 200;
 
  private static final String LUA_QS(String s) { return "'"+s+"'"; }
  private static final String LUA_QL(Object o) { return LUA_QS(String.valueOf(o)); }
 
  private static final int     LUA_COMPAT_LSTR   =    1; // 1 for compatibility, 2 for old behavior
  private static final boolean LUA_COMPAT_VARARG = true
   
    public static boolean isReservedKeyword(String varName) {
      return RESERVED_LOCAL_VAR_KEYWORDS_TABLE.containsKey(varName);
    }
   
  /*
  ** Marks the end of a patch list. It is an invalid value both as an absolute
  ** address, and as a list link (would link an element to itself).
  */
  static final int NO_JUMP = (-1);

  /*
  ** grep "ORDER OPR" if you change these enums
  */
  static final int
    OPR_ADD=0, OPR_SUB=1, OPR_MUL=2, OPR_DIV=3, OPR_MOD=4, OPR_POW=5,
    OPR_CONCAT=6,
    OPR_NE=7, OPR_EQ=8,
    OPR_LT=9, OPR_LE=10, OPR_GT=11, OPR_GE=12,
    OPR_AND=13, OPR_OR=14,
    OPR_NOBINOPR=15;

  static final int
    OPR_MINUS=0, OPR_NOT=1, OPR_LEN=2, OPR_NOUNOPR=3;

  /* exp kind */
  static final int    
    VVOID = 0/* no value */
    VNIL = 1,
    VTRUE = 2,
    VFALSE = 3,
    VK = 4,    /* info = index of constant in `k' */
    VKNUM = 5/* nval = numerical value */
    VLOCAL = 6/* info = local register */
    VUPVAL = 7,       /* info = index of upvalue in `upvalues' */
    VGLOBAL = 8/* info = index of table, aux = index of global name in `k' */
    VINDEXED = 9/* info = table register, aux = index register (or `k') */
    VJMP = 10,    /* info = instruction pc */
    VRELOCABLE = 11/* info = instruction pc */
    VNONRELOC = 12/* info = result register */
    VCALL = 13/* info = instruction pc */
    VVARARG = 14/* info = instruction pc */
 
  /* semantics information */
  private static class SemInfo {
    LuaValue r;
    LuaString ts;
  };

  private static class Token {
    int token;
    final SemInfo seminfo = new SemInfo();
    public void set(Token other) {
      this.token = other.token;
      this.seminfo.r = other.seminfo.r;
      this.seminfo.ts = other.seminfo.ts;
    }
  };
 
  int current;  /* current character (charint) */
  int linenumber;  /* input line counter */
  int lastline;  /* line of last token `consumed' */
  final Token t = new Token()/* current token */
  final Token lookahead = new Token()/* look ahead token */
  FuncState fs;  /* `FuncState' is private to the parser */
  LuaC L;
  InputStream z;  /* input stream */
  byte[] buff;  /* buffer for tokens */
  int nbuff; /* length of buffer */
  LuaString source;  /* current source name */
  byte decpoint;  /* locale decimal point */

  /* ORDER RESERVED */
  final static String luaX_tokens [] = {
      "and", "break", "do", "else", "elseif",
      "end", "false", "for", "function", "if",
      "in", "local", "nil", "not", "or", "repeat",
      "return", "then", "true", "until", "while",
      "..", "...", "==", ">=", "<=", "~=",
      "<number>", "<name>", "<string>", "<eof>",
  };

  final static int
    /* terminal symbols denoted by reserved words */
    TK_AND=257,  TK_BREAK=258, TK_DO=259, TK_ELSE=260, TK_ELSEIF=261,
    TK_END=262, TK_FALSE=263, TK_FOR=264, TK_FUNCTION=265, TK_IF=266,
    TK_IN=267, TK_LOCAL=268, TK_NIL=269, TK_NOT=270, TK_OR=271, TK_REPEAT=272,
    TK_RETURN=273, TK_THEN=274, TK_TRUE=275, TK_UNTIL=276, TK_WHILE=277,
    /* other terminal symbols */
    TK_CONCAT=278, TK_DOTS=279, TK_EQ=280, TK_GE=281, TK_LE=282, TK_NE=283,
    TK_NUMBER=284, TK_NAME=285, TK_STRING=286, TK_EOS=287;

  final static int FIRST_RESERVED = TK_AND;
  final static int NUM_RESERVED = TK_WHILE+1-FIRST_RESERVED;
 
  final static Hashtable RESERVED = new Hashtable();
  static {
    for ( int i=0; i<NUM_RESERVED; i++ ) {
      LuaString ts = (LuaString) LuaValue.valueOf( luaX_tokens[i] );
      RESERVED.put(ts, new Integer(FIRST_RESERVED+i));
    }
  }

  private boolean isalnum(int c) {
    return (c >= '0' && c <= '9')
      || (c >= 'a' && c <= 'z')
      || (c >= 'A' && c <= 'Z')
      || (c == '_');
    // return Character.isLetterOrDigit(c);
  }
 
  private boolean isalpha(int c) {
    return (c >= 'a' && c <= 'z')
      || (c >= 'A' && c <= 'Z');
  }
 
  private boolean isdigit(int c) {
    return (c >= '0' && c <= '9');
  }
 
  private boolean isspace(int c) {
    return (c <= ' ');
  }
 
 
  public LexState(LuaC state, InputStream stream) {
    this.z = stream;
    this.buff = new byte[32];
    this.L = state;
  }

  void nextChar() {
    try {
       current = z.read();
    } catch ( IOException e ) {
      e.printStackTrace();
      current = EOZ;
    }
  }

  boolean currIsNewline() {
    return current == '\n' || current == '\r';
  }

  void save_and_next() {
    save( current );
    nextChar();
  }

  void save(int c) {
    if ( buff == null || nbuff + 1 > buff.length )
      buff = LuaC.realloc( buff, nbuff*2+1 );
    buff[nbuff++] = (byte) c;
  }


  String token2str( int token ) {
    if ( token < FIRST_RESERVED ) {
      return iscntrl(token)?
          L.pushfstring( "char("+((int)token)+")" ):
          L.pushfstring( String.valueOf( (char) token ) );
    } else {
      return luaX_tokens[token-FIRST_RESERVED];
    }
  }

  private static boolean iscntrl(int token) {
    return token < ' ';
  }

  String txtToken(int token) {
    switch ( token ) {
    case TK_NAME:
    case TK_STRING:
    case TK_NUMBER:
      return new String( buff, 0, nbuff );
    default:
      return token2str( token );
    }
  }

  void lexerror( String msg, int token ) {
    String cid = chunkid( source.tojstring() ); // TODO: get source name from source
    L.pushfstring( cid+":"+linenumber+": "+msg );
    if ( token != 0 )
      L.pushfstring( "syntax error: "+msg+" near "+txtToken(token) );
    throw new LuaError(cid+":"+linenumber+": "+msg);
  }

  String chunkid( String source ) {
     if ( source.startsWith("=") )
       return source.substring(1);
     String end = "";
     if ( source.startsWith("@") ) {
       source = source.substring(1);
     } else {
       source = "[string \""+source;
       end = "\"]";
     }
     int n = source.length() + end.length();
     if ( n > MAXSRC )
       source = source.substring(0,MAXSRC-end.length()-3) + "...";
     return source + end;
  }

  void syntaxerror( String msg ) {
    lexerror( msg, t.token );
  }

  // only called by new_localvarliteral() for var names.
  LuaString newstring( String s ) {
    byte[] b = s.getBytes();
    return L.newTString(b, 0, b.length);
  }

  LuaString newstring( byte[] bytes, int offset, int len ) {
    return L.newTString( bytes, offset, len );
  }

  void inclinenumber() {
    int old = current;
    LuaC._assert( currIsNewline() );
    nextChar(); /* skip '\n' or '\r' */
    if ( currIsNewline() && current != old )
      nextChar(); /* skip '\n\r' or '\r\n' */
    if ( ++linenumber >= MAX_INT )
      syntaxerror("chunk has too many lines");
  }

  void setinput( LuaC L, int firstByte, InputStream z, LuaString source ) {
    this.decpoint = '.';
    this.L = L;
    this.lookahead.token = TK_EOS; /* no look-ahead token */
    this.z = z;
    this.fs = null;
    this.linenumber = 1;
    this.lastline = 1;
    this.source = source;
    this.nbuff = 0;   /* initialize buffer */
    this.current = firstByte; /* read first char */
    this.skipShebang();
  }
 
  private void skipShebang() {
    if ( current == '#' )
      while (!currIsNewline() && current != EOZ)
        nextChar();
  }
 


  /*
  ** =======================================================
  ** LEXICAL ANALYZER
  ** =======================================================
  */


  boolean check_next(String set) {
    if (set.indexOf(current) < 0)
      return false;
    save_and_next();
    return true;
  }

  void buffreplace(byte from, byte to) {
    int n = nbuff;
    byte[] p = buff;
    while ((--n) >= 0)
      if (p[n] == from)
        p[n] = to;
  }

  boolean str2d(String str, SemInfo seminfo) {
    double d;
    str = str.trim(); // TODO: get rid of this
    if ( str.startsWith("0x") ) {
      d = Long.parseLong(str.substring(2), 16);
    }
    else
      d = Double.parseDouble(str);
    seminfo.r = LuaValue.valueOf(d);
    return true;
  }

  //
  // TODO: reexamine this source and see if it should be ported differently
  //
  // static void trydecpoint (LexState *ls, SemInfo *seminfo) {
  //    /* format error: try to update decimal point separator */
  //    struct lconv *cv = localeconv();
  //    char old = this.decpoint;
  //    this.decpoint = (cv ? cv->decimal_point[0] : '.');
  //    buffreplace(ls, old, this.decpoint);  /* try updated decimal separator */
  //    if (!luaO_str2d(luaZ_buffer(this.buff), &seminfo->r)) {
  //      /* format error with correct decimal point: no more options */
  //      buffreplace(ls, this.decpoint, '.');  /* undo change (for error message) */
  //      luaX_lexerror(ls, "malformed number", TK_NUMBER);
  //    }
  //  }
  //
  /*
  void trydecpoint(String str, SemInfo seminfo) {
    NumberFormat nf = NumberFormat.getInstance();
    try {
      Number n = nf.parse(str);
      double d = n.doubleValue();
      seminfo.r = new LDouble(d);
    } catch (ParseException e) {
      lexerror("malformed number", TK_NUMBER);
    }
  }
  */

  void read_numeral(SemInfo seminfo) {
    LuaC._assert (isdigit(current));
    do {
      save_and_next();
    } while (isdigit(current) || current == '.');
    if (check_next("Ee")) /* `E'? */
      check_next("+-"); /* optional exponent sign */
    while (isalnum(current) || current == '_')
      save_and_next();
    save('\0');
    buffreplace((byte)'.', decpoint); /* follow locale for decimal point */
    String str = new String(buff, 0, nbuff);
//    if (!str2d(str, seminfo)) /* format error? */
//      trydecpoint(str, seminfo); /* try to update decimal point separator */
    str2d(str, seminfo);
  }

  int skip_sep() {
    int count = 0;
    int s = current;
    LuaC._assert (s == '[' || s == ']');
    save_and_next();
    while (current == '=') {
      save_and_next();
      count++;
    }
    return (current == s) ? count : (-count) - 1;
  }

  void read_long_string(SemInfo seminfo, int sep) {
    int cont = 0;
    save_and_next(); /* skip 2nd `[' */
    if (currIsNewline()) /* string starts with a newline? */
      inclinenumber(); /* skip it */
    for (boolean endloop = false; !endloop;) {
      switch (current) {
      case EOZ:
        lexerror((seminfo != null) ? "unfinished long string"
            : "unfinished long comment", TK_EOS);
        break; /* to avoid warnings */
      case '[': {
        if (skip_sep() == sep) {
          save_and_next(); /* skip 2nd `[' */
          cont++;
          if (LUA_COMPAT_LSTR == 1) {
            if (sep == 0)
              lexerror("nesting of [[...]] is deprecated", '[');
          }
        }
        break;
      }
      case ']': {
        if (skip_sep() == sep) {
          save_and_next(); /* skip 2nd `]' */
          if (LUA_COMPAT_LSTR == 2) {
            cont--;
            if (sep == 0 && cont >= 0)
              break;
          }
          endloop = true;
        }
        break;
      }
      case '\n':
      case '\r': {
        save('\n');
        inclinenumber();
        if (seminfo == null)
          nbuff = 0; /* avoid wasting space */
        break;
      }
      default: {
        if (seminfo != null)
          save_and_next();
        else
          nextChar();
      }
      }
    }
    if (seminfo != null)
      seminfo.ts = newstring(buff, 2 + sep, nbuff - 2 * (2 + sep));
  }

  void read_string(int del, SemInfo seminfo) {
    save_and_next();
    while (current != del) {
      switch (current) {
      case EOZ:
        lexerror("unfinished string", TK_EOS);
        continue; /* to avoid warnings */
      case '\n':
      case '\r':
        lexerror("unfinished string", TK_STRING);
        continue; /* to avoid warnings */
      case '\\': {
        int c;
        nextChar(); /* do not save the `\' */
        switch (current) {
        case 'a': /* bell */
          c = '\u0007';
          break;
        case 'b': /* backspace */
          c = '\b';
          break;
        case 'f': /* form feed */
          c = '\f';
          break;
        case 'n': /* newline */
          c = '\n';
          break;
        case 'r': /* carriage return */
          c = '\r';
          break;
        case 't': /* tab */
          c = '\t';
          break;
        case 'v': /* vertical tab */
          c = '\u000B';
          break;
        case '\n': /* go through */
        case '\r':
          save('\n');
          inclinenumber();
          continue;
        case EOZ:
          continue; /* will raise an error next loop */
        default: {
          if (!isdigit(current))
            save_and_next(); /* handles \\, \", \', and \? */
          else { /* \xxx */
            int i = 0;
            c = 0;
            do {
              c = 10 * c + (current - '0');
              nextChar();
            } while (++i < 3 && isdigit(current));
            if (c > UCHAR_MAX)
              lexerror("escape sequence too large", TK_STRING);
            save(c);
          }
          continue;
        }
        }
        save(c);
        nextChar();
        continue;
      }
      default:
        save_and_next();
      }
    }
    save_and_next(); /* skip delimiter */
    seminfo.ts = newstring(buff, 1, nbuff - 2);
  }

  int llex(SemInfo seminfo) {
    nbuff = 0;
    while (true) {
      switch (current) {
      case '\n':
      case '\r': {
        inclinenumber();
        continue;
      }
      case '-': {
        nextChar();
        if (current != '-')
          return '-';
        /* else is a comment */
        nextChar();
        if (current == '[') {
          int sep = skip_sep();
          nbuff = 0; /* `skip_sep' may dirty the buffer */
          if (sep >= 0) {
            read_long_string(null, sep); /* long comment */
            nbuff = 0;
            continue;
          }
        }
        /* else short comment */
        while (!currIsNewline() && current != EOZ)
          nextChar();
        continue;
      }
      case '[': {
        int sep = skip_sep();
        if (sep >= 0) {
          read_long_string(seminfo, sep);
          return TK_STRING;
        } else if (sep == -1)
          return '[';
        else
          lexerror("invalid long string delimiter", TK_STRING);
      }
      case '=': {
        nextChar();
        if (current != '=')
          return '=';
        else {
          nextChar();
          return TK_EQ;
        }
      }
      case '<': {
        nextChar();
        if (current != '=')
          return '<';
        else {
          nextChar();
          return TK_LE;
        }
      }
      case '>': {
        nextChar();
        if (current != '=')
          return '>';
        else {
          nextChar();
          return TK_GE;
        }
      }
      case '~': {
        nextChar();
        if (current != '=')
          return '~';
        else {
          nextChar();
          return TK_NE;
        }
      }
      case '"':
      case '\'': {
        read_string(current, seminfo);
        return TK_STRING;
      }
      case '.': {
        save_and_next();
        if (check_next(".")) {
          if (check_next("."))
            return TK_DOTS; /* ... */
          else
            return TK_CONCAT; /* .. */
        } else if (!isdigit(current))
          return '.';
        else {
          read_numeral(seminfo);
          return TK_NUMBER;
        }
      }
      case EOZ: {
        return TK_EOS;
      }
      default: {
        if (isspace(current)) {
          LuaC._assert (!currIsNewline());
          nextChar();
          continue;
        } else if (isdigit(current)) {
          read_numeral(seminfo);
          return TK_NUMBER;
        } else if (isalpha(current) || current == '_') {
          /* identifier or reserved word */
          LuaString ts;
          do {
            save_and_next();
          } while (isalnum(current) || current == '_');
          ts = newstring(buff, 0, nbuff);
          if ( RESERVED.containsKey(ts) )
            return ((Integer)RESERVED.get(ts)).intValue();
          else {
            seminfo.ts = ts;
            return TK_NAME;
          }
        } else {
          int c = current;
          nextChar();
          return c; /* single-char tokens (+ - / ...) */
        }
      }
      }
    }
  }

  void next() {
    lastline = linenumber;
    if (lookahead.token != TK_EOS) { /* is there a look-ahead token? */
      t.set( lookahead ); /* use this one */
      lookahead.token = TK_EOS; /* and discharge it */
    } else
      t.token = llex(t.seminfo); /* read next token */
  }

  void lookahead() {
    LuaC._assert (lookahead.token == TK_EOS);
    lookahead.token = llex(lookahead.seminfo);
  }

  // =============================================================
  // from lcode.h
  // =============================================================
 
 
  // =============================================================
  // from lparser.c
  // =============================================================

  static class expdesc {
    int k; // expkind, from enumerated list, above
    static class U { // originally a union
      static class S {
        int info, aux;
      }
      final S s = new S();
      private LuaValue _nval;
      public void setNval(LuaValue r) {
        _nval = r;
      }
      public LuaValue nval() {
        return (_nval == null? LuaInteger.valueOf(s.info): _nval);
      }
    };
    final U u = new U();
    final IntPtr t = new IntPtr(); /* patch list of `exit when true' */
    final IntPtr f = new IntPtr(); /* patch list of `exit when false' */
    void init( int k, int i ) {
      this.f.i = NO_JUMP;
      this.t.i = NO_JUMP;
      this.k = k;
      this.u.s.info = i;
    }

    boolean hasjumps() {
      return (t.i != f.i);
    }

    boolean isnumeral() {
      return (k == VKNUM && t.i == NO_JUMP && f.i == NO_JUMP);
    }

    public void setvalue(expdesc other) {
      this.k = other.k;
      this.u._nval = other.u._nval;
      this.u.s.info = other.u.s.info;
      this.u.s.aux = other.u.s.aux;
      this.t.i = other.t.i;
      this.f.i = other.f.i;
    }
  }
   
  boolean hasmultret(int k) {
    return ((k) == VCALL || (k) == VVARARG);
  }

  /*----------------------------------------------------------------------
  name    args  description
  ------------------------------------------------------------------------*/
 
  /*
   * * prototypes for recursive non-terminal functions
   */

  void error_expected(int token) {
    syntaxerror(L.pushfstring(LUA_QS(token2str(token)) + " expected"));
  }

  boolean testnext(int c) {
    if (t.token == c) {
      next();
      return true;
    } else
      return false;
  }

  void check(int c) {
    if (t.token != c)
      error_expected(c);
  }

  void checknext (int c) {
    check(c);
    next();
  }

  void check_condition(boolean c, String msg) {
    if (!(c))
      syntaxerror(msg);
  }


  void check_match(int what, int who, int where) {
    if (!testnext(what)) {
      if (where == linenumber)
        error_expected(what);
      else {
        syntaxerror(L.pushfstring(LUA_QS(token2str(what))
            + " expected " + "(to close " + LUA_QS(token2str(who))
            + " at line " + where + ")"));
      }
    }
  }

  LuaString str_checkname() {
    LuaString ts;
    check(TK_NAME);
    ts = t.seminfo.ts;
    next();
    return ts;
  }
 
  void codestring(expdesc e, LuaString s) {
    e.init(VK, fs.stringK(s));
  }

  void checkname(expdesc e) {
    codestring(e, str_checkname());
  }

 
  int registerlocalvar(LuaString varname) {
    FuncState fs = this.fs;
    Prototype f = fs.f;
    if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length)
      f.locvars = LuaC.realloc( f.locvars, fs.nlocvars*2+1 );
    f.locvars[fs.nlocvars] = new LocVars(varname,0,0);
    return fs.nlocvars++;
  }

 
//
//  #define new_localvarliteral(ls,v,n) \
//    this.new_localvar(luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n)
//
  void new_localvarliteral(String v, int n) {
    LuaString ts = newstring(v);
    new_localvar(ts, n);
  }

  void new_localvar(LuaString name, int n) {
    FuncState fs = this.fs;
    fs.checklimit(fs.nactvar + n + 1, FuncState.LUAI_MAXVARS, "local variables");
    fs.actvar[fs.nactvar + n] = (short) registerlocalvar(name);
  }

  void adjustlocalvars(int nvars) {
    FuncState fs = this.fs;
    fs.nactvar = (short) (fs.nactvar + nvars);
    for (; nvars > 0; nvars--) {
      fs.getlocvar(fs.nactvar - nvars).startpc = fs.pc;
    }
  }

  void removevars(int tolevel) {
    FuncState fs = this.fs;
    while (fs.nactvar > tolevel)
      fs.getlocvar(--fs.nactvar).endpc = fs.pc;
  }
 
  void singlevar(expdesc var) {
    LuaString varname = this.str_checkname();
    FuncState fs = this.fs;
    if (fs.singlevaraux(varname, var, 1) == VGLOBAL)
      var.u.s.info = fs.stringK(varname); /* info points to global name */
  }
 
  void adjust_assign(int nvars, int nexps, expdesc e) {
    FuncState fs = this.fs;
    int extra = nvars - nexps;
    if (hasmultret(e.k)) {
      /* includes call itself */
      extra++;
      if (extra < 0)
        extra = 0;
      /* last exp. provides the difference */
      fs.setreturns(e, extra);
      if (extra > 1)
        fs.reserveregs(extra - 1);
    } else {
      /* close last expression */
      if (e.k != VVOID)
        fs.exp2nextreg(e);
      if (extra > 0) {
        int reg = fs.freereg;
        fs.reserveregs(extra);
        fs.nil(reg, extra);
      }
    }
  }
 
  void enterlevel() {
    if (++L.nCcalls > LUAI_MAXCCALLS)
      lexerror("chunk has too many syntax levels", 0);
  }
 
  void leavelevel() {
    L.nCcalls--;
  }
 
  void pushclosure(FuncState func, expdesc v) {
    FuncState fs = this.fs;
    Prototype f = fs.f;
    if (f.p == null || fs.np + 1 > f.p.length)
      f.p = LuaC.realloc( f.p, fs.np*2 + 1 );
    f.p[fs.np++] = func.f;
    v.init(VRELOCABLE, fs.codeABx(Lua.OP_CLOSURE, 0, fs.np - 1));
    for (int i = 0; i < func.f.nups; i++) {
      int o = (func.upvalues[i].k == VLOCAL) ? Lua.OP_MOVE
          : Lua.OP_GETUPVAL;
      fs.codeABC(o, 0, func.upvalues[i].info, 0);
    }
  }
 
  void open_func (FuncState fs) {
      LuaC L = this.L;
      Prototype f = new Prototype();
      if ( this.fs!=null )
        f.source = this.fs.f.source;
      fs.f = f;
      fs.prev = this.fs;  /* linked list of funcstates */
      fs.ls = this;
      fs.L = L;
      this.fs = fs;
      fs.pc = 0;
      fs.lasttarget = -1;
      fs.jpc = new IntPtr( NO_JUMP );
      fs.freereg = 0;
      fs.nk = 0;
      fs.np = 0;
      fs.nlocvars = 0;
      fs.nactvar = 0;
      fs.bl = null;
      f.maxstacksize = 2/* registers 0/1 are always valid */
      //fs.h = new LTable();
      fs.htable = new Hashtable();
  }

  void close_func() {
    FuncState fs = this.fs;
    Prototype f = fs.f;
    this.removevars(0);
    fs.ret(0, 0); /* final return */
    f.code = LuaC.realloc(f.code, fs.pc);
    f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc);
    // f.sizelineinfo = fs.pc;
    f.k = LuaC.realloc(f.k, fs.nk);
    f.p = LuaC.realloc(f.p, fs.np);
    f.locvars = LuaC.realloc(f.locvars, fs.nlocvars);
    // f.sizelocvars = fs.nlocvars;
    f.upvalues = LuaC.realloc(f.upvalues, f.nups);
    // LuaC._assert (CheckCode.checkcode(f));
    LuaC._assert (fs.bl == null);
    this.fs = fs.prev;
//    L.top -= 2; /* remove table and prototype from the stack */
    // /* last token read was anchored in defunct function; must reanchor it
    // */
    // if (fs!=null) ls.anchor_token();
  }

  /*============================================================*/
  /* GRAMMAR RULES */
  /*============================================================*/

  void field(expdesc v) {
    /* field -> ['.' | ':'] NAME */
    FuncState fs = this.fs;
    expdesc key = new expdesc();
    fs.exp2anyreg(v);
    this.next(); /* skip the dot or colon */
    this.checkname(key);
    fs.indexed(v, key);
  }
 
  void yindex(expdesc v) {
    /* index -> '[' expr ']' */
    this.next(); /* skip the '[' */
    this.expr(v);
    this.fs.exp2val(v);
    this.checknext(']');
  }


/*
  ** {======================================================================
  ** Rules for Constructors
  ** =======================================================================
  */


  static class ConsControl {
    expdesc v = new expdesc(); /* last list item read */
    expdesc t; /* table descriptor */
    int nh; /* total number of `record' elements */
    int na; /* total number of array elements */
    int tostore; /* number of array elements pending to be stored */
  };


  void recfield(ConsControl cc) {
    /* recfield -> (NAME | `['exp1`]') = exp1 */
    FuncState fs = this.fs;
    int reg = this.fs.freereg;
    expdesc key = new expdesc();
    expdesc val = new expdesc();
    int rkkey;
    if (this.t.token == TK_NAME) {
      fs.checklimit(cc.nh, MAX_INT, "items in a constructor");
      this.checkname(key);
    } else
      /* this.t.token == '[' */
      this.yindex(key);
    cc.nh++;
    this.checknext('=');
    rkkey = fs.exp2RK(key);
    this.expr(val);
    fs.codeABC(Lua.OP_SETTABLE, cc.t.u.s.info, rkkey, fs.exp2RK(val));
    fs.freereg = reg; /* free registers */
  }

  void listfield (ConsControl cc) {
    this.expr(cc.v);
    fs.checklimit(cc.na, MAX_INT, "items in a constructor");
    cc.na++;
    cc.tostore++;
  }


  void constructor(expdesc t) {
    /* constructor -> ?? */
    FuncState fs = this.fs;
    int line = this.linenumber;
    int pc = fs.codeABC(Lua.OP_NEWTABLE, 0, 0, 0);
    ConsControl cc = new ConsControl();
    cc.na = cc.nh = cc.tostore = 0;
    cc.t = t;
    t.init(VRELOCABLE, pc);
    cc.v.init(VVOID, 0); /* no value (yet) */
    fs.exp2nextreg(t); /* fix it at stack top (for gc) */
    this.checknext('{');
    do {
      LuaC._assert (cc.v.k == VVOID || cc.tostore > 0);
      if (this.t.token == '}')
        break;
      fs.closelistfield(cc);
      switch (this.t.token) {
      case TK_NAME: { /* may be listfields or recfields */
        this.lookahead();
        if (this.lookahead.token != '=') /* expression? */
          this.listfield(cc);
        else
          this.recfield(cc);
        break;
      }
      case '[': { /* constructor_item -> recfield */
        this.recfield(cc);
        break;
      }
      default: { /* constructor_part -> listfield */
        this.listfield(cc);
        break;
      }
      }
    } while (this.testnext(',') || this.testnext(';'));
    this.check_match('}', '{', line);
    fs.lastlistfield(cc);
    InstructionPtr i = new InstructionPtr(fs.f.code, pc);
    LuaC.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */
    LuaC.SETARG_C(i, luaO_int2fb(cc.nh))/* set initial table size */
  }
 
  /*
  ** converts an integer to a "floating point byte", represented as
  ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
  ** eeeee != 0 and (xxx) otherwise.
  */
  static int luaO_int2fb (int x) {
    int e = 0/* expoent */
    while (x >= 16) {
      x = (x+1) >> 1;
      e++;
    }
    if (x < 8) return x;
    else return ((e+1) << 3) | (((int)x) - 8);
  }


  /* }====================================================================== */

  void parlist () {
    /* parlist -> [ param { `,' param } ] */
    FuncState fs = this.fs;
    Prototype f = fs.f;
    int nparams = 0;
    f.is_vararg = 0;
    if (this.t.token != ')') {  /* is `parlist' not empty? */
      do {
        switch (this.t.token) {
          case TK_NAME: /* param . NAME */
            this.new_localvar(this.str_checkname(), nparams++);
            break;
          }
          case TK_DOTS: /* param . `...' */
            this.next();
            if (LUA_COMPAT_VARARG) {
              /* use `arg' as default name */
              this.new_localvarliteral("arg", nparams++);
              f.is_vararg = LuaC.VARARG_HASARG | LuaC.VARARG_NEEDSARG;
            }
            f.is_vararg |= LuaC.VARARG_ISVARARG;
            break;
          }
          default: this.syntaxerror("<name> or " + LUA_QL("...") + " expected");
        }
      } while ((f.is_vararg==0) && this.testnext(','));
    }
    this.adjustlocalvars(nparams);
    f.numparams = (fs.nactvar - (f.is_vararg & LuaC.VARARG_HASARG));
    fs.reserveregs(fs.nactvar)/* reserve register for parameters */
  }


  void body(expdesc e, boolean needself, int line) {
    /* body -> `(' parlist `)' chunk END */
    FuncState new_fs = new FuncState();
    open_func(new_fs);
    new_fs.f.linedefined = line;
    this.checknext('(');
    if (needself) {
      new_localvarliteral("self", 0);
      adjustlocalvars(1);
    }
    this.parlist();
    this.checknext(')');
    this.chunk();
    new_fs.f.lastlinedefined = this.linenumber;
    this.check_match(TK_END, TK_FUNCTION, line);
    this.close_func();
    this.pushclosure(new_fs, e);
  }
 
  int explist1(expdesc v) {
    /* explist1 -> expr { `,' expr } */
    int n = 1; /* at least one expression */
    this.expr(v);
    while (this.testnext(',')) {
      fs.exp2nextreg(v);
      this.expr(v);
      n++;
    }
    return n;
  }


  void funcargs(expdesc f) {
    FuncState fs = this.fs;
    expdesc args = new expdesc();
    int base, nparams;
    int line = this.linenumber;
    switch (this.t.token) {
    case '(': { /* funcargs -> `(' [ explist1 ] `)' */
      if (line != this.lastline)
        this.syntaxerror("ambiguous syntax (function call x new statement)");
      this.next();
      if (this.t.token == ')') /* arg list is empty? */
        args.k = VVOID;
      else {
        this.explist1(args);
        fs.setmultret(args);
      }
      this.check_match(')', '(', line);
      break;
    }
    case '{': { /* funcargs -> constructor */
      this.constructor(args);
      break;
    }
    case TK_STRING: { /* funcargs -> STRING */
      this.codestring(args, this.t.seminfo.ts);
      this.next(); /* must use `seminfo' before `next' */
      break;
    }
    default: {
      this.syntaxerror("function arguments expected");
      return;
    }
    }
    LuaC._assert (f.k == VNONRELOC);
    base = f.u.s.info; /* base register for call */
    if (hasmultret(args.k))
      nparams = Lua.LUA_MULTRET; /* open call */
    else {
      if (args.k != VVOID)
        fs.exp2nextreg(args); /* close last argument */
      nparams = fs.freereg - (base + 1);
    }
    f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2));
    fs.fixline(line);
    fs.freereg = base+1/* call remove function and arguments and leaves
               * (unless changed) one result */
  }


  /*
  ** {======================================================================
  ** Expression parsing
  ** =======================================================================
  */

  void prefixexp(expdesc v) {
    /* prefixexp -> NAME | '(' expr ')' */
    switch (this.t.token) {
    case '(': {
      int line = this.linenumber;
      this.next();
      this.expr(v);
      this.check_match(')', '(', line);
      fs.dischargevars(v);
      return;
    }
    case TK_NAME: {
      this.singlevar(v);
      return;
    }
    default: {
      this.syntaxerror("unexpected symbol");
      return;
    }
    }
  }


  void primaryexp(expdesc v) {
    /*
     * primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs |
     * funcargs }
     */
    FuncState fs = this.fs;
    this.prefixexp(v);
    for (;;) {
      switch (this.t.token) {
      case '.': { /* field */
        this.field(v);
        break;
      }
      case '[': { /* `[' exp1 `]' */
        expdesc key = new expdesc();
        fs.exp2anyreg(v);
        this.yindex(key);
        fs.indexed(v, key);
        break;
      }
      case ':': { /* `:' NAME funcargs */
        expdesc key = new expdesc();
        this.next();
        this.checkname(key);
        fs.self(v, key);
        this.funcargs(v);
        break;
      }
      case '(':
      case TK_STRING:
      case '{': { /* funcargs */
        fs.exp2nextreg(v);
        this.funcargs(v);
        break;
      }
      default:
        return;
      }
    }
  }


  void simpleexp(expdesc v) {
    /*
     * simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor |
     * FUNCTION body | primaryexp
     */
    switch (this.t.token) {
    case TK_NUMBER: {
      v.init(VKNUM, 0);
      v.u.setNval(this.t.seminfo.r);
      break;
    }
    case TK_STRING: {
      this.codestring(v, this.t.seminfo.ts);
      break;
    }
    case TK_NIL: {
      v.init(VNIL, 0);
      break;
    }
    case TK_TRUE: {
      v.init(VTRUE, 0);
      break;
    }
    case TK_FALSE: {
      v.init(VFALSE, 0);
      break;
    }
    case TK_DOTS: { /* vararg */
      FuncState fs = this.fs;
      this.check_condition(fs.f.is_vararg!=0, "cannot use " + LUA_QL("...")
          + " outside a vararg function");
      fs.f.is_vararg &= ~LuaC.VARARG_NEEDSARG; /* don't need 'arg' */
      v.init(VVARARG, fs.codeABC(Lua.OP_VARARG, 0, 1, 0));
      break;
    }
    case '{': { /* constructor */
      this.constructor(v);
      return;
    }
    case TK_FUNCTION: {
      this.next();
      this.body(v, false, this.linenumber);
      return;
    }
    default: {
      this.primaryexp(v);
      return;
    }
    }
    this.next();
  }


  int getunopr(int op) {
    switch (op) {
    case TK_NOT:
      return OPR_NOT;
    case '-':
      return OPR_MINUS;
    case '#':
      return OPR_LEN;
    default:
      return OPR_NOUNOPR;
    }
  }


  int getbinopr(int op) {
    switch (op) {
    case '+':
      return OPR_ADD;
    case '-':
      return OPR_SUB;
    case '*':
      return OPR_MUL;
    case '/':
      return OPR_DIV;
    case '%':
      return OPR_MOD;
    case '^':
      return OPR_POW;
    case TK_CONCAT:
      return OPR_CONCAT;
    case TK_NE:
      return OPR_NE;
    case TK_EQ:
      return OPR_EQ;
    case '<':
      return OPR_LT;
    case TK_LE:
      return OPR_LE;
    case '>':
      return OPR_GT;
    case TK_GE:
      return OPR_GE;
    case TK_AND:
      return OPR_AND;
    case TK_OR:
      return OPR_OR;
    default:
      return OPR_NOBINOPR;
    }
  }

  static class Priority {
    final byte left; /* left priority for each binary operator */

    final byte right; /* right priority */

    public Priority(int i, int j) {
      left = (byte) i;
      right = (byte) j;
    }
  };
 
  static Priority[] priority = /* ORDER OPR */
     new Priority(6, 6), new Priority(6, 6), new Priority(7, 7), new Priority(7, 7), new Priority(7, 7)/* `+' `-' `/' `%' */
     new Priority(10, 9), new Priority(5, 4),                 /* power and concat (right associative) */
     new Priority(3, 3), new Priority(3, 3),                  /* equality and inequality */
     new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), new Priority(3, 3)/* order */
     new Priority(2, 2), new Priority(1, 1)                   /* logical (and/or) */
  };

  static final int UNARY_PRIORITY  = 8/* priority for unary operators */


  /*
  ** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
  ** where `binop' is any binary operator with a priority higher than `limit'
  */
  int subexpr(expdesc v, int limit) {
    int op;
    int uop;
    this.enterlevel();
    uop = getunopr(this.t.token);
    if (uop != OPR_NOUNOPR) {
      this.next();
      this.subexpr(v, UNARY_PRIORITY);
      fs.prefix(uop, v);
    } else
      this.simpleexp(v);
    /* expand while operators have priorities higher than `limit' */
    op = getbinopr(this.t.token);
    while (op != OPR_NOBINOPR && priority[op].left > limit) {
      expdesc v2 = new expdesc();
      int nextop;
      this.next();
      fs.infix(op, v);
      /* read sub-expression with higher priority */
      nextop = this.subexpr(v2, priority[op].right);
      fs.posfix(op, v, v2);
      op = nextop;
    }
    this.leavelevel();
    return op; /* return first untreated operator */
  }

  void expr(expdesc v) {
    this.subexpr(v, 0);
  }

  /* }==================================================================== */



  /*
  ** {======================================================================
  ** Rules for Statements
  ** =======================================================================
  */


  boolean block_follow (int token) {
    switch (token) {
        case TK_ELSE: case TK_ELSEIF: case TK_END:
        case TK_UNTIL: case TK_EOS:
          return true;
        default: return false;
    }
  }


  void block () {
    /* block -> chunk */
    FuncState fs = this.fs;
    BlockCnt bl = new BlockCnt();
    fs.enterblock(bl, false);
    this.chunk();
    LuaC._assert(bl.breaklist.i == NO_JUMP);
    fs.leaveblock();
  }


  /*
  ** structure to chain all variables in the left-hand side of an
  ** assignment
  */
  static class LHS_assign {
    LHS_assign prev;
    /* variable (global, local, upvalue, or indexed) */
    expdesc v = new expdesc();
  };


  /*
  ** check whether, in an assignment to a local variable, the local variable
  ** is needed in a previous assignment (to a table). If so, save original
  ** local value in a safe place and use this safe copy in the previous
  ** assignment.
  */
  void check_conflict (LHS_assign lh, expdesc v) {
    FuncState fs = this.fs;
    int extra = fs.freereg;  /* eventual position to save local variable */
    boolean conflict = false;
    for (; lh!=null; lh = lh.prev) {
      if (lh.v.k == VINDEXED) {
        if (lh.v.u.s.info == v.u.s.info) {  /* conflict? */
          conflict = true;
          lh.v.u.s.info = extra;  /* previous assignment will use safe copy */
        }
        if (lh.v.u.s.aux == v.u.s.info) {  /* conflict? */
          conflict = true;
          lh.v.u.s.aux = extra;  /* previous assignment will use safe copy */
        }
      }
    }
    if (conflict) {
      fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.s.info, 0); /* make copy */
      fs.reserveregs(1);
    }
  }


  void assignment (LHS_assign lh, int nvars) {
    expdesc e = new expdesc();
    this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED,
                        "syntax error");
    if (this.testnext(',')) {  /* assignment -> `,' primaryexp assignment */
        LHS_assign nv = new LHS_assign();
        nv.prev = lh;
        this.primaryexp(nv.v);
        if (nv.v.k == VLOCAL)
          this.check_conflict(lh, nv.v);
        this.assignment(nv, nvars+1);
    }
    else /* assignment . `=' explist1 */
        int nexps;
        this.checknext('=');
        nexps = this.explist1(e);
        if (nexps != nvars) {
          this.adjust_assign(nvars, nexps, e);
          if (nexps > nvars)
            this.fs.freereg -= nexps - nvars;  /* remove extra values */
      }
      else {
        fs.setoneret(e)/* close last expression */
        fs.storevar(lh.v, e);
        return/* avoid default */
      }
    }
    e.init(VNONRELOC, this.fs.freereg-1)/* default assignment */
    fs.storevar(lh.v, e);
  }


  int cond() {
    /* cond -> exp */
    expdesc v = new expdesc();
    /* read condition */
    this.expr(v);
    /* `falses' are all equal here */
    if (v.k == VNIL)
      v.k = VFALSE;
    fs.goiftrue(v);
    return v.f.i;
  }


  void breakstat() {
    FuncState fs = this.fs;
    BlockCnt bl = fs.bl;
    boolean upval = false;
    while (bl != null && !bl.isbreakable) {
      upval |= bl.upval;
      bl = bl.previous;
    }
    if (bl == null)
      this.syntaxerror("no loop to break");
    if (upval)
      fs.codeABC(Lua.OP_CLOSE, bl.nactvar, 0, 0);
    fs.concat(bl.breaklist, fs.jump());
  }


  void whilestat (int line) {
    /* whilestat -> WHILE cond DO block END */
    FuncState fs = this.fs;
    int whileinit;
    int condexit;
    BlockCnt bl = new BlockCnt();
    this.next()/* skip WHILE */
    whileinit = fs.getlabel();
    condexit = this.cond();
    fs.enterblock(bl, true);
    this.checknext(TK_DO);
    this.block();
    fs.patchlist(fs.jump(), whileinit);
    this.check_match(TK_END, TK_WHILE, line);
    fs.leaveblock();
    fs.patchtohere(condexit)/* false conditions finish the loop */
  }

  void repeatstat(int line) {
    /* repeatstat -> REPEAT block UNTIL cond */
    int condexit;
    FuncState fs = this.fs;
    int repeat_init = fs.getlabel();
    BlockCnt bl1 = new BlockCnt();
    BlockCnt bl2 = new BlockCnt();
    fs.enterblock(bl1, true); /* loop block */
    fs.enterblock(bl2, false); /* scope block */
    this.next(); /* skip REPEAT */
    this.chunk();
    this.check_match(TK_UNTIL, TK_REPEAT, line);
    condexit = this.cond(); /* read condition (inside scope block) */
    if (!bl2.upval) { /* no upvalues? */
      fs.leaveblock(); /* finish scope */
      fs.patchlist(condexit, repeat_init); /* close the loop */
    } else { /* complete semantics when there are upvalues */
      this.breakstat(); /* if condition then break */
      fs.patchtohere(condexit); /* else... */
      fs.leaveblock(); /* finish scope... */
      fs.patchlist(fs.jump(), repeat_init); /* and repeat */
    }
    fs.leaveblock(); /* finish loop */
  }


  int exp1() {
    expdesc e = new expdesc();
    int k;
    this.expr(e);
    k = e.k;
    fs.exp2nextreg(e);
    return k;
  }


  void forbody(int base, int line, int nvars, boolean isnum) {
    /* forbody -> DO block */
    BlockCnt bl = new BlockCnt();
    FuncState fs = this.fs;
    int prep, endfor;
    this.adjustlocalvars(3); /* control variables */
    this.checknext(TK_DO);
    prep = isnum ? fs.codeAsBx(Lua.OP_FORPREP, base, NO_JUMP) : fs.jump();
    fs.enterblock(bl, false); /* scope for declared variables */
    this.adjustlocalvars(nvars);
    fs.reserveregs(nvars);
    this.block();
    fs.leaveblock(); /* end of scope for declared variables */
    fs.patchtohere(prep);
    endfor = (isnum) ? fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP) : fs
        .codeABC(Lua.OP_TFORLOOP, base, 0, nvars);
    fs.fixline(line); /* pretend that `Lua.OP_FOR' starts the loop */
    fs.patchlist((isnum ? endfor : fs.jump()), prep + 1);
  }


  void fornum(LuaString varname, int line) {
    /* fornum -> NAME = exp1,exp1[,exp1] forbody */
    FuncState fs = this.fs;
    int base = fs.freereg;
    this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX, 0);
    this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT, 1);
    this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP, 2);
    this.new_localvar(varname, 3);
    this.checknext('=');
    this.exp1(); /* initial value */
    this.checknext(',');
    this.exp1(); /* limit */
    if (this.testnext(','))
      this.exp1(); /* optional step */
    else { /* default step = 1 */
      fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1)));
      fs.reserveregs(1);
    }
    this.forbody(base, line, 1, true);
  }


  void forlist(LuaString indexname) {
    /* forlist -> NAME {,NAME} IN explist1 forbody */
    FuncState fs = this.fs;
    expdesc e = new expdesc();
    int nvars = 0;
    int line;
    int base = fs.freereg;
    /* create control variables */
    this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR, nvars++);
    this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE, nvars++);
    this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL, nvars++);
    /* create declared variables */
    this.new_localvar(indexname, nvars++);
    while (this.testnext(','))
      this.new_localvar(this.str_checkname(), nvars++);
    this.checknext(TK_IN);
    line = this.linenumber;
    this.adjust_assign(3, this.explist1(e), e);
    fs.checkstack(3); /* extra space to call generator */
    this.forbody(base, line, nvars - 3, false);
  }


  void forstat(int line) {
    /* forstat -> FOR (fornum | forlist) END */
    FuncState fs = this.fs;
    LuaString varname;
    BlockCnt bl = new BlockCnt();
    fs.enterblock(bl, true); /* scope for loop and control variables */
    this.next(); /* skip `for' */
    varname = this.str_checkname(); /* first variable name */
    switch (this.t.token) {
    case '=':
      this.fornum(varname, line);
      break;
    case ',':
    case TK_IN:
      this.forlist(varname);
      break;
    default:
      this.syntaxerror(LUA_QL("=") + " or " + LUA_QL("in") + " expected");
    }
    this.check_match(TK_END, TK_FOR, line);
    fs.leaveblock(); /* loop scope (`break' jumps to this point) */
  }


  int test_then_block() {
    /* test_then_block -> [IF | ELSEIF] cond THEN block */
    int condexit;
    this.next(); /* skip IF or ELSEIF */
    condexit = this.cond();
    this.checknext(TK_THEN);
    this.block(); /* `then' part */
    return condexit;
  }


  void ifstat(int line) {
    /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block]
     * END */
    FuncState fs = this.fs;
    int flist;
    IntPtr escapelist = new IntPtr(NO_JUMP);
    flist = test_then_block(); /* IF cond THEN block */
    while (this.t.token == TK_ELSEIF) {
      fs.concat(escapelist, fs.jump());
      fs.patchtohere(flist);
      flist = test_then_block(); /* ELSEIF cond THEN block */
    }
    if (this.t.token == TK_ELSE) {
      fs.concat(escapelist, fs.jump());
      fs.patchtohere(flist);
      this.next(); /* skip ELSE (after patch, for correct line info) */
      this.block(); /* `else' part */
    } else
      fs.concat(escapelist, flist);
    fs.patchtohere(escapelist.i);
    this.check_match(TK_END, TK_IF, line);
  }

  void localfunc() {
    expdesc v = new expdesc();
    expdesc b = new expdesc();
    FuncState fs = this.fs;
    this.new_localvar(this.str_checkname(), 0);
    v.init(VLOCAL, fs.freereg);
    fs.reserveregs(1);
    this.adjustlocalvars(1);
    this.body(b, false, this.linenumber);
    fs.storevar(v, b);
    /* debug information will only see the variable after this point! */
    fs.getlocvar(fs.nactvar - 1).startpc = fs.pc;
  }


  void localstat() {
    /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
    int nvars = 0;
    int nexps;
    expdesc e = new expdesc();
    do {
      this.new_localvar(this.str_checkname(), nvars++);
    } while (this.testnext(','));
    if (this.testnext('='))
      nexps = this.explist1(e);
    else {
      e.k = VVOID;
      nexps = 0;
    }
    this.adjust_assign(nvars, nexps, e);
    this.adjustlocalvars(nvars);
  }


  boolean funcname(expdesc v) {
    /* funcname -> NAME {field} [`:' NAME] */
    boolean needself = false;
    this.singlevar(v);
    while (this.t.token == '.')
      this.field(v);
    if (this.t.token == ':') {
      needself = true;
      this.field(v);
    }
    return needself;
  }


  void funcstat(int line) {
    /* funcstat -> FUNCTION funcname body */
    boolean needself;
    expdesc v = new expdesc();
    expdesc b = new expdesc();
    this.next(); /* skip FUNCTION */
    needself = this.funcname(v);
    this.body(b, needself, line);
    fs.storevar(v, b);
    fs.fixline(line); /* definition `happens' in the first line */
  }


  void exprstat() {
    /* stat -> func | assignment */
    FuncState fs = this.fs;
    LHS_assign v = new LHS_assign();
    this.primaryexp(v.v);
    if (v.v.k == VCALL) /* stat -> func */
      LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */
    else { /* stat -> assignment */
      v.prev = null;
      this.assignment(v, 1);
    }
  }

  void retstat() {
    /* stat -> RETURN explist */
    FuncState fs = this.fs;
    expdesc e = new expdesc();
    int first, nret; /* registers with returned values */
    this.next(); /* skip RETURN */
    if (block_follow(this.t.token) || this.t.token == ';')
      first = nret = 0; /* return no values */
    else {
      nret = this.explist1(e); /* optional return values */
      if (hasmultret(e.k)) {
        fs.setmultret(e);
        if (e.k == VCALL && nret == 1) { /* tail call? */
          LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL);
          LuaC._assert (Lua.GETARG_A(fs.getcode(e)) == fs.nactvar);
        }
        first = fs.nactvar;
        nret = Lua.LUA_MULTRET; /* return all values */
      } else {
        if (nret == 1) /* only one single value? */
          first = fs.exp2anyreg(e);
        else {
          fs.exp2nextreg(e); /* values must go to the `stack' */
          first = fs.nactvar; /* return all `active' values */
          LuaC._assert (nret == fs.freereg - first);
        }
      }
    }
    fs.ret(first, nret);
  }


  boolean statement() {
    int line = this.linenumber; /* may be needed for error messages */
    switch (this.t.token) {
    case TK_IF: { /* stat -> ifstat */
      this.ifstat(line);
      return false;
    }
    case TK_WHILE: { /* stat -> whilestat */
      this.whilestat(line);
      return false;
    }
    case TK_DO: { /* stat -> DO block END */
      this.next(); /* skip DO */
      this.block();
      this.check_match(TK_END, TK_DO, line);
      return false;
    }
    case TK_FOR: { /* stat -> forstat */
      this.forstat(line);
      return false;
    }
    case TK_REPEAT: { /* stat -> repeatstat */
      this.repeatstat(line);
      return false;
    }
    case TK_FUNCTION: {
      this.funcstat(line); /* stat -> funcstat */
      return false;
    }
    case TK_LOCAL: { /* stat -> localstat */
      this.next(); /* skip LOCAL */
      if (this.testnext(TK_FUNCTION)) /* local function? */
        this.localfunc();
      else
        this.localstat();
      return false;
    }
    case TK_RETURN: { /* stat -> retstat */
      this.retstat();
      return true; /* must be last statement */
    }
    case TK_BREAK: { /* stat -> breakstat */
      this.next(); /* skip BREAK */
      this.breakstat();
      return true; /* must be last statement */
    }
    default: {
      this.exprstat();
      return false; /* to avoid warnings */
    }
    }
  }

  void chunk() {
    /* chunk -> { stat [`;'] } */
    boolean islast = false;
    this.enterlevel();
    while (!islast && !block_follow(this.t.token)) {
      islast = this.statement();
      this.testnext(';');
      LuaC._assert (this.fs.f.maxstacksize >= this.fs.freereg
          && this.fs.freereg >= this.fs.nactvar);
      this.fs.freereg = this.fs.nactvar; /* free registers */
    }
    this.leavelevel();
  }

  /* }====================================================================== */
   
TOP

Related Classes of org.luaj.vm2.compiler.LexState$ConsControl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.