/*
* Copyright 2010 Jon S Akhtar (Sylvanaar)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sylvanaar.idea.Lua.lang.parser.kahlua;
import com.intellij.diagnostic.PluginException;
import com.intellij.lang.ASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiParser;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.psi.tree.IElementType;
import com.sylvanaar.idea.Lua.lang.lexer.LuaTokenTypes;
import com.sylvanaar.idea.Lua.lang.parser.LuaElementTypes;
import com.sylvanaar.idea.Lua.lang.parser.LuaPsiBuilder;
import org.jetbrains.annotations.NotNull;
import se.krka.kahlua.vm.Prototype;
import java.io.IOException;
import java.io.Reader;
public class KahluaParser implements PsiParser, LuaElementTypes {
public int nCcalls = 0;
static Logger log = Logger.getInstance("Lua.parser.KahluaParser");
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<String, Boolean> RESERVED_LOCAL_VAR_KEYWORDS_TABLE =
// new Hashtable<String, Boolean>();
//
// static {
// int i = 0;
// while (i < RESERVED_LOCAL_VAR_KEYWORDS.length) {
// String RESERVED_LOCAL_VAR_KEYWORD = RESERVED_LOCAL_VAR_KEYWORDS[i];
// RESERVED_LOCAL_VAR_KEYWORDS_TABLE.put(RESERVED_LOCAL_VAR_KEYWORD, Boolean.TRUE);
// i++;
// }
// }
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; // TO DO, convert to unicode CHAR_MAX?
private static final int LUAI_MAXCCALLS = 200;
private LuaPsiBuilder builder = null;
// public KahluaParser(Project project) {
// }
private static String LUA_QS(String s) {
return "'" + s + "'";
}
private static String LUA_QL(Object o) {
return LUA_QS(String.valueOf(o));
}
// 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 */
int current = 0; /* current character (charint) */
int linenumber = 0; /* input line counter */
int lastline = 0; /* line of last token `consumed' */
IElementType t = null; /* current token */
IElementType lookahead = null; /* look ahead token */
FuncState fs = null; /* `FuncState' is private to the parser */
Reader z = null; /* input stream */
byte[] buff = null; /* buffer for tokens */
int nbuff = 0; /* length of buffer */
String source = null; /* current source name */
public KahluaParser(Reader stream, int firstByte, String source) {
this.z = stream;
this.buff = new byte[32];
this.lookahead = null; /* no look-ahead token */
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();
}
public KahluaParser() {};
void nextChar() {
try {
current = z.read();
} catch (IOException e) {
e.printStackTrace();
current = EOZ;
}
}
boolean currIsNewline() {
return current == '\n' || current == '\r';
}
void lexerror(String msg, IElementType token) {
String cid = source;
String errorMessage;
if (token != null) {
errorMessage = /*cid + ":" + linenumber + ": " +*/ msg + " near `" + token + "`";
} else {
errorMessage = /*cid + ":" + linenumber + ": " +*/ msg;
}
builder.error(errorMessage);
//throw new KahluaException(errorMessage);
}
// private static String trim(String s, int max) {
// if (s.length() > max) {
// return s.substring(0, max - 3) + "...";
// }
// return s;
// }
void syntaxerror(String msg) {
lexerror(msg, t);
}
private void skipShebang() {
if (current == '#')
while (!currIsNewline() && current != EOZ)
nextChar();
}
/*
** =======================================================
** LEXICAL ANALYZER
** =======================================================
*/
void next() {
lastline = linenumber;
builder.advanceLexer();
t = builder.getTokenType();
// /*
// if (lookahead != TK_EOS) { /* is there a look-ahead token? */
// t.set( lookahead ); /* use this one */
// lookahead = TK_EOS; /* and discharge it */
// } else
// t = llex(t); /* read next token */
// */
}
void lookahead() {
// FuncState._assert (lookahead == TK_EOS);
PsiBuilder.Marker current = builder.mark();
builder.advanceLexer();
lookahead = builder.getTokenType();
current.rollbackTo();
t = builder.getTokenType();
}
// =============================================================
// from lcode.h
// =============================================================
// =============================================================
// from lparser.c
// =============================================================
boolean hasmultret(int k) {
return ((k) == VCALL || (k) == VVARARG);
}
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
/*
* * prototypes for recursive non-terminal functions
*/
void error_expected(IElementType token) {
syntaxerror(token.toString() + " expected");
}
boolean testnext(IElementType c) {
if (t == c) {
next();
return true;
}
return false;
}
void check(IElementType c) {
if (t != c)
error_expected(c);
}
void checknext(IElementType c) {
check(c);
next();
}
void check_condition(boolean c, String msg) {
if (!(c))
syntaxerror(msg);
}
void check_match(IElementType what, IElementType who, int where) {
if (!testnext(what)) {
if (where == linenumber)
error_expected(what);
else {
syntaxerror(what
+ " expected " + "(to close " + who.toString()
+ " at line " + where + ")");
}
}
}
String str_checkname() {
String ts;
check(NAME);
ts = builder.text();
next();
return ts;
}
void codestring(ExpDesc e, String s) {
e.init(VK, fs.stringK(s));
}
void checkname(ExpDesc e) {
codestring(e, str_checkname());
}
int registerlocalvar(String varname) {
FuncState fs = this.fs;
if (fs.locvars == null || fs.nlocvars + 1 > fs.locvars.length)
fs.locvars = FuncState.realloc(fs.locvars, fs.nlocvars * 2 + 1);
fs.locvars[fs.nlocvars] = varname;
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) {
new_localvar(v, n);
}
void new_localvar(String name, int n) {
FuncState fs = this.fs;
if (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 = (fs.nactvar + nvars);
}
void removevars(int tolevel) {
FuncState fs = this.fs;
fs.nactvar = tolevel;
}
static final int DEC_REF = 0;
static final int DEC_G = 2;
static final int DEC_GL = 3;
void singlevar(ExpDesc var, int statementType) {
PsiBuilder.Marker ref = builder.mark();
PsiBuilder.Marker mark = builder.mark();
String varname = this.str_checkname();
FuncState fs = this.fs;
int type = fs.singlevaraux(varname, var, 1);
switch (type) {
case VGLOBAL:
var.info = fs.stringK(varname); /* info points to global name */
mark.done(statementType >= DEC_G ? GLOBAL_NAME_DECL : GLOBAL_NAME);
break;
case VUPVAL:
mark.done(UPVAL_NAME);
break;
case VLOCAL:
mark.done(statementType == DEC_GL ? LOCAL_NAME_DECL : LOCAL_NAME);
break;
default:
mark.error("Impossible identifier type");
}
ref.done(REFERENCE);
}
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 (++nCcalls > LUAI_MAXCCALLS)
lexerror("chunk has too many syntax levels", EMPTY_INPUT);
}
void leavelevel() {
nCcalls--;
}
void pushclosure(FuncState func, ExpDesc v) {
FuncState fs = this.fs;
Prototype f = fs.f;
if (f.prototypes == null || fs.np + 1 > f.prototypes.length)
f.prototypes = FuncState.realloc(f.prototypes, fs.np * 2 + 1);
f.prototypes[fs.np++] = func.f;
v.init(VRELOCABLE, fs.codeABx(FuncState.OP_CLOSURE, 0, fs.np - 1));
for (int i = 0; i < func.f.numUpvalues; i++) {
int o = (func.upvalues_k[i] == VLOCAL) ? FuncState.OP_MOVE
: FuncState.OP_GETUPVAL;
fs.codeABC(o, 0, func.upvalues_info[i], 0);
}
}
void close_func() {
FuncState fs = this.fs;
Prototype f = fs.f;
f.isVararg = fs.isVararg != 0;
this.removevars(0);
fs.ret(0, 0); /* final return */
f.code = FuncState.realloc(f.code, fs.pc);
f.lines = FuncState.realloc(f.lines, fs.pc);
// f.sizelineinfo = fs.pc;
f.constants = FuncState.realloc(f.constants, fs.nk);
f.prototypes = FuncState.realloc(f.prototypes, fs.np);
fs.locvars = FuncState.realloc(fs.locvars, fs.nlocvars);
// f.sizelocvars = fs.nlocvars;
fs.upvalues = FuncState.realloc(fs.upvalues, f.numUpvalues);
// FuncState._assert (CheckCode.checkcode(f));
FuncState._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 */
PsiBuilder.Marker mark = builder.mark();
this.checkname(key);
mark.done(FIELD_NAME);
//mark.precede().done(REFERENCE);
fs.indexed(v, key);
}
void field_org(ExpDesc v) {
/* field -> ['.' | ':'] NAME */
ExpDesc key = new ExpDesc();
this.next(); /* skip the dot or colon */
this.checkname(key);
}
void yindex(ExpDesc v) {
/* index -> '[' expr ']' */
this.next(); /* skip the '[' */
// PsiBuilder.Marker mark = builder.mark();
this.expr(v);
// mark.done(FIELD_NAME);
this.fs.exp2val(v);
this.checknext(RBRACK);
}
void yindex_org(ExpDesc v) {
/* index -> '[' expr ']' */
this.next(); /* skip the '[' */
this.expr(v);
this.checknext(RBRACK);
}
/*
** {======================================================================
** Rules for Constructors
** =======================================================================
*/
void recfield(ConsControl cc) {
/* recfield -> (NAME | `['exp1`]') = exp1 */
PsiBuilder.Marker mark = builder.mark();
PsiBuilder.Marker field = builder.mark();
FuncState fs = this.fs;
int reg = this.fs.freereg;
ExpDesc key = new ExpDesc();
ExpDesc val = new ExpDesc();
int rkkey;
if (this.t == NAME) {
fs.checklimit(cc.nh, MAX_INT, "items in a constructor");
this.checkname(key);
field.done(FIELD_NAME);
} else {
field.drop();
/* this.t == '[' */
this.yindex(key);
}
cc.nh++;
this.checknext(ASSIGN);
rkkey = fs.exp2RK(key);
this.expr(val);
fs.codeABC(FuncState.OP_SETTABLE, cc.t.info, rkkey, fs.exp2RK(val));
fs.freereg = reg; /* free registers */
mark.done(KEY_ASSIGNMENT);
}
void listfield(ConsControl cc) {
PsiBuilder.Marker mark = builder.mark();
this.expr(cc.v);
fs.checklimit(cc.na, MAX_INT, "items in a constructor");
cc.na++;
cc.tostore++;
mark.done(IDX_ASSIGNMENT);
}
void constructor(ExpDesc t) {
PsiBuilder.Marker mark = builder.mark();
/* constructor -> ?? */
FuncState fs = this.fs;
int line = this.linenumber;
int pc = fs.codeABC(FuncState.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(LCURLY);
do {
FuncState._assert(cc.v.k == VVOID || cc.tostore > 0);
if (this.t == RCURLY)
break;
fs.closelistfield(cc);
if (this.t == NAME) {
/* may be listfields or recfields */
this.lookahead();
if (this.lookahead != ASSIGN) /* expression? */
this.listfield(cc);
else
this.recfield(cc);
// break;
} else if (this.t == LBRACK) { /* constructor_item -> recfield */
this.recfield(cc);
// break;
} else { /* constructor_part -> listfield */
this.listfield(cc);
// break;
}
} while (this.testnext(COMMA) || this.testnext(SEMI));
this.check_match(RCURLY, LCURLY, line);
fs.lastlistfield(cc);
InstructionPtr i = new InstructionPtr(fs.f.code, pc);
FuncState.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */
FuncState.SETARG_C(i, luaO_int2fb(cc.nh)); /* set initial table size */
mark.done(TABLE_CONSTUCTOR);
}
/*
** 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) | (x - 8);
}
/* }====================================================================== */
void parlist() {
//log.info(">>> parlist");
/* parlist -> [ param { `,' param } ] */
FuncState fs = this.fs;
Prototype f = fs.f;
int nparams = 0;
fs.isVararg = 0;
if (this.t != RPAREN) { /* is `parlist' not empty? */
do {
PsiBuilder.Marker parm = builder.mark();
// PsiBuilder.Marker mark = builder.mark();
if (this.t == NAME) {
/* param . NAME */
String name = this.str_checkname();
// mark.done(LOCAL_NAME_DECL);
this.new_localvar(name, nparams++);
parm.done(PARAMETER);
// break;
} else if (this.t == ELLIPSIS) { /* param . `...' */
this.next();
// mark.done(LOCAL_NAME_DECL);
parm.done(PARAMETER);
fs.isVararg |= FuncState.VARARG_ISVARARG;
// break;
} else {
// mark.drop();
parm.drop();
this.syntaxerror("<name> or " + LUA_QL("...") + " expected");
}
} while ((fs.isVararg == 0) && this.testnext(COMMA));
}
this.adjustlocalvars(nparams);
f.numParams = (fs.nactvar - (fs.isVararg & FuncState.VARARG_HASARG));
fs.reserveregs(fs.nactvar); /* reserve register for parameters */
//log.info("<<< parlist");
}
void body(ExpDesc e, boolean needself, int line) {
/* body -> `(' parlist `)' chunk END */
FuncState new_fs = new FuncState(this);
new_fs.linedefined = line;
this.checknext(LPAREN);
if (needself) {
PsiBuilder.Marker self = builder.mark();
new_localvarliteral("self", 0);
adjustlocalvars(1);
self.done(SELF_PARAMETER);
}
PsiBuilder.Marker mark = builder.mark();
this.parlist();
mark.done(LuaElementTypes.PARAMETER_LIST);
this.checknext(RPAREN);
mark = builder.mark();
this.chunk();
mark.done(BLOCK);
closingBlock = true;
new_fs.lastlinedefined = this.linenumber;
this.check_match(END, FUNCTION, line);
this.close_func();
this.pushclosure(new_fs, e);
}
int explist1(ExpDesc v) {
PsiBuilder.Marker mark = builder.mark();
/* explist1 -> expr { `,' expr } */
int n = 1; /* at least one expression */
this.expr(v);
while (this.testnext(COMMA)) {
fs.exp2nextreg(v);
this.expr(v);
n++;
}
mark.done(EXPR_LIST);
return n;
}
void funcargs(ExpDesc f) {
PsiBuilder.Marker mark = builder.mark();
FuncState fs = this.fs;
ExpDesc args = new ExpDesc();
int base, nparams;
int line = this.linenumber;
if (this.t == LPAREN) { /* funcargs -> `(' [ explist1 ] `)' */
if (line != this.lastline)
this.syntaxerror("ambiguous syntax (function call x new statement)");
this.next();
if (this.t == RPAREN) /* arg list is empty? */
args.k = VVOID;
else {
this.explist1(args);
fs.setmultret(args);
}
this.check_match(RPAREN, LPAREN, line);
// break;
} else if (this.t == LCURLY) {
PsiBuilder.Marker exprlist = builder.mark();
/* funcargs -> constructor */
this.constructor(args);
exprlist.done(EXPR_LIST);
} else if (this.t == STRING || this.t == LONGSTRING) { /* funcargs -> STRING */
this.codestring(args, builder.text());
PsiBuilder.Marker litstring = builder.mark();
this.next(); /* must use `seminfo' before `next' */
litstring.done(LITERAL_EXPRESSION);
litstring.precede().done(EXPR_LIST);
} else {
this.syntaxerror("function arguments expected");
}
FuncState._assert(f.k == VNONRELOC);
base = f.info; /* base register for call */
if (hasmultret(args.k))
nparams = FuncState.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(FuncState.OP_CALL, base, nparams + 1, 2));
fs.fixline(line);
fs.freereg = base + 1; /* call remove function and arguments and leaves
* (unless changed) one result */
mark.done(FUNCTION_CALL_ARGS);
}
/*
** {======================================================================
** Expression parsing
** =======================================================================
*/
void prefixexp(ExpDesc v, int statementType) {
/* prefixexp -> NAME | '(' expr ')' */
if (this.t == LPAREN) {
int line = this.linenumber;
PsiBuilder.Marker mark = builder.mark();
this.next();
this.expr(v);
mark.done(PARENTHEICAL_EXPRESSION);
this.check_match(RPAREN, LPAREN, line);
fs.dischargevars(v);
return;
} else if (this.t == NAME) {
this.singlevar(v, statementType);
return;
}
this.syntaxerror("unexpected symbol in prefix expression");
}
// void primaryexp(ExpDesc v) {
// /*
// * primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs |
// * funcargs }
// */
// FuncState fs = this.fs;
// this.prefixexp(v);
// for (;;) {
// switch (this.t) {
// case DOT: { /* field */
// this.field(v);
// break;
// }
// case LBRACK: { /* `[' exp1 `]' */
// ExpDesc key = new ExpDesc();
// fs.exp2anyreg(v);
// this.yindex(key);
// fs.indexed(v, key);
// break;
// }
// case COLON: { /* `:' NAME funcargs */
// ExpDesc key = new ExpDesc();
// this.next();
// this.checkname(key);
// fs.self(v, key);
// this.funcargs(v);
// break;
// }
// case LPAREN:
// case STRING:
// case LONGSTRING:
// case LCURLY: { /* funcargs */
// fs.exp2nextreg(v);
// this.funcargs(v);
// break;
// }
// default:
// return;
// }
// }
// }
void primaryexp(ExpDesc v, int statementType) {
/*
* primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs |
* funcargs }
*/
PsiBuilder.Marker mark = builder.mark();
FuncState fs = this.fs;
this.prefixexp(v, statementType);
for (; ;) {
if (this.t == DOT) { /* field */
this.field(v);
mark.done(GETTABLE);
mark = mark.precede();
mark.done(COMPOUND_REFERENCE);
mark = mark.precede();
} else if (this.t == LBRACK) { /* `[' exp1 `]' */
ExpDesc key = new ExpDesc();
fs.exp2anyreg(v);
this.yindex(key);
fs.indexed(v, key);
mark.done(GETTABLE);
mark = mark.precede();
mark.done(COMPOUND_REFERENCE);
mark = mark.precede();
} else if (this.t == COLON) { /* `:' NAME funcargs */
ExpDesc key = new ExpDesc();
this.next();
PsiBuilder.Marker tmp = builder.mark();
this.checkname(key);
tmp.done(FIELD_NAME);
mark.done(GETTABLE);
mark = mark.precede();
mark.done(COMPOUND_REFERENCE);
mark = mark.precede();
fs.self(v, key);
this.funcargs(v);
mark.done(FUNCTION_CALL_EXPR);
mark = mark.precede();
} else if (this.t == LPAREN
|| this.t == STRING || this.t == LONGSTRING
|| this.t == LCURLY) { /* funcargs */
fs.exp2nextreg(v);
this.funcargs(v);
mark.done(FUNCTION_CALL_EXPR);
mark = mark.precede();
//break;
} else {
mark.drop();
return;
}
}
}
void simpleexp(ExpDesc v) {
/*
* simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor |
* FUNCTION body | primaryexp
*/
PsiBuilder.Marker mark = builder.mark();
try {
if (this.t == NUMBER) {
v.init(VKNUM, 0);
v.setNval(0); // TODO
} else if (this.t == STRING || this.t == LONGSTRING) {
this.codestring(v, builder.text()); //TODO
} else if (this.t == NIL) {
v.init(VNIL, 0);
} else if (this.t == TRUE) {
v.init(VTRUE, 0);
} else if (this.t == FALSE) {
v.init(VFALSE, 0);
} else if (this.t == ELLIPSIS) { /* vararg */
FuncState fs = this.fs;
this.check_condition(fs.isVararg != 0, "cannot use " + LUA_QL("...")
+ " outside a vararg function");
fs.isVararg &= ~FuncState.VARARG_NEEDSARG; /* don't need 'arg' */
v.init(VVARARG, fs.codeABC(FuncState.OP_VARARG, 0, 1, 0));
PsiBuilder.Marker ref = mark.precede() ;
this.next();
mark.done(LOCAL_NAME);
ref.done(REFERENCE);
mark=null;
return;
} else if (this.t == LCURLY) { /* constructor */
this.constructor(v);
return;
} else if (this.t == FUNCTION) {
PsiBuilder.Marker funcStmt = builder.mark();
this.next();
this.body(v, false, this.linenumber);
funcStmt.done(ANONYMOUS_FUNCTION_EXPRESSION);
return;
} else {
this.primaryexp(v, DEC_REF);
return;
}
this.next();
mark.done(LITERAL_EXPRESSION);
mark = null;
}
finally {
if (mark != null)
mark.drop();
}
}
int getunopr(IElementType op) {
if (op == NOT)
return OPR_NOT;
if (op == MINUS)
return OPR_MINUS;
if (op == GETN)
return OPR_LEN;
return OPR_NOUNOPR;
}
int getbinopr(IElementType op) {
if (op == PLUS)
return OPR_ADD;
if (op == MINUS)
return OPR_SUB;
if (op == MULT)
return OPR_MUL;
if (op == DIV)
return OPR_DIV;
if (op == MOD)
return OPR_MOD;
if (op == EXP)
return OPR_POW;
if (op == CONCAT)
return OPR_CONCAT;
if (op == NE)
return OPR_NE;
if (op == EQ)
return OPR_EQ;
if (op == LT)
return OPR_LT;
if (op == LE)
return OPR_LE;
if (op == GT)
return OPR_GT;
if (op == GE)
return OPR_GE;
if (op == AND)
return OPR_AND;
if (op == OR)
return OPR_OR;
return OPR_NOBINOPR;
}
static final int[] priorityLeft = {
6, 6, 7, 7, 7, /* `+' `-' `/' `%' */
10, 5, /* power and concat (right associative) */
3, 3, /* equality and inequality */
3, 3, 3, 3, /* order */
2, 1, /* logical (and/or) */
};
static final int[] priorityRight = { /* ORDER OPR */
6, 6, 7, 7, 7, /* `+' `-' `/' `%' */
9, 4, /* power and concat (right associative) */
3, 3, /* equality and inequality */
3, 3, 3, 3, /* order */
2, 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;
PsiBuilder.Marker mark = builder.mark();
PsiBuilder.Marker oper;
this.enterlevel();
uop = getunopr(this.t);
if (uop != OPR_NOUNOPR) {
PsiBuilder.Marker mark2 = builder.mark();
oper = builder.mark();
this.next();
oper.done(UNARY_OP);
this.subexpr(v, UNARY_PRIORITY);
mark2.done(UNARY_EXP);
fs.prefix(uop, v);
} else {
this.simpleexp(v);
}
/* expand while operators have priorities higher than `limit' */
op = getbinopr(this.t);
while (op != OPR_NOBINOPR && priorityLeft[op] > limit) {
ExpDesc v2 = new ExpDesc();
int nextop;
oper = builder.mark();
this.next();
oper.done(BINARY_OP);
fs.infix(op, v);
/* read sub-expression with higher priority */
nextop = this.subexpr(v2, priorityRight[op]);
fs.posfix(op, v, v2);
op = nextop;
mark.done(BINARY_EXP);
mark = mark.precede();
}
mark.drop();
this.leavelevel();
return op; /* return first untreated operator */
}
void expr(ExpDesc v) {
// PsiBuilder.Marker mark = builder.mark();
this.subexpr(v, 0);
// mark.done(EXPR);
// next();
}
/* }==================================================================== */
/*
** {======================================================================
** Rules for Statements
** =======================================================================
*/
boolean block_follow(IElementType token) {
return token == ELSE || token == ELSEIF ||
token == END || token == UNTIL || token == null;
}
void block() {
PsiBuilder.Marker mark = builder.mark();
/* block -> chunk */
FuncState fs = this.fs;
BlockCnt bl = new BlockCnt();
fs.enterblock(bl, false);
this.chunk();
FuncState._assert(bl.breaklist == NO_JUMP);
fs.leaveblock();
mark.done(BLOCK);
}
/*
** 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.info == v.info) { /* conflict? */
conflict = true;
lh.v.info = extra; /* previous assignment will use safe copy */
}
if (lh.v.aux == v.info) { /* conflict? */
conflict = true;
lh.v.aux = extra; /* previous assignment will use safe copy */
}
}
}
if (conflict) {
fs.codeABC(FuncState.OP_MOVE, fs.freereg, v.info, 0); /* make copy */
fs.reserveregs(1);
}
}
void assignment(LHS_assign lh, int nvars, PsiBuilder.Marker expr) {
// PsiBuilder.Marker mark = builder.mark();
ExpDesc e = new ExpDesc();
this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED,
"syntax error");
if (this.testnext(COMMA)) { /* assignment -> `,' primaryexp assignment */
LHS_assign nv = new LHS_assign();
nv.prev = lh;
lookahead();
boolean def = lookahead != DOT && lookahead != LBRACK;
this.primaryexp(nv.v, def?DEC_G:DEC_REF);
if (nv.v.k == VLOCAL)
this.check_conflict(lh, nv.v);
this.assignment(nv, nvars + 1, expr); // recurse with an additional variable
} else { /* assignment . `=' explist1 */
int nexps;
if (t != ASSIGN)
expr.error("= expexted");
else
expr.done(IDENTIFIER_LIST);
next();
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);
// mark.done(ASSIGN_STMT);
}
int cond() {
PsiBuilder.Marker mark = builder.mark();
/* 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);
mark.done(CONDITIONAL_EXPR);
return v.f;
}
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");
}
else {
if (upval)
fs.codeABC(FuncState.OP_CLOSE, bl.nactvar, 0, 0);
bl.breaklist = fs.concat(bl.breaklist, fs.jump());
}
}
void whilestat(int line) {
PsiBuilder.Marker mark = builder.mark();
/* 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(DO);
this.block();
fs.patchlist(fs.jump(), whileinit);
this.check_match(END, WHILE, line);
fs.leaveblock();
fs.patchtohere(condexit); /* false conditions finish the loop */
mark.done(WHILE_BLOCK);
}
void repeatstat(int line) {
PsiBuilder.Marker mark = builder.mark();
/* 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 */
PsiBuilder.Marker block = builder.mark();
this.chunk();
block.done(BLOCK);
this.check_match(UNTIL, 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 */
mark.done(REPEAT_BLOCK);
}
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(DO);
prep = isnum ? fs.codeAsBx(FuncState.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(FuncState.OP_FORLOOP, base, NO_JUMP) : fs
.codeABC(FuncState.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(String 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);
// PsiBuilder.Marker mark = builder.mark();
this.new_localvar(varname, 3);
// mark.done(LOCAL_NAME_DECL);
this.checknext(ASSIGN);
this.exp1(); /* initial value */
this.checknext(COMMA);
this.exp1(); /* limit */
if (this.testnext(COMMA)) {
this.exp1(); /* optional step */
}
else { /* default step = 1 */
fs.codeABx(FuncState.OP_LOADK, fs.freereg, fs.numberK(1));
fs.reserveregs(1);
}
this.forbody(base, line, 1, true);
}
void forlist(String 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++);
// next();
while (this.testnext(COMMA)) {
PsiBuilder.Marker mark = builder.mark();
String name = this.str_checkname();
mark.done(LOCAL_NAME_DECL);
this.new_localvar(name, nvars++);
}
this.checknext(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;
String varname;
BlockCnt bl = new BlockCnt();
fs.enterblock(bl, true); /* scope for loop and control variables */
PsiBuilder.Marker mark = builder.mark();
boolean numeric = false;
this.checknext(FOR); /* skip `for' */
PsiBuilder.Marker var_mark = builder.mark();
varname = this.str_checkname(); /* first variable name */
var_mark.done(LOCAL_NAME_DECL);
if (this.t == ASSIGN) {
numeric = true;
this.fornum(varname, line);
} else if (this.t == COMMA || this.t == IN) {
this.forlist(varname);
} else {
this.syntaxerror(LUA_QL("=") + " or " + LUA_QL("in") + " expected");
}
this.check_match(END, FOR, line);
mark.done(numeric ? NUMERIC_FOR_BLOCK : GENERIC_FOR_BLOCK);
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(THEN);
this.block(); /* `then' part */
return condexit;
}
void ifstat(int line) {
PsiBuilder.Marker mark = builder.mark();
/* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block]
* END */
FuncState fs = this.fs;
int flist;
int escapelist = NO_JUMP;
flist = test_then_block(); /* IF cond THEN block */
while (this.t == ELSEIF) {
escapelist = fs.concat(escapelist, fs.jump());
fs.patchtohere(flist);
flist = test_then_block(); /* ELSEIF cond THEN block */
}
if (this.t == ELSE) {
escapelist = fs.concat(escapelist, fs.jump());
fs.patchtohere(flist);
this.next(); /* skip ELSE (after patch, for correct line info) */
this.block(); /* `else' part */
} else
escapelist = fs.concat(escapelist, flist);
fs.patchtohere(escapelist);
this.check_match(END, IF, line);
mark.done(IF_THEN_BLOCK);
}
// need to parse the same as local foo; function foo() end
void localfunc(PsiBuilder.Marker stat) {
ExpDesc v = new ExpDesc();
ExpDesc b = new ExpDesc();
FuncState fs = this.fs;
PsiBuilder.Marker funcStmt = stat;
next();
//PsiBuilder.Marker funcName = builder.mark();
PsiBuilder.Marker mark = builder.mark();
String name = this.str_checkname();
mark.done(LOCAL_NAME_DECL);
// funcName.done(FUNCTION_IDENTIFIER);
this.new_localvar(name, 0);
v.init(VLOCAL, fs.freereg);
fs.reserveregs(1);
this.adjustlocalvars(1);
this.body(b, false, this.linenumber);
funcStmt.done(LOCAL_FUNCTION);
fs.storevar(v, b);
/* debug information will only see the variable after this point! */
}
void localstat(PsiBuilder.Marker stat) {
// PsiBuilder.Marker mark = stat;
PsiBuilder.Marker names = builder.mark();
/* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
int nvars = 0;
int nexps;
ExpDesc e = new ExpDesc();
do {
PsiBuilder.Marker mark = builder.mark();
String name = this.str_checkname();
mark.done(LOCAL_NAME_DECL);
this.new_localvar(name, nvars++);
} while (this.testnext(COMMA));
names.done(IDENTIFIER_LIST);
if (this.testnext(ASSIGN)) {
nexps = this.explist1(e);
stat.done(LOCAL_DECL_WITH_ASSIGNMENT);
}
else {
e.k = VVOID;
nexps = 0;
stat.done(LOCAL_DECL);
}
this.adjust_assign(nvars, nexps, e);
this.adjustlocalvars(nvars);
}
boolean funcname(ExpDesc v) {
//log.info(">>> funcname");
/* funcname -> NAME {field} [`:' NAME] */
boolean needself = false;
lookahead();
boolean def = lookahead != DOT && lookahead != COLON;
PsiBuilder.Marker tmp = builder.mark();
boolean isCompound = false;
this.singlevar(v, def?DEC_G:DEC_REF);
// OK this should work like GETTABLE( REF(a) ID(b) )
while (this.t == DOT) {
this.field(v);
tmp.done(GETTABLE);
tmp = tmp.precede();
isCompound = true;
}
if (this.t == COLON) {
needself = true;
this.field(v);
// tmp.done(GETSELF);
tmp.done(GETTABLE);
tmp = tmp.precede();
isCompound = true;
}
if (isCompound)
tmp.done(COMPOUND_REFERENCE);
else
tmp.drop();
return needself;
}
void funcstat(int line) {
//log.info(">>> funcstat");
PsiBuilder.Marker funcStmt = builder.mark();
/* 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);
funcStmt.done(FUNCTION_DEFINITION);
fs.storevar(v, b);
fs.fixline(line); /* definition `happens' in the first line */
///log.info("<<< funcstat");
}
void funcargs_org(ExpDesc f) {
PsiBuilder.Marker mark = builder.mark();
FuncState fs = this.fs;
ExpDesc args = new ExpDesc();
int base, nparams;
int line = this.linenumber;
if (this.t == LPAREN) { /* funcargs -> `(' [ explist1 ] `)' */
if (line != this.lastline)
this.syntaxerror("ambiguous syntax (function call x new statement)");
this.next();
if (this.t == RPAREN) /* arg list is empty? */
args.k = VVOID;
else {
this.explist1(args);
fs.setmultret(args);
}
this.check_match(RPAREN, LPAREN, line);
// break;
} else if (this.t == LCURLY) {
/* funcargs -> constructor */
this.constructor(args);
} else if (this.t == STRING || this.t == LONGSTRING) { /* funcargs -> STRING */
this.codestring(args, builder.text());
this.next(); /* must use `seminfo' before `next' */
} else {
this.syntaxerror("function arguments expected");
}
}
private static final short PRI_CALL = 0x0001;
private static final short PRI_COMP = 0x0002;
short primaryexp_org(ExpDesc v) {
boolean isfunc = false;
boolean isCompound = false;
/*
* primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs |
* funcargs }
*/
FuncState fs = this.fs;
this.prefixexp(v, DEC_REF);
for (;;) {
if (this.t == DOT) { /* field */
this.field_org(v);
isfunc = false;
isCompound = true;
} else if (this.t == LBRACK) { /* `[' exp1 `]' */
ExpDesc key = new ExpDesc();
this.yindex_org(key);
fs.indexed(v, key);
isfunc = false;
isCompound = true;
} else if (this.t == COLON) { /* `:' NAME funcargs */
ExpDesc key = new ExpDesc();
this.next();
this.checkname(key);
fs.self(v, key);
this.funcargs_org(v);
isfunc = true;
isCompound = true;
} else if (this.t == LPAREN
|| this.t == STRING || this.t == LONGSTRING
|| this.t == LCURLY) { /* funcargs */
this.funcargs_org(v);
isfunc = true;
} else {
short rc = isfunc ? PRI_CALL : 0;
rc |= isCompound ? PRI_COMP : 0;
return rc;
}
}
}
void exprstat() {
/* stat -> func | assignment */
FuncState fs = this.fs;
/* DANGER - because of this, this parser will produce invalid bytecode */
/* this is here so we can know which rule we are actually processing */
/* because unlike the lua parser, we need to know in advance */
LHS_assign v = new LHS_assign();
PsiBuilder.Marker lookahead = builder.mark();
short info = primaryexp_org(v.v);
boolean isassign = (info & PRI_CALL) == 0;
boolean isCompound = (info & PRI_COMP) != 0;
boolean isComplete = !isassign;
if (isassign) {// need to see if it is a complete assignment statement
while(t != ASSIGN && !builder.eof() && !LuaTokenTypes.KEYWORDS.contains(t))
next();
if (t == ASSIGN)
isComplete = true;
}
lookahead.rollbackTo();
this.t = builder.getTokenType();
v = new LHS_assign();
PsiBuilder.Marker outer = builder.mark();
this.primaryexp(v.v, (isassign&&!isCompound&&isComplete)?DEC_G:DEC_REF);
if (v.v.k == VCALL) /* stat -> func */ {
if (isassign)
builder.error("invalid assign prediction (is call)");
outer.done(FUNCTION_CALL);
FuncState.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */
}
else { /* stat -> assignment */
if (!isassign)
builder.error("invalid call prediction (is assignment)");
v.prev = null;
this.assignment(v, 1, outer);
outer.precede().done(ASSIGN_STMT);
}
}
boolean closingBlock = false;
void retstat() {
PsiBuilder.Marker mark = builder.mark();
boolean tailCall = false;
/* 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) || this.t == SEMI)
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? */
tailCall = true;
FuncState.SET_OPCODE(fs.getcodePtr(e), FuncState.OP_TAILCALL);
FuncState._assert(FuncState.GETARG_A(fs.getcode(e)) == fs.nactvar);
}
first = fs.nactvar;
nret = FuncState.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 */
FuncState._assert(nret == fs.freereg - first);
}
}
}
mark.done(tailCall?RETURN_STATEMENT_WITH_TAIL_CALL:RETURN_STATEMENT);
closingBlock = true;
fs.ret(first, nret);
}
boolean statement() {
try {
//log.info(">>> statement");
int line = this.linenumber; /* may be needed for error messages */
if (this.t == IF) { /* stat -> ifstat */
this.ifstat(line);
return false;
}
if (this.t == WHILE) { /* stat -> whilestat */
this.whilestat(line);
return false;
}
if (this.t == DO) { /* stat -> DO block END */
PsiBuilder.Marker mark = builder.mark();
this.next(); /* skip DO */
this.block();
this.check_match(END, DO, line);
mark.done(DO_BLOCK);
return false;
}
if (this.t == FOR) { /* stat -> forstat */
this.forstat(line);
return false;
}
if (this.t == REPEAT) { /* stat -> repeatstat */
this.repeatstat(line);
return false;
}
if (this.t == FUNCTION) {
this.funcstat(line); /* stat -> funcstat */
return false;
}
if (this.t == LOCAL) { /* stat -> localstat */
PsiBuilder.Marker stat = builder.mark();
this.next(); /* skip LOCAL */
if (this.t == FUNCTION) /* local function? */
this.localfunc(stat);
else
this.localstat(stat);
return false;
}
if (this.t == RETURN) { /* stat -> retstat */
this.retstat();
return true; /* must be last statement */
}
if (this.t == BREAK) { /* stat -> breakstat */
this.next(); /* skip BREAK */
this.breakstat();
return true; /* must be last statement */
}
this.exprstat();
return false; /* to avoid warnings */
} finally {
//log.info("<<< statement");
}
}
void chunk() {
//log.info(">>> chunk");
/* chunk -> { stat [`;'] } */
boolean islast = false;
this.enterlevel();
while (!islast && !block_follow(this.t)) {
// final PsiBuilder.Marker mark = builder.mark();
islast = this.statement();
this.testnext(SEMI);
// PsiBuilder.Marker mark = builder.mark();
// if (this.testnext(SEMI))
// mark.done(LUA_TOKEN);
// else
// mark.drop();
FuncState._assert(this.fs.f.maxStacksize >= this.fs.freereg
&& this.fs.freereg >= this.fs.nactvar);
this.fs.freereg = this.fs.nactvar; /* free registers */
}
this.leavelevel();
// if (builder.isError() || closingBlock)
// cleanAfterError(builder);
//log.info("<<< chunk");
}
private void cleanAfterError(LuaPsiBuilder builder) {
int i = 0;
PsiBuilder.Marker em = builder.mark();
while (!builder.eof() && !(END.equals(builder.getTokenType())) ) {
builder.advanceLexer();
i++;
}
if (i > 0) {
builder.advanceLexer();
em.error("Attempting to recover from previous errors.");
builder.setError(false);
} else {
em.rollbackTo();
if (closingBlock)
closingBlock = false;
}
}
/* }====================================================================== */
@NotNull
@Override
public ASTNode parse(IElementType root, PsiBuilder builder) {
final LuaPsiBuilder psiBuilder = new LuaPsiBuilder(builder);
try {
final PsiBuilder.Marker rootMarker = psiBuilder.mark();
String name = "todo:name";
source = name;
KahluaParser lexstate = new KahluaParser(z, 0, source);
FuncState funcstate = new FuncState(lexstate);
// lexstate.buff = buff;
/* main func. is always vararg */
funcstate.isVararg = FuncState.VARARG_ISVARARG;
funcstate.f.name = name;
lexstate.builder = psiBuilder;
lexstate.t = psiBuilder.getTokenType();
// lexstate.builder.debug();
// if (lexstate.t == null) // Try to kludge in handling of partial parses
// lexstate.next(); /* read first token */
lexstate.chunk();
// if (psiBuilder.isError() || closingBlock)
// cleanAfterError(psiBuilder);
int pos = psiBuilder.getCurrentOffset();
PsiBuilder.Marker mark = psiBuilder.mark();
while (!psiBuilder.eof())
psiBuilder.advanceLexer();
if (psiBuilder.getCurrentOffset()>pos)
mark.error("Unparsed code");
else
mark.drop();
// lexstate.check(EMPTY_INPUT);
lexstate.close_func();
FuncState._assert(funcstate.prev == null);
FuncState._assert(funcstate.f.numUpvalues == 0);
FuncState._assert(lexstate.fs == null);
// return funcstate.f;
if (root != null)
rootMarker.done(root);
} catch (ProcessCanceledException e) {
throw e;
} catch (Exception e) {
throw new PluginException("Exception During Parse At Offset: "+ builder.getCurrentOffset() + "\n\n" + builder.getOriginalText(), e, PluginId.getId("Lua"));
}
return builder.getTreeBuilt();
}
}