2009-05-21 19:01:41 +00:00
|
|
|
/*
|
2018-11-08 16:32:45 +00:00
|
|
|
** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $
|
2009-05-21 19:01:41 +00:00
|
|
|
** Lua Parser
|
|
|
|
** See Copyright Notice in lua.h
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#define lparser_c
|
|
|
|
#define LUA_CORE
|
|
|
|
|
|
|
|
#include "lua.h"
|
|
|
|
|
|
|
|
#include "lcode.h"
|
|
|
|
#include "ldebug.h"
|
|
|
|
#include "ldo.h"
|
|
|
|
#include "lfunc.h"
|
|
|
|
#include "llex.h"
|
|
|
|
#include "lmem.h"
|
|
|
|
#include "lobject.h"
|
|
|
|
#include "lopcodes.h"
|
|
|
|
#include "lparser.h"
|
|
|
|
#include "lstate.h"
|
|
|
|
#include "lstring.h"
|
|
|
|
#include "ltable.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
Update lua plugin to 5.2.3
Prior to this patch the Lua plugin used version 5.1.4. This change
reduces the number of modifications in the Lua source using some new
defines and because the upstream source is now more flexible.
Unless otherwise stated, l*.[ch] files are taken unmodified from the
upstream lua-5.2.3.
fscanf.c:
file descriptors in rockbox are just ints, they are hidden behind a
void* now so liolib requires less modifications. fscanf is updated to
use void* too.
getc.c: this is a new file required for getc implementation in lauxlib.c
lauxlib.c: LoadF replaced FILE* with int, the rockbox file
descriptor int are cast to FILE* (actually void* due to typedef).
getc uses the PREFIX version. stdin is not used, as per 5.1.4.
lbaselib.c: now uses strspn in the number parsing. print uses DEBUGF now
rather than being commented out.
lbitlib.c: use the built-in version from 5.2.3 rather than Reuben
Thomas's external library. Backwards compatible and adds some new bit
operations.
ldo.c: the LUAI_THROW/TRY defines are now in the core lua code, so have
been removed from rockconf.h
liolib.c: here the implementation has changed to use the LStream from
the original source, and cast the FILE* pointers to int. This has
reduced the number of modifications from the upstream version.
llex.c: the only change from upstream is to remove the locale include.
lmathlib.c: updated from the 5.2.3 version and re-applied the changes
that were made vs 5.1.4 for random numbers and to remove unsupported
float functions.
loadlib.c: upstream version, with the 5.1.4 changes for missing
functions.
lobject.c: upstream version, with ctype.h added and sprintf changed to
snprintf.
loslib.c: upstream version with locale.h removed and 5.1.4 changes for
unsupportable functions.
lstrlib.c: sprintf changed to snprintf.
ltable.c: upstream with the hashnum function from 5.1.4 to avoid frexp
in luai_hashnum.
luaconf.h: updated to 5.2.3 version, restored relevant parts from the
original 5.1.4 configuration. The COMPAT defines that are no longer
available are not included.
lundump.c: VERSION macro conflicts with the core Rockbox equivalent.
rocklib.c: luaL_reg is no longer available, replaced by luaL_Reg
equivalent. Moved checkboolean/optboolean functions to this file and out
of core lua files. luaL_getn is no longer available, replaced by
luaL_rawlen. luaL_register is deprecated, use the newlib/setfuncs
replacements. rli_init has to be called before setting up the newlib to
avoid overwriting the rb table.
rocklib_aux.pl: use rli_checkboolean from rocklib.c.
rocklua.c: new default bits library used, update the library loading
code with idiomatic 5.2 code.
strcspn.c: no longer needed, but strspn.c is required for strspn in
lbaselib.c
Change-Id: I0c7945c755f79083afe98ec117e1e8cf13de2651
Reviewed-on: http://gerrit.rockbox.org/774
Tested: Richard Quirk <richard.quirk@gmail.com>
Reviewed-by: Marcin Bukat <marcin.bukat@gmail.com>
2014-03-19 18:31:31 +00:00
|
|
|
#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
|
2009-05-21 19:01:41 +00:00
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]])
|
|
|
|
|
|
|
|
#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m)
|
2009-05-21 19:01:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** nodes for block list (list of active blocks)
|
|
|
|
*/
|
|
|
|
typedef struct BlockCnt {
|
|
|
|
struct BlockCnt *previous; /* chain */
|
2014-04-02 18:46:06 +00:00
|
|
|
int breaklist; /* list of jumps out of this loop */
|
|
|
|
lu_byte nactvar; /* # active locals outside the breakable structure */
|
2009-05-21 19:01:41 +00:00
|
|
|
lu_byte upval; /* true if some variable in the block is an upvalue */
|
2014-04-02 18:46:06 +00:00
|
|
|
lu_byte isbreakable; /* true if `block' is a loop */
|
2009-05-21 19:01:41 +00:00
|
|
|
} BlockCnt;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** prototypes for recursive non-terminal functions
|
|
|
|
*/
|
2014-04-02 18:46:06 +00:00
|
|
|
static void chunk (LexState *ls);
|
2009-05-21 19:01:41 +00:00
|
|
|
static void expr (LexState *ls, expdesc *v);
|
|
|
|
|
|
|
|
|
|
|
|
static void anchor_token (LexState *ls) {
|
|
|
|
if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {
|
|
|
|
TString *ts = ls->t.seminfo.ts;
|
|
|
|
luaX_newstring(ls, getstr(ts), ts->tsv.len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void error_expected (LexState *ls, int token) {
|
2009-05-21 19:01:41 +00:00
|
|
|
luaX_syntaxerror(ls,
|
2014-04-02 18:46:06 +00:00
|
|
|
luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token)));
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void errorlimit (FuncState *fs, int limit, const char *what) {
|
|
|
|
const char *msg = (fs->f->linedefined == 0) ?
|
|
|
|
luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) :
|
|
|
|
luaO_pushfstring(fs->L, "function at line %d has more than %d %s",
|
|
|
|
fs->f->linedefined, limit, what);
|
|
|
|
luaX_lexerror(fs->ls, msg, 0);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int testnext (LexState *ls, int c) {
|
|
|
|
if (ls->t.token == c) {
|
|
|
|
luaX_next(ls);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void check (LexState *ls, int c) {
|
|
|
|
if (ls->t.token != c)
|
|
|
|
error_expected(ls, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void checknext (LexState *ls, int c) {
|
|
|
|
check(ls, c);
|
|
|
|
luaX_next(ls);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void check_match (LexState *ls, int what, int who, int where) {
|
|
|
|
if (!testnext(ls, what)) {
|
|
|
|
if (where == ls->linenumber)
|
|
|
|
error_expected(ls, what);
|
|
|
|
else {
|
|
|
|
luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
|
2014-04-02 18:46:06 +00:00
|
|
|
LUA_QS " expected (to close " LUA_QS " at line %d)",
|
2009-05-21 19:01:41 +00:00
|
|
|
luaX_token2str(ls, what), luaX_token2str(ls, who), where));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static TString *str_checkname (LexState *ls) {
|
|
|
|
TString *ts;
|
|
|
|
check(ls, TK_NAME);
|
|
|
|
ts = ls->t.seminfo.ts;
|
|
|
|
luaX_next(ls);
|
|
|
|
return ts;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void init_exp (expdesc *e, expkind k, int i) {
|
|
|
|
e->f = e->t = NO_JUMP;
|
|
|
|
e->k = k;
|
2014-04-02 18:46:06 +00:00
|
|
|
e->u.s.info = i;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void codestring (LexState *ls, expdesc *e, TString *s) {
|
|
|
|
init_exp(e, VK, luaK_stringK(ls->fs, s));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void checkname(LexState *ls, expdesc *e) {
|
2009-05-21 19:01:41 +00:00
|
|
|
codestring(ls, e, str_checkname(ls));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int registerlocalvar (LexState *ls, TString *varname) {
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
Proto *f = fs->f;
|
|
|
|
int oldsize = f->sizelocvars;
|
|
|
|
luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
|
2014-04-02 18:46:06 +00:00
|
|
|
LocVar, SHRT_MAX, "too many local variables");
|
2009-05-21 19:01:41 +00:00
|
|
|
while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL;
|
|
|
|
f->locvars[fs->nlocvars].varname = varname;
|
|
|
|
luaC_objbarrier(ls->L, f, varname);
|
|
|
|
return fs->nlocvars++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
#define new_localvarliteral(ls,v,n) \
|
|
|
|
new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n)
|
2009-05-21 19:01:41 +00:00
|
|
|
|
Update lua plugin to 5.2.3
Prior to this patch the Lua plugin used version 5.1.4. This change
reduces the number of modifications in the Lua source using some new
defines and because the upstream source is now more flexible.
Unless otherwise stated, l*.[ch] files are taken unmodified from the
upstream lua-5.2.3.
fscanf.c:
file descriptors in rockbox are just ints, they are hidden behind a
void* now so liolib requires less modifications. fscanf is updated to
use void* too.
getc.c: this is a new file required for getc implementation in lauxlib.c
lauxlib.c: LoadF replaced FILE* with int, the rockbox file
descriptor int are cast to FILE* (actually void* due to typedef).
getc uses the PREFIX version. stdin is not used, as per 5.1.4.
lbaselib.c: now uses strspn in the number parsing. print uses DEBUGF now
rather than being commented out.
lbitlib.c: use the built-in version from 5.2.3 rather than Reuben
Thomas's external library. Backwards compatible and adds some new bit
operations.
ldo.c: the LUAI_THROW/TRY defines are now in the core lua code, so have
been removed from rockconf.h
liolib.c: here the implementation has changed to use the LStream from
the original source, and cast the FILE* pointers to int. This has
reduced the number of modifications from the upstream version.
llex.c: the only change from upstream is to remove the locale include.
lmathlib.c: updated from the 5.2.3 version and re-applied the changes
that were made vs 5.1.4 for random numbers and to remove unsupported
float functions.
loadlib.c: upstream version, with the 5.1.4 changes for missing
functions.
lobject.c: upstream version, with ctype.h added and sprintf changed to
snprintf.
loslib.c: upstream version with locale.h removed and 5.1.4 changes for
unsupportable functions.
lstrlib.c: sprintf changed to snprintf.
ltable.c: upstream with the hashnum function from 5.1.4 to avoid frexp
in luai_hashnum.
luaconf.h: updated to 5.2.3 version, restored relevant parts from the
original 5.1.4 configuration. The COMPAT defines that are no longer
available are not included.
lundump.c: VERSION macro conflicts with the core Rockbox equivalent.
rocklib.c: luaL_reg is no longer available, replaced by luaL_Reg
equivalent. Moved checkboolean/optboolean functions to this file and out
of core lua files. luaL_getn is no longer available, replaced by
luaL_rawlen. luaL_register is deprecated, use the newlib/setfuncs
replacements. rli_init has to be called before setting up the newlib to
avoid overwriting the rb table.
rocklib_aux.pl: use rli_checkboolean from rocklib.c.
rocklua.c: new default bits library used, update the library loading
code with idiomatic 5.2 code.
strcspn.c: no longer needed, but strspn.c is required for strspn in
lbaselib.c
Change-Id: I0c7945c755f79083afe98ec117e1e8cf13de2651
Reviewed-on: http://gerrit.rockbox.org/774
Tested: Richard Quirk <richard.quirk@gmail.com>
Reviewed-by: Marcin Bukat <marcin.bukat@gmail.com>
2014-03-19 18:31:31 +00:00
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void new_localvar (LexState *ls, TString *name, int n) {
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables");
|
|
|
|
fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name));
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void adjustlocalvars (LexState *ls, int nvars) {
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
fs->nactvar = cast_byte(fs->nactvar + nvars);
|
|
|
|
for (; nvars; nvars--) {
|
2014-04-02 18:46:06 +00:00
|
|
|
getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void removevars (LexState *ls, int tolevel) {
|
|
|
|
FuncState *fs = ls->fs;
|
2009-05-21 19:01:41 +00:00
|
|
|
while (fs->nactvar > tolevel)
|
2014-04-02 18:46:06 +00:00
|
|
|
getlocvar(fs, --fs->nactvar).endpc = fs->pc;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static int indexupvalue (FuncState *fs, TString *name, expdesc *v) {
|
2009-05-21 19:01:41 +00:00
|
|
|
int i;
|
|
|
|
Proto *f = fs->f;
|
|
|
|
int oldsize = f->sizeupvalues;
|
2014-04-02 18:46:06 +00:00
|
|
|
for (i=0; i<f->nups; i++) {
|
|
|
|
if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) {
|
|
|
|
lua_assert(f->upvalues[i] == name);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* new one */
|
|
|
|
luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues");
|
|
|
|
luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues,
|
|
|
|
TString *, MAX_INT, "");
|
|
|
|
while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL;
|
|
|
|
f->upvalues[f->nups] = name;
|
|
|
|
luaC_objbarrier(fs->L, f, name);
|
|
|
|
lua_assert(v->k == VLOCAL || v->k == VUPVAL);
|
|
|
|
fs->upvalues[f->nups].k = cast_byte(v->k);
|
|
|
|
fs->upvalues[f->nups].info = cast_byte(v->u.s.info);
|
|
|
|
return f->nups++;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int searchvar (FuncState *fs, TString *n) {
|
|
|
|
int i;
|
2014-04-02 18:46:06 +00:00
|
|
|
for (i=fs->nactvar-1; i >= 0; i--) {
|
|
|
|
if (n == getlocvar(fs, i).varname)
|
2009-05-21 19:01:41 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1; /* not found */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void markupval (FuncState *fs, int level) {
|
|
|
|
BlockCnt *bl = fs->bl;
|
2014-04-02 18:46:06 +00:00
|
|
|
while (bl && bl->nactvar > level) bl = bl->previous;
|
|
|
|
if (bl) bl->upval = 1;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
2014-04-02 18:46:06 +00:00
|
|
|
if (fs == NULL) { /* no more levels? */
|
|
|
|
init_exp(var, VGLOBAL, NO_REG); /* default is global variable */
|
|
|
|
return VGLOBAL;
|
|
|
|
}
|
2009-05-21 19:01:41 +00:00
|
|
|
else {
|
2014-04-02 18:46:06 +00:00
|
|
|
int v = searchvar(fs, n); /* look up at current level */
|
|
|
|
if (v >= 0) {
|
|
|
|
init_exp(var, VLOCAL, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
if (!base)
|
|
|
|
markupval(fs, v); /* local will be used as an upval */
|
|
|
|
return VLOCAL;
|
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
else { /* not found at current level; try upper one */
|
|
|
|
if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)
|
|
|
|
return VGLOBAL;
|
|
|
|
var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */
|
|
|
|
var->k = VUPVAL; /* upvalue in this level */
|
2009-05-21 19:01:41 +00:00
|
|
|
return VUPVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void singlevar (LexState *ls, expdesc *var) {
|
|
|
|
TString *varname = str_checkname(ls);
|
|
|
|
FuncState *fs = ls->fs;
|
2014-04-02 18:46:06 +00:00
|
|
|
if (singlevaraux(fs, varname, var, 1) == VGLOBAL)
|
|
|
|
var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int extra = nvars - nexps;
|
|
|
|
if (hasmultret(e->k)) {
|
|
|
|
extra++; /* includes call itself */
|
|
|
|
if (extra < 0) extra = 0;
|
|
|
|
luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
|
|
|
|
if (extra > 1) luaK_reserveregs(fs, extra-1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */
|
|
|
|
if (extra > 0) {
|
|
|
|
int reg = fs->freereg;
|
|
|
|
luaK_reserveregs(fs, extra);
|
|
|
|
luaK_nil(fs, reg, extra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void enterlevel (LexState *ls) {
|
2014-04-02 18:46:06 +00:00
|
|
|
if (++ls->L->nCcalls > LUAI_MAXCCALLS)
|
|
|
|
luaX_lexerror(ls, "chunk has too many syntax levels", 0);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define leavelevel(ls) ((ls)->L->nCcalls--)
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {
|
|
|
|
bl->breaklist = NO_JUMP;
|
|
|
|
bl->isbreakable = isbreakable;
|
2009-05-21 19:01:41 +00:00
|
|
|
bl->nactvar = fs->nactvar;
|
|
|
|
bl->upval = 0;
|
|
|
|
bl->previous = fs->bl;
|
|
|
|
fs->bl = bl;
|
|
|
|
lua_assert(fs->freereg == fs->nactvar);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void leaveblock (FuncState *fs) {
|
|
|
|
BlockCnt *bl = fs->bl;
|
|
|
|
fs->bl = bl->previous;
|
2014-04-02 18:46:06 +00:00
|
|
|
removevars(fs->ls, bl->nactvar);
|
|
|
|
if (bl->upval)
|
|
|
|
luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
|
|
|
|
/* a block either controls scope or breaks (never both) */
|
|
|
|
lua_assert(!bl->isbreakable || !bl->upval);
|
2009-05-21 19:01:41 +00:00
|
|
|
lua_assert(bl->nactvar == fs->nactvar);
|
|
|
|
fs->freereg = fs->nactvar; /* free registers */
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_patchtohere(fs, bl->breaklist);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void pushclosure (LexState *ls, FuncState *func, expdesc *v) {
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
2014-04-02 18:46:06 +00:00
|
|
|
Proto *f = fs->f;
|
|
|
|
int oldsize = f->sizep;
|
|
|
|
int i;
|
|
|
|
luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,
|
|
|
|
MAXARG_Bx, "constant table overflow");
|
|
|
|
while (oldsize < f->sizep) f->p[oldsize++] = NULL;
|
|
|
|
f->p[fs->np++] = func->f;
|
|
|
|
luaC_objbarrier(ls->L, f, func->f);
|
|
|
|
init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));
|
|
|
|
for (i=0; i<func->f->nups; i++) {
|
|
|
|
OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
|
|
|
|
luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-08 16:32:45 +00:00
|
|
|
static void open_func (LexState *ls, FuncState *fs) {
|
2009-05-21 19:01:41 +00:00
|
|
|
lua_State *L = ls->L;
|
2014-04-02 18:46:06 +00:00
|
|
|
Proto *f = luaF_newproto(L);
|
|
|
|
fs->f = f;
|
2009-05-21 19:01:41 +00:00
|
|
|
fs->prev = ls->fs; /* linked list of funcstates */
|
|
|
|
fs->ls = ls;
|
2014-04-02 18:46:06 +00:00
|
|
|
fs->L = L;
|
2009-05-21 19:01:41 +00:00
|
|
|
ls->fs = fs;
|
|
|
|
fs->pc = 0;
|
2014-04-02 18:46:06 +00:00
|
|
|
fs->lasttarget = -1;
|
2009-05-21 19:01:41 +00:00
|
|
|
fs->jpc = NO_JUMP;
|
|
|
|
fs->freereg = 0;
|
|
|
|
fs->nk = 0;
|
|
|
|
fs->np = 0;
|
|
|
|
fs->nlocvars = 0;
|
|
|
|
fs->nactvar = 0;
|
|
|
|
fs->bl = NULL;
|
|
|
|
f->source = ls->source;
|
|
|
|
f->maxstacksize = 2; /* registers 0/1 are always valid */
|
2019-08-05 05:03:08 +00:00
|
|
|
#ifdef LUA_OPTIMIZE_DEBUG
|
|
|
|
fs->lastline = 0;
|
|
|
|
fs->lastlineOffset = 0;
|
|
|
|
fs->lineinfoLastPC = -1;
|
|
|
|
#endif
|
2014-04-02 18:46:06 +00:00
|
|
|
fs->h = luaH_new(L, 0, 0);
|
|
|
|
/* anchor table of constants and prototype (to avoid being collected) */
|
2009-05-21 19:01:41 +00:00
|
|
|
sethvalue2s(L, L->top, fs->h);
|
|
|
|
incr_top(L);
|
2014-04-02 18:46:06 +00:00
|
|
|
setptvalue2s(L, L->top, f);
|
|
|
|
incr_top(L);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void close_func (LexState *ls) {
|
|
|
|
lua_State *L = ls->L;
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
Proto *f = fs->f;
|
2014-04-02 18:46:06 +00:00
|
|
|
removevars(ls, 0);
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_ret(fs, 0, 0); /* final return */
|
|
|
|
luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);
|
|
|
|
f->sizecode = fs->pc;
|
2019-08-05 05:03:08 +00:00
|
|
|
#ifdef LUA_OPTIMIZE_DEBUG
|
|
|
|
f->packedlineinfo[fs->lastlineOffset+1]=0;
|
|
|
|
luaM_reallocvector(L, f->packedlineinfo, f->sizelineinfo,
|
|
|
|
fs->lastlineOffset+2, unsigned char);
|
2019-08-08 12:23:58 +00:00
|
|
|
f->sizelineinfo = fs->lastlineOffset + 2;
|
2019-08-05 05:03:08 +00:00
|
|
|
#else
|
2009-05-21 19:01:41 +00:00
|
|
|
luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);
|
|
|
|
f->sizelineinfo = fs->pc;
|
2019-08-05 05:03:08 +00:00
|
|
|
#endif
|
2009-05-21 19:01:41 +00:00
|
|
|
luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);
|
|
|
|
f->sizek = fs->nk;
|
|
|
|
luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);
|
|
|
|
f->sizep = fs->np;
|
|
|
|
luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
|
|
|
|
f->sizelocvars = fs->nlocvars;
|
2014-04-02 18:46:06 +00:00
|
|
|
luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *);
|
|
|
|
f->sizeupvalues = f->nups;
|
|
|
|
lua_assert(luaG_checkcode(f));
|
2009-05-21 19:01:41 +00:00
|
|
|
lua_assert(fs->bl == NULL);
|
|
|
|
ls->fs = fs->prev;
|
2014-04-02 18:46:06 +00:00
|
|
|
/* last token read was anchored in defunct function; must reanchor it */
|
|
|
|
if (fs) anchor_token(ls);
|
2018-11-08 16:32:45 +00:00
|
|
|
L->top -= 2; /* remove table and prototype from the stack */
|
2014-04-02 18:46:06 +00:00
|
|
|
}
|
|
|
|
|
2019-08-05 05:03:08 +00:00
|
|
|
#ifdef LUA_OPTIMIZE_DEBUG
|
|
|
|
static void compile_stripdebug(lua_State *L, Proto *f) {
|
|
|
|
int level;
|
|
|
|
#ifdef LUA_OPTIMIZE_DEBUG_USER
|
|
|
|
lua_pushlightuserdata(L, &luaG_stripdebug);
|
|
|
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
|
|
level = lua_isnil(L, -1) ? LUA_OPTIMIZE_DEBUG : lua_tointeger(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
#else
|
|
|
|
level = LUA_OPTIMIZE_DEBUG;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (level > 1) {
|
|
|
|
luaG_stripdebug(L, f, level, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2014-04-02 18:46:06 +00:00
|
|
|
|
|
|
|
Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
|
|
|
|
struct LexState lexstate;
|
|
|
|
struct FuncState funcstate;
|
2019-07-12 10:23:52 +00:00
|
|
|
TString *tname = luaS_new(L, name);
|
|
|
|
setsvalue2s(L, L->top, tname); /* protect name */
|
|
|
|
incr_top(L);
|
2014-04-02 18:46:06 +00:00
|
|
|
lexstate.buff = buff;
|
2019-07-12 10:23:52 +00:00
|
|
|
luaX_setinput(L, &lexstate, z, tname);
|
2018-11-08 16:32:45 +00:00
|
|
|
open_func(&lexstate, &funcstate);
|
2014-04-02 18:46:06 +00:00
|
|
|
funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
|
|
|
|
luaX_next(&lexstate); /* read first token */
|
|
|
|
chunk(&lexstate);
|
|
|
|
check(&lexstate, TK_EOS);
|
|
|
|
close_func(&lexstate);
|
2019-07-12 10:23:52 +00:00
|
|
|
L->top--; /* remove 'name' from stack */
|
2019-08-05 05:03:08 +00:00
|
|
|
#ifdef LUA_OPTIMIZE_DEBUG
|
|
|
|
compile_stripdebug(L, funcstate.f);
|
|
|
|
#endif
|
2014-04-02 18:46:06 +00:00
|
|
|
lua_assert(funcstate.prev == NULL);
|
|
|
|
lua_assert(funcstate.f->nups == 0);
|
|
|
|
lua_assert(lexstate.fs == NULL);
|
|
|
|
return funcstate.f;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*============================================================*/
|
|
|
|
/* GRAMMAR RULES */
|
|
|
|
/*============================================================*/
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void field (LexState *ls, expdesc *v) {
|
|
|
|
/* field -> ['.' | ':'] NAME */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
expdesc key;
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_exp2anyreg(fs, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
luaX_next(ls); /* skip the dot or colon */
|
|
|
|
checkname(ls, &key);
|
|
|
|
luaK_indexed(fs, v, &key);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void yindex (LexState *ls, expdesc *v) {
|
|
|
|
/* index -> '[' expr ']' */
|
|
|
|
luaX_next(ls); /* skip the '[' */
|
|
|
|
expr(ls, v);
|
|
|
|
luaK_exp2val(ls->fs, v);
|
|
|
|
checknext(ls, ']');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** {======================================================================
|
|
|
|
** Rules for Constructors
|
|
|
|
** =======================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
struct ConsControl {
|
|
|
|
expdesc v; /* 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 */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void recfield (LexState *ls, struct ConsControl *cc) {
|
|
|
|
/* recfield -> (NAME | `['exp1`]') = exp1 */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int reg = ls->fs->freereg;
|
|
|
|
expdesc key, val;
|
|
|
|
int rkkey;
|
|
|
|
if (ls->t.token == TK_NAME) {
|
2014-04-02 18:46:06 +00:00
|
|
|
luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
|
2009-05-21 19:01:41 +00:00
|
|
|
checkname(ls, &key);
|
|
|
|
}
|
|
|
|
else /* ls->t.token == '[' */
|
|
|
|
yindex(ls, &key);
|
|
|
|
cc->nh++;
|
|
|
|
checknext(ls, '=');
|
|
|
|
rkkey = luaK_exp2RK(fs, &key);
|
|
|
|
expr(ls, &val);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val));
|
2009-05-21 19:01:41 +00:00
|
|
|
fs->freereg = reg; /* free registers */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void closelistfield (FuncState *fs, struct ConsControl *cc) {
|
|
|
|
if (cc->v.k == VVOID) return; /* there is no list item */
|
|
|
|
luaK_exp2nextreg(fs, &cc->v);
|
|
|
|
cc->v.k = VVOID;
|
|
|
|
if (cc->tostore == LFIELDS_PER_FLUSH) {
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */
|
2009-05-21 19:01:41 +00:00
|
|
|
cc->tostore = 0; /* no more items pending */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
|
|
|
|
if (cc->tostore == 0) return;
|
|
|
|
if (hasmultret(cc->v.k)) {
|
|
|
|
luaK_setmultret(fs, &cc->v);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET);
|
2009-05-21 19:01:41 +00:00
|
|
|
cc->na--; /* do not count last expression (unknown number of elements) */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (cc->v.k != VVOID)
|
|
|
|
luaK_exp2nextreg(fs, &cc->v);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void listfield (LexState *ls, struct ConsControl *cc) {
|
|
|
|
expr(ls, &cc->v);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
|
2009-05-21 19:01:41 +00:00
|
|
|
cc->na++;
|
|
|
|
cc->tostore++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void constructor (LexState *ls, expdesc *t) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* constructor -> ?? */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int line = ls->linenumber;
|
|
|
|
int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
|
|
|
|
struct ConsControl cc;
|
|
|
|
cc.na = cc.nh = cc.tostore = 0;
|
|
|
|
cc.t = t;
|
|
|
|
init_exp(t, VRELOCABLE, pc);
|
|
|
|
init_exp(&cc.v, VVOID, 0); /* no value (yet) */
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */
|
2009-05-21 19:01:41 +00:00
|
|
|
checknext(ls, '{');
|
|
|
|
do {
|
|
|
|
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
|
|
|
|
if (ls->t.token == '}') break;
|
|
|
|
closelistfield(fs, &cc);
|
2014-04-02 18:46:06 +00:00
|
|
|
switch(ls->t.token) {
|
|
|
|
case TK_NAME: { /* may be listfields or recfields */
|
|
|
|
luaX_lookahead(ls);
|
|
|
|
if (ls->lookahead.token != '=') /* expression? */
|
|
|
|
listfield(ls, &cc);
|
|
|
|
else
|
|
|
|
recfield(ls, &cc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '[': { /* constructor_item -> recfield */
|
|
|
|
recfield(ls, &cc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: { /* constructor_part -> listfield */
|
|
|
|
listfield(ls, &cc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-05-21 19:01:41 +00:00
|
|
|
} while (testnext(ls, ',') || testnext(ls, ';'));
|
|
|
|
check_match(ls, '}', '{', line);
|
|
|
|
lastlistfield(fs, &cc);
|
|
|
|
SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
|
|
|
|
SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* }====================================================================== */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void parlist (LexState *ls) {
|
|
|
|
/* parlist -> [ param { `,' param } ] */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
Proto *f = fs->f;
|
|
|
|
int nparams = 0;
|
|
|
|
f->is_vararg = 0;
|
|
|
|
if (ls->t.token != ')') { /* is `parlist' not empty? */
|
|
|
|
do {
|
|
|
|
switch (ls->t.token) {
|
|
|
|
case TK_NAME: { /* param -> NAME */
|
2014-04-02 18:46:06 +00:00
|
|
|
new_localvar(ls, str_checkname(ls), nparams++);
|
2009-05-21 19:01:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_DOTS: { /* param -> `...' */
|
|
|
|
luaX_next(ls);
|
2014-04-02 18:46:06 +00:00
|
|
|
#if defined(LUA_COMPAT_VARARG)
|
|
|
|
/* use `arg' as default name */
|
|
|
|
new_localvarliteral(ls, "arg", nparams++);
|
|
|
|
f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG;
|
|
|
|
#endif
|
|
|
|
f->is_vararg |= VARARG_ISVARARG;
|
2009-05-21 19:01:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: luaX_syntaxerror(ls, "<name> or " LUA_QL("...") " expected");
|
|
|
|
}
|
|
|
|
} while (!f->is_vararg && testnext(ls, ','));
|
|
|
|
}
|
|
|
|
adjustlocalvars(ls, nparams);
|
2014-04-02 18:46:06 +00:00
|
|
|
f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG));
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void body (LexState *ls, expdesc *e, int needself, int line) {
|
|
|
|
/* body -> `(' parlist `)' chunk END */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState new_fs;
|
2018-11-08 16:32:45 +00:00
|
|
|
open_func(ls, &new_fs);
|
2009-05-21 19:01:41 +00:00
|
|
|
new_fs.f->linedefined = line;
|
|
|
|
checknext(ls, '(');
|
2014-04-02 18:46:06 +00:00
|
|
|
if (needself) {
|
|
|
|
new_localvarliteral(ls, "self", 0);
|
2009-05-21 19:01:41 +00:00
|
|
|
adjustlocalvars(ls, 1);
|
|
|
|
}
|
|
|
|
parlist(ls);
|
|
|
|
checknext(ls, ')');
|
2014-04-02 18:46:06 +00:00
|
|
|
chunk(ls);
|
2009-05-21 19:01:41 +00:00
|
|
|
new_fs.f->lastlinedefined = ls->linenumber;
|
|
|
|
check_match(ls, TK_END, TK_FUNCTION, line);
|
|
|
|
close_func(ls);
|
2014-04-02 18:46:06 +00:00
|
|
|
pushclosure(ls, &new_fs, e);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static int explist1 (LexState *ls, expdesc *v) {
|
|
|
|
/* explist1 -> expr { `,' expr } */
|
2009-05-21 19:01:41 +00:00
|
|
|
int n = 1; /* at least one expression */
|
|
|
|
expr(ls, v);
|
|
|
|
while (testnext(ls, ',')) {
|
|
|
|
luaK_exp2nextreg(ls->fs, v);
|
|
|
|
expr(ls, v);
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void funcargs (LexState *ls, expdesc *f) {
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
expdesc args;
|
|
|
|
int base, nparams;
|
2014-04-02 18:46:06 +00:00
|
|
|
int line = ls->linenumber;
|
2009-05-21 19:01:41 +00:00
|
|
|
switch (ls->t.token) {
|
2014-04-02 18:46:06 +00:00
|
|
|
case '(': { /* funcargs -> `(' [ explist1 ] `)' */
|
|
|
|
if (line != ls->lastline)
|
|
|
|
luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)");
|
2009-05-21 19:01:41 +00:00
|
|
|
luaX_next(ls);
|
|
|
|
if (ls->t.token == ')') /* arg list is empty? */
|
|
|
|
args.k = VVOID;
|
|
|
|
else {
|
2014-04-02 18:46:06 +00:00
|
|
|
explist1(ls, &args);
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_setmultret(fs, &args);
|
|
|
|
}
|
|
|
|
check_match(ls, ')', '(', line);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '{': { /* funcargs -> constructor */
|
|
|
|
constructor(ls, &args);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_STRING: { /* funcargs -> STRING */
|
|
|
|
codestring(ls, &args, ls->t.seminfo.ts);
|
|
|
|
luaX_next(ls); /* must use `seminfo' before `next' */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
luaX_syntaxerror(ls, "function arguments expected");
|
2014-04-02 18:46:06 +00:00
|
|
|
return;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_assert(f->k == VNONRELOC);
|
2014-04-02 18:46:06 +00:00
|
|
|
base = f->u.s.info; /* base register for call */
|
2009-05-21 19:01:41 +00:00
|
|
|
if (hasmultret(args.k))
|
|
|
|
nparams = LUA_MULTRET; /* open call */
|
|
|
|
else {
|
|
|
|
if (args.k != VVOID)
|
|
|
|
luaK_exp2nextreg(fs, &args); /* close last argument */
|
|
|
|
nparams = fs->freereg - (base+1);
|
|
|
|
}
|
|
|
|
init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
|
|
|
|
luaK_fixline(fs, line);
|
|
|
|
fs->freereg = base+1; /* call remove function and arguments and leaves
|
|
|
|
(unless changed) one result */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** {======================================================================
|
|
|
|
** Expression parsing
|
|
|
|
** =======================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void prefixexp (LexState *ls, expdesc *v) {
|
|
|
|
/* prefixexp -> NAME | '(' expr ')' */
|
2009-05-21 19:01:41 +00:00
|
|
|
switch (ls->t.token) {
|
|
|
|
case '(': {
|
|
|
|
int line = ls->linenumber;
|
|
|
|
luaX_next(ls);
|
|
|
|
expr(ls, v);
|
|
|
|
check_match(ls, ')', '(', line);
|
|
|
|
luaK_dischargevars(ls->fs, v);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TK_NAME: {
|
|
|
|
singlevar(ls, v);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
luaX_syntaxerror(ls, "unexpected symbol");
|
2014-04-02 18:46:06 +00:00
|
|
|
return;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void primaryexp (LexState *ls, expdesc *v) {
|
|
|
|
/* primaryexp ->
|
|
|
|
prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
2014-04-02 18:46:06 +00:00
|
|
|
prefixexp(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
for (;;) {
|
|
|
|
switch (ls->t.token) {
|
2014-04-02 18:46:06 +00:00
|
|
|
case '.': { /* field */
|
|
|
|
field(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '[': { /* `[' exp1 `]' */
|
|
|
|
expdesc key;
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_exp2anyreg(fs, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
yindex(ls, &key);
|
|
|
|
luaK_indexed(fs, v, &key);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ':': { /* `:' NAME funcargs */
|
|
|
|
expdesc key;
|
|
|
|
luaX_next(ls);
|
|
|
|
checkname(ls, &key);
|
|
|
|
luaK_self(fs, v, &key);
|
2014-04-02 18:46:06 +00:00
|
|
|
funcargs(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '(': case TK_STRING: case '{': { /* funcargs */
|
|
|
|
luaK_exp2nextreg(fs, v);
|
2014-04-02 18:46:06 +00:00
|
|
|
funcargs(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void simpleexp (LexState *ls, expdesc *v) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
|
|
|
|
constructor | FUNCTION body | primaryexp */
|
2009-05-21 19:01:41 +00:00
|
|
|
switch (ls->t.token) {
|
|
|
|
case TK_NUMBER: {
|
|
|
|
init_exp(v, VKNUM, 0);
|
|
|
|
v->u.nval = ls->t.seminfo.r;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_STRING: {
|
|
|
|
codestring(ls, v, ls->t.seminfo.ts);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_NIL: {
|
|
|
|
init_exp(v, VNIL, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_TRUE: {
|
|
|
|
init_exp(v, VTRUE, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_FALSE: {
|
|
|
|
init_exp(v, VFALSE, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TK_DOTS: { /* vararg */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
check_condition(ls, fs->f->is_vararg,
|
|
|
|
"cannot use " LUA_QL("...") " outside a vararg function");
|
2014-04-02 18:46:06 +00:00
|
|
|
fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */
|
2009-05-21 19:01:41 +00:00
|
|
|
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '{': { /* constructor */
|
|
|
|
constructor(ls, v);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TK_FUNCTION: {
|
|
|
|
luaX_next(ls);
|
|
|
|
body(ls, v, 0, ls->linenumber);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default: {
|
2014-04-02 18:46:06 +00:00
|
|
|
primaryexp(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
luaX_next(ls);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static UnOpr getunopr (int op) {
|
|
|
|
switch (op) {
|
|
|
|
case TK_NOT: return OPR_NOT;
|
|
|
|
case '-': return OPR_MINUS;
|
|
|
|
case '#': return OPR_LEN;
|
|
|
|
default: return OPR_NOUNOPR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BinOpr 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 const struct {
|
|
|
|
lu_byte left; /* left priority for each binary operator */
|
|
|
|
lu_byte right; /* right priority */
|
|
|
|
} priority[] = { /* ORDER OPR */
|
2014-04-02 18:46:06 +00:00
|
|
|
{6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */
|
|
|
|
{10, 9}, {5, 4}, /* power and concat (right associative) */
|
|
|
|
{3, 3}, {3, 3}, /* equality and inequality */
|
|
|
|
{3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */
|
|
|
|
{2, 2}, {1, 1} /* logical (and/or) */
|
2009-05-21 19:01:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define 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'
|
|
|
|
*/
|
2014-04-02 18:46:06 +00:00
|
|
|
static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) {
|
2009-05-21 19:01:41 +00:00
|
|
|
BinOpr op;
|
|
|
|
UnOpr uop;
|
|
|
|
enterlevel(ls);
|
|
|
|
uop = getunopr(ls->t.token);
|
|
|
|
if (uop != OPR_NOUNOPR) {
|
|
|
|
luaX_next(ls);
|
|
|
|
subexpr(ls, v, UNARY_PRIORITY);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_prefix(ls->fs, uop, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
else simpleexp(ls, v);
|
|
|
|
/* expand while operators have priorities higher than `limit' */
|
|
|
|
op = getbinopr(ls->t.token);
|
|
|
|
while (op != OPR_NOBINOPR && priority[op].left > limit) {
|
|
|
|
expdesc v2;
|
|
|
|
BinOpr nextop;
|
|
|
|
luaX_next(ls);
|
|
|
|
luaK_infix(ls->fs, op, v);
|
|
|
|
/* read sub-expression with higher priority */
|
|
|
|
nextop = subexpr(ls, &v2, priority[op].right);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_posfix(ls->fs, op, v, &v2);
|
2009-05-21 19:01:41 +00:00
|
|
|
op = nextop;
|
|
|
|
}
|
|
|
|
leavelevel(ls);
|
|
|
|
return op; /* return first untreated operator */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void expr (LexState *ls, expdesc *v) {
|
|
|
|
subexpr(ls, v, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* }==================================================================== */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** {======================================================================
|
|
|
|
** Rules for Statements
|
|
|
|
** =======================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static int block_follow (int token) {
|
|
|
|
switch (token) {
|
|
|
|
case TK_ELSE: case TK_ELSEIF: case TK_END:
|
|
|
|
case TK_UNTIL: case TK_EOS:
|
|
|
|
return 1;
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-21 19:01:41 +00:00
|
|
|
static void block (LexState *ls) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* block -> chunk */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
BlockCnt bl;
|
|
|
|
enterblock(fs, &bl, 0);
|
2014-04-02 18:46:06 +00:00
|
|
|
chunk(ls);
|
|
|
|
lua_assert(bl.breaklist == NO_JUMP);
|
2009-05-21 19:01:41 +00:00
|
|
|
leaveblock(fs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** structure to chain all variables in the left-hand side of an
|
|
|
|
** assignment
|
|
|
|
*/
|
|
|
|
struct LHS_assign {
|
|
|
|
struct LHS_assign *prev;
|
|
|
|
expdesc v; /* variable (global, local, upvalue, or indexed) */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2014-04-02 18:46:06 +00:00
|
|
|
** 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.
|
2009-05-21 19:01:41 +00:00
|
|
|
*/
|
|
|
|
static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int extra = fs->freereg; /* eventual position to save local variable */
|
|
|
|
int conflict = 0;
|
2014-04-02 18:46:06 +00:00
|
|
|
for (; lh; lh = lh->prev) {
|
|
|
|
if (lh->v.k == VINDEXED) {
|
|
|
|
if (lh->v.u.s.info == v->u.s.info) { /* conflict? */
|
2009-05-21 19:01:41 +00:00
|
|
|
conflict = 1;
|
2014-04-02 18:46:06 +00:00
|
|
|
lh->v.u.s.info = extra; /* previous assignment will use safe copy */
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */
|
2009-05-21 19:01:41 +00:00
|
|
|
conflict = 1;
|
2014-04-02 18:46:06 +00:00
|
|
|
lh->v.u.s.aux = extra; /* previous assignment will use safe copy */
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (conflict) {
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_reserveregs(fs, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
|
|
|
|
expdesc e;
|
2014-04-02 18:46:06 +00:00
|
|
|
check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,
|
|
|
|
"syntax error");
|
|
|
|
if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */
|
2009-05-21 19:01:41 +00:00
|
|
|
struct LHS_assign nv;
|
|
|
|
nv.prev = lh;
|
2014-04-02 18:46:06 +00:00
|
|
|
primaryexp(ls, &nv.v);
|
|
|
|
if (nv.v.k == VLOCAL)
|
2009-05-21 19:01:41 +00:00
|
|
|
check_conflict(ls, lh, &nv.v);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,
|
|
|
|
"variables in assignment");
|
2009-05-21 19:01:41 +00:00
|
|
|
assignment(ls, &nv, nvars+1);
|
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
else { /* assignment -> `=' explist1 */
|
2009-05-21 19:01:41 +00:00
|
|
|
int nexps;
|
|
|
|
checknext(ls, '=');
|
2014-04-02 18:46:06 +00:00
|
|
|
nexps = explist1(ls, &e);
|
2009-05-21 19:01:41 +00:00
|
|
|
if (nexps != nvars) {
|
|
|
|
adjust_assign(ls, nvars, nexps, &e);
|
|
|
|
if (nexps > nvars)
|
|
|
|
ls->fs->freereg -= nexps - nvars; /* remove extra values */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
luaK_setoneret(ls->fs, &e); /* close last expression */
|
|
|
|
luaK_storevar(ls->fs, &lh->v, &e);
|
|
|
|
return; /* avoid default */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
|
|
|
|
luaK_storevar(ls->fs, &lh->v, &e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cond (LexState *ls) {
|
|
|
|
/* cond -> exp */
|
|
|
|
expdesc v;
|
|
|
|
expr(ls, &v); /* read condition */
|
|
|
|
if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */
|
|
|
|
luaK_goiftrue(ls->fs, &v);
|
|
|
|
return v.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void breakstat (LexState *ls) {
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
2014-04-02 18:46:06 +00:00
|
|
|
BlockCnt *bl = fs->bl;
|
|
|
|
int upval = 0;
|
|
|
|
while (bl && !bl->isbreakable) {
|
|
|
|
upval |= bl->upval;
|
|
|
|
bl = bl->previous;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
if (!bl)
|
|
|
|
luaX_syntaxerror(ls, "no loop to break");
|
|
|
|
if (upval)
|
|
|
|
luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
|
|
|
|
luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void whilestat (LexState *ls, int line) {
|
|
|
|
/* whilestat -> WHILE cond DO block END */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int whileinit;
|
|
|
|
int condexit;
|
|
|
|
BlockCnt bl;
|
|
|
|
luaX_next(ls); /* skip WHILE */
|
|
|
|
whileinit = luaK_getlabel(fs);
|
|
|
|
condexit = cond(ls);
|
|
|
|
enterblock(fs, &bl, 1);
|
|
|
|
checknext(ls, TK_DO);
|
|
|
|
block(ls);
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_patchlist(fs, luaK_jump(fs), whileinit);
|
2009-05-21 19:01:41 +00:00
|
|
|
check_match(ls, TK_END, TK_WHILE, line);
|
|
|
|
leaveblock(fs);
|
|
|
|
luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void repeatstat (LexState *ls, int line) {
|
|
|
|
/* repeatstat -> REPEAT block UNTIL cond */
|
|
|
|
int condexit;
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int repeat_init = luaK_getlabel(fs);
|
|
|
|
BlockCnt bl1, bl2;
|
|
|
|
enterblock(fs, &bl1, 1); /* loop block */
|
|
|
|
enterblock(fs, &bl2, 0); /* scope block */
|
|
|
|
luaX_next(ls); /* skip REPEAT */
|
2014-04-02 18:46:06 +00:00
|
|
|
chunk(ls);
|
2009-05-21 19:01:41 +00:00
|
|
|
check_match(ls, TK_UNTIL, TK_REPEAT, line);
|
|
|
|
condexit = cond(ls); /* read condition (inside scope block) */
|
2014-04-02 18:46:06 +00:00
|
|
|
if (!bl2.upval) { /* no upvalues? */
|
|
|
|
leaveblock(fs); /* finish scope */
|
|
|
|
luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */
|
|
|
|
}
|
|
|
|
else { /* complete semantics when there are upvalues */
|
|
|
|
breakstat(ls); /* if condition then break */
|
|
|
|
luaK_patchtohere(ls->fs, condexit); /* else... */
|
|
|
|
leaveblock(fs); /* finish scope... */
|
|
|
|
luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */
|
|
|
|
}
|
2009-05-21 19:01:41 +00:00
|
|
|
leaveblock(fs); /* finish loop */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int exp1 (LexState *ls) {
|
|
|
|
expdesc e;
|
2014-04-02 18:46:06 +00:00
|
|
|
int k;
|
2009-05-21 19:01:41 +00:00
|
|
|
expr(ls, &e);
|
2014-04-02 18:46:06 +00:00
|
|
|
k = e.k;
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_exp2nextreg(ls->fs, &e);
|
2014-04-02 18:46:06 +00:00
|
|
|
return k;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void forbody (LexState *ls, int base, int line, int nvars, int isnum) {
|
|
|
|
/* forbody -> DO block */
|
|
|
|
BlockCnt bl;
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int prep, endfor;
|
|
|
|
adjustlocalvars(ls, 3); /* control variables */
|
|
|
|
checknext(ls, TK_DO);
|
|
|
|
prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);
|
|
|
|
enterblock(fs, &bl, 0); /* scope for declared variables */
|
|
|
|
adjustlocalvars(ls, nvars);
|
|
|
|
luaK_reserveregs(fs, nvars);
|
|
|
|
block(ls);
|
|
|
|
leaveblock(fs); /* end of scope for declared variables */
|
|
|
|
luaK_patchtohere(fs, prep);
|
2014-04-02 18:46:06 +00:00
|
|
|
endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) :
|
|
|
|
luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars);
|
|
|
|
luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */
|
|
|
|
luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void fornum (LexState *ls, TString *varname, int line) {
|
|
|
|
/* fornum -> NAME = exp1,exp1[,exp1] forbody */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
int base = fs->freereg;
|
2014-04-02 18:46:06 +00:00
|
|
|
new_localvarliteral(ls, "(for index)", 0);
|
|
|
|
new_localvarliteral(ls, "(for limit)", 1);
|
|
|
|
new_localvarliteral(ls, "(for step)", 2);
|
|
|
|
new_localvar(ls, varname, 3);
|
2009-05-21 19:01:41 +00:00
|
|
|
checknext(ls, '=');
|
|
|
|
exp1(ls); /* initial value */
|
|
|
|
checknext(ls, ',');
|
|
|
|
exp1(ls); /* limit */
|
|
|
|
if (testnext(ls, ','))
|
|
|
|
exp1(ls); /* optional step */
|
|
|
|
else { /* default step = 1 */
|
2014-04-02 18:46:06 +00:00
|
|
|
luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1));
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_reserveregs(fs, 1);
|
|
|
|
}
|
|
|
|
forbody(ls, base, line, 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void forlist (LexState *ls, TString *indexname) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* forlist -> NAME {,NAME} IN explist1 forbody */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
expdesc e;
|
2014-04-02 18:46:06 +00:00
|
|
|
int nvars = 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
int line;
|
|
|
|
int base = fs->freereg;
|
|
|
|
/* create control variables */
|
2014-04-02 18:46:06 +00:00
|
|
|
new_localvarliteral(ls, "(for generator)", nvars++);
|
|
|
|
new_localvarliteral(ls, "(for state)", nvars++);
|
|
|
|
new_localvarliteral(ls, "(for control)", nvars++);
|
2009-05-21 19:01:41 +00:00
|
|
|
/* create declared variables */
|
2014-04-02 18:46:06 +00:00
|
|
|
new_localvar(ls, indexname, nvars++);
|
|
|
|
while (testnext(ls, ','))
|
|
|
|
new_localvar(ls, str_checkname(ls), nvars++);
|
2009-05-21 19:01:41 +00:00
|
|
|
checknext(ls, TK_IN);
|
|
|
|
line = ls->linenumber;
|
2014-04-02 18:46:06 +00:00
|
|
|
adjust_assign(ls, 3, explist1(ls, &e), &e);
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_checkstack(fs, 3); /* extra space to call generator */
|
|
|
|
forbody(ls, base, line, nvars - 3, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void forstat (LexState *ls, int line) {
|
|
|
|
/* forstat -> FOR (fornum | forlist) END */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
TString *varname;
|
|
|
|
BlockCnt bl;
|
|
|
|
enterblock(fs, &bl, 1); /* scope for loop and control variables */
|
|
|
|
luaX_next(ls); /* skip `for' */
|
|
|
|
varname = str_checkname(ls); /* first variable name */
|
|
|
|
switch (ls->t.token) {
|
|
|
|
case '=': fornum(ls, varname, line); break;
|
|
|
|
case ',': case TK_IN: forlist(ls, varname); break;
|
|
|
|
default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected");
|
|
|
|
}
|
|
|
|
check_match(ls, TK_END, TK_FOR, line);
|
|
|
|
leaveblock(fs); /* loop scope (`break' jumps to this point) */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static int test_then_block (LexState *ls) {
|
2009-05-21 19:01:41 +00:00
|
|
|
/* test_then_block -> [IF | ELSEIF] cond THEN block */
|
2014-04-02 18:46:06 +00:00
|
|
|
int condexit;
|
2009-05-21 19:01:41 +00:00
|
|
|
luaX_next(ls); /* skip IF or ELSEIF */
|
2014-04-02 18:46:06 +00:00
|
|
|
condexit = cond(ls);
|
2009-05-21 19:01:41 +00:00
|
|
|
checknext(ls, TK_THEN);
|
2014-04-02 18:46:06 +00:00
|
|
|
block(ls); /* `then' part */
|
|
|
|
return condexit;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ifstat (LexState *ls, int line) {
|
|
|
|
/* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
|
|
|
|
FuncState *fs = ls->fs;
|
2014-04-02 18:46:06 +00:00
|
|
|
int flist;
|
|
|
|
int escapelist = NO_JUMP;
|
|
|
|
flist = test_then_block(ls); /* IF cond THEN block */
|
|
|
|
while (ls->t.token == TK_ELSEIF) {
|
|
|
|
luaK_concat(fs, &escapelist, luaK_jump(fs));
|
|
|
|
luaK_patchtohere(fs, flist);
|
|
|
|
flist = test_then_block(ls); /* ELSEIF cond THEN block */
|
|
|
|
}
|
|
|
|
if (ls->t.token == TK_ELSE) {
|
|
|
|
luaK_concat(fs, &escapelist, luaK_jump(fs));
|
|
|
|
luaK_patchtohere(fs, flist);
|
|
|
|
luaX_next(ls); /* skip ELSE (after patch, for correct line info) */
|
2009-05-21 19:01:41 +00:00
|
|
|
block(ls); /* `else' part */
|
2014-04-02 18:46:06 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
luaK_concat(fs, &escapelist, flist);
|
|
|
|
luaK_patchtohere(fs, escapelist);
|
2009-05-21 19:01:41 +00:00
|
|
|
check_match(ls, TK_END, TK_IF, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void localfunc (LexState *ls) {
|
2014-04-02 18:46:06 +00:00
|
|
|
expdesc v, b;
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
2014-04-02 18:46:06 +00:00
|
|
|
new_localvar(ls, str_checkname(ls), 0);
|
|
|
|
init_exp(&v, VLOCAL, fs->freereg);
|
|
|
|
luaK_reserveregs(fs, 1);
|
|
|
|
adjustlocalvars(ls, 1);
|
|
|
|
body(ls, &b, 0, ls->linenumber);
|
|
|
|
luaK_storevar(fs, &v, &b);
|
2009-05-21 19:01:41 +00:00
|
|
|
/* debug information will only see the variable after this point! */
|
2014-04-02 18:46:06 +00:00
|
|
|
getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void localstat (LexState *ls) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
|
2009-05-21 19:01:41 +00:00
|
|
|
int nvars = 0;
|
|
|
|
int nexps;
|
|
|
|
expdesc e;
|
|
|
|
do {
|
2014-04-02 18:46:06 +00:00
|
|
|
new_localvar(ls, str_checkname(ls), nvars++);
|
2009-05-21 19:01:41 +00:00
|
|
|
} while (testnext(ls, ','));
|
|
|
|
if (testnext(ls, '='))
|
2014-04-02 18:46:06 +00:00
|
|
|
nexps = explist1(ls, &e);
|
2009-05-21 19:01:41 +00:00
|
|
|
else {
|
|
|
|
e.k = VVOID;
|
|
|
|
nexps = 0;
|
|
|
|
}
|
|
|
|
adjust_assign(ls, nvars, nexps, &e);
|
|
|
|
adjustlocalvars(ls, nvars);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int funcname (LexState *ls, expdesc *v) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* funcname -> NAME {field} [`:' NAME] */
|
|
|
|
int needself = 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
singlevar(ls, v);
|
|
|
|
while (ls->t.token == '.')
|
2014-04-02 18:46:06 +00:00
|
|
|
field(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
if (ls->t.token == ':') {
|
2014-04-02 18:46:06 +00:00
|
|
|
needself = 1;
|
|
|
|
field(ls, v);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
return needself;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void funcstat (LexState *ls, int line) {
|
|
|
|
/* funcstat -> FUNCTION funcname body */
|
2014-04-02 18:46:06 +00:00
|
|
|
int needself;
|
2009-05-21 19:01:41 +00:00
|
|
|
expdesc v, b;
|
|
|
|
luaX_next(ls); /* skip FUNCTION */
|
2014-04-02 18:46:06 +00:00
|
|
|
needself = funcname(ls, &v);
|
|
|
|
body(ls, &b, needself, line);
|
2009-05-21 19:01:41 +00:00
|
|
|
luaK_storevar(ls->fs, &v, &b);
|
|
|
|
luaK_fixline(ls->fs, line); /* definition `happens' in the first line */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void exprstat (LexState *ls) {
|
|
|
|
/* stat -> func | assignment */
|
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
struct LHS_assign v;
|
2014-04-02 18:46:06 +00:00
|
|
|
primaryexp(ls, &v.v);
|
|
|
|
if (v.v.k == VCALL) /* stat -> func */
|
|
|
|
SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
|
|
|
|
else { /* stat -> assignment */
|
2009-05-21 19:01:41 +00:00
|
|
|
v.prev = NULL;
|
|
|
|
assignment(ls, &v, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void retstat (LexState *ls) {
|
2014-04-02 18:46:06 +00:00
|
|
|
/* stat -> RETURN explist */
|
2009-05-21 19:01:41 +00:00
|
|
|
FuncState *fs = ls->fs;
|
|
|
|
expdesc e;
|
|
|
|
int first, nret; /* registers with returned values */
|
2014-04-02 18:46:06 +00:00
|
|
|
luaX_next(ls); /* skip RETURN */
|
|
|
|
if (block_follow(ls->t.token) || ls->t.token == ';')
|
2009-05-21 19:01:41 +00:00
|
|
|
first = nret = 0; /* return no values */
|
|
|
|
else {
|
2014-04-02 18:46:06 +00:00
|
|
|
nret = explist1(ls, &e); /* optional return values */
|
2009-05-21 19:01:41 +00:00
|
|
|
if (hasmultret(e.k)) {
|
|
|
|
luaK_setmultret(fs, &e);
|
|
|
|
if (e.k == VCALL && nret == 1) { /* tail call? */
|
|
|
|
SET_OPCODE(getcode(fs,&e), OP_TAILCALL);
|
|
|
|
lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);
|
|
|
|
}
|
|
|
|
first = fs->nactvar;
|
|
|
|
nret = LUA_MULTRET; /* return all values */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (nret == 1) /* only one single value? */
|
|
|
|
first = luaK_exp2anyreg(fs, &e);
|
|
|
|
else {
|
|
|
|
luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */
|
|
|
|
first = fs->nactvar; /* return all `active' values */
|
|
|
|
lua_assert(nret == fs->freereg - first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
luaK_ret(fs, first, nret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static int statement (LexState *ls) {
|
2009-05-21 19:01:41 +00:00
|
|
|
int line = ls->linenumber; /* may be needed for error messages */
|
|
|
|
switch (ls->t.token) {
|
|
|
|
case TK_IF: { /* stat -> ifstat */
|
|
|
|
ifstat(ls, line);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
case TK_WHILE: { /* stat -> whilestat */
|
|
|
|
whilestat(ls, line);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
case TK_DO: { /* stat -> DO block END */
|
|
|
|
luaX_next(ls); /* skip DO */
|
|
|
|
block(ls);
|
|
|
|
check_match(ls, TK_END, TK_DO, line);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
case TK_FOR: { /* stat -> forstat */
|
|
|
|
forstat(ls, line);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
case TK_REPEAT: { /* stat -> repeatstat */
|
|
|
|
repeatstat(ls, line);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
case TK_FUNCTION: {
|
|
|
|
funcstat(ls, line); /* stat -> funcstat */
|
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
case TK_LOCAL: { /* stat -> localstat */
|
|
|
|
luaX_next(ls); /* skip LOCAL */
|
|
|
|
if (testnext(ls, TK_FUNCTION)) /* local function? */
|
|
|
|
localfunc(ls);
|
|
|
|
else
|
|
|
|
localstat(ls);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0;
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
case TK_RETURN: { /* stat -> retstat */
|
|
|
|
retstat(ls);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 1; /* must be last statement */
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
case TK_BREAK: { /* stat -> breakstat */
|
|
|
|
luaX_next(ls); /* skip BREAK */
|
|
|
|
breakstat(ls);
|
|
|
|
return 1; /* must be last statement */
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
2014-04-02 18:46:06 +00:00
|
|
|
default: {
|
2009-05-21 19:01:41 +00:00
|
|
|
exprstat(ls);
|
2014-04-02 18:46:06 +00:00
|
|
|
return 0; /* to avoid warnings */
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
}
|
Update lua plugin to 5.2.3
Prior to this patch the Lua plugin used version 5.1.4. This change
reduces the number of modifications in the Lua source using some new
defines and because the upstream source is now more flexible.
Unless otherwise stated, l*.[ch] files are taken unmodified from the
upstream lua-5.2.3.
fscanf.c:
file descriptors in rockbox are just ints, they are hidden behind a
void* now so liolib requires less modifications. fscanf is updated to
use void* too.
getc.c: this is a new file required for getc implementation in lauxlib.c
lauxlib.c: LoadF replaced FILE* with int, the rockbox file
descriptor int are cast to FILE* (actually void* due to typedef).
getc uses the PREFIX version. stdin is not used, as per 5.1.4.
lbaselib.c: now uses strspn in the number parsing. print uses DEBUGF now
rather than being commented out.
lbitlib.c: use the built-in version from 5.2.3 rather than Reuben
Thomas's external library. Backwards compatible and adds some new bit
operations.
ldo.c: the LUAI_THROW/TRY defines are now in the core lua code, so have
been removed from rockconf.h
liolib.c: here the implementation has changed to use the LStream from
the original source, and cast the FILE* pointers to int. This has
reduced the number of modifications from the upstream version.
llex.c: the only change from upstream is to remove the locale include.
lmathlib.c: updated from the 5.2.3 version and re-applied the changes
that were made vs 5.1.4 for random numbers and to remove unsupported
float functions.
loadlib.c: upstream version, with the 5.1.4 changes for missing
functions.
lobject.c: upstream version, with ctype.h added and sprintf changed to
snprintf.
loslib.c: upstream version with locale.h removed and 5.1.4 changes for
unsupportable functions.
lstrlib.c: sprintf changed to snprintf.
ltable.c: upstream with the hashnum function from 5.1.4 to avoid frexp
in luai_hashnum.
luaconf.h: updated to 5.2.3 version, restored relevant parts from the
original 5.1.4 configuration. The COMPAT defines that are no longer
available are not included.
lundump.c: VERSION macro conflicts with the core Rockbox equivalent.
rocklib.c: luaL_reg is no longer available, replaced by luaL_Reg
equivalent. Moved checkboolean/optboolean functions to this file and out
of core lua files. luaL_getn is no longer available, replaced by
luaL_rawlen. luaL_register is deprecated, use the newlib/setfuncs
replacements. rli_init has to be called before setting up the newlib to
avoid overwriting the rb table.
rocklib_aux.pl: use rli_checkboolean from rocklib.c.
rocklua.c: new default bits library used, update the library loading
code with idiomatic 5.2 code.
strcspn.c: no longer needed, but strspn.c is required for strspn in
lbaselib.c
Change-Id: I0c7945c755f79083afe98ec117e1e8cf13de2651
Reviewed-on: http://gerrit.rockbox.org/774
Tested: Richard Quirk <richard.quirk@gmail.com>
Reviewed-by: Marcin Bukat <marcin.bukat@gmail.com>
2014-03-19 18:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
static void chunk (LexState *ls) {
|
|
|
|
/* chunk -> { stat [`;'] } */
|
|
|
|
int islast = 0;
|
|
|
|
enterlevel(ls);
|
|
|
|
while (!islast && !block_follow(ls->t.token)) {
|
|
|
|
islast = statement(ls);
|
|
|
|
testnext(ls, ';');
|
|
|
|
lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
|
|
|
|
ls->fs->freereg >= ls->fs->nactvar);
|
|
|
|
ls->fs->freereg = ls->fs->nactvar; /* free registers */
|
|
|
|
}
|
|
|
|
leavelevel(ls);
|
2009-05-21 19:01:41 +00:00
|
|
|
}
|
|
|
|
|
2014-04-02 18:46:06 +00:00
|
|
|
/* }====================================================================== */
|