|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
-** $Id: lparser.c,v 1.49 1999/12/22 16:58:36 roberto Exp roberto $
|
|
|
|
|
|
+** $Id: lparser.c,v 1.50 1999/12/23 18:19:57 roberto Exp roberto $
|
|
** LL(1) Parser and code generator for Lua
|
|
** LL(1) Parser and code generator for Lua
|
|
** See Copyright Notice in lua.h
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
*/
|
|
@@ -63,6 +63,8 @@ typedef enum {
|
|
typedef struct vardesc {
|
|
typedef struct vardesc {
|
|
varkind k;
|
|
varkind k;
|
|
int info;
|
|
int info;
|
|
|
|
+ varkind prev_k; /* for debug information (NAMEs) */
|
|
|
|
+ int prev_info;
|
|
} vardesc;
|
|
} vardesc;
|
|
|
|
|
|
|
|
|
|
@@ -70,7 +72,7 @@ typedef struct vardesc {
|
|
** Expression List descriptor:
|
|
** Expression List descriptor:
|
|
** tells number of expressions in the list,
|
|
** tells number of expressions in the list,
|
|
** and, if last expression is open (a function call),
|
|
** and, if last expression is open (a function call),
|
|
-** where is its pc index of "nparam"
|
|
|
|
|
|
+** where is its pc index of `nparam'
|
|
*/
|
|
*/
|
|
typedef struct listdesc {
|
|
typedef struct listdesc {
|
|
int n;
|
|
int n;
|
|
@@ -93,7 +95,7 @@ typedef struct constdesc {
|
|
/* state needed to generate code for a given function */
|
|
/* state needed to generate code for a given function */
|
|
typedef struct FuncState {
|
|
typedef struct FuncState {
|
|
TProtoFunc *f; /* current function header */
|
|
TProtoFunc *f; /* current function header */
|
|
- struct FuncState *prev; /* enclosuring function */
|
|
|
|
|
|
+ struct FuncState *prev; /* enclosing function */
|
|
int pc; /* next position to code */
|
|
int pc; /* next position to code */
|
|
int stacksize; /* number of values on activation register */
|
|
int stacksize; /* number of values on activation register */
|
|
int maxstacksize; /* maximum number of values on activation register */
|
|
int maxstacksize; /* maximum number of values on activation register */
|
|
@@ -107,34 +109,13 @@ typedef struct FuncState {
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
-** prototypes for non-terminal functions
|
|
|
|
|
|
+** prototypes for recursive non-terminal functions
|
|
*/
|
|
*/
|
|
-static int assignment (LexState *ls, vardesc *v, int nvars);
|
|
|
|
-static int cond (LexState *ls);
|
|
|
|
-static int funcname (LexState *ls, vardesc *v);
|
|
|
|
-static int funcparams (LexState *ls, int slf);
|
|
|
|
-static int listfields (LexState *ls);
|
|
|
|
-static int localnamelist (LexState *ls);
|
|
|
|
-static int optional (LexState *ls, int c);
|
|
|
|
-static int recfields (LexState *ls);
|
|
|
|
-static int stat (LexState *ls);
|
|
|
|
-static void block (LexState *ls);
|
|
|
|
static void body (LexState *ls, int needself, int line);
|
|
static void body (LexState *ls, int needself, int line);
|
|
static void chunk (LexState *ls);
|
|
static void chunk (LexState *ls);
|
|
static void constructor (LexState *ls);
|
|
static void constructor (LexState *ls);
|
|
-static void decinit (LexState *ls, listdesc *d);
|
|
|
|
-static void exp0 (LexState *ls, vardesc *v);
|
|
|
|
|
|
+static void exp (LexState *ls, vardesc *v);
|
|
static void exp1 (LexState *ls);
|
|
static void exp1 (LexState *ls);
|
|
-static void exp2 (LexState *ls, vardesc *v);
|
|
|
|
-static void explist (LexState *ls, listdesc *e);
|
|
|
|
-static void explist1 (LexState *ls, listdesc *e);
|
|
|
|
-static void ifpart (LexState *ls, int line);
|
|
|
|
-static void parlist (LexState *ls);
|
|
|
|
-static void part (LexState *ls, constdesc *cd);
|
|
|
|
-static void recfield (LexState *ls);
|
|
|
|
-static void ret (LexState *ls);
|
|
|
|
-static void var_or_func (LexState *ls, vardesc *v);
|
|
|
|
-static void var_or_func_tail (LexState *ls, vardesc *v);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -268,7 +249,7 @@ static void code_string (LexState *ls, TaggedString *s) {
|
|
|
|
|
|
#define LIM 20
|
|
#define LIM 20
|
|
static int real_constant (LexState *ls, real r) {
|
|
static int real_constant (LexState *ls, real r) {
|
|
- /* check whether 'r' has appeared within the last LIM entries */
|
|
|
|
|
|
+ /* check whether `r' has appeared within the last LIM entries */
|
|
TProtoFunc *f = ls->fs->f;
|
|
TProtoFunc *f = ls->fs->f;
|
|
TObject *cnt = f->consts;
|
|
TObject *cnt = f->consts;
|
|
int c = f->nconsts;
|
|
int c = f->nconsts;
|
|
@@ -279,7 +260,7 @@ static int real_constant (LexState *ls, real r) {
|
|
}
|
|
}
|
|
/* not found; create a new entry */
|
|
/* not found; create a new entry */
|
|
c = next_constant(ls, f);
|
|
c = next_constant(ls, f);
|
|
- cnt = f->consts; /* 'next_constant' may reallocate this vector */
|
|
|
|
|
|
+ cnt = f->consts; /* `next_constant' may reallocate this vector */
|
|
ttype(&cnt[c]) = LUA_T_NUMBER;
|
|
ttype(&cnt[c]) = LUA_T_NUMBER;
|
|
nvalue(&cnt[c]) = r;
|
|
nvalue(&cnt[c]) = r;
|
|
return c;
|
|
return c;
|
|
@@ -412,6 +393,31 @@ static void check_debugline (LexState *ls) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+static void code_setname (LexState *ls, const vardesc *v) {
|
|
|
|
+ if (ls->L->debug) {
|
|
|
|
+ switch (v->prev_k) {
|
|
|
|
+ case VGLOBAL:
|
|
|
|
+ code_oparg(ls, SETNAME, v->prev_info, 0);
|
|
|
|
+ code_byte(ls, -LUA_T_NGLOBAL);
|
|
|
|
+ break;
|
|
|
|
+ case VLOCAL: {
|
|
|
|
+ TaggedString *varname = ls->fs->localvar[v->prev_info];
|
|
|
|
+ code_oparg(ls, SETNAME, string_constant(ls, ls->fs, varname), 0);
|
|
|
|
+ code_byte(ls, -LUA_T_NLOCAL);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case VDOT:
|
|
|
|
+ code_oparg(ls, SETNAME, v->prev_info, 0);
|
|
|
|
+ code_byte(ls, -LUA_T_NDOT);
|
|
|
|
+ break;
|
|
|
|
+ default: /* VINDEXED or VEXP: no debug information */
|
|
|
|
+ code_oparg(ls, SETNAME, 0, 0);
|
|
|
|
+ code_byte(ls, -LUA_T_NIL);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
static void adjuststack (LexState *ls, int n) {
|
|
static void adjuststack (LexState *ls, int n) {
|
|
if (n > 0)
|
|
if (n > 0)
|
|
code_oparg(ls, POP, n, -n);
|
|
code_oparg(ls, POP, n, -n);
|
|
@@ -468,7 +474,7 @@ static void code_args (LexState *ls, int nparams, int dots) {
|
|
|
|
|
|
|
|
|
|
static void unloaddot (LexState *ls, vardesc *v) {
|
|
static void unloaddot (LexState *ls, vardesc *v) {
|
|
- /* dotted variables <a.x> must be stored like regular indexed vars <a["x"]> */
|
|
|
|
|
|
+ /* dotted variables <a.x> must be stored as regular indexed vars <a["x"]> */
|
|
if (v->k == VDOT) {
|
|
if (v->k == VDOT) {
|
|
code_constant(ls, v->info);
|
|
code_constant(ls, v->info);
|
|
v->k = VINDEXED;
|
|
v->k = VINDEXED;
|
|
@@ -486,15 +492,19 @@ static void lua_pushvar (LexState *ls, vardesc *var) {
|
|
assertglobal(ls, var->info); /* make sure that there is a global */
|
|
assertglobal(ls, var->info); /* make sure that there is a global */
|
|
break;
|
|
break;
|
|
case VDOT:
|
|
case VDOT:
|
|
|
|
+ code_setname(ls, var);
|
|
code_oparg(ls, GETDOTTED, var->info, 0);
|
|
code_oparg(ls, GETDOTTED, var->info, 0);
|
|
break;
|
|
break;
|
|
case VINDEXED:
|
|
case VINDEXED:
|
|
|
|
+ code_setname(ls, var);
|
|
code_opcode(ls, GETTABLE, -1);
|
|
code_opcode(ls, GETTABLE, -1);
|
|
break;
|
|
break;
|
|
case VEXP:
|
|
case VEXP:
|
|
close_exp(ls, var->info, 1); /* function must return 1 value */
|
|
close_exp(ls, var->info, 1); /* function must return 1 value */
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ var->prev_k = var->k; /* save previous var kind and info */
|
|
|
|
+ var->prev_info = var->info;
|
|
var->k = VEXP;
|
|
var->k = VEXP;
|
|
var->info = 0; /* now this is a closed expression */
|
|
var->info = 0; /* now this is a closed expression */
|
|
}
|
|
}
|
|
@@ -510,6 +520,7 @@ static void storevar (LexState *ls, const vardesc *var) {
|
|
assertglobal(ls, var->info); /* make sure that there is a global */
|
|
assertglobal(ls, var->info); /* make sure that there is a global */
|
|
break;
|
|
break;
|
|
case VINDEXED:
|
|
case VINDEXED:
|
|
|
|
+ code_setname(ls, var);
|
|
code_opcode(ls, SETTABLEPOP, -3);
|
|
code_opcode(ls, SETTABLEPOP, -3);
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
@@ -656,7 +667,7 @@ static void check (LexState *ls, int c) {
|
|
static void check_match (LexState *ls, int what, int who, int where) {
|
|
static void check_match (LexState *ls, int what, int who, int where) {
|
|
if (ls->token != what)
|
|
if (ls->token != what)
|
|
error_unmatched(ls, what, who, where);
|
|
error_unmatched(ls, what, who, where);
|
|
- check_debugline(ls); /* to 'mark' the 'what' */
|
|
|
|
|
|
+ check_debugline(ls); /* to `mark' the `what' */
|
|
next(ls);
|
|
next(ls);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -705,241 +716,298 @@ TProtoFunc *luaY_parser (lua_State *L, ZIO *z) {
|
|
/*============================================================*/
|
|
/*============================================================*/
|
|
|
|
|
|
|
|
|
|
-static void chunk (LexState *ls) {
|
|
|
|
- /* chunk -> { stat [;] } ret */
|
|
|
|
- while (stat(ls)) {
|
|
|
|
- LUA_ASSERT(ls->L, ls->fs->stacksize == ls->fs->nlocalvar,
|
|
|
|
- "stack size != # local vars");
|
|
|
|
- optional(ls, ';');
|
|
|
|
- }
|
|
|
|
- ret(ls); /* optional return */
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
|
|
|
|
-static void whilestat (LexState *ls, int line) {
|
|
|
|
- /* whilestat -> WHILE cond DO block END */
|
|
|
|
- FuncState *fs = ls->fs;
|
|
|
|
- TProtoFunc *f = fs->f;
|
|
|
|
- int while_init = fs->pc;
|
|
|
|
- int cond_end, cond_size;
|
|
|
|
- next(ls);
|
|
|
|
- cond_end = cond(ls);
|
|
|
|
- check(ls, DO);
|
|
|
|
- block(ls);
|
|
|
|
- check_match(ls, END, WHILE, line);
|
|
|
|
- cond_size = cond_end-while_init;
|
|
|
|
- check_pc(ls, cond_size);
|
|
|
|
- memcpy(f->code+fs->pc, f->code+while_init, cond_size);
|
|
|
|
- luaO_memdown(f->code+while_init, f->code+cond_end, fs->pc-while_init);
|
|
|
|
- while_init += JMPSIZE + fix_jump(ls, while_init, JMP, fs->pc-cond_size);
|
|
|
|
- fix_upjmp(ls, IFTUPJMP, while_init);
|
|
|
|
|
|
+static int SaveWord (LexState *ls) {
|
|
|
|
+ int res = ls->fs->pc;
|
|
|
|
+ check_pc(ls, JMPSIZE);
|
|
|
|
+ ls->fs->pc += JMPSIZE; /* open space */
|
|
|
|
+ return res;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void repeatstat (LexState *ls, int line) {
|
|
|
|
- /* repeatstat -> REPEAT block UNTIL exp1 */
|
|
|
|
- FuncState *fs = ls->fs;
|
|
|
|
- int repeat_init = fs->pc;
|
|
|
|
- next(ls);
|
|
|
|
- block(ls);
|
|
|
|
- check_match(ls, UNTIL, REPEAT, line);
|
|
|
|
- exp1(ls);
|
|
|
|
- fix_upjmp(ls, IFFUPJMP, repeat_init);
|
|
|
|
- deltastack(ls, -1); /* pops condition */
|
|
|
|
|
|
+static int SaveWordPop (LexState *ls) {
|
|
|
|
+ deltastack(ls, -1); /* pop condition */
|
|
|
|
+ return SaveWord(ls);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void localstat (LexState *ls) {
|
|
|
|
- /* stat -> LOCAL localnamelist decinit */
|
|
|
|
- FuncState *fs = ls->fs;
|
|
|
|
- listdesc d;
|
|
|
|
- int nvars;
|
|
|
|
- check_debugline(ls);
|
|
|
|
- next(ls);
|
|
|
|
- nvars = localnamelist(ls);
|
|
|
|
- decinit(ls, &d);
|
|
|
|
- adjustlocalvars(ls, nvars, fs->lastsetline);
|
|
|
|
- adjust_mult_assign(ls, nvars, &d);
|
|
|
|
|
|
+static int cond (LexState *ls) {
|
|
|
|
+ /* cond -> exp1 */
|
|
|
|
+ exp1(ls);
|
|
|
|
+ return SaveWordPop(ls);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static int funcstat (LexState *ls, int line) {
|
|
|
|
- /* funcstat -> FUNCTION funcname body */
|
|
|
|
- int needself;
|
|
|
|
|
|
+static void explist1 (LexState *ls, listdesc *d) {
|
|
vardesc v;
|
|
vardesc v;
|
|
- if (ls->fs->prev) /* inside other function? */
|
|
|
|
- return 0;
|
|
|
|
- check_debugline(ls);
|
|
|
|
- next(ls);
|
|
|
|
- needself = funcname(ls, &v);
|
|
|
|
- body(ls, needself, line);
|
|
|
|
- storevar(ls, &v);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ exp(ls, &v);
|
|
|
|
+ d->n = 1;
|
|
|
|
+ while (ls->token == ',') {
|
|
|
|
+ d->n++;
|
|
|
|
+ lua_pushvar(ls, &v);
|
|
|
|
+ next(ls);
|
|
|
|
+ exp(ls, &v);
|
|
|
|
+ }
|
|
|
|
+ if (v.k == VEXP)
|
|
|
|
+ d->pc = v.info;
|
|
|
|
+ else {
|
|
|
|
+ lua_pushvar(ls, &v);
|
|
|
|
+ d->pc = 0;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void namestat (LexState *ls) {
|
|
|
|
- /* stat -> func | ['%'] NAME assignment */
|
|
|
|
- vardesc v;
|
|
|
|
- check_debugline(ls);
|
|
|
|
- var_or_func(ls, &v);
|
|
|
|
- if (v.k == VEXP) { /* stat -> func */
|
|
|
|
- if (v.info == 0) /* is just an upper value? */
|
|
|
|
- luaX_error(ls, "syntax error");
|
|
|
|
- close_exp(ls, v.info, 0);
|
|
|
|
- }
|
|
|
|
- else { /* stat -> ['%'] NAME assignment */
|
|
|
|
- int left = assignment(ls, &v, 1);
|
|
|
|
- adjuststack(ls, left); /* remove eventual garbage left on stack */
|
|
|
|
|
|
+static void explist (LexState *ls, listdesc *d) {
|
|
|
|
+ switch (ls->token) {
|
|
|
|
+ case ELSE: case ELSEIF: case END: case UNTIL:
|
|
|
|
+ case EOS: case ';': case ')':
|
|
|
|
+ d->pc = 0;
|
|
|
|
+ d->n = 0;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ explist1(ls, d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static int stat (LexState *ls) {
|
|
|
|
- int line = ls->linenumber; /* may be needed for error messages */
|
|
|
|
|
|
+static int funcparams (LexState *ls, int slf, vardesc *v) {
|
|
|
|
+ FuncState *fs = ls->fs;
|
|
|
|
+ int slevel = fs->stacksize - slf - 1; /* where is func in the stack */
|
|
switch (ls->token) {
|
|
switch (ls->token) {
|
|
- case IF: /* stat -> IF ifpart END */
|
|
|
|
- ifpart(ls, line);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ case '(': { /* funcparams -> '(' explist ')' */
|
|
|
|
+ int line = ls->linenumber;
|
|
|
|
+ listdesc e;
|
|
|
|
+ next(ls);
|
|
|
|
+ explist(ls, &e);
|
|
|
|
+ check_match(ls, ')', '(', line);
|
|
|
|
+ close_exp(ls, e.pc, MULT_RET); /* close 1 for old semantics */
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
- case WHILE: /* stat -> whilestat */
|
|
|
|
- whilestat(ls, line);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ case '{': /* funcparams -> constructor */
|
|
|
|
+ constructor(ls);
|
|
|
|
+ break;
|
|
|
|
|
|
- case DO: { /* stat -> DO block END */
|
|
|
|
|
|
+ case STRING: /* funcparams -> STRING */
|
|
|
|
+ code_string(ls, ls->seminfo.ts); /* must use 'seminfo' before `next' */
|
|
next(ls);
|
|
next(ls);
|
|
- block(ls);
|
|
|
|
- check_match(ls, END, DO, line);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
|
|
+ break;
|
|
|
|
|
|
- case REPEAT: /* stat -> repeatstat */
|
|
|
|
- repeatstat(ls, line);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ default:
|
|
|
|
+ luaX_error(ls, "function arguments expected");
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ code_setname(ls, v);
|
|
|
|
+ code_byte(ls, CALL);
|
|
|
|
+ code_byte(ls, 0); /* save space for nresult */
|
|
|
|
+ code_byte(ls, (Byte)slevel);
|
|
|
|
+ fs->stacksize = slevel; /* call will remove func and params */
|
|
|
|
+ return fs->pc-1;
|
|
|
|
+}
|
|
|
|
|
|
- case FUNCTION: /* stat -> funcstat */
|
|
|
|
- return funcstat(ls, line);
|
|
|
|
|
|
|
|
- case LOCAL: /* stat -> localstat */
|
|
|
|
- localstat(ls);
|
|
|
|
- return 1;
|
|
|
|
|
|
+static void var_or_func_tail (LexState *ls, vardesc *v) {
|
|
|
|
+ for (;;) {
|
|
|
|
+ switch (ls->token) {
|
|
|
|
+ case '.': /* var_or_func_tail -> '.' NAME */
|
|
|
|
+ next(ls);
|
|
|
|
+ lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
+ v->k = VDOT;
|
|
|
|
+ v->info = checkname(ls);
|
|
|
|
+ break;
|
|
|
|
|
|
- case NAME: case '%': /* stat -> namestat */
|
|
|
|
- namestat(ls);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ case '[': /* var_or_func_tail -> '[' exp1 ']' */
|
|
|
|
+ next(ls);
|
|
|
|
+ lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
+ exp1(ls);
|
|
|
|
+ check(ls, ']');
|
|
|
|
+ v->k = VINDEXED;
|
|
|
|
+ break;
|
|
|
|
|
|
- case RETURN: case ';': case ELSE: case ELSEIF:
|
|
|
|
- case END: case UNTIL: case EOS: /* 'stat' follow */
|
|
|
|
- return 0;
|
|
|
|
|
|
+ case ':': { /* var_or_func_tail -> ':' NAME funcparams */
|
|
|
|
+ int name;
|
|
|
|
+ next(ls);
|
|
|
|
+ name = checkname(ls);
|
|
|
|
+ lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
+ code_setname(ls, v);
|
|
|
|
+ code_oparg(ls, PUSHSELF, name, 1);
|
|
|
|
+ v->prev_k = VDOT; /* ':' is syntactic sugar for '.' */
|
|
|
|
+ v->prev_info = name;
|
|
|
|
+ v->k = VEXP;
|
|
|
|
+ v->info = funcparams(ls, 1, v);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
- default:
|
|
|
|
- error_unexpected(ls);
|
|
|
|
- return 0; /* to avoid warnings */
|
|
|
|
|
|
+ case '(': case STRING: case '{': /* var_or_func_tail -> funcparams */
|
|
|
|
+ lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
+ v->k = VEXP;
|
|
|
|
+ v->info = funcparams(ls, 0, v);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default: return; /* should be follow... */
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int SaveWord (LexState *ls) {
|
|
|
|
- int res = ls->fs->pc;
|
|
|
|
- check_pc(ls, JMPSIZE);
|
|
|
|
- ls->fs->pc += JMPSIZE; /* open space */
|
|
|
|
- return res;
|
|
|
|
-}
|
|
|
|
|
|
|
|
-static int SaveWordPop (LexState *ls) {
|
|
|
|
- deltastack(ls, -1); /* pop condition */
|
|
|
|
- return SaveWord(ls);
|
|
|
|
|
|
+static void var_or_func (LexState *ls, vardesc *v) {
|
|
|
|
+ /* var_or_func -> ['%'] NAME var_or_func_tail */
|
|
|
|
+ if (optional(ls, '%')) { /* upvalue? */
|
|
|
|
+ pushupvalue(ls, str_checkname(ls));
|
|
|
|
+ v->k = VEXP;
|
|
|
|
+ v->info = 0; /* closed expression */
|
|
|
|
+ }
|
|
|
|
+ else /* variable name */
|
|
|
|
+ singlevar(ls, str_checkname(ls), v, 0);
|
|
|
|
+ var_or_func_tail(ls, v);
|
|
}
|
|
}
|
|
|
|
|
|
-static int cond (LexState *ls) {
|
|
|
|
- /* cond -> exp1 */
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+** {======================================================================
|
|
|
|
+** Rules for Constructors
|
|
|
|
+** =======================================================================
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+static void recfield (LexState *ls) {
|
|
|
|
+ /* recfield -> (NAME | '['exp1']') = exp1 */
|
|
|
|
+ switch (ls->token) {
|
|
|
|
+ case NAME:
|
|
|
|
+ code_constant(ls, checkname(ls));
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case '[':
|
|
|
|
+ next(ls);
|
|
|
|
+ exp1(ls);
|
|
|
|
+ check(ls, ']');
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default: luaX_error(ls, "NAME or `[' expected");
|
|
|
|
+ }
|
|
|
|
+ check(ls, '=');
|
|
exp1(ls);
|
|
exp1(ls);
|
|
- return SaveWordPop(ls);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static void block (LexState *ls) {
|
|
|
|
- /* block -> chunk */
|
|
|
|
- FuncState *fs = ls->fs;
|
|
|
|
- int nlocalvar = fs->nlocalvar;
|
|
|
|
- chunk(ls);
|
|
|
|
- adjuststack(ls, fs->nlocalvar - nlocalvar);
|
|
|
|
- for (; fs->nlocalvar > nlocalvar; fs->nlocalvar--)
|
|
|
|
- luaI_unregisterlocalvar(ls, fs->lastsetline);
|
|
|
|
-}
|
|
|
|
|
|
|
|
-static int funcname (LexState *ls, vardesc *v) {
|
|
|
|
- /* funcname -> NAME [':' NAME | '.' NAME] */
|
|
|
|
- int needself = 0;
|
|
|
|
- singlevar(ls, str_checkname(ls), v, 0);
|
|
|
|
- if (ls->token == ':' || ls->token == '.') {
|
|
|
|
- needself = (ls->token == ':');
|
|
|
|
|
|
+static int recfields (LexState *ls) {
|
|
|
|
+ /* recfields -> { ',' recfield } [','] */
|
|
|
|
+ int n = 1; /* one has been read before */
|
|
|
|
+ while (ls->token == ',') {
|
|
next(ls);
|
|
next(ls);
|
|
- lua_pushvar(ls, v);
|
|
|
|
- code_constant(ls, checkname(ls));
|
|
|
|
- v->k = VINDEXED;
|
|
|
|
|
|
+ if (ls->token == ';' || ls->token == '}')
|
|
|
|
+ break;
|
|
|
|
+ recfield(ls);
|
|
|
|
+ n++;
|
|
|
|
+ if (n%RFIELDS_PER_FLUSH == 0)
|
|
|
|
+ flush_record(ls, RFIELDS_PER_FLUSH);
|
|
}
|
|
}
|
|
- return needself;
|
|
|
|
|
|
+ flush_record(ls, n%RFIELDS_PER_FLUSH);
|
|
|
|
+ return n;
|
|
}
|
|
}
|
|
|
|
|
|
-static void body (LexState *ls, int needself, int line) {
|
|
|
|
- /* body -> '(' parlist ')' chunk END */
|
|
|
|
- FuncState newfs;
|
|
|
|
- init_state(ls, &newfs, ls->fs->f->source);
|
|
|
|
- newfs.f->lineDefined = line;
|
|
|
|
- check(ls, '(');
|
|
|
|
- if (needself)
|
|
|
|
- add_localvar(ls, luaS_newfixed(ls->L, "self"));
|
|
|
|
- parlist(ls);
|
|
|
|
- check(ls, ')');
|
|
|
|
- chunk(ls);
|
|
|
|
- check_match(ls, END, FUNCTION, line);
|
|
|
|
- close_func(ls);
|
|
|
|
- func_onstack(ls, &newfs);
|
|
|
|
|
|
+
|
|
|
|
+static int listfields (LexState *ls) {
|
|
|
|
+ /* listfields -> { ',' exp1 } [','] */
|
|
|
|
+ int n = 1; /* one has been read before */
|
|
|
|
+ while (ls->token == ',') {
|
|
|
|
+ next(ls);
|
|
|
|
+ if (ls->token == ';' || ls->token == '}')
|
|
|
|
+ break;
|
|
|
|
+ exp1(ls);
|
|
|
|
+ n++;
|
|
|
|
+ if (n%LFIELDS_PER_FLUSH == 0)
|
|
|
|
+ flush_list(ls, n/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH);
|
|
|
|
+ }
|
|
|
|
+ flush_list(ls, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH);
|
|
|
|
+ return n;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void ifpart (LexState *ls, int line) {
|
|
|
|
- /* ifpart -> cond THEN block [ELSE block | ELSEIF ifpart] */
|
|
|
|
- int c;
|
|
|
|
- int e;
|
|
|
|
- next(ls); /* skip IF or ELSEIF */
|
|
|
|
- c = cond(ls);
|
|
|
|
- check(ls, THEN);
|
|
|
|
- block(ls);
|
|
|
|
- e = SaveWord(ls);
|
|
|
|
- if (ls->token == ELSEIF)
|
|
|
|
- ifpart(ls, line);
|
|
|
|
- else {
|
|
|
|
- if (optional(ls, ELSE))
|
|
|
|
- block(ls);
|
|
|
|
- check_match(ls, END, IF, line);
|
|
|
|
|
|
+
|
|
|
|
+static void constructor_part (LexState *ls, constdesc *cd) {
|
|
|
|
+ switch (ls->token) {
|
|
|
|
+ case ';': case '}': /* constructor_part -> empty */
|
|
|
|
+ cd->n = 0;
|
|
|
|
+ cd->k = ls->token;
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ case NAME: {
|
|
|
|
+ vardesc v;
|
|
|
|
+ exp(ls, &v);
|
|
|
|
+ if (ls->token == '=') {
|
|
|
|
+ switch (v.k) {
|
|
|
|
+ case VGLOBAL:
|
|
|
|
+ code_constant(ls, v.info);
|
|
|
|
+ break;
|
|
|
|
+ case VLOCAL:
|
|
|
|
+ code_string(ls, ls->fs->localvar[v.info]);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ error_unexpected(ls);
|
|
|
|
+ }
|
|
|
|
+ next(ls);
|
|
|
|
+ exp1(ls);
|
|
|
|
+ cd->n = recfields(ls);
|
|
|
|
+ cd->k = 1; /* record */
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ lua_pushvar(ls, &v);
|
|
|
|
+ cd->n = listfields(ls);
|
|
|
|
+ cd->k = 0; /* list */
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ case '[': /* constructor_part -> recfield recfields */
|
|
|
|
+ recfield(ls);
|
|
|
|
+ cd->n = recfields(ls);
|
|
|
|
+ cd->k = 1; /* record */
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default: /* constructor_part -> exp1 listfields */
|
|
|
|
+ exp1(ls);
|
|
|
|
+ cd->n = listfields(ls);
|
|
|
|
+ cd->k = 0; /* list */
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
- codeIf(ls, c, e);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void ret (LexState *ls) {
|
|
|
|
- /* ret -> [RETURN explist sc] */
|
|
|
|
- if (optional(ls, RETURN)) {
|
|
|
|
- listdesc e;
|
|
|
|
- check_debugline(ls);
|
|
|
|
- explist(ls, &e);
|
|
|
|
- if (e.pc > 0) { /* expression is an open function call? */
|
|
|
|
- Byte *code = ls->fs->f->code;
|
|
|
|
- code[e.pc-2] = TAILCALL; /* instead of a conventional CALL */
|
|
|
|
- code[e.pc-1] = (Byte)ls->fs->nlocalvar;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- code_oparg(ls, RETCODE, ls->fs->nlocalvar, 0);
|
|
|
|
- ls->fs->stacksize = ls->fs->nlocalvar; /* removes all temp values */
|
|
|
|
- optional(ls, ';');
|
|
|
|
|
|
+static void constructor (LexState *ls) {
|
|
|
|
+ /* constructor -> '{' constructor_part [';' constructor_part] '}' */
|
|
|
|
+ int line = ls->linenumber;
|
|
|
|
+ int pc = SaveWord(ls);
|
|
|
|
+ int nelems;
|
|
|
|
+ constdesc cd;
|
|
|
|
+ deltastack(ls, 1);
|
|
|
|
+ check(ls, '{');
|
|
|
|
+ constructor_part(ls, &cd);
|
|
|
|
+ nelems = cd.n;
|
|
|
|
+ if (ls->token == ';') {
|
|
|
|
+ constdesc other_cd;
|
|
|
|
+ next(ls);
|
|
|
|
+ constructor_part(ls, &other_cd);
|
|
|
|
+ if (cd.k == other_cd.k) /* repeated parts? */
|
|
|
|
+ luaX_error(ls, "invalid constructor syntax");
|
|
|
|
+ nelems += other_cd.n;
|
|
}
|
|
}
|
|
|
|
+ check_match(ls, '}', '{', line);
|
|
|
|
+ fix_opcode(ls, pc, CREATEARRAY, nelems);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* }====================================================================== */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
|
|
/*
|
|
/*
|
|
|
|
+** {======================================================================
|
|
** For parsing expressions, we use a classic stack with priorities.
|
|
** For parsing expressions, we use a classic stack with priorities.
|
|
-** Each binary operator is represented by its index in "binop" + FIRSTBIN
|
|
|
|
|
|
+** Each binary operator is represented by its index in `binop' + FIRSTBIN
|
|
** (EQ=2, NE=3, ... '^'=13). The unary NOT is 0 and UNMINUS is 1.
|
|
** (EQ=2, NE=3, ... '^'=13). The unary NOT is 0 and UNMINUS is 1.
|
|
|
|
+** =======================================================================
|
|
*/
|
|
*/
|
|
|
|
|
|
#define INDNOT 0
|
|
#define INDNOT 0
|
|
@@ -969,30 +1037,6 @@ typedef struct stack_op {
|
|
} stack_op;
|
|
} stack_op;
|
|
|
|
|
|
|
|
|
|
-static void exp1 (LexState *ls) {
|
|
|
|
- vardesc v;
|
|
|
|
- exp0(ls, &v);
|
|
|
|
- lua_pushvar(ls, &v);
|
|
|
|
- if (is_in(ls->token, expfollow) < 0)
|
|
|
|
- luaX_error(ls, "malformed expression");
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-static void exp0 (LexState *ls, vardesc *v) {
|
|
|
|
- /* exp0 -> exp2 {(AND | OR) exp2} */
|
|
|
|
- exp2(ls, v);
|
|
|
|
- while (ls->token == AND || ls->token == OR) {
|
|
|
|
- int op = (ls->token == AND) ? ONFJMP : ONTJMP;
|
|
|
|
- int pc;
|
|
|
|
- lua_pushvar(ls, v);
|
|
|
|
- next(ls);
|
|
|
|
- pc = SaveWordPop(ls);
|
|
|
|
- exp2(ls, v);
|
|
|
|
- lua_pushvar(ls, v);
|
|
|
|
- fix_jump(ls, pc, op, ls->fs->pc);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
|
|
|
|
static void push (LexState *ls, stack_op *s, int op) {
|
|
static void push (LexState *ls, stack_op *s, int op) {
|
|
if (s->top >= MAXOPS)
|
|
if (s->top >= MAXOPS)
|
|
@@ -1015,8 +1059,8 @@ static void simpleexp (LexState *ls, vardesc *v, stack_op *s) {
|
|
case NUMBER: { /* simpleexp -> NUMBER */
|
|
case NUMBER: { /* simpleexp -> NUMBER */
|
|
real r = ls->seminfo.r;
|
|
real r = ls->seminfo.r;
|
|
next(ls);
|
|
next(ls);
|
|
- /* dirty trick: check whether it is a -NUMBER not followed by '^' */
|
|
|
|
- /* (because the priority of '^' is higher than '-'...) */
|
|
|
|
|
|
+ /* dirty trick: check whether it is a -NUMBER not followed by '^' */
|
|
|
|
+ /* (because the priority of '^' is higher than the priority of '-') */
|
|
if (s->top > 0 && s->ops[s->top-1] == INDMINUS && ls->token != '^') {
|
|
if (s->top > 0 && s->ops[s->top-1] == INDMINUS && ls->token != '^') {
|
|
s->top--; /* remove '-' from stack */
|
|
s->top--; /* remove '-' from stack */
|
|
r = -r;
|
|
r = -r;
|
|
@@ -1044,9 +1088,9 @@ static void simpleexp (LexState *ls, vardesc *v, stack_op *s) {
|
|
body(ls, 0, ls->linenumber);
|
|
body(ls, 0, ls->linenumber);
|
|
break;
|
|
break;
|
|
|
|
|
|
- case '(': /* simpleexp -> '(' exp0 ')' */
|
|
|
|
|
|
+ case '(': /* simpleexp -> '(' exp ')' */
|
|
next(ls);
|
|
next(ls);
|
|
- exp0(ls, v);
|
|
|
|
|
|
+ exp(ls, v);
|
|
check(ls, ')');
|
|
check(ls, ')');
|
|
return;
|
|
return;
|
|
|
|
|
|
@@ -1072,7 +1116,7 @@ static void prefixexp (LexState *ls, vardesc *v, stack_op *s) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void exp2 (LexState *ls, vardesc *v) {
|
|
|
|
|
|
+static void arith_exp (LexState *ls, vardesc *v) {
|
|
stack_op s;
|
|
stack_op s;
|
|
int op;
|
|
int op;
|
|
s.top = 0;
|
|
s.top = 0;
|
|
@@ -1094,156 +1138,118 @@ static void exp2 (LexState *ls, vardesc *v) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void var_or_func (LexState *ls, vardesc *v) {
|
|
|
|
- /* var_or_func -> ['%'] NAME var_or_func_tail */
|
|
|
|
- if (optional(ls, '%')) { /* upvalue? */
|
|
|
|
- pushupvalue(ls, str_checkname(ls));
|
|
|
|
- v->k = VEXP;
|
|
|
|
- v->info = 0; /* closed expression */
|
|
|
|
- }
|
|
|
|
- else /* variable name */
|
|
|
|
- singlevar(ls, str_checkname(ls), v, 0);
|
|
|
|
- var_or_func_tail(ls, v);
|
|
|
|
|
|
+static void exp1 (LexState *ls) {
|
|
|
|
+ vardesc v;
|
|
|
|
+ exp(ls, &v);
|
|
|
|
+ lua_pushvar(ls, &v);
|
|
|
|
+ if (is_in(ls->token, expfollow) < 0)
|
|
|
|
+ luaX_error(ls, "malformed expression");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void var_or_func_tail (LexState *ls, vardesc *v) {
|
|
|
|
- for (;;) {
|
|
|
|
- switch (ls->token) {
|
|
|
|
- case '.': /* var_or_func_tail -> '.' NAME */
|
|
|
|
- next(ls);
|
|
|
|
- lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
- v->k = VDOT;
|
|
|
|
- v->info = checkname(ls);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case '[': /* var_or_func_tail -> '[' exp1 ']' */
|
|
|
|
- next(ls);
|
|
|
|
- lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
- exp1(ls);
|
|
|
|
- check(ls, ']');
|
|
|
|
- v->k = VINDEXED;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case ':': /* var_or_func_tail -> ':' NAME funcparams */
|
|
|
|
- next(ls);
|
|
|
|
- lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
- code_oparg(ls, PUSHSELF, checkname(ls), 1);
|
|
|
|
- v->k = VEXP;
|
|
|
|
- v->info = funcparams(ls, 1);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case '(': case STRING: case '{': /* var_or_func_tail -> funcparams */
|
|
|
|
- lua_pushvar(ls, v); /* `v' must be on stack */
|
|
|
|
- v->k = VEXP;
|
|
|
|
- v->info = funcparams(ls, 0);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default: return; /* should be follow... */
|
|
|
|
- }
|
|
|
|
|
|
+static void exp (LexState *ls, vardesc *v) {
|
|
|
|
+ /* exp -> arith_exp {(AND | OR) arith_exp} */
|
|
|
|
+ arith_exp(ls, v);
|
|
|
|
+ while (ls->token == AND || ls->token == OR) {
|
|
|
|
+ OpCode op = (ls->token == AND) ? ONFJMP : ONTJMP;
|
|
|
|
+ int pc;
|
|
|
|
+ lua_pushvar(ls, v);
|
|
|
|
+ next(ls);
|
|
|
|
+ pc = SaveWordPop(ls);
|
|
|
|
+ arith_exp(ls, v);
|
|
|
|
+ lua_pushvar(ls, v);
|
|
|
|
+ fix_jump(ls, pc, op, ls->fs->pc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int funcparams (LexState *ls, int slf) {
|
|
|
|
- FuncState *fs = ls->fs;
|
|
|
|
- int slevel = fs->stacksize - slf - 1; /* where is func in the stack */
|
|
|
|
- switch (ls->token) {
|
|
|
|
- case '(': { /* funcparams -> '(' explist ')' */
|
|
|
|
- int line = ls->linenumber;
|
|
|
|
- listdesc e;
|
|
|
|
- next(ls);
|
|
|
|
- explist(ls, &e);
|
|
|
|
- check_match(ls, ')', '(', line);
|
|
|
|
- close_exp(ls, e.pc, MULT_RET); /* close 1 for old semantics */
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- case '{': /* funcparams -> constructor */
|
|
|
|
- constructor(ls);
|
|
|
|
- break;
|
|
|
|
|
|
+/* }==================================================================== */
|
|
|
|
|
|
- case STRING: /* funcparams -> STRING */
|
|
|
|
- code_string(ls, ls->seminfo.ts); /* must use 'seminfo' before `next' */
|
|
|
|
- next(ls);
|
|
|
|
- break;
|
|
|
|
|
|
|
|
- default:
|
|
|
|
- luaX_error(ls, "function arguments expected");
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- code_byte(ls, CALL);
|
|
|
|
- code_byte(ls, 0); /* save space for nresult */
|
|
|
|
- code_byte(ls, (Byte)slevel);
|
|
|
|
- fs->stacksize = slevel; /* call will remove func and params */
|
|
|
|
- return fs->pc-1;
|
|
|
|
|
|
+/*
|
|
|
|
+** {======================================================================
|
|
|
|
+** Rules for Statements
|
|
|
|
+** =======================================================================
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void block (LexState *ls) {
|
|
|
|
+ /* block -> chunk */
|
|
|
|
+ FuncState *fs = ls->fs;
|
|
|
|
+ int nlocalvar = fs->nlocalvar;
|
|
|
|
+ chunk(ls);
|
|
|
|
+ adjuststack(ls, fs->nlocalvar - nlocalvar);
|
|
|
|
+ for (; fs->nlocalvar > nlocalvar; fs->nlocalvar--)
|
|
|
|
+ luaI_unregisterlocalvar(ls, fs->lastsetline);
|
|
}
|
|
}
|
|
|
|
|
|
-static void explist (LexState *ls, listdesc *d) {
|
|
|
|
- switch (ls->token) {
|
|
|
|
- case ELSE: case ELSEIF: case END: case UNTIL:
|
|
|
|
- case EOS: case ';': case ')':
|
|
|
|
- d->pc = 0;
|
|
|
|
- d->n = 0;
|
|
|
|
- break;
|
|
|
|
|
|
|
|
- default:
|
|
|
|
- explist1(ls, d);
|
|
|
|
|
|
+static int assignment (LexState *ls, vardesc *v, int nvars) {
|
|
|
|
+ int left = 0;
|
|
|
|
+ checklimit(ls, nvars, MAXVARSLH, "variables in a multiple assignment");
|
|
|
|
+ unloaddot(ls, v);
|
|
|
|
+ if (ls->token == ',') { /* assignment -> ',' NAME assignment */
|
|
|
|
+ vardesc nv;
|
|
|
|
+ next(ls);
|
|
|
|
+ var_or_func(ls, &nv);
|
|
|
|
+ if (nv.k == VEXP)
|
|
|
|
+ luaX_error(ls, "syntax error");
|
|
|
|
+ left = assignment(ls, &nv, nvars+1);
|
|
}
|
|
}
|
|
-}
|
|
|
|
-
|
|
|
|
-static void explist1 (LexState *ls, listdesc *d) {
|
|
|
|
- vardesc v;
|
|
|
|
- exp0(ls, &v);
|
|
|
|
- d->n = 1;
|
|
|
|
- while (ls->token == ',') {
|
|
|
|
- d->n++;
|
|
|
|
- lua_pushvar(ls, &v);
|
|
|
|
|
|
+ else { /* assignment -> '=' explist1 */
|
|
|
|
+ listdesc d;
|
|
|
|
+ if (ls->token != '=')
|
|
|
|
+ error_unexpected(ls);
|
|
next(ls);
|
|
next(ls);
|
|
- exp0(ls, &v);
|
|
|
|
|
|
+ explist1(ls, &d);
|
|
|
|
+ adjust_mult_assign(ls, nvars, &d);
|
|
}
|
|
}
|
|
- if (v.k == VEXP)
|
|
|
|
- d->pc = v.info;
|
|
|
|
- else {
|
|
|
|
- lua_pushvar(ls, &v);
|
|
|
|
- d->pc = 0;
|
|
|
|
|
|
+ if (v->k != VINDEXED || left+(nvars-1) == 0) {
|
|
|
|
+ /* global/local var or indexed var without values in between */
|
|
|
|
+ storevar(ls, v);
|
|
|
|
+ }
|
|
|
|
+ else { /* indexed var with values in between*/
|
|
|
|
+ code_setname(ls, v);
|
|
|
|
+ code_oparg(ls, SETTABLE, left+(nvars-1), -1);
|
|
|
|
+ left += 2; /* table&index are not popped, because they aren't on top */
|
|
}
|
|
}
|
|
|
|
+ return left;
|
|
}
|
|
}
|
|
|
|
|
|
-static void parlist (LexState *ls) {
|
|
|
|
- int nparams = 0;
|
|
|
|
- int dots = 0;
|
|
|
|
- switch (ls->token) {
|
|
|
|
- case DOTS: /* parlist -> DOTS */
|
|
|
|
- next(ls);
|
|
|
|
- dots = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case NAME: /* parlist, tailparlist -> NAME [',' tailparlist] */
|
|
|
|
- init:
|
|
|
|
- store_localvar(ls, str_checkname(ls), nparams++);
|
|
|
|
- if (ls->token == ',') {
|
|
|
|
- next(ls);
|
|
|
|
- switch (ls->token) {
|
|
|
|
- case DOTS: /* tailparlist -> DOTS */
|
|
|
|
- next(ls);
|
|
|
|
- dots = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case NAME: /* tailparlist -> NAME [',' tailparlist] */
|
|
|
|
- goto init;
|
|
|
|
|
|
|
|
- default: luaX_error(ls, "NAME or `...' expected");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
|
|
+static void whilestat (LexState *ls, int line) {
|
|
|
|
+ /* whilestat -> WHILE cond DO block END */
|
|
|
|
+ FuncState *fs = ls->fs;
|
|
|
|
+ TProtoFunc *f = fs->f;
|
|
|
|
+ int while_init = fs->pc;
|
|
|
|
+ int cond_end, cond_size;
|
|
|
|
+ next(ls);
|
|
|
|
+ cond_end = cond(ls);
|
|
|
|
+ check(ls, DO);
|
|
|
|
+ block(ls);
|
|
|
|
+ check_match(ls, END, WHILE, line);
|
|
|
|
+ cond_size = cond_end-while_init;
|
|
|
|
+ check_pc(ls, cond_size);
|
|
|
|
+ memcpy(f->code+fs->pc, f->code+while_init, cond_size);
|
|
|
|
+ luaO_memdown(f->code+while_init, f->code+cond_end, fs->pc-while_init);
|
|
|
|
+ while_init += JMPSIZE + fix_jump(ls, while_init, JMP, fs->pc-cond_size);
|
|
|
|
+ fix_upjmp(ls, IFTUPJMP, while_init);
|
|
|
|
+}
|
|
|
|
|
|
- case ')': break; /* parlist -> empty */
|
|
|
|
|
|
|
|
- default: luaX_error(ls, "NAME or `...' expected");
|
|
|
|
- }
|
|
|
|
- code_args(ls, nparams, dots);
|
|
|
|
|
|
+static void repeatstat (LexState *ls, int line) {
|
|
|
|
+ /* repeatstat -> REPEAT block UNTIL exp1 */
|
|
|
|
+ FuncState *fs = ls->fs;
|
|
|
|
+ int repeat_init = fs->pc;
|
|
|
|
+ next(ls);
|
|
|
|
+ block(ls);
|
|
|
|
+ check_match(ls, UNTIL, REPEAT, line);
|
|
|
|
+ exp1(ls);
|
|
|
|
+ fix_upjmp(ls, IFFUPJMP, repeat_init);
|
|
|
|
+ deltastack(ls, -1); /* pops condition */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
static int localnamelist (LexState *ls) {
|
|
static int localnamelist (LexState *ls) {
|
|
/* localnamelist -> NAME {',' NAME} */
|
|
/* localnamelist -> NAME {',' NAME} */
|
|
int i = 1;
|
|
int i = 1;
|
|
@@ -1255,6 +1261,7 @@ static int localnamelist (LexState *ls) {
|
|
return i;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
static void decinit (LexState *ls, listdesc *d) {
|
|
static void decinit (LexState *ls, listdesc *d) {
|
|
/* decinit -> ['=' explist1] */
|
|
/* decinit -> ['=' explist1] */
|
|
if (ls->token == '=') {
|
|
if (ls->token == '=') {
|
|
@@ -1268,156 +1275,212 @@ static void decinit (LexState *ls, listdesc *d) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static int assignment (LexState *ls, vardesc *v, int nvars) {
|
|
|
|
- int left = 0;
|
|
|
|
- checklimit(ls, nvars, MAXVARSLH, "variables in a multiple assignment");
|
|
|
|
- unloaddot(ls, v);
|
|
|
|
- if (ls->token == ',') { /* assignment -> ',' NAME assignment */
|
|
|
|
- vardesc nv;
|
|
|
|
|
|
+static void localstat (LexState *ls) {
|
|
|
|
+ /* stat -> LOCAL localnamelist decinit */
|
|
|
|
+ FuncState *fs = ls->fs;
|
|
|
|
+ listdesc d;
|
|
|
|
+ int nvars;
|
|
|
|
+ check_debugline(ls);
|
|
|
|
+ next(ls);
|
|
|
|
+ nvars = localnamelist(ls);
|
|
|
|
+ decinit(ls, &d);
|
|
|
|
+ adjustlocalvars(ls, nvars, fs->lastsetline);
|
|
|
|
+ adjust_mult_assign(ls, nvars, &d);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int funcname (LexState *ls, vardesc *v) {
|
|
|
|
+ /* funcname -> NAME [':' NAME | '.' NAME] */
|
|
|
|
+ int needself = 0;
|
|
|
|
+ singlevar(ls, str_checkname(ls), v, 0);
|
|
|
|
+ if (ls->token == ':' || ls->token == '.') {
|
|
|
|
+ needself = (ls->token == ':');
|
|
next(ls);
|
|
next(ls);
|
|
- var_or_func(ls, &nv);
|
|
|
|
- if (nv.k == VEXP)
|
|
|
|
- luaX_error(ls, "syntax error");
|
|
|
|
- left = assignment(ls, &nv, nvars+1);
|
|
|
|
|
|
+ lua_pushvar(ls, v);
|
|
|
|
+ code_constant(ls, checkname(ls));
|
|
|
|
+ v->k = VINDEXED;
|
|
}
|
|
}
|
|
- else { /* assignment -> '=' explist1 */
|
|
|
|
- listdesc d;
|
|
|
|
- if (ls->token != '=')
|
|
|
|
- error_unexpected(ls);
|
|
|
|
- next(ls);
|
|
|
|
- explist1(ls, &d);
|
|
|
|
- adjust_mult_assign(ls, nvars, &d);
|
|
|
|
|
|
+ return needself;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int funcstat (LexState *ls, int line) {
|
|
|
|
+ /* funcstat -> FUNCTION funcname body */
|
|
|
|
+ int needself;
|
|
|
|
+ vardesc v;
|
|
|
|
+ if (ls->fs->prev) /* inside other function? */
|
|
|
|
+ return 0;
|
|
|
|
+ check_debugline(ls);
|
|
|
|
+ next(ls);
|
|
|
|
+ needself = funcname(ls, &v);
|
|
|
|
+ body(ls, needself, line);
|
|
|
|
+ storevar(ls, &v);
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void namestat (LexState *ls) {
|
|
|
|
+ /* stat -> func | ['%'] NAME assignment */
|
|
|
|
+ vardesc v;
|
|
|
|
+ check_debugline(ls);
|
|
|
|
+ var_or_func(ls, &v);
|
|
|
|
+ if (v.k == VEXP) { /* stat -> func */
|
|
|
|
+ if (v.info == 0) /* is just an upper value? */
|
|
|
|
+ luaX_error(ls, "syntax error");
|
|
|
|
+ close_exp(ls, v.info, 0);
|
|
}
|
|
}
|
|
- if (v->k != VINDEXED || left+(nvars-1) == 0) {
|
|
|
|
- /* global/local var or indexed var without values in between */
|
|
|
|
- storevar(ls, v);
|
|
|
|
|
|
+ else { /* stat -> ['%'] NAME assignment */
|
|
|
|
+ int left = assignment(ls, &v, 1);
|
|
|
|
+ adjuststack(ls, left); /* remove eventual garbage left on stack */
|
|
}
|
|
}
|
|
- else { /* indexed var with values in between*/
|
|
|
|
- code_oparg(ls, SETTABLE, left+(nvars-1), -1);
|
|
|
|
- left += 2; /* table&index are not popped, because they aren't on top */
|
|
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void ifpart (LexState *ls, int line) {
|
|
|
|
+ /* ifpart -> cond THEN block [ELSE block | ELSEIF ifpart] */
|
|
|
|
+ int c;
|
|
|
|
+ int e;
|
|
|
|
+ next(ls); /* skip IF or ELSEIF */
|
|
|
|
+ c = cond(ls);
|
|
|
|
+ check(ls, THEN);
|
|
|
|
+ block(ls);
|
|
|
|
+ e = SaveWord(ls);
|
|
|
|
+ if (ls->token == ELSEIF)
|
|
|
|
+ ifpart(ls, line);
|
|
|
|
+ else {
|
|
|
|
+ if (optional(ls, ELSE))
|
|
|
|
+ block(ls);
|
|
|
|
+ check_match(ls, END, IF, line);
|
|
}
|
|
}
|
|
- return left;
|
|
|
|
|
|
+ codeIf(ls, c, e);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static void constructor (LexState *ls) {
|
|
|
|
- /* constructor -> '{' part [';' part] '}' */
|
|
|
|
- int line = ls->linenumber;
|
|
|
|
- int pc = SaveWord(ls);
|
|
|
|
- int nelems;
|
|
|
|
- constdesc cd;
|
|
|
|
- deltastack(ls, 1);
|
|
|
|
- check(ls, '{');
|
|
|
|
- part(ls, &cd);
|
|
|
|
- nelems = cd.n;
|
|
|
|
- if (ls->token == ';') {
|
|
|
|
- constdesc other_cd;
|
|
|
|
- next(ls);
|
|
|
|
- part(ls, &other_cd);
|
|
|
|
- if (cd.k == other_cd.k) /* repeated parts? */
|
|
|
|
- luaX_error(ls, "invalid constructor syntax");
|
|
|
|
- nelems += other_cd.n;
|
|
|
|
|
|
+static int stat (LexState *ls) {
|
|
|
|
+ int line = ls->linenumber; /* may be needed for error messages */
|
|
|
|
+ switch (ls->token) {
|
|
|
|
+ case IF: /* stat -> IF ifpart END */
|
|
|
|
+ ifpart(ls, line);
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ case WHILE: /* stat -> whilestat */
|
|
|
|
+ whilestat(ls, line);
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ case DO: { /* stat -> DO block END */
|
|
|
|
+ next(ls);
|
|
|
|
+ block(ls);
|
|
|
|
+ check_match(ls, END, DO, line);
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ case REPEAT: /* stat -> repeatstat */
|
|
|
|
+ repeatstat(ls, line);
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ case FUNCTION: /* stat -> funcstat */
|
|
|
|
+ return funcstat(ls, line);
|
|
|
|
+
|
|
|
|
+ case LOCAL: /* stat -> localstat */
|
|
|
|
+ localstat(ls);
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ case NAME: case '%': /* stat -> namestat */
|
|
|
|
+ namestat(ls);
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ case RETURN: case ';': case ELSE: case ELSEIF:
|
|
|
|
+ case END: case UNTIL: case EOS: /* 'stat' follow */
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ error_unexpected(ls);
|
|
|
|
+ return 0; /* to avoid warnings */
|
|
}
|
|
}
|
|
- check_match(ls, '}', '{', line);
|
|
|
|
- fix_opcode(ls, pc, CREATEARRAY, nelems);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static void part (LexState *ls, constdesc *cd) {
|
|
|
|
|
|
+
|
|
|
|
+static void parlist (LexState *ls) {
|
|
|
|
+ int nparams = 0;
|
|
|
|
+ int dots = 0;
|
|
switch (ls->token) {
|
|
switch (ls->token) {
|
|
- case ';': case '}': /* part -> empty */
|
|
|
|
- cd->n = 0;
|
|
|
|
- cd->k = ls->token;
|
|
|
|
- return;
|
|
|
|
|
|
+ case DOTS: /* parlist -> DOTS */
|
|
|
|
+ next(ls);
|
|
|
|
+ dots = 1;
|
|
|
|
+ break;
|
|
|
|
|
|
- case NAME: {
|
|
|
|
- vardesc v;
|
|
|
|
- exp0(ls, &v);
|
|
|
|
- if (ls->token == '=') {
|
|
|
|
- switch (v.k) {
|
|
|
|
- case VGLOBAL:
|
|
|
|
- code_constant(ls, v.info);
|
|
|
|
- break;
|
|
|
|
- case VLOCAL:
|
|
|
|
- code_string(ls, ls->fs->localvar[v.info]);
|
|
|
|
|
|
+ case NAME: /* parlist, tailparlist -> NAME [',' tailparlist] */
|
|
|
|
+ init:
|
|
|
|
+ store_localvar(ls, str_checkname(ls), nparams++);
|
|
|
|
+ if (ls->token == ',') {
|
|
|
|
+ next(ls);
|
|
|
|
+ switch (ls->token) {
|
|
|
|
+ case DOTS: /* tailparlist -> DOTS */
|
|
|
|
+ next(ls);
|
|
|
|
+ dots = 1;
|
|
break;
|
|
break;
|
|
- default:
|
|
|
|
- error_unexpected(ls);
|
|
|
|
|
|
+
|
|
|
|
+ case NAME: /* tailparlist -> NAME [',' tailparlist] */
|
|
|
|
+ goto init;
|
|
|
|
+
|
|
|
|
+ default: luaX_error(ls, "NAME or `...' expected");
|
|
}
|
|
}
|
|
- next(ls);
|
|
|
|
- exp1(ls);
|
|
|
|
- cd->n = recfields(ls);
|
|
|
|
- cd->k = 1; /* record */
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- lua_pushvar(ls, &v);
|
|
|
|
- cd->n = listfields(ls);
|
|
|
|
- cd->k = 0; /* list */
|
|
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
- }
|
|
|
|
|
|
|
|
- case '[': /* part -> recfield recfields */
|
|
|
|
- recfield(ls);
|
|
|
|
- cd->n = recfields(ls);
|
|
|
|
- cd->k = 1; /* record */
|
|
|
|
- break;
|
|
|
|
|
|
+ case ')': break; /* parlist -> empty */
|
|
|
|
|
|
- default: /* part -> exp1 listfields */
|
|
|
|
- exp1(ls);
|
|
|
|
- cd->n = listfields(ls);
|
|
|
|
- cd->k = 0; /* list */
|
|
|
|
- break;
|
|
|
|
|
|
+ default: luaX_error(ls, "NAME or `...' expected");
|
|
}
|
|
}
|
|
|
|
+ code_args(ls, nparams, dots);
|
|
}
|
|
}
|
|
|
|
|
|
-static int recfields (LexState *ls) {
|
|
|
|
- /* recfields -> { ',' recfield } [','] */
|
|
|
|
- int n = 1; /* one has been read before */
|
|
|
|
- while (ls->token == ',') {
|
|
|
|
- next(ls);
|
|
|
|
- if (ls->token == ';' || ls->token == '}')
|
|
|
|
- break;
|
|
|
|
- recfield(ls);
|
|
|
|
- n++;
|
|
|
|
- if (n%RFIELDS_PER_FLUSH == 0)
|
|
|
|
- flush_record(ls, RFIELDS_PER_FLUSH);
|
|
|
|
- }
|
|
|
|
- flush_record(ls, n%RFIELDS_PER_FLUSH);
|
|
|
|
- return n;
|
|
|
|
|
|
+
|
|
|
|
+static void body (LexState *ls, int needself, int line) {
|
|
|
|
+ /* body -> '(' parlist ')' chunk END */
|
|
|
|
+ FuncState newfs;
|
|
|
|
+ init_state(ls, &newfs, ls->fs->f->source);
|
|
|
|
+ newfs.f->lineDefined = line;
|
|
|
|
+ check(ls, '(');
|
|
|
|
+ if (needself)
|
|
|
|
+ add_localvar(ls, luaS_newfixed(ls->L, "self"));
|
|
|
|
+ parlist(ls);
|
|
|
|
+ check(ls, ')');
|
|
|
|
+ chunk(ls);
|
|
|
|
+ check_match(ls, END, FUNCTION, line);
|
|
|
|
+ close_func(ls);
|
|
|
|
+ func_onstack(ls, &newfs);
|
|
}
|
|
}
|
|
|
|
|
|
-static int listfields (LexState *ls) {
|
|
|
|
- /* listfields -> { ',' exp1 } [','] */
|
|
|
|
- int n = 1; /* one has been read before */
|
|
|
|
- while (ls->token == ',') {
|
|
|
|
- next(ls);
|
|
|
|
- if (ls->token == ';' || ls->token == '}')
|
|
|
|
- break;
|
|
|
|
- exp1(ls);
|
|
|
|
- n++;
|
|
|
|
- if (n%LFIELDS_PER_FLUSH == 0)
|
|
|
|
- flush_list(ls, n/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH);
|
|
|
|
|
|
+
|
|
|
|
+static void ret (LexState *ls) {
|
|
|
|
+ /* ret -> [RETURN explist sc] */
|
|
|
|
+ if (optional(ls, RETURN)) {
|
|
|
|
+ listdesc e;
|
|
|
|
+ check_debugline(ls);
|
|
|
|
+ explist(ls, &e);
|
|
|
|
+ if (e.pc > 0) { /* expression is an open function call? */
|
|
|
|
+ Byte *code = ls->fs->f->code;
|
|
|
|
+ code[e.pc-2] = TAILCALL; /* instead of a conventional CALL */
|
|
|
|
+ code[e.pc-1] = (Byte)ls->fs->nlocalvar;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ code_oparg(ls, RETCODE, ls->fs->nlocalvar, 0);
|
|
|
|
+ ls->fs->stacksize = ls->fs->nlocalvar; /* removes all temp values */
|
|
|
|
+ optional(ls, ';');
|
|
}
|
|
}
|
|
- flush_list(ls, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH);
|
|
|
|
- return n;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static void recfield (LexState *ls) {
|
|
|
|
- /* recfield -> (NAME | '['exp1']') = exp1 */
|
|
|
|
- switch (ls->token) {
|
|
|
|
- case NAME:
|
|
|
|
- code_constant(ls, checkname(ls));
|
|
|
|
- break;
|
|
|
|
|
|
+/* }====================================================================== */
|
|
|
|
|
|
- case '[':
|
|
|
|
- next(ls);
|
|
|
|
- exp1(ls);
|
|
|
|
- check(ls, ']');
|
|
|
|
- break;
|
|
|
|
|
|
|
|
- default: luaX_error(ls, "NAME or `[' expected");
|
|
|
|
|
|
+static void chunk (LexState *ls) {
|
|
|
|
+ /* chunk -> { stat [;] } ret */
|
|
|
|
+ while (stat(ls)) {
|
|
|
|
+ LUA_ASSERT(ls->L, ls->fs->stacksize == ls->fs->nlocalvar,
|
|
|
|
+ "stack size != # local vars");
|
|
|
|
+ optional(ls, ';');
|
|
}
|
|
}
|
|
- check(ls, '=');
|
|
|
|
- exp1(ls);
|
|
|
|
|
|
+ ret(ls); /* optional return */
|
|
}
|
|
}
|
|
|
|
|