/******************************************************************************* * 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; //TODO 与3.0不一样 import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.Hashtable; import org.luaj.vm2.LocVars; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Prototype; import org.luaj.vm2.compiler.FuncState.BlockCnt; import org.luaj.vm2.lib.MathLib; 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 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 */ VNONRELOC = 6, /* info = result register */ VLOCAL = 7, /* info = local register */ VUPVAL = 8, /* info = index of upvalue in `upvalues' */ VINDEXED = 9, /* info = table register, aux = index register (or `k') */ VJMP = 10, /* info = instruction pc */ VRELOCABLE = 11, /* info = instruction pc */ VCALL = 12, /* info = instruction pc */ VVARARG = 13; /* 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 */ final Token lookahead2 = new Token(); /* look ahead token */ FuncState fs; /* `FuncState' is private to the parser */ LuaC L; InputStream z; /* input stream */ char[] buff; /* buffer for tokens */ int nbuff; /* length of buffer */ Dyndata dyd = new Dyndata(); /* dynamic structures used by the parser */ LuaString source; /* current source name */ LuaString envn; /* environment variable name */ byte decpoint; /* locale decimal point */ /* ORDER RESERVED */ final static String luaX_tokens [] = { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "..", "...", "==", ">=", "<=", "~=", "::", "<eos>", "<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_GOTO=266, TK_IF=267, TK_IN=268, TK_LOCAL=269, TK_NIL=270, TK_NOT=271, TK_OR=272, TK_REPEAT=273, TK_RETURN=274, TK_THEN=275, TK_TRUE=276, TK_UNTIL=277, TK_WHILE=278, /* other terminal symbols */ TK_CONCAT=279, TK_DOTS=280, TK_EQ=281, TK_GE=282, TK_LE=283, TK_NE=284, TK_DBCOLON=285, TK_EOS=286, TK_NUMBER=287, TK_NAME=288, TK_STRING=289; 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 isxdigit(int c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } private boolean isspace(int c) { return (c <= ' '); } //是否标准语法 private boolean standardSyntax = true; public LexState(LuaC state, InputStream stream, boolean standardSyntax) { this.z = stream; this.buff = new char[32]; this.L = state; this.standardSyntax = standardSyntax; } 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++] = (char) 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 = Lua.chunkid( source.tojstring() ); L.pushfstring( cid+":"+linenumber+": "+msg ); if ( token != 0 ) L.pushfstring( "syntax error: "+msg+" near "+txtToken(token) ); // throw new LuaError(cid+":"+linenumber+": "+msg); throw new LuaError(cid+":"+linenumber+": "+msg+" near "+txtToken(token));//modified by song } void syntaxerror( String msg ) { lexerror( msg, t.token ); } // only called by new_localvarliteral() for var names. LuaString newstring( String s ) { return L.newTString(s); } LuaString newstring( char[] chars, int offset, int len ) { return L.newTString(new String(chars, 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.lookahead2.token = TK_EOS; this.z = z; this.fs = null; this.linenumber = 1; this.lastline = 1; this.source = source; this.envn = LuaValue.ENV; /* environment variable name */ 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(char from, char to) { int n = nbuff; char[] p = buff; while ((--n) >= 0) if (p[n] == from) p[n] = to; } LuaValue strx2number(String str, SemInfo seminfo) { char[] c = str.toCharArray(); int s = 0; while ( s < c.length && isspace(c[s])) ++s; // Check for negative sign double sgn = 1.0; if (s < c.length && c[s] == '-') { sgn = -1.0; ++s; } /* Check for "0x" */ if (s + 2 >= c.length ) return LuaValue.ZERO; if (c[s++] != '0') return LuaValue.ZERO; if (c[s] != 'x' && c[s] != 'X') return LuaValue.ZERO; ++s; // read integer part. double m = 0; int e = 0; while (s < c.length && isxdigit(c[s])) m = (m * 16) + hexvalue(c[s++]); if (s < c.length && c[s] == '.') { ++s; // skip dot while (s < c.length && isxdigit(c[s])) { m = (m * 16) + hexvalue(c[s++]); e -= 4; // Each fractional part shifts right by 2^4 } } if (s < c.length && (c[s] == 'p' || c[s] == 'P')) { ++s; int exp1 = 0; boolean neg1 = false; if (s < c.length && c[s] == '-') { neg1 = true; ++s; } while (s < c.length && isdigit(c[s])) exp1 = exp1 * 10 + c[s++] - '0'; if (neg1) exp1 = -exp1; e += exp1; } return LuaValue.valueOf(sgn * m * MathLib.dpow_d(2.0, e)); } boolean str2d(String str, SemInfo seminfo) { if (str.indexOf('n')>=0 || str.indexOf('N')>=0) seminfo.r = LuaValue.ZERO; else if (str.indexOf('x')>=0 || str.indexOf('X')>=0) seminfo.r = strx2number(str, seminfo); else seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim())); return true; } void read_numeral(SemInfo seminfo) { String expo = "Ee"; int first = current; LuaC._assert (isdigit(current)); save_and_next(); if (first == '0' && check_next("Xx")) expo = "Pp"; while (true) { if (check_next(expo)) check_next("+-"); if(isxdigit(current) || current == '.') save_and_next(); else break; } save('\0'); String str = new String(buff, 0, nbuff); 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 = L.newTString(LuaString.valueOf(buff, 2 + sep, nbuff - 2 * (2 + sep))); } int hexvalue(int c) { return c <= '9'? c - '0': c <= 'F'? c + 10 - 'A': c + 10 - 'a'; } int readhexaesc() { nextChar(); int c1 = current; nextChar(); int c2 = current; if (!isxdigit(c1) || !isxdigit(c2)) lexerror("hexadecimal digit expected 'x"+((char)c1)+((char)c2), TK_STRING); return (hexvalue(c1) << 4) + hexvalue(c2); } 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 'x': c = readhexaesc(); break; case '\n': /* go through */ case '\r': save('\n'); inclinenumber(); continue; case EOZ: continue; /* will raise an error next loop */ case 'z': { /* zap following span of spaces */ nextChar(); /* skip the 'z' */ while (isspace(current)) { if (currIsNewline()) inclinenumber(); else nextChar(); } continue; } 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 = L.newTString(LuaString.valueOf(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 ':': { nextChar(); if (current != ':') return ':'; else { nextChar(); return TK_DBCOLON; } } 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 '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { 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 */ lookahead.set(lookahead2); lookahead2.token = TK_EOS; } else { t.token = llex(t.seminfo); /* read next token */ } } void lookahead() { if (lookahead.token == TK_EOS) { lookahead.token = llex(lookahead.seminfo); } if (lookahead2.token == TK_EOS) { lookahead2.token = llex(lookahead2.seminfo); } } // ============================================================= // from lcode.h // ============================================================= // ============================================================= // from lparser.c // ============================================================= static final boolean vkisvar(final int k) { return (VLOCAL <= (k) && (k) <= VINDEXED); } static final boolean vkisinreg(final int k) { return ((k) == VNONRELOC || (k) == VLOCAL); } static class expdesc { int k; // expkind, from enumerated list, above static class U { // originally a union short ind_idx; // index (R/K) short ind_t; // table(register or upvalue) short ind_vt; // whether 't' is register (VLOCAL) or (UPVALUE) private LuaValue _nval; int info; public void setNval(LuaValue r) { _nval = r; } public LuaValue nval() { return (_nval == null? LuaInteger.valueOf(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.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.f.i = other.f.i; this.k = other.k; this.t.i = other.t.i; this.u._nval = other.u._nval; this.u.ind_idx = other.u.ind_idx; this.u.ind_t = other.u.ind_t; this.u.ind_vt = other.u.ind_vt; this.u.info = other.u.info; } } /* description of active local variable */ static class Vardesc { final short idx; /* variable index in stack */ Vardesc(int idx) { this.idx = (short) idx; } }; /* description of pending goto statements and label statements */ static class Labeldesc { LuaString name; /* label identifier */ int pc; /* position in code */ int line; /* line where it appeared */ short nactvar; /* local level where it appears in current block */ public Labeldesc(LuaString name, int pc, int line, short nactvar) { this.name = name; this.pc = pc; this.line = line; this.nactvar = nactvar; } }; /* dynamic structures used by the parser */ static class Dyndata { Vardesc[] actvar; /* list of active local variables */ int n_actvar = 0; Labeldesc[] gt; /* list of pending gotos */ int n_gt = 0; Labeldesc[] label; /* list of active labels */ int n_label = 0; }; boolean hasmultret(int k) { return ((k) == VCALL || (k) == VVARARG); } /*---------------------------------------------------------------------- name args description ------------------------------------------------------------------------*/ void anchor_token () { /* last token from outer function must be EOS */ LuaC._assert(fs != null || t.token == TK_EOS); if (t.token == TK_NAME || t.token == TK_STRING) { LuaString ts = t.seminfo.ts; // TODO: is this necessary? L.cachedLuaString(t.seminfo.ts); } } /* semantic error */ void semerror (String msg) { t.token = 0; /* remove 'near to' from final message */ syntaxerror(msg); } 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++; } void new_localvar(LuaString name) { int reg = registerlocalvar(name); fs.checklimit(dyd.n_actvar + 1, FuncState.LUAI_MAXVARS, "local variables"); if (dyd.actvar == null || dyd.n_actvar + 1 > dyd.actvar.length) dyd.actvar = LuaC.realloc(dyd.actvar, Math.max(1, dyd.n_actvar * 2)); dyd.actvar[dyd.n_actvar++] = new Vardesc(reg); } void new_localvarliteral(String v) { LuaString ts = newstring(v); new_localvar(ts); } 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 (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ expdesc key = new expdesc(); FuncState.singlevaraux(fs, this.envn, var, 1); /* get environment variable */ LuaC._assert(var.k == VLOCAL || var.k == VUPVAL); this.codestring(key, varname); /* key is variable name */ fs.indexed(var, key); /* env[varname] */ } } 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 closegoto(int g, Labeldesc label) { FuncState fs = this.fs; Labeldesc[] gl = this.dyd.gt; Labeldesc gt = gl[g]; LuaC._assert(gt.name.eq_b(label.name)); if (gt.nactvar < label.nactvar) { LuaString vname = fs.getlocvar(gt.nactvar).varname; String msg = L.pushfstring("<goto " + gt.name + "> at line " + gt.line + " jumps into the scope of local '" + vname.tojstring() + "'"); semerror(msg); } fs.patchlist(gt.pc, label.pc); /* remove goto from pending list */ System.arraycopy(gl, g + 1, gl, g, this.dyd.n_gt - g - 1); gl[--this.dyd.n_gt] = null; } /* ** try to close a goto with existing labels; this solves backward jumps */ boolean findlabel (int g) { int i; BlockCnt bl = fs.bl; Dyndata dyd = this.dyd; Labeldesc gt = dyd.gt[g]; /* check labels in current block for a match */ for (i = bl.firstlabel; i < dyd.n_label; i++) { Labeldesc lb = dyd.label[i]; if (lb.name.eq_b(gt.name)) { /* correct label? */ if (gt.nactvar > lb.nactvar && (bl.upval || dyd.n_label > bl.firstlabel)) fs.patchclose(gt.pc, lb.nactvar); closegoto(g, lb); /* close it */ return true; } } return false; /* label not found; cannot close goto */ } /* Caller must LuaC.grow() the vector before calling this. */ int newlabelentry(Labeldesc[] l, int index, LuaString name, int line, int pc) { l[index] = new Labeldesc(name, pc, line, fs.nactvar); return index; } /* ** check whether new label 'lb' matches any pending gotos in current ** block; solves forward jumps */ void findgotos (Labeldesc lb) { Labeldesc[] gl = dyd.gt; int i = fs.bl.firstgoto; while (i < dyd.n_gt) { if (gl[i].name.eq_b(lb.name)) closegoto(i, lb); else i++; } } /* ** create a label named "break" to resolve break statements */ void breaklabel () { LuaString n = LuaString.valueOf("break"); int l = newlabelentry(dyd.label=LuaC.grow(dyd.label, dyd.n_label+1), dyd.n_label++, n, 0, fs.pc); findgotos(dyd.label[l]); } /* ** generates an error for an undefined 'goto'; choose appropriate ** message when label name is a reserved word (which can only be 'break') */ void undefgoto (Labeldesc gt) { String msg = L.pushfstring(isReservedKeyword(gt.name.tojstring()) ? "<"+gt.name+"> at line "+gt.line+" not inside a loop" : "no visible label '"+gt.name+"' for <goto> at line "+gt.line); semerror(msg); } Prototype addprototype () { Prototype clp; Prototype f = fs.f; /* prototype of current function */ if (f.p == null || fs.np >= f.p.length) { f.p = LuaC.realloc(f.p, Math.max(1, fs.np * 2)); } f.p[fs.np++] = clp = new Prototype(); return clp; } void codeclosure (expdesc v) { FuncState fs = this.fs.prev; v.init(VRELOCABLE, fs.codeABx(LuaC.OP_CLOSURE, 0, fs.np - 1)); fs.exp2nextreg(v); /* fix it at stack top (for GC) */ } void open_func (FuncState fs, BlockCnt bl) { fs.prev = this.fs; /* linked list of funcstates */ fs.ls = this; 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.nups = 0; fs.nlocvars = 0; fs.nactvar = 0; fs.firstlocal = dyd.n_actvar; fs.bl = null; fs.f.source = this.source; fs.f.maxstacksize = 2; /* registers 0/1 are always valid */ fs.enterblock(bl, false); } void close_func() { FuncState fs = this.fs; Prototype f = fs.f; fs.ret(0, 0); /* final return */ fs.leaveblock(); f.code = LuaC.realloc(f.code, fs.pc); f.lineinfo = LuaC.realloc(f.lineinfo, 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.upvalues = LuaC.realloc(f.upvalues, fs.nups); LuaC._assert (fs.bl == null); this.fs = fs.prev; // last token read was anchored in defunct function; must reanchor it // ls.anchor_token(); } /*============================================================*/ /* GRAMMAR RULES */ /*============================================================*/ void fieldsel(expdesc v) { /* fieldsel -> ['.' | ':'] NAME */ FuncState fs = this.fs; expdesc key = new expdesc(); fs.exp2anyregup(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.info, rkkey, fs.exp2RK(val)); fs.freereg = (short)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(); f.is_vararg = 1; break; } default: this.syntaxerror("<name> or " + LUA_QL("...") + " expected"); } } while ((f.is_vararg==0) && this.testnext(',')); } this.adjustlocalvars(nparams); f.numparams = fs.nactvar; 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(); BlockCnt bl = new BlockCnt(); new_fs.f = addprototype(); new_fs.f.linedefined = line; open_func(new_fs, bl); this.checknext('('); if (needself) { new_localvarliteral("self"); adjustlocalvars(1); } this.parlist(); this.checknext(')'); this.statlist(); new_fs.f.lastlinedefined = this.linenumber; this.check_match(TK_END, TK_FUNCTION, line); this.codeclosure(e); this.close_func(); } int explist(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, int line) { FuncState fs = this.fs; expdesc args = new expdesc(); int base, nparams; switch (this.t.token) { case '(': { /* funcargs -> `(' [ explist1 ] `)' */ this.next(); if (this.t.token == ')') /* arg list is empty? */ args.k = VVOID; else { this.explist(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.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 = (short)(base+1); /* call remove function and arguments and leaves * (unless changed) one result */ } /* ** {====================================================================== ** Expression parsing ** ======================================================================= */ void primaryexp (expdesc v) { /* primaryexp -> NAME | '(' expr ')' */ switch (t.token) { case '(': { int line = linenumber; this.next(); this.expr(v); this.check_match(')', '(', line); fs.dischargevars(v); return; } case TK_NAME: { singlevar(v); return; } default: { this.syntaxerror("unexpected symbol " + t.token + " (" + ((char) t.token) + ")"); return; } } } void suffixedexp (expdesc v) { /* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ int line = linenumber; primaryexp(v); for (; ; ) { switch (t.token) { case '.': { /* fieldsel */ if(standardSyntax){//标准语法 this.fieldsel(v); } else { this.lookahead(); if (t.token == '.' && lookahead.token == TK_NAME && ( lookahead2.token == '(' || lookahead2.token == TK_STRING || lookahead2.token == '{')) { //修改:和.调用方式 dongxicheng t.token = ':'; expdesc key = new expdesc(); this.next(); this.checkname(key); fs.self(v, key); this.funcargs(v, line); } else { this.fieldsel(v); } } break; } case '[': { /* `[' exp1 `]' */ expdesc key = new expdesc(); fs.exp2anyregup(v); this.yindex(key); fs.indexed(v, key); break; } case ':': { /* `:' NAME funcargs */ if(standardSyntax){//标准语法 expdesc key = new expdesc(); this.next(); this.checkname(key); fs.self(v, key); this.funcargs(v, line); } else {//非标准语法 this.fieldsel(v); } break; } case '(': case TK_STRING: case '{': { /* funcargs */ fs.exp2nextreg(v); this.funcargs(v, line); 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"); 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.suffixedexp(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) { int line = linenumber; this.next(); this.subexpr(v, UNARY_PRIORITY); fs.prefix(uop, v, line); } 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 line = linenumber; this.next(); fs.infix(op, v); /* read sub-expression with higher priority */ int nextop = this.subexpr(v2, priority[op].right); fs.posfix(op, v, v2, line); op = nextop; } this.leavelevel(); return op; /* return first untreated operator */ } void expr(expdesc v) { this.subexpr(v, 0); } /* }==================================================================== */ /* ** {====================================================================== ** Rules for Statements ** ======================================================================= */ boolean block_follow (boolean withuntil) { switch (t.token) { case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_EOS: return true; case TK_UNTIL: return withuntil; default: return false; } } void block () { /* block -> chunk */ FuncState fs = this.fs; BlockCnt bl = new BlockCnt(); fs.enterblock(bl, false); this.statlist(); 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; short extra = (short) fs.freereg; /* eventual position to save local variable */ boolean conflict = false; for (; lh!=null; lh = lh.prev) { if (lh.v.k == VINDEXED) { /* table is the upvalue/local being assigned now? */ if (lh.v.u.ind_vt == v.k && lh.v.u.ind_t == v.u.info) { conflict = true; lh.v.u.ind_vt = VLOCAL; lh.v.u.ind_t = extra; /* previous assignment will use safe copy */ } /* index is the local being assigned? (index cannot be upvalue) */ if (v.k == VLOCAL && lh.v.u.ind_idx == v.u.info) { conflict = true; lh.v.u.ind_idx = extra; /* previous assignment will use safe copy */ } } } if (conflict) { /* copy upvalue/local value to a temporary (in position 'extra') */ int op = (v.k == VLOCAL) ? Lua.OP_MOVE : Lua.OP_GETUPVAL; fs.codeABC(op, extra, v.u.info, 0); 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.suffixedexp(nv.v); if (nv.v.k != VINDEXED) this.check_conflict(lh, nv.v); this.assignment(nv, nvars+1); } else { /* assignment . `=' explist1 */ int nexps; this.checknext('='); nexps = this.explist(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 gotostat(int pc) { int line = linenumber; LuaString label; int g; if (testnext(TK_GOTO)) label = str_checkname(); else { next(); /* skip break */ label = LuaString.valueOf("break"); } g = newlabelentry(dyd.gt =LuaC.grow(dyd.gt, dyd.n_gt+1), dyd.n_gt++, label, line, pc); findlabel(g); /* close it if label already defined */ } /* skip no-op statements */ void skipnoopstat () { while (t.token == ';' || t.token == TK_DBCOLON) statement(); } void labelstat (LuaString label, int line) { /* label -> '::' NAME '::' */ int l; /* index of new label being created */ fs.checkrepeated(dyd.label, dyd.n_label, label); /* check for repeated labels */ checknext(TK_DBCOLON); /* skip double colon */ /* create new entry for this label */ l = newlabelentry(dyd.label=LuaC.grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.pc); skipnoopstat(); /* skip other no-op statements */ if (block_follow(false)) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ dyd.label[l].nactvar = fs.bl.nactvar; } findgotos(dyd.label[l]); } 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.statlist(); this.check_match(TK_UNTIL, TK_REPEAT, line); condexit = this.cond(); /* read condition (inside scope block) */ if (bl2.upval) { /* upvalues? */ fs.patchclose(condexit, bl2.nactvar); } fs.leaveblock(); /* finish scope */ fs.patchlist(condexit, repeat_init); /* close the loop */ 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); if (isnum) /* numeric for? */ endfor = fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP); else { /* generic for */ fs.codeABC(Lua.OP_TFORCALL, base, 0, nvars); fs.fixline(line); endfor = fs.codeAsBx(Lua.OP_TFORLOOP, base + 2, NO_JUMP); } fs.patchlist(endfor, prep + 1); fs.fixline(line); } 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); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP); this.new_localvar(varname); 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 = 4; /* gen, state, control, plus at least one declared var */ int line; int base = fs.freereg; /* create control variables */ this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL); /* create declared variables */ this.new_localvar(indexname); while (this.testnext(',')) { this.new_localvar(this.str_checkname()); ++nvars; } this.checknext(TK_IN); line = this.linenumber; this.adjust_assign(3, this.explist(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) */ } void test_then_block(IntPtr escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ expdesc v = new expdesc(); BlockCnt bl = new BlockCnt(); int jf; /* instruction to skip 'then' code (if condition is false) */ this.next(); /* skip IF or ELSEIF */ expr(v); /* read expression */ this.checknext(TK_THEN); if (t.token == TK_GOTO || t.token == TK_BREAK) { fs.goiffalse(v); /* will jump to label if condition is true */ fs.enterblock(bl, false); /* must enter block before 'goto' */ gotostat(v.t.i); /* handle goto/break */ skipnoopstat(); /* skip other no-op statements */ if (block_follow(false)) { /* 'goto' is the entire block? */ fs.leaveblock(); return; /* and that is it */ } else /* must skip over 'then' part if condition is false */ jf = fs.jump(); } else { /* regular case (not goto/break) */ fs.goiftrue(v); /* skip over block if condition is false */ fs.enterblock(bl, false); jf = v.f.i; } statlist(); /* `then' part */ fs.leaveblock(); if (t.token == TK_ELSE || t.token == TK_ELSEIF) fs.concat(escapelist, fs.jump()); /* must jump over it */ fs.patchtohere(jf); } void ifstat(int line) { IntPtr escapelist = new IntPtr(NO_JUMP); /* exit list for finished parts */ test_then_block(escapelist); /* IF cond THEN block */ while (t.token == TK_ELSEIF) test_then_block(escapelist); /* ELSEIF cond THEN block */ if (testnext(TK_ELSE)) block(); /* `else' part */ check_match(TK_END, TK_IF, line); fs.patchtohere(escapelist.i); /* patch escape list to 'if' end */ } void localfunc() { expdesc b = new expdesc(); FuncState fs = this.fs; this.new_localvar(this.str_checkname()); this.adjustlocalvars(1); this.body(b, false, this.linenumber); /* 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.explist(e); else { e.k = VVOID; nexps = 0; } this.adjust_assign(nvars, nexps, e); this.adjustlocalvars(nvars); } boolean funcname(expdesc v) { /* funcname -> NAME {field} [`:' NAME] */ if(standardSyntax){//TODO . 和 : 兼容 boolean ismethod = false; this.singlevar(v); while (this.t.token == '.') this.fieldsel(v); if (this.t.token == ':') { ismethod = true; this.fieldsel(v); } return ismethod; } else { boolean needself = false; this.singlevar(v); while (this.t.token == '.') { needself = true; this.fieldsel(v); } if (this.t.token == ':') { needself = false; this.fieldsel(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.suffixedexp(v.v); if (t.token == '=' || t.token == ',') { /* stat -> assignment ? */ v.prev = null; assignment(v, 1); } else { /* stat -> func */ check_condition(v.v.k == VCALL, "syntax error"); LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ } } void retstat() { /* stat -> RETURN explist */ FuncState fs = this.fs; expdesc e = new expdesc(); int first, nret; /* registers with returned values */ if (block_follow(true) || this.t.token == ';') first = nret = 0; /* return no values */ else { nret = this.explist(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); testnext(';'); /* skip optional semicolon */ } void statement() { int line = this.linenumber; /* may be needed for error messages */ enterlevel(); switch (this.t.token) { case ';': { /* stat -> ';' (empty statement) */ next(); /* skip ';' */ break; } case TK_IF: { /* stat -> ifstat */ this.ifstat(line); break; } case TK_WHILE: { /* stat -> whilestat */ this.whilestat(line); break; } case TK_DO: { /* stat -> DO block END */ this.next(); /* skip DO */ this.block(); this.check_match(TK_END, TK_DO, line); break; } case TK_FOR: { /* stat -> forstat */ this.forstat(line); break; } case TK_REPEAT: { /* stat -> repeatstat */ this.repeatstat(line); break; } case TK_FUNCTION: { this.funcstat(line); /* stat -> funcstat */ break; } case TK_LOCAL: { /* stat -> localstat */ this.next(); /* skip LOCAL */ if (this.testnext(TK_FUNCTION)) /* local function? */ this.localfunc(); else this.localstat(); break; } case TK_DBCOLON: { /* stat -> label */ next(); /* skip double colon */ labelstat(str_checkname(), line); break; } case TK_RETURN: { /* stat -> retstat */ next(); /* skip RETURN */ this.retstat(); break; } case TK_BREAK: case TK_GOTO: { /* stat -> breakstat */ this.gotostat(fs.jump()); break; } default: { this.exprstat(); break; } } LuaC._assert(fs.f.maxstacksize >= fs.freereg && fs.freereg >= fs.nactvar); fs.freereg = fs.nactvar; /* free registers */ leavelevel(); } void statlist() { /* statlist -> { stat [`;'] } */ while (!block_follow(true)) { if (t.token == TK_RETURN) { statement(); return; /* 'return' must be last statement */ } statement(); } } /* ** compiles the main function, which is a regular vararg function with an ** upvalue named LUA_ENV */ public void mainfunc(FuncState funcstate) { BlockCnt bl = new BlockCnt(); open_func(funcstate, bl); fs.f.is_vararg = 1; /* main function is always vararg */ expdesc v = new expdesc(); v.init(VLOCAL, 0); /* create and... */ fs.newupvalue(envn, v); /* ...set environment upvalue */ next(); /* read first token */ statlist(); /* parse main body */ check(TK_EOS); close_func(); } /* }====================================================================== */ }