diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 18 | ||||
-rw-r--r-- | src/lapi.c | 796 | ||||
-rw-r--r-- | src/lapi.h | 11 | ||||
-rw-r--r-- | src/lauxlib.c | 133 | ||||
-rw-r--r-- | src/lbuffer.c | 75 | ||||
-rw-r--r-- | src/lbuiltin.c | 736 | ||||
-rw-r--r-- | src/lbuiltin.h | 14 | ||||
-rw-r--r-- | src/lcode.c | 701 | ||||
-rw-r--r-- | src/lcode.h | 70 | ||||
-rw-r--r-- | src/ldebug.c | 466 | ||||
-rw-r--r-- | src/ldebug.h | 21 | ||||
-rw-r--r-- | src/ldo.c | 544 | ||||
-rw-r--r-- | src/ldo.h | 33 | ||||
-rw-r--r-- | src/lfunc.c | 121 | ||||
-rw-r--r-- | src/lfunc.h | 15 | ||||
-rw-r--r-- | src/lgc.c | 442 | ||||
-rw-r--r-- | src/lgc.h | 9 | ||||
-rw-r--r-- | src/lib/Makefile | 6 | ||||
-rw-r--r-- | src/lib/README | 6 | ||||
-rw-r--r-- | src/lib/lauxlib.c | 216 | ||||
-rw-r--r-- | src/lib/lbaselib.c | 651 | ||||
-rw-r--r-- | src/lib/ldblib.c | 277 | ||||
-rw-r--r-- | src/lib/linit.c | 17 | ||||
-rw-r--r-- | src/lib/liolib.c | 729 | ||||
-rw-r--r-- | src/lib/lmathlib.c | 193 | ||||
-rw-r--r-- | src/lib/lstrlib.c | 503 | ||||
-rw-r--r-- | src/llex.c | 495 | ||||
-rw-r--r-- | src/llex.h | 70 | ||||
-rw-r--r-- | src/llimits.h | 204 | ||||
-rw-r--r-- | src/lmem.c | 178 | ||||
-rw-r--r-- | src/lmem.h | 45 | ||||
-rw-r--r-- | src/lobject.c | 170 | ||||
-rw-r--r-- | src/lobject.h | 250 | ||||
-rw-r--r-- | src/lopcodes.h | 210 | ||||
-rw-r--r-- | src/lparser.c | 1910 | ||||
-rw-r--r-- | src/lparser.h | 50 | ||||
-rw-r--r-- | src/lstate.c | 153 | ||||
-rw-r--r-- | src/lstate.h | 87 | ||||
-rw-r--r-- | src/lstring.c | 335 | ||||
-rw-r--r-- | src/lstring.h | 35 | ||||
-rw-r--r-- | src/ltable.c | 336 | ||||
-rw-r--r-- | src/ltable.h | 34 | ||||
-rw-r--r-- | src/ltests.c | 543 | ||||
-rw-r--r-- | src/ltm.c | 271 | ||||
-rw-r--r-- | src/ltm.h | 71 | ||||
-rw-r--r-- | src/lua/Makefile | 4 | ||||
-rw-r--r-- | src/lua/README | 50 | ||||
-rw-r--r-- | src/lua/lua.c | 314 | ||||
-rw-r--r-- | src/luac/Makefile | 6 | ||||
-rw-r--r-- | src/luac/README | 34 | ||||
-rw-r--r-- | src/luac/dump.c | 173 | ||||
-rw-r--r-- | src/luac/luac.c | 252 | ||||
-rw-r--r-- | src/luac/luac.h | 37 | ||||
-rw-r--r-- | src/luac/opcode.c | 102 | ||||
-rw-r--r-- | src/luac/opcode.h | 70 | ||||
-rw-r--r-- | src/luac/opt.c | 326 | ||||
-rw-r--r-- | src/luac/print.c | 252 | ||||
-rw-r--r-- | src/luac/print.h | 55 | ||||
-rw-r--r-- | src/luac/stubs.c | 176 | ||||
-rw-r--r-- | src/luac/test.c | 253 | ||||
-rw-r--r-- | src/lundump.c | 303 | ||||
-rw-r--r-- | src/lundump.h | 28 | ||||
-rw-r--r-- | src/lvm.c | 1023 | ||||
-rw-r--r-- | src/lvm.h | 28 | ||||
-rw-r--r-- | src/lzio.c | 68 | ||||
-rw-r--r-- | src/lzio.h | 27 |
66 files changed, 8492 insertions, 7339 deletions
diff --git a/src/Makefile b/src/Makefile index 7d01a924..66e0ea8d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -# makefile for lua basic library +# makefile for Lua core library LUA= .. @@ -6,9 +6,8 @@ include $(LUA)/config OBJS= \ lapi.o \ - lauxlib.o \ - lbuffer.o \ - lbuiltin.o \ + lcode.o \ + ldebug.o \ ldo.o \ lfunc.o \ lgc.o \ @@ -19,6 +18,7 @@ OBJS= \ lstate.o \ lstring.o \ ltable.o \ + ltests.o \ ltm.o \ lundump.o \ lvm.o \ @@ -26,9 +26,8 @@ OBJS= \ SRCS= \ lapi.c \ - lauxlib.c \ - lbuffer.c \ - lbuiltin.c \ + lcode.c \ + ldebug.c \ ldo.c \ lfunc.c \ lgc.c \ @@ -39,16 +38,19 @@ SRCS= \ lstate.c \ lstring.c \ ltable.c \ + ltests.c \ ltm.c \ lundump.c \ lvm.c \ lzio.c \ lapi.h \ - lbuiltin.h \ + lcode.h \ + ldebug.h \ ldo.h \ lfunc.h \ lgc.h \ llex.h \ + llimits.h \ lmem.h \ lobject.h \ lopcodes.h \ @@ -1,15 +1,15 @@ /* -** $Id: lapi.c,v 1.47b 1999/06/22 20:37:23 roberto Exp $ +** $Id: lapi.c,v 1.110 2000/10/30 12:50:09 roberto Exp $ ** Lua API ** See Copyright Notice in lua.h */ -#include <stdlib.h> #include <string.h> +#include "lua.h" + #include "lapi.h" -#include "lauxlib.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" @@ -19,676 +19,476 @@ #include "lstring.h" #include "ltable.h" #include "ltm.h" -#include "lua.h" -#include "luadebug.h" #include "lvm.h" -char lua_ident[] = "$Lua: " LUA_VERSION " " LUA_COPYRIGHT " $\n" - "$Authors: " LUA_AUTHORS " $"; - - - -TObject *luaA_Address (lua_Object o) { - return (o != LUA_NOOBJECT) ? Address(o) : NULL; -} - - -static lua_Type normalized_type (TObject *o) -{ - int t = ttype(o); - switch (t) { - case LUA_T_PMARK: - return LUA_T_PROTO; - case LUA_T_CMARK: - return LUA_T_CPROTO; - case LUA_T_CLMARK: - return LUA_T_CLOSURE; - default: - return t; - } -} +const char lua_ident[] = "$Lua: " LUA_VERSION " " LUA_COPYRIGHT " $\n" + "$Authors: " LUA_AUTHORS " $"; -static void set_normalized (TObject *d, TObject *s) -{ - d->value = s->value; - d->ttype = normalized_type(s); -} +#define Index(L,i) ((i) >= 0 ? (L->Cbase+((i)-1)) : (L->top+(i))) -static TObject *luaA_protovalue (TObject *o) -{ - return (normalized_type(o) == LUA_T_CLOSURE) ? protovalue(o) : o; -} +#define api_incr_top(L) incr_top -void luaA_packresults (void) -{ - luaV_pack(L->Cstack.lua2C, L->Cstack.num, L->stack.top); - incr_top; -} -int luaA_passresults (void) { - L->Cstack.base = L->Cstack.lua2C; /* position of first result */ - return L->Cstack.num; +TObject *luaA_index (lua_State *L, int index) { + return Index(L, index); } -static void checkCparams (int nParams) -{ - if (L->stack.top-L->stack.stack < L->Cstack.base+nParams) - lua_error("API error - wrong number of arguments in C2lua stack"); -} - - -static lua_Object put_luaObject (TObject *o) { - luaD_openstack((L->stack.top-L->stack.stack)-L->Cstack.base); - L->stack.stack[L->Cstack.base++] = *o; - return L->Cstack.base; /* this is +1 real position (see Ref) */ +static TObject *luaA_indexAcceptable (lua_State *L, int index) { + if (index >= 0) { + TObject *o = L->Cbase+(index-1); + if (o >= L->top) return NULL; + else return o; + } + else return L->top+index; } -static lua_Object put_luaObjectonTop (void) { - luaD_openstack((L->stack.top-L->stack.stack)-L->Cstack.base); - L->stack.stack[L->Cstack.base++] = *(--L->stack.top); - return L->Cstack.base; /* this is +1 real position (see Ref) */ +void luaA_pushobject (lua_State *L, const TObject *o) { + *L->top = *o; + incr_top; } - -static void top2LC (int n) { - /* Put the 'n' elements on the top as the Lua2C contents */ - L->Cstack.base = (L->stack.top-L->stack.stack); /* new base */ - L->Cstack.lua2C = L->Cstack.base-n; /* position of the new results */ - L->Cstack.num = n; /* number of results */ +LUA_API int lua_stackspace (lua_State *L) { + return (L->stack_last - L->top); } -lua_Object lua_pop (void) { - checkCparams(1); - return put_luaObjectonTop(); -} - /* -** Get a parameter, returning the object handle or LUA_NOOBJECT on error. -** 'number' must be 1 to get the first parameter. +** basic stack manipulation */ -lua_Object lua_lua2C (int number) -{ - if (number <= 0 || number > L->Cstack.num) return LUA_NOOBJECT; - /* Ref(L->stack.stack+(L->Cstack.lua2C+number-1)) == - L->stack.stack+(L->Cstack.lua2C+number-1)-L->stack.stack+1 == */ - return L->Cstack.lua2C+number; -} -int lua_callfunction (lua_Object function) -{ - if (function == LUA_NOOBJECT) - return 1; - else { - luaD_openstack((L->stack.top-L->stack.stack)-L->Cstack.base); - set_normalized(L->stack.stack+L->Cstack.base, Address(function)); - return luaD_protectedrun(); - } +LUA_API int lua_gettop (lua_State *L) { + return (L->top - L->Cbase); } -lua_Object lua_gettagmethod (int tag, char *event) -{ - return put_luaObject(luaT_gettagmethod(tag, event)); +LUA_API void lua_settop (lua_State *L, int index) { + if (index >= 0) + luaD_adjusttop(L, L->Cbase, index); + else + L->top = L->top+index+1; /* index is negative */ } -lua_Object lua_settagmethod (int tag, char *event) -{ - checkCparams(1); - luaT_settagmethod(tag, event, L->stack.top-1); - return put_luaObjectonTop(); +LUA_API void lua_remove (lua_State *L, int index) { + StkId p = luaA_index(L, index); + while (++p < L->top) *(p-1) = *p; + L->top--; } -lua_Object lua_seterrormethod (void) { - lua_Object temp; - checkCparams(1); - temp = lua_getglobal("_ERRORMESSAGE"); - lua_setglobal("_ERRORMESSAGE"); - return temp; +LUA_API void lua_insert (lua_State *L, int index) { + StkId p = luaA_index(L, index); + StkId q; + for (q = L->top; q>p; q--) + *q = *(q-1); + *p = *L->top; } -lua_Object lua_gettable (void) -{ - checkCparams(2); - luaV_gettable(); - return put_luaObjectonTop(); +LUA_API void lua_pushvalue (lua_State *L, int index) { + *L->top = *luaA_index(L, index); + api_incr_top(L); } -lua_Object lua_rawgettable (void) { - checkCparams(2); - if (ttype(L->stack.top-2) != LUA_T_ARRAY) - lua_error("indexed expression not a table in rawgettable"); - *(L->stack.top-2) = *luaH_get(avalue(L->stack.top-2), L->stack.top-1); - --L->stack.top; - return put_luaObjectonTop(); -} - -void lua_settable (void) { - checkCparams(3); - luaD_checkstack(3); /* may need that to call T.M. */ - luaV_settable(L->stack.top-3); - L->stack.top -= 2; /* pop table and index */ -} +/* +** access functions (stack -> C) +*/ -void lua_rawsettable (void) { - checkCparams(3); - luaV_rawsettable(L->stack.top-3); +LUA_API int lua_type (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL) ? LUA_TNONE : ttype(o); } - -lua_Object lua_createtable (void) -{ - TObject o; - luaC_checkGC(); - avalue(&o) = luaH_new(0); - ttype(&o) = LUA_T_ARRAY; - return put_luaObject(&o); +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + return (t == LUA_TNONE) ? "no value" : luaO_typenames[t]; } -lua_Object lua_getglobal (char *name) -{ - luaD_checkstack(2); /* may need that to call T.M. */ - luaV_getglobal(luaS_new(name)); - return put_luaObjectonTop(); +LUA_API int lua_iscfunction (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL) ? 0 : iscfunction(o); } +LUA_API int lua_isnumber (lua_State *L, int index) { + TObject *o = luaA_indexAcceptable(L, index); + return (o == NULL) ? 0 : (tonumber(o) == 0); +} -lua_Object lua_rawgetglobal (char *name) -{ - TaggedString *ts = luaS_new(name); - return put_luaObject(&ts->u.s.globalval); +LUA_API int lua_isstring (lua_State *L, int index) { + int t = lua_type(L, index); + return (t == LUA_TSTRING || t == LUA_TNUMBER); } -void lua_setglobal (char *name) -{ - checkCparams(1); - luaD_checkstack(2); /* may need that to call T.M. */ - luaV_setglobal(luaS_new(name)); +LUA_API int lua_tag (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL) ? LUA_NOTAG : luaT_tag(o); } +LUA_API int lua_equal (lua_State *L, int index1, int index2) { + StkId o1 = luaA_indexAcceptable(L, index1); + StkId o2 = luaA_indexAcceptable(L, index2); + if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */ + else return luaO_equalObj(o1, o2); +} -void lua_rawsetglobal (char *name) -{ - TaggedString *ts = luaS_new(name); - checkCparams(1); - luaS_rawsetglobal(ts, --L->stack.top); +LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { + StkId o1 = luaA_indexAcceptable(L, index1); + StkId o2 = luaA_indexAcceptable(L, index2); + if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */ + else return luaV_lessthan(L, o1, o2, L->top); } -int lua_isnil (lua_Object o) -{ - return (o!= LUA_NOOBJECT) && (ttype(Address(o)) == LUA_T_NIL); +LUA_API double lua_tonumber (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL || tonumber(o)) ? 0 : nvalue(o); } -int lua_istable (lua_Object o) -{ - return (o!= LUA_NOOBJECT) && (ttype(Address(o)) == LUA_T_ARRAY); +LUA_API const char *lua_tostring (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL || tostring(L, o)) ? NULL : svalue(o); } -int lua_isuserdata (lua_Object o) -{ - return (o!= LUA_NOOBJECT) && (ttype(Address(o)) == LUA_T_USERDATA); +LUA_API size_t lua_strlen (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL || tostring(L, o)) ? 0 : tsvalue(o)->len; } -int lua_iscfunction (lua_Object o) -{ - return (lua_tag(o) == LUA_T_CPROTO); +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL || !iscfunction(o)) ? NULL : clvalue(o)->f.c; } -int lua_isnumber (lua_Object o) -{ - return (o!= LUA_NOOBJECT) && (tonumber(Address(o)) == 0); +LUA_API void *lua_touserdata (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + return (o == NULL || ttype(o) != LUA_TUSERDATA) ? NULL : + tsvalue(o)->u.d.value; } -int lua_isstring (lua_Object o) -{ - int t = lua_tag(o); - return (t == LUA_T_STRING) || (t == LUA_T_NUMBER); +LUA_API const void *lua_topointer (lua_State *L, int index) { + StkId o = luaA_indexAcceptable(L, index); + if (o == NULL) return NULL; + switch (ttype(o)) { + case LUA_TTABLE: + return hvalue(o); + case LUA_TFUNCTION: + return clvalue(o); + default: return NULL; + } } -int lua_isfunction (lua_Object o) -{ - int t = lua_tag(o); - return (t == LUA_T_PROTO) || (t == LUA_T_CPROTO); -} -double lua_getnumber (lua_Object object) -{ - if (object == LUA_NOOBJECT) return 0.0; - if (tonumber(Address(object))) return 0.0; - else return (nvalue(Address(object))); -} +/* +** push functions (C -> stack) +*/ -char *lua_getstring (lua_Object object) -{ - luaC_checkGC(); /* "tostring" may create a new string */ - if (object == LUA_NOOBJECT || tostring(Address(object))) - return NULL; - else return (svalue(Address(object))); -} -long lua_strlen (lua_Object object) -{ - luaC_checkGC(); /* "tostring" may create a new string */ - if (object == LUA_NOOBJECT || tostring(Address(object))) - return 0L; - else return (tsvalue(Address(object))->u.s.len); +LUA_API void lua_pushnil (lua_State *L) { + ttype(L->top) = LUA_TNIL; + api_incr_top(L); } -void *lua_getuserdata (lua_Object object) -{ - if (object == LUA_NOOBJECT || ttype(Address(object)) != LUA_T_USERDATA) - return NULL; - else return tsvalue(Address(object))->u.d.v; -} -lua_CFunction lua_getcfunction (lua_Object object) -{ - if (!lua_iscfunction(object)) - return NULL; - else return fvalue(luaA_protovalue(Address(object))); +LUA_API void lua_pushnumber (lua_State *L, double n) { + nvalue(L->top) = n; + ttype(L->top) = LUA_TNUMBER; + api_incr_top(L); } -void lua_pushnil (void) -{ - ttype(L->stack.top) = LUA_T_NIL; - incr_top; +LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { + tsvalue(L->top) = luaS_newlstr(L, s, len); + ttype(L->top) = LUA_TSTRING; + api_incr_top(L); } -void lua_pushnumber (double n) -{ - ttype(L->stack.top) = LUA_T_NUMBER; - nvalue(L->stack.top) = n; - incr_top; -} -void lua_pushlstring (char *s, long len) -{ - tsvalue(L->stack.top) = luaS_newlstr(s, len); - ttype(L->stack.top) = LUA_T_STRING; - incr_top; - luaC_checkGC(); -} - -void lua_pushstring (char *s) -{ +LUA_API void lua_pushstring (lua_State *L, const char *s) { if (s == NULL) - lua_pushnil(); + lua_pushnil(L); else - lua_pushlstring(s, strlen(s)); + lua_pushlstring(L, s, strlen(s)); } -void lua_pushcclosure (lua_CFunction fn, int n) -{ - if (fn == NULL) - lua_error("API error - attempt to push a NULL Cfunction"); - checkCparams(n); - ttype(L->stack.top) = LUA_T_CPROTO; - fvalue(L->stack.top) = fn; - incr_top; - luaV_closure(n); - luaC_checkGC(); -} -void lua_pushusertag (void *u, int tag) -{ - if (tag < 0 && tag != LUA_ANYTAG) - luaT_realtag(tag); /* error if tag is not valid */ - tsvalue(L->stack.top) = luaS_createudata(u, tag); - ttype(L->stack.top) = LUA_T_USERDATA; - incr_top; - luaC_checkGC(); +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + luaV_Cclosure(L, fn, n); } -void luaA_pushobject (TObject *o) -{ - *L->stack.top = *o; - incr_top; -} -void lua_pushobject (lua_Object o) { - if (o == LUA_NOOBJECT) - lua_error("API error - attempt to push a NOOBJECT"); - set_normalized(L->stack.top, Address(o)); - incr_top; +LUA_API void lua_pushusertag (lua_State *L, void *u, int tag) { + /* ORDER LUA_T */ + if (!(tag == LUA_ANYTAG || tag == LUA_TUSERDATA || validtag(tag))) + luaO_verror(L, "invalid tag for a userdata (%d)", tag); + tsvalue(L->top) = luaS_createudata(L, u, tag); + ttype(L->top) = LUA_TUSERDATA; + api_incr_top(L); } -int lua_tag (lua_Object lo) -{ - if (lo == LUA_NOOBJECT) - return LUA_T_NIL; - else { - TObject *o = Address(lo); - int t; - switch (t = ttype(o)) { - case LUA_T_USERDATA: - return o->value.ts->u.d.tag; - case LUA_T_ARRAY: - return o->value.a->htag; - case LUA_T_PMARK: - return LUA_T_PROTO; - case LUA_T_CMARK: - return LUA_T_CPROTO; - case LUA_T_CLOSURE: case LUA_T_CLMARK: - return o->value.cl->consts[0].ttype; -#ifdef DEBUG - case LUA_T_LINE: - LUA_INTERNALERROR("invalid type"); -#endif - default: - return t; - } - } -} - -void lua_settag (int tag) -{ - checkCparams(1); - luaT_realtag(tag); - switch (ttype(L->stack.top-1)) { - case LUA_T_ARRAY: - (L->stack.top-1)->value.a->htag = tag; - break; - case LUA_T_USERDATA: - (L->stack.top-1)->value.ts->u.d.tag = tag; - break; - default: - luaL_verror("cannot change the tag of a %.20s", - luaO_typename(L->stack.top-1)); - } - L->stack.top--; -} +/* +** get functions (Lua -> stack) +*/ -TaggedString *luaA_nextvar (TaggedString *g) { - if (g == NULL) - g = (TaggedString *)L->rootglobal.next; /* first variable */ - else { - /* check whether name is in global var list */ - luaL_arg_check((GCnode *)g != g->head.next, 1, "variable name expected"); - g = (TaggedString *)g->head.next; /* get next */ - } - while (g && g->u.s.globalval.ttype == LUA_T_NIL) /* skip globals with nil */ - g = (TaggedString *)g->head.next; - if (g) { - ttype(L->stack.top) = LUA_T_STRING; tsvalue(L->stack.top) = g; - incr_top; - luaA_pushobject(&g->u.s.globalval); - } - return g; +LUA_API void lua_getglobal (lua_State *L, const char *name) { + StkId top = L->top; + *top = *luaV_getglobal(L, luaS_new(L, name)); + L->top = top; + api_incr_top(L); } -char *lua_nextvar (char *varname) { - TaggedString *g = (varname == NULL) ? NULL : luaS_new(varname); - g = luaA_nextvar(g); - if (g) { - top2LC(2); - return g->str; - } - else { - top2LC(0); - return NULL; - } +LUA_API void lua_gettable (lua_State *L, int index) { + StkId t = Index(L, index); + StkId top = L->top; + *(top-1) = *luaV_gettable(L, t); + L->top = top; /* tag method may change top */ } -int luaA_next (Hash *t, int i) { - int tsize = nhash(t); - for (; i<tsize; i++) { - Node *n = node(t, i); - if (ttype(val(n)) != LUA_T_NIL) { - luaA_pushobject(ref(n)); - luaA_pushobject(val(n)); - return i+1; /* index to be used next time */ - } - } - return 0; /* no more elements */ +LUA_API void lua_rawget (lua_State *L, int index) { + StkId t = Index(L, index); + LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected"); + *(L->top - 1) = *luaH_get(L, hvalue(t), L->top - 1); } -int lua_next (lua_Object o, int i) { - TObject *t = Address(o); - if (ttype(t) != LUA_T_ARRAY) - lua_error("API error - object is not a table in `lua_next'"); - i = luaA_next(avalue(t), i); - top2LC((i==0) ? 0 : 2); - return i; +LUA_API void lua_rawgeti (lua_State *L, int index, int n) { + StkId o = Index(L, index); + LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected"); + *L->top = *luaH_getnum(hvalue(o), n); + api_incr_top(L); } - -/* -** {====================================================== -** To manipulate some state information -** ======================================================= -*/ - -lua_State *lua_setstate (lua_State *st) { - lua_State *old = lua_state; - lua_state = st; - return old; +LUA_API void lua_getglobals (lua_State *L) { + hvalue(L->top) = L->gt; + ttype(L->top) = LUA_TTABLE; + api_incr_top(L); } -lua_LHFunction lua_setlinehook (lua_LHFunction func) { - lua_LHFunction old = L->linehook; - L->linehook = func; - return old; -} -lua_CHFunction lua_setcallhook (lua_CHFunction func) { - lua_CHFunction old = L->callhook; - L->callhook = func; - return old; +LUA_API int lua_getref (lua_State *L, int ref) { + if (ref == LUA_REFNIL) + ttype(L->top) = LUA_TNIL; + else if (0 <= ref && ref < L->refSize && + (L->refArray[ref].st == LOCK || L->refArray[ref].st == HOLD)) + *L->top = L->refArray[ref].o; + else + return 0; + api_incr_top(L); + return 1; } -int lua_setdebug (int debug) { - int old = L->debug; - L->debug = debug; - return old; + +LUA_API void lua_newtable (lua_State *L) { + hvalue(L->top) = luaH_new(L, 0); + ttype(L->top) = LUA_TTABLE; + api_incr_top(L); } -/* }====================================================== */ /* -** {====================================================== -** Debug interface -** ======================================================= +** set functions (stack -> Lua) */ -lua_Function lua_stackedfunction (int level) -{ - StkId i; - for (i = (L->stack.top-1)-L->stack.stack; i>=0; i--) { - int t = L->stack.stack[i].ttype; - if (t == LUA_T_CLMARK || t == LUA_T_PMARK || t == LUA_T_CMARK) - if (level-- == 0) - return Ref(L->stack.stack+i); - } - return LUA_NOOBJECT; +LUA_API void lua_setglobal (lua_State *L, const char *name) { + StkId top = L->top; + luaV_setglobal(L, luaS_new(L, name)); + L->top = top-1; /* remove element from the top */ } -int lua_nups (lua_Function func) { - TObject *o = luaA_Address(func); - return (!o || normalized_type(o) != LUA_T_CLOSURE) ? 0 : o->value.cl->nelems; +LUA_API void lua_settable (lua_State *L, int index) { + StkId t = Index(L, index); + StkId top = L->top; + luaV_settable(L, t, top-2); + L->top = top-2; /* pop index and value */ } -int lua_currentline (lua_Function func) -{ - TObject *f = Address(func); - return (f+1 < L->stack.top && (f+1)->ttype == LUA_T_LINE) ? - (f+1)->value.i : -1; +LUA_API void lua_rawset (lua_State *L, int index) { + StkId t = Index(L, index); + LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected"); + *luaH_set(L, hvalue(t), L->top-2) = *(L->top-1); + L->top -= 2; } -lua_Object lua_getlocal (lua_Function func, int local_number, char **name) { - /* check whether func is a Lua function */ - if (lua_tag(func) != LUA_T_PROTO) - return LUA_NOOBJECT; - else { - TObject *f = Address(func); - TProtoFunc *fp = luaA_protovalue(f)->value.tf; - *name = luaF_getlocalname(fp, local_number, lua_currentline(func)); - if (*name) { - /* if "*name", there must be a LUA_T_LINE */ - /* therefore, f+2 points to function base */ - return put_luaObject((f+2)+(local_number-1)); - } - else - return LUA_NOOBJECT; - } +LUA_API void lua_rawseti (lua_State *L, int index, int n) { + StkId o = Index(L, index); + LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected"); + *luaH_setint(L, hvalue(o), n) = *(L->top-1); + L->top--; } -int lua_setlocal (lua_Function func, int local_number) -{ - /* check whether func is a Lua function */ - if (lua_tag(func) != LUA_T_PROTO) - return 0; - else { - TObject *f = Address(func); - TProtoFunc *fp = luaA_protovalue(f)->value.tf; - char *name = luaF_getlocalname(fp, local_number, lua_currentline(func)); - checkCparams(1); - --L->stack.top; - if (name) { - /* if "name", there must be a LUA_T_LINE */ - /* therefore, f+2 points to function base */ - *((f+2)+(local_number-1)) = *L->stack.top; - return 1; - } - else - return 0; - } +LUA_API void lua_setglobals (lua_State *L) { + StkId newtable = --L->top; + LUA_ASSERT(ttype(newtable) == LUA_TTABLE, "table expected"); + L->gt = hvalue(newtable); } -void lua_funcinfo (lua_Object func, char **source, int *linedefined) { - if (!lua_isfunction(func)) - lua_error("API error - `funcinfo' called with a non-function value"); +LUA_API int lua_ref (lua_State *L, int lock) { + int ref; + if (ttype(L->top-1) == LUA_TNIL) + ref = LUA_REFNIL; else { - TObject *f = luaA_protovalue(Address(func)); - if (normalized_type(f) == LUA_T_PROTO) { - *source = tfvalue(f)->source->str; - *linedefined = tfvalue(f)->lineDefined; + if (L->refFree != NONEXT) { /* is there a free place? */ + ref = L->refFree; + L->refFree = L->refArray[ref].st; } - else { - *source = "(C)"; - *linedefined = -1; + else { /* no more free places */ + luaM_growvector(L, L->refArray, L->refSize, 1, struct Ref, + "reference table overflow", MAX_INT); + L->nblocks += sizeof(struct Ref); + ref = L->refSize++; } + L->refArray[ref].o = *(L->top-1); + L->refArray[ref].st = lock ? LOCK : HOLD; } + L->top--; + return ref; } -static int checkfunc (TObject *o) -{ - return luaO_equalObj(o, L->stack.top); -} - +/* +** "do" functions (run Lua code) +** (most of them are in ldo.c) +*/ -char *lua_getobjname (lua_Object o, char **name) -{ /* try to find a name for given function */ - set_normalized(L->stack.top, Address(o)); /* to be accessed by "checkfunc" */ - if ((*name = luaS_travsymbol(checkfunc)) != NULL) - return "global"; - else if ((*name = luaT_travtagmethods(checkfunc)) != NULL) - return "tag-method"; - else return ""; +LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults) { + luaD_call(L, L->top-(nargs+1), nresults); } -/* }====================================================== */ - /* -** {====================================================== -** BLOCK mechanism -** ======================================================= +** Garbage-collection functions */ +/* GC values are expressed in Kbytes: #bytes/2^10 */ +#define GCscale(x) ((int)((x)>>10)) +#define GCunscale(x) ((unsigned long)(x)<<10) -#ifndef MAX_C_BLOCKS -#define MAX_C_BLOCKS 1000 /* arbitrary limit */ -#endif - +LUA_API int lua_getgcthreshold (lua_State *L) { + return GCscale(L->GCthreshold); +} -void lua_beginblock (void) { - luaM_growvector(L->Cblocks, L->numCblocks, 1, struct C_Lua_Stack, - "too many nested blocks", MAX_C_BLOCKS); - L->Cblocks[L->numCblocks] = L->Cstack; - L->numCblocks++; +LUA_API int lua_getgccount (lua_State *L) { + return GCscale(L->nblocks); } -void lua_endblock (void) { - --L->numCblocks; - L->Cstack = L->Cblocks[L->numCblocks]; - luaD_adjusttop(L->Cstack.base); +LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold) { + if (newthreshold > GCscale(ULONG_MAX)) + L->GCthreshold = ULONG_MAX; + else + L->GCthreshold = GCunscale(newthreshold); + luaC_checkGC(L); } +/* +** miscellaneous functions +*/ -int lua_ref (int lock) { - int ref; - checkCparams(1); - ref = luaC_ref(L->stack.top-1, lock); - L->stack.top--; - return ref; +LUA_API void lua_settag (lua_State *L, int tag) { + luaT_realtag(L, tag); + switch (ttype(L->top-1)) { + case LUA_TTABLE: + hvalue(L->top-1)->htag = tag; + break; + case LUA_TUSERDATA: + tsvalue(L->top-1)->u.d.tag = tag; + break; + default: + luaO_verror(L, "cannot change the tag of a %.20s", + luaO_typename(L->top-1)); + } } - -lua_Object lua_getref (int ref) { - TObject *o = luaC_getref(ref); - return (o ? put_luaObject(o) : LUA_NOOBJECT); +LUA_API void lua_unref (lua_State *L, int ref) { + if (ref >= 0) { + LUA_ASSERT(ref < L->refSize && L->refArray[ref].st < 0, "invalid ref"); + L->refArray[ref].st = L->refFree; + L->refFree = ref; + } } -/* }====================================================== */ +LUA_API int lua_next (lua_State *L, int index) { + StkId t = luaA_index(L, index); + Node *n; + LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected"); + n = luaH_next(L, hvalue(t), luaA_index(L, -1)); + if (n) { + *(L->top-1) = *key(n); + *L->top = *val(n); + api_incr_top(L); + return 1; + } + else { /* no more elements */ + L->top -= 1; /* remove key */ + return 0; + } +} -#ifdef LUA_COMPAT2_5 -/* -** API: set a function as a fallback -*/ +LUA_API int lua_getn (lua_State *L, int index) { + Hash *h = hvalue(luaA_index(L, index)); + const TObject *value = luaH_getstr(h, luaS_new(L, "n")); /* value = h.n */ + if (ttype(value) == LUA_TNUMBER) + return (int)nvalue(value); + else { + Number max = 0; + int i = h->size; + Node *n = h->node; + while (i--) { + if (ttype(key(n)) == LUA_TNUMBER && + ttype(val(n)) != LUA_TNIL && + nvalue(key(n)) > max) + max = nvalue(key(n)); + n++; + } + return (int)max; + } +} + -static void do_unprotectedrun (lua_CFunction f, int nParams, int nResults) { - luaD_openstack(nParams); - (L->stack.top-nParams)->ttype = LUA_T_CPROTO; - (L->stack.top-nParams)->value.f = f; - luaD_calln(nParams, nResults); +LUA_API void lua_concat (lua_State *L, int n) { + StkId top = L->top; + luaV_strconc(L, n, top); + L->top = top-(n-1); + luaC_checkGC(L); } -lua_Object lua_setfallback (char *name, lua_CFunction fallback) { - lua_pushstring(name); - lua_pushcfunction(fallback); - do_unprotectedrun(luaT_setfallback, 2, 1); - return put_luaObjectonTop(); +LUA_API void *lua_newuserdata (lua_State *L, size_t size) { + TString *ts = luaS_newudata(L, size, NULL); + tsvalue(L->top) = ts; + ttype(L->top) = LUA_TUSERDATA; + api_incr_top(L); + return ts->u.d.value; } -#endif @@ -1,5 +1,5 @@ /* -** $Id: lapi.h,v 1.4 1999/02/23 14:57:28 roberto Exp $ +** $Id: lapi.h,v 1.20 2000/08/31 14:08:27 roberto Exp $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ @@ -8,15 +8,10 @@ #define lapi_h -#include "lua.h" #include "lobject.h" -TObject *luaA_Address (lua_Object o); -void luaA_pushobject (TObject *o); -void luaA_packresults (void); -int luaA_passresults (void); -TaggedString *luaA_nextvar (TaggedString *g); -int luaA_next (Hash *t, int i); +TObject *luaA_index (lua_State *L, int index); +void luaA_pushobject (lua_State *L, const TObject *o); #endif diff --git a/src/lauxlib.c b/src/lauxlib.c deleted file mode 100644 index db929c4f..00000000 --- a/src/lauxlib.c +++ /dev/null @@ -1,133 +0,0 @@ -/* -** $Id: lauxlib.c,v 1.17 1999/03/11 18:59:19 roberto Exp $ -** Auxiliary functions for building Lua libraries -** See Copyright Notice in lua.h -*/ - - -#include <stdarg.h> -#include <stdio.h> -#include <string.h> - -/* Please Notice: This file uses only the official API of Lua -** Any function declared here could be written as an application function. -** With care, these functions can be used by other libraries. -*/ - -#include "lauxlib.h" -#include "lua.h" -#include "luadebug.h" - - - -int luaL_findstring (char *name, char *list[]) { - int i; - for (i=0; list[i]; i++) - if (strcmp(list[i], name) == 0) - return i; - return -1; /* name not found */ -} - -void luaL_argerror (int numarg, char *extramsg) { - lua_Function f = lua_stackedfunction(0); - char *funcname; - lua_getobjname(f, &funcname); - numarg -= lua_nups(f); - if (funcname == NULL) - funcname = "?"; - if (extramsg == NULL) - luaL_verror("bad argument #%d to function `%.50s'", numarg, funcname); - else - luaL_verror("bad argument #%d to function `%.50s' (%.100s)", - numarg, funcname, extramsg); -} - -char *luaL_check_lstr (int numArg, long *len) -{ - lua_Object o = lua_getparam(numArg); - luaL_arg_check(lua_isstring(o), numArg, "string expected"); - if (len) *len = lua_strlen(o); - return lua_getstring(o); -} - -char *luaL_opt_lstr (int numArg, char *def, long *len) -{ - return (lua_getparam(numArg) == LUA_NOOBJECT) ? def : - luaL_check_lstr(numArg, len); -} - -double luaL_check_number (int numArg) -{ - lua_Object o = lua_getparam(numArg); - luaL_arg_check(lua_isnumber(o), numArg, "number expected"); - return lua_getnumber(o); -} - - -double luaL_opt_number (int numArg, double def) -{ - return (lua_getparam(numArg) == LUA_NOOBJECT) ? def : - luaL_check_number(numArg); -} - - -lua_Object luaL_tablearg (int arg) -{ - lua_Object o = lua_getparam(arg); - luaL_arg_check(lua_istable(o), arg, "table expected"); - return o; -} - -lua_Object luaL_functionarg (int arg) -{ - lua_Object o = lua_getparam(arg); - luaL_arg_check(lua_isfunction(o), arg, "function expected"); - return o; -} - -lua_Object luaL_nonnullarg (int numArg) -{ - lua_Object o = lua_getparam(numArg); - luaL_arg_check(o != LUA_NOOBJECT, numArg, "value expected"); - return o; -} - -void luaL_openlib (struct luaL_reg *l, int n) -{ - int i; - lua_open(); /* make sure lua is already open */ - for (i=0; i<n; i++) - lua_register(l[i].name, l[i].func); -} - - -void luaL_verror (char *fmt, ...) -{ - char buff[500]; - va_list argp; - va_start(argp, fmt); - vsprintf(buff, fmt, argp); - va_end(argp); - lua_error(buff); -} - - -void luaL_chunkid (char *out, char *source, int len) { - len -= 13; /* 13 = strlen("string ''...\0") */ - if (*source == '@') - sprintf(out, "file `%.*s'", len, source+1); - else if (*source == '(') - strcpy(out, "(C code)"); - else { - char *b = strchr(source , '\n'); /* stop string at first new line */ - int lim = (b && (b-source)<len) ? b-source : len; - sprintf(out, "string `%.*s'", lim, source); - strcpy(out+lim+(13-5), "...'"); /* 5 = strlen("...'\0") */ - } -} - - -void luaL_filesource (char *out, char *filename, int len) { - if (filename == NULL) filename = "(stdin)"; - sprintf(out, "@%.*s", len-2, filename); /* -2 for '@' and '\0' */ -} diff --git a/src/lbuffer.c b/src/lbuffer.c deleted file mode 100644 index 81ff16ca..00000000 --- a/src/lbuffer.c +++ /dev/null @@ -1,75 +0,0 @@ -/* -** $Id: lbuffer.c,v 1.9 1999/02/26 15:48:55 roberto Exp $ -** Auxiliary functions for building Lua libraries -** See Copyright Notice in lua.h -*/ - - -#include <stdio.h> - -#include "lauxlib.h" -#include "lmem.h" -#include "lstate.h" - - -/*------------------------------------------------------- -** Auxiliary buffer --------------------------------------------------------*/ - - -#define EXTRABUFF 32 - - -#define openspace(size) if (L->Mbuffnext+(size) > L->Mbuffsize) Openspace(size) - -static void Openspace (int size) { - lua_State *l = L; /* to optimize */ - size += EXTRABUFF; - l->Mbuffsize = l->Mbuffnext+size; - luaM_growvector(l->Mbuffer, l->Mbuffnext, size, char, arrEM, MAX_INT); -} - - -char *luaL_openspace (int size) { - openspace(size); - return L->Mbuffer+L->Mbuffnext; -} - - -void luaL_addchar (int c) { - openspace(1); - L->Mbuffer[L->Mbuffnext++] = (char)c; -} - - -void luaL_resetbuffer (void) { - L->Mbuffnext = L->Mbuffbase; -} - - -void luaL_addsize (int n) { - L->Mbuffnext += n; -} - -int luaL_getsize (void) { - return L->Mbuffnext-L->Mbuffbase; -} - -int luaL_newbuffer (int size) { - int old = L->Mbuffbase; - openspace(size); - L->Mbuffbase = L->Mbuffnext; - return old; -} - - -void luaL_oldbuffer (int old) { - L->Mbuffnext = L->Mbuffbase; - L->Mbuffbase = old; -} - - -char *luaL_buffer (void) { - return L->Mbuffer+L->Mbuffbase; -} - diff --git a/src/lbuiltin.c b/src/lbuiltin.c deleted file mode 100644 index c88ccd41..00000000 --- a/src/lbuiltin.c +++ /dev/null @@ -1,736 +0,0 @@ -/* -** $Id: lbuiltin.c,v 1.59 1999/06/17 17:04:03 roberto Exp $ -** Built-in functions -** See Copyright Notice in lua.h -*/ - - -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "lapi.h" -#include "lauxlib.h" -#include "lbuiltin.h" -#include "ldo.h" -#include "lfunc.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" -#include "lua.h" -#include "lundump.h" -#include "lvm.h" - - - -/* -** {====================================================== -** Auxiliary functions -** ======================================================= -*/ - - -static void pushtagstring (TaggedString *s) { - TObject o; - o.ttype = LUA_T_STRING; - o.value.ts = s; - luaA_pushobject(&o); -} - - -static real getsize (Hash *h) { - real max = 0; - int i; - for (i = 0; i<nhash(h); i++) { - Node *n = h->node+i; - if (ttype(ref(n)) == LUA_T_NUMBER && - ttype(val(n)) != LUA_T_NIL && - nvalue(ref(n)) > max) - max = nvalue(ref(n)); - } - return max; -} - - -static real getnarg (Hash *a) { - TObject index; - TObject *value; - /* value = table.n */ - ttype(&index) = LUA_T_STRING; - tsvalue(&index) = luaS_new("n"); - value = luaH_get(a, &index); - return (ttype(value) == LUA_T_NUMBER) ? nvalue(value) : getsize(a); -} - - -static Hash *gethash (int arg) { - return avalue(luaA_Address(luaL_tablearg(arg))); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** Functions that use only the official API -** ======================================================= -*/ - - -/* -** If your system does not support "stderr", redefine this function, or -** redefine _ERRORMESSAGE so that it won't need _ALERT. -*/ -static void luaB_alert (void) { - fputs(luaL_check_string(1), stderr); -} - - -/* -** Standard implementation of _ERRORMESSAGE. -** The library "iolib" redefines _ERRORMESSAGE for better error information. -*/ -static void error_message (void) { - lua_Object al = lua_rawgetglobal("_ALERT"); - if (lua_isfunction(al)) { /* avoid error loop if _ALERT is not defined */ - char buff[600]; - sprintf(buff, "lua error: %.500s\n", luaL_check_string(1)); - lua_pushstring(buff); - lua_callfunction(al); - } -} - - -/* -** If your system does not support "stdout", just remove this function. -** If you need, you can define your own "print" function, following this -** model but changing "fputs" to put the strings at a proper place -** (a console window or a log file, for instance). -*/ -#ifndef MAXPRINT -#define MAXPRINT 40 /* arbitrary limit */ -#endif - -static void luaB_print (void) { - lua_Object args[MAXPRINT]; - lua_Object obj; - int n = 0; - int i; - while ((obj = lua_getparam(n+1)) != LUA_NOOBJECT) { - luaL_arg_check(n < MAXPRINT, n+1, "too many arguments"); - args[n++] = obj; - } - for (i=0; i<n; i++) { - lua_pushobject(args[i]); - if (lua_call("tostring")) - lua_error("error in `tostring' called by `print'"); - obj = lua_getresult(1); - if (!lua_isstring(obj)) - lua_error("`tostring' must return a string to `print'"); - if (i>0) fputs("\t", stdout); - fputs(lua_getstring(obj), stdout); - } - fputs("\n", stdout); -} - - -static void luaB_tonumber (void) { - int base = luaL_opt_int(2, 10); - if (base == 10) { /* standard conversion */ - lua_Object o = lua_getparam(1); - if (lua_isnumber(o)) lua_pushnumber(lua_getnumber(o)); - else lua_pushnil(); /* not a number */ - } - else { - char *s = luaL_check_string(1); - long n; - luaL_arg_check(0 <= base && base <= 36, 2, "base out of range"); - n = strtol(s, &s, base); - while (isspace((unsigned char)*s)) s++; /* skip trailing spaces */ - if (*s) lua_pushnil(); /* invalid format: return nil */ - else lua_pushnumber(n); - } -} - - -static void luaB_error (void) { - lua_error(lua_getstring(lua_getparam(1))); -} - -static void luaB_setglobal (void) { - char *n = luaL_check_string(1); - lua_Object value = luaL_nonnullarg(2); - lua_pushobject(value); - lua_setglobal(n); - lua_pushobject(value); /* return given value */ -} - -static void luaB_rawsetglobal (void) { - char *n = luaL_check_string(1); - lua_Object value = luaL_nonnullarg(2); - lua_pushobject(value); - lua_rawsetglobal(n); - lua_pushobject(value); /* return given value */ -} - -static void luaB_getglobal (void) { - lua_pushobject(lua_getglobal(luaL_check_string(1))); -} - -static void luaB_rawgetglobal (void) { - lua_pushobject(lua_rawgetglobal(luaL_check_string(1))); -} - -static void luaB_luatag (void) { - lua_pushnumber(lua_tag(lua_getparam(1))); -} - -static void luaB_settag (void) { - lua_Object o = luaL_tablearg(1); - lua_pushobject(o); - lua_settag(luaL_check_int(2)); - lua_pushobject(o); /* return first argument */ -} - -static void luaB_newtag (void) { - lua_pushnumber(lua_newtag()); -} - -static void luaB_copytagmethods (void) { - lua_pushnumber(lua_copytagmethods(luaL_check_int(1), - luaL_check_int(2))); -} - -static void luaB_rawgettable (void) { - lua_pushobject(luaL_nonnullarg(1)); - lua_pushobject(luaL_nonnullarg(2)); - lua_pushobject(lua_rawgettable()); -} - -static void luaB_rawsettable (void) { - lua_pushobject(luaL_nonnullarg(1)); - lua_pushobject(luaL_nonnullarg(2)); - lua_pushobject(luaL_nonnullarg(3)); - lua_rawsettable(); -} - -static void luaB_settagmethod (void) { - lua_Object nf = luaL_nonnullarg(3); - lua_pushobject(nf); - lua_pushobject(lua_settagmethod(luaL_check_int(1), luaL_check_string(2))); -} - -static void luaB_gettagmethod (void) { - lua_pushobject(lua_gettagmethod(luaL_check_int(1), luaL_check_string(2))); -} - -static void luaB_seterrormethod (void) { - lua_Object nf = luaL_functionarg(1); - lua_pushobject(nf); - lua_pushobject(lua_seterrormethod()); -} - -static void luaB_collectgarbage (void) { - lua_pushnumber(lua_collectgarbage(luaL_opt_int(1, 0))); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** Functions that could use only the official API but -** do not, for efficiency. -** ======================================================= -*/ - -static void luaB_dostring (void) { - long l; - char *s = luaL_check_lstr(1, &l); - if (*s == ID_CHUNK) - lua_error("`dostring' cannot run pre-compiled code"); - if (lua_dobuffer(s, l, luaL_opt_string(2, s)) == 0) - if (luaA_passresults() == 0) - lua_pushuserdata(NULL); /* at least one result to signal no errors */ -} - - -static void luaB_dofile (void) { - char *fname = luaL_opt_string(1, NULL); - if (lua_dofile(fname) == 0) - if (luaA_passresults() == 0) - lua_pushuserdata(NULL); /* at least one result to signal no errors */ -} - - -static void luaB_call (void) { - lua_Object f = luaL_nonnullarg(1); - Hash *arg = gethash(2); - char *options = luaL_opt_string(3, ""); - lua_Object err = lua_getparam(4); - int narg = (int)getnarg(arg); - int i, status; - if (err != LUA_NOOBJECT) { /* set new error method */ - lua_pushobject(err); - err = lua_seterrormethod(); - } - /* push arg[1...n] */ - luaD_checkstack(narg); - for (i=0; i<narg; i++) - *(L->stack.top++) = *luaH_getint(arg, i+1); - status = lua_callfunction(f); - if (err != LUA_NOOBJECT) { /* restore old error method */ - lua_pushobject(err); - lua_seterrormethod(); - } - if (status != 0) { /* error in call? */ - if (strchr(options, 'x')) { - lua_pushnil(); - return; /* return nil to signal the error */ - } - else - lua_error(NULL); - } - else { /* no errors */ - if (strchr(options, 'p')) - luaA_packresults(); - else - luaA_passresults(); - } -} - - -static void luaB_nextvar (void) { - TObject *o = luaA_Address(luaL_nonnullarg(1)); - TaggedString *g; - if (ttype(o) == LUA_T_NIL) - g = NULL; - else { - luaL_arg_check(ttype(o) == LUA_T_STRING, 1, "variable name expected"); - g = tsvalue(o); - } - if (!luaA_nextvar(g)) - lua_pushnil(); -} - - -static void luaB_next (void) { - Hash *a = gethash(1); - TObject *k = luaA_Address(luaL_nonnullarg(2)); - int i = (ttype(k) == LUA_T_NIL) ? 0 : luaH_pos(a, k)+1; - if (luaA_next(a, i) == 0) - lua_pushnil(); -} - - -static void luaB_tostring (void) { - lua_Object obj = lua_getparam(1); - TObject *o = luaA_Address(obj); - char buff[64]; - switch (ttype(o)) { - case LUA_T_NUMBER: - lua_pushstring(lua_getstring(obj)); - return; - case LUA_T_STRING: - lua_pushobject(obj); - return; - case LUA_T_ARRAY: - sprintf(buff, "table: %p", (void *)o->value.a); - break; - case LUA_T_CLOSURE: - sprintf(buff, "function: %p", (void *)o->value.cl); - break; - case LUA_T_PROTO: - sprintf(buff, "function: %p", (void *)o->value.tf); - break; - case LUA_T_CPROTO: - sprintf(buff, "function: %p", (void *)o->value.f); - break; - case LUA_T_USERDATA: - sprintf(buff, "userdata: %p", o->value.ts->u.d.v); - break; - case LUA_T_NIL: - lua_pushstring("nil"); - return; - default: - LUA_INTERNALERROR("invalid type"); - } - lua_pushstring(buff); -} - - -static void luaB_type (void) { - lua_Object o = luaL_nonnullarg(1); - lua_pushstring(luaO_typename(luaA_Address(o))); - lua_pushnumber(lua_tag(o)); -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** "Extra" functions -** These functions can be written in Lua, so you can -** delete them if you need a tiny Lua implementation. -** If you delete them, remove their entries in array -** "builtin_funcs". -** ======================================================= -*/ - -static void luaB_assert (void) { - lua_Object p = lua_getparam(1); - if (p == LUA_NOOBJECT || lua_isnil(p)) - luaL_verror("assertion failed! %.100s", luaL_opt_string(2, "")); -} - - -static void luaB_foreachi (void) { - Hash *t = gethash(1); - int i; - int n = (int)getnarg(t); - TObject f; - /* 'f' cannot be a pointer to TObject, because it is on the stack, and the - stack may be reallocated by the call. Moreover, some C compilers do not - initialize structs, so we must do the assignment after the declaration */ - f = *luaA_Address(luaL_functionarg(2)); - luaD_checkstack(3); /* for f, ref, and val */ - for (i=1; i<=n; i++) { - *(L->stack.top++) = f; - ttype(L->stack.top) = LUA_T_NUMBER; nvalue(L->stack.top++) = i; - *(L->stack.top++) = *luaH_getint(t, i); - luaD_calln(2, 1); - if (ttype(L->stack.top-1) != LUA_T_NIL) - return; - L->stack.top--; - } -} - - -static void luaB_foreach (void) { - Hash *a = gethash(1); - int i; - TObject f; /* see comment in 'foreachi' */ - f = *luaA_Address(luaL_functionarg(2)); - luaD_checkstack(3); /* for f, ref, and val */ - for (i=0; i<a->nhash; i++) { - Node *nd = &(a->node[i]); - if (ttype(val(nd)) != LUA_T_NIL) { - *(L->stack.top++) = f; - *(L->stack.top++) = *ref(nd); - *(L->stack.top++) = *val(nd); - luaD_calln(2, 1); - if (ttype(L->stack.top-1) != LUA_T_NIL) - return; - L->stack.top--; /* remove result */ - } - } -} - - -static void luaB_foreachvar (void) { - GCnode *g; - TObject f; /* see comment in 'foreachi' */ - f = *luaA_Address(luaL_functionarg(1)); - luaD_checkstack(4); /* for extra var name, f, var name, and globalval */ - for (g = L->rootglobal.next; g; g = g->next) { - TaggedString *s = (TaggedString *)g; - if (s->u.s.globalval.ttype != LUA_T_NIL) { - pushtagstring(s); /* keep (extra) s on stack to avoid GC */ - *(L->stack.top++) = f; - pushtagstring(s); - *(L->stack.top++) = s->u.s.globalval; - luaD_calln(2, 1); - if (ttype(L->stack.top-1) != LUA_T_NIL) { - L->stack.top--; - *(L->stack.top-1) = *L->stack.top; /* remove extra s */ - return; - } - L->stack.top-=2; /* remove result and extra s */ - } - } -} - - -static void luaB_getn (void) { - lua_pushnumber(getnarg(gethash(1))); -} - - -static void luaB_tinsert (void) { - Hash *a = gethash(1); - lua_Object v = lua_getparam(3); - int n = (int)getnarg(a); - int pos; - if (v != LUA_NOOBJECT) - pos = luaL_check_int(2); - else { /* called with only 2 arguments */ - v = luaL_nonnullarg(2); - pos = n+1; - } - luaV_setn(a, n+1); /* a.n = n+1 */ - for ( ;n>=pos; n--) - luaH_move(a, n, n+1); /* a[n+1] = a[n] */ - luaH_setint(a, pos, luaA_Address(v)); /* a[pos] = v */ -} - - -static void luaB_tremove (void) { - Hash *a = gethash(1); - int n = (int)getnarg(a); - int pos = luaL_opt_int(2, n); - if (n <= 0) return; /* table is "empty" */ - luaA_pushobject(luaH_getint(a, pos)); /* result = a[pos] */ - for ( ;pos<n; pos++) - luaH_move(a, pos+1, pos); /* a[pos] = a[pos+1] */ - luaV_setn(a, n-1); /* a.n = n-1 */ - luaH_setint(a, n, &luaO_nilobject); /* a[n] = nil */ -} - - -/* { -** Quicksort -*/ - -static void swap (Hash *a, int i, int j) { - TObject temp; - temp = *luaH_getint(a, i); - luaH_move(a, j, i); - luaH_setint(a, j, &temp); -} - -static int sort_comp (lua_Object f, TObject *a, TObject *b) { - /* notice: the caller (auxsort) must check stack space */ - if (f != LUA_NOOBJECT) { - *(L->stack.top) = *luaA_Address(f); - *(L->stack.top+1) = *a; - *(L->stack.top+2) = *b; - L->stack.top += 3; - luaD_calln(2, 1); - } - else { /* a < b? */ - *(L->stack.top) = *a; - *(L->stack.top+1) = *b; - L->stack.top += 2; - luaV_comparison(LUA_T_NUMBER, LUA_T_NIL, LUA_T_NIL, IM_LT); - } - return ttype(--(L->stack.top)) != LUA_T_NIL; -} - -static void auxsort (Hash *a, int l, int u, lua_Object f) { - while (l < u) { /* for tail recursion */ - TObject *P; - int i, j; - /* sort elements a[l], a[(l+u)/2] and a[u] */ - if (sort_comp(f, luaH_getint(a, u), luaH_getint(a, l))) /* a[l]>a[u]? */ - swap(a, l, u); - if (u-l == 1) break; /* only 2 elements */ - i = (l+u)/2; - P = luaH_getint(a, i); - if (sort_comp(f, P, luaH_getint(a, l))) /* a[l]>a[i]? */ - swap(a, l, i); - else if (sort_comp(f, luaH_getint(a, u), P)) /* a[i]>a[u]? */ - swap(a, i, u); - if (u-l == 2) break; /* only 3 elements */ - P = L->stack.top++; - *P = *luaH_getint(a, i); /* save pivot on stack (for GC) */ - swap(a, i, u-1); /* put median element as pivot (a[u-1]) */ - /* a[l] <= P == a[u-1] <= a[u], only needs to sort from l+1 to u-2 */ - i = l; j = u-1; - for (;;) { - /* invariant: a[l..i] <= P <= a[j..u] */ - while (sort_comp(f, luaH_getint(a, ++i), P)) /* stop when a[i] >= P */ - if (i>u) lua_error("invalid order function for sorting"); - while (sort_comp(f, P, luaH_getint(a, --j))) /* stop when a[j] <= P */ - if (j<l) lua_error("invalid order function for sorting"); - if (j<i) break; - swap(a, i, j); - } - swap(a, u-1, i); /* swap pivot (a[u-1]) with a[i] */ - L->stack.top--; /* remove pivot from stack */ - /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ - /* adjust so that smaller "half" is in [j..i] and larger one in [l..u] */ - if (i-l < u-i) { - j=l; i=i-1; l=i+2; - } - else { - j=i+1; i=u; u=j-2; - } - auxsort(a, j, i, f); /* call recursively the smaller one */ - } /* repeat the routine for the larger one */ -} - -static void luaB_sort (void) { - lua_Object t = lua_getparam(1); - Hash *a = gethash(1); - int n = (int)getnarg(a); - lua_Object func = lua_getparam(2); - luaL_arg_check(func == LUA_NOOBJECT || lua_isfunction(func), 2, - "function expected"); - luaD_checkstack(4); /* for Pivot, f, a, b (sort_comp) */ - auxsort(a, 1, n, func); - lua_pushobject(t); -} - -/* }}===================================================== */ - - -/* -** ====================================================== */ - - - -#ifdef DEBUG -/* -** {====================================================== -** some DEBUG functions -** ======================================================= -*/ - -static void mem_query (void) { - lua_pushnumber(totalmem); - lua_pushnumber(numblocks); -} - - -static void query_strings (void) { - lua_pushnumber(L->string_root[luaL_check_int(1)].nuse); -} - - -static void countlist (void) { - char *s = luaL_check_string(1); - GCnode *l = (s[0]=='t') ? L->roottable.next : (s[0]=='c') ? L->rootcl.next : - (s[0]=='p') ? L->rootproto.next : L->rootglobal.next; - int i=0; - while (l) { - i++; - l = l->next; - } - lua_pushnumber(i); -} - - -static void testC (void) { -#define getnum(s) ((*s++) - '0') -#define getname(s) (nome[0] = *s++, nome) - - static int locks[10]; - lua_Object reg[10]; - char nome[2]; - char *s = luaL_check_string(1); - nome[1] = 0; - for (;;) { - switch (*s++) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - lua_pushnumber(*(s-1) - '0'); - break; - - case 'c': reg[getnum(s)] = lua_createtable(); break; - case 'C': { lua_CFunction f = lua_getcfunction(lua_getglobal(getname(s))); - lua_pushcclosure(f, getnum(s)); - break; - } - case 'P': reg[getnum(s)] = lua_pop(); break; - case 'g': { int n=getnum(s); reg[n]=lua_getglobal(getname(s)); break; } - case 'G': { int n = getnum(s); - reg[n] = lua_rawgetglobal(getname(s)); - break; - } - case 'l': locks[getnum(s)] = lua_ref(1); break; - case 'L': locks[getnum(s)] = lua_ref(0); break; - case 'r': { int n=getnum(s); reg[n]=lua_getref(locks[getnum(s)]); break; } - case 'u': lua_unref(locks[getnum(s)]); break; - case 'p': { int n = getnum(s); reg[n] = lua_getparam(getnum(s)); break; } - case '=': lua_setglobal(getname(s)); break; - case 's': lua_pushstring(getname(s)); break; - case 'o': lua_pushobject(reg[getnum(s)]); break; - case 'f': lua_call(getname(s)); break; - case 'i': reg[getnum(s)] = lua_gettable(); break; - case 'I': reg[getnum(s)] = lua_rawgettable(); break; - case 't': lua_settable(); break; - case 'T': lua_rawsettable(); break; - case 'N' : lua_pushstring(lua_nextvar(lua_getstring(reg[getnum(s)]))); - break; - case 'n' : { int n=getnum(s); - n=lua_next(reg[n], (int)lua_getnumber(reg[getnum(s)])); - lua_pushnumber(n); break; - } - default: luaL_verror("unknown command in `testC': %c", *(s-1)); - } - if (*s == 0) return; - if (*s++ != ' ') lua_error("missing ` ' between commands in `testC'"); - } -} - -/* }====================================================== */ -#endif - - - -static struct luaL_reg builtin_funcs[] = { -#ifdef LUA_COMPAT2_5 - {"setfallback", luaT_setfallback}, -#endif -#ifdef DEBUG - {"testC", testC}, - {"totalmem", mem_query}, - {"count", countlist}, - {"querystr", query_strings}, -#endif - {"_ALERT", luaB_alert}, - {"_ERRORMESSAGE", error_message}, - {"call", luaB_call}, - {"collectgarbage", luaB_collectgarbage}, - {"copytagmethods", luaB_copytagmethods}, - {"dofile", luaB_dofile}, - {"dostring", luaB_dostring}, - {"error", luaB_error}, - {"getglobal", luaB_getglobal}, - {"gettagmethod", luaB_gettagmethod}, - {"newtag", luaB_newtag}, - {"next", luaB_next}, - {"nextvar", luaB_nextvar}, - {"print", luaB_print}, - {"rawgetglobal", luaB_rawgetglobal}, - {"rawgettable", luaB_rawgettable}, - {"rawsetglobal", luaB_rawsetglobal}, - {"rawsettable", luaB_rawsettable}, - {"seterrormethod", luaB_seterrormethod}, - {"setglobal", luaB_setglobal}, - {"settag", luaB_settag}, - {"settagmethod", luaB_settagmethod}, - {"tag", luaB_luatag}, - {"tonumber", luaB_tonumber}, - {"tostring", luaB_tostring}, - {"type", luaB_type}, - /* "Extra" functions */ - {"assert", luaB_assert}, - {"foreach", luaB_foreach}, - {"foreachi", luaB_foreachi}, - {"foreachvar", luaB_foreachvar}, - {"getn", luaB_getn}, - {"sort", luaB_sort}, - {"tinsert", luaB_tinsert}, - {"tremove", luaB_tremove} -}; - - -#define INTFUNCSIZE (sizeof(builtin_funcs)/sizeof(builtin_funcs[0])) - - -void luaB_predefine (void) { - /* pre-register mem error messages, to avoid loop when error arises */ - luaS_newfixedstring(tableEM); - luaS_newfixedstring(memEM); - luaL_openlib(builtin_funcs, (sizeof(builtin_funcs)/sizeof(builtin_funcs[0]))); - lua_pushstring(LUA_VERSION); - lua_setglobal("_VERSION"); -} - diff --git a/src/lbuiltin.h b/src/lbuiltin.h deleted file mode 100644 index bcb11fc0..00000000 --- a/src/lbuiltin.h +++ /dev/null @@ -1,14 +0,0 @@ -/* -** $Id: lbuiltin.h,v 1.1 1997/09/16 19:25:59 roberto Exp $ -** Built-in functions -** See Copyright Notice in lua.h -*/ - -#ifndef lbuiltin_h -#define lbuiltin_h - - -void luaB_predefine (void); - - -#endif diff --git a/src/lcode.c b/src/lcode.c new file mode 100644 index 00000000..6882240d --- /dev/null +++ b/src/lcode.c @@ -0,0 +1,701 @@ +/* +** $Id: lcode.c,v 1.51 2000/09/29 12:42:13 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + + +#include "stdlib.h" + +#include "lua.h" + +#include "lcode.h" +#include "ldo.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +void luaK_error (LexState *ls, const char *msg) { + luaX_error(ls, msg, ls->t.token); +} + + +/* +** Returns the the previous instruction, for optimizations. +** If there is a jump target between this and the current instruction, +** returns a dummy instruction to avoid wrong optimizations. +*/ +static Instruction previous_instruction (FuncState *fs) { + if (fs->pc > fs->lasttarget) /* no jumps to current position? */ + return fs->f->code[fs->pc-1]; /* returns previous instruction */ + else + return CREATE_0(OP_END); /* no optimizations after an `END' */ +} + + +int luaK_jump (FuncState *fs) { + int j = luaK_code1(fs, OP_JMP, NO_JUMP); + if (j == fs->lasttarget) { /* possible jumps to this jump? */ + luaK_concat(fs, &j, fs->jlt); /* keep them on hold */ + fs->jlt = NO_JUMP; + } + return j; +} + + +static void luaK_fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + if (dest == NO_JUMP) + SETARG_S(*jmp, NO_JUMP); /* point to itself to represent end of list */ + else { /* jump is relative to position following jump instruction */ + int offset = dest-(pc+1); + if (abs(offset) > MAXARG_S) + luaK_error(fs->ls, "control structure too long"); + SETARG_S(*jmp, offset); + } +} + + +static int luaK_getjump (FuncState *fs, int pc) { + int offset = GETARG_S(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +/* +** returns current `pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +** discharge list of jumps to last target. +*/ +int luaK_getlabel (FuncState *fs) { + if (fs->pc != fs->lasttarget) { + int lasttarget = fs->lasttarget; + fs->lasttarget = fs->pc; + luaK_patchlist(fs, fs->jlt, lasttarget); /* discharge old list `jlt' */ + fs->jlt = NO_JUMP; /* nobody jumps to this new label (yet) */ + } + return fs->pc; +} + + +void luaK_deltastack (FuncState *fs, int delta) { + fs->stacklevel += delta; + if (fs->stacklevel > fs->f->maxstacksize) { + if (fs->stacklevel > MAXSTACK) + luaK_error(fs->ls, "function or expression too complex"); + fs->f->maxstacksize = fs->stacklevel; + } +} + + +void luaK_kstr (LexState *ls, int c) { + luaK_code1(ls->fs, OP_PUSHSTRING, c); +} + + +static int number_constant (FuncState *fs, Number r) { + /* check whether `r' has appeared within the last LOOKBACKNUMS entries */ + Proto *f = fs->f; + int c = f->nknum; + int lim = c < LOOKBACKNUMS ? 0 : c-LOOKBACKNUMS; + while (--c >= lim) + if (f->knum[c] == r) return c; + /* not found; create a new entry */ + luaM_growvector(fs->L, f->knum, f->nknum, 1, Number, + "constant table overflow", MAXARG_U); + c = f->nknum++; + f->knum[c] = r; + return c; +} + + +void luaK_number (FuncState *fs, Number f) { + if (f <= (Number)MAXARG_S && (Number)(int)f == f) + luaK_code1(fs, OP_PUSHINT, (int)f); /* f has a short integer value */ + else + luaK_code1(fs, OP_PUSHNUM, number_constant(fs, f)); +} + + +void luaK_adjuststack (FuncState *fs, int n) { + if (n > 0) + luaK_code1(fs, OP_POP, n); + else + luaK_code1(fs, OP_PUSHNIL, -n); +} + + +int luaK_lastisopen (FuncState *fs) { + /* check whether last instruction is an open function call */ + Instruction i = previous_instruction(fs); + if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET) + return 1; + else return 0; +} + + +void luaK_setcallreturns (FuncState *fs, int nresults) { + if (luaK_lastisopen(fs)) { /* expression is an open function call? */ + SETARG_B(fs->f->code[fs->pc-1], nresults); /* set number of results */ + luaK_deltastack(fs, nresults); /* push results */ + } +} + + +static int discharge (FuncState *fs, expdesc *var) { + switch (var->k) { + case VLOCAL: + luaK_code1(fs, OP_GETLOCAL, var->u.index); + break; + case VGLOBAL: + luaK_code1(fs, OP_GETGLOBAL, var->u.index); + break; + case VINDEXED: + luaK_code0(fs, OP_GETTABLE); + break; + case VEXP: + return 0; /* nothing to do */ + } + var->k = VEXP; + var->u.l.t = var->u.l.f = NO_JUMP; + return 1; +} + + +static void discharge1 (FuncState *fs, expdesc *var) { + discharge(fs, var); + /* if it has jumps then it is already discharged */ + if (var->u.l.t == NO_JUMP && var->u.l.f == NO_JUMP) + luaK_setcallreturns(fs, 1); /* call must return 1 value */ +} + + +void luaK_storevar (LexState *ls, const expdesc *var) { + FuncState *fs = ls->fs; + switch (var->k) { + case VLOCAL: + luaK_code1(fs, OP_SETLOCAL, var->u.index); + break; + case VGLOBAL: + luaK_code1(fs, OP_SETGLOBAL, var->u.index); + break; + case VINDEXED: /* table is at top-3; pop 3 elements after operation */ + luaK_code2(fs, OP_SETTABLE, 3, 3); + break; + default: + LUA_INTERNALERROR("invalid var kind to store"); + } +} + + +static OpCode invertjump (OpCode op) { + switch (op) { + case OP_JMPNE: return OP_JMPEQ; + case OP_JMPEQ: return OP_JMPNE; + case OP_JMPLT: return OP_JMPGE; + case OP_JMPLE: return OP_JMPGT; + case OP_JMPGT: return OP_JMPLE; + case OP_JMPGE: return OP_JMPLT; + case OP_JMPT: case OP_JMPONT: return OP_JMPF; + case OP_JMPF: case OP_JMPONF: return OP_JMPT; + default: + LUA_INTERNALERROR("invalid jump instruction"); + return OP_END; /* to avoid warnings */ + } +} + + +static void luaK_patchlistaux (FuncState *fs, int list, int target, + OpCode special, int special_target) { + Instruction *code = fs->f->code; + while (list != NO_JUMP) { + int next = luaK_getjump(fs, list); + Instruction *i = &code[list]; + OpCode op = GET_OPCODE(*i); + if (op == special) /* this `op' already has a value */ + luaK_fixjump(fs, list, special_target); + else { + luaK_fixjump(fs, list, target); /* do the patch */ + if (op == OP_JMPONT) /* remove eventual values */ + SET_OPCODE(*i, OP_JMPT); + else if (op == OP_JMPONF) + SET_OPCODE(*i, OP_JMPF); + } + list = next; + } +} + + +void luaK_patchlist (FuncState *fs, int list, int target) { + if (target == fs->lasttarget) /* same target that list `jlt'? */ + luaK_concat(fs, &fs->jlt, list); /* delay fixing */ + else + luaK_patchlistaux(fs, list, target, OP_END, 0); +} + + +static int need_value (FuncState *fs, int list, OpCode hasvalue) { + /* check whether list has a jump without a value */ + for (; list != NO_JUMP; list = luaK_getjump(fs, list)) + if (GET_OPCODE(fs->f->code[list]) != hasvalue) return 1; + return 0; /* not found */ +} + + +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (*l1 == NO_JUMP) + *l1 = l2; + else { + int list = *l1; + for (;;) { /* traverse `l1' */ + int next = luaK_getjump(fs, list); + if (next == NO_JUMP) { /* end of list? */ + luaK_fixjump(fs, list, l2); + return; + } + list = next; + } + } +} + + +static void luaK_testgo (FuncState *fs, expdesc *v, int invert, OpCode jump) { + int prevpos; /* position of last instruction */ + Instruction *previous; + int *golist, *exitlist; + if (!invert) { + golist = &v->u.l.f; /* go if false */ + exitlist = &v->u.l.t; /* exit if true */ + } + else { + golist = &v->u.l.t; /* go if true */ + exitlist = &v->u.l.f; /* exit if false */ + } + discharge1(fs, v); + prevpos = fs->pc-1; + previous = &fs->f->code[prevpos]; + LUA_ASSERT(*previous==previous_instruction(fs), "no jump allowed here"); + if (!ISJUMP(GET_OPCODE(*previous))) + prevpos = luaK_code1(fs, jump, NO_JUMP); + else { /* last instruction is already a jump */ + if (invert) + SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous))); + } + luaK_concat(fs, exitlist, prevpos); /* insert last jump in `exitlist' */ + luaK_patchlist(fs, *golist, luaK_getlabel(fs)); + *golist = NO_JUMP; +} + + +void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue) { + luaK_testgo(fs, v, 1, keepvalue ? OP_JMPONF : OP_JMPF); +} + + +static void luaK_goiffalse (FuncState *fs, expdesc *v, int keepvalue) { + luaK_testgo(fs, v, 0, keepvalue ? OP_JMPONT : OP_JMPT); +} + + +static int code_label (FuncState *fs, OpCode op, int arg) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_code1(fs, op, arg); +} + + +void luaK_tostack (LexState *ls, expdesc *v, int onlyone) { + FuncState *fs = ls->fs; + if (!discharge(fs, v)) { /* `v' is an expression? */ + OpCode previous = GET_OPCODE(fs->f->code[fs->pc-1]); + if (!ISJUMP(previous) && v->u.l.f == NO_JUMP && v->u.l.t == NO_JUMP) { + /* expression has no jumps */ + if (onlyone) + luaK_setcallreturns(fs, 1); /* call must return 1 value */ + } + else { /* expression has jumps */ + int final; /* position after whole expression */ + int j = NO_JUMP; /* eventual jump over values */ + int p_nil = NO_JUMP; /* position of an eventual PUSHNIL */ + int p_1 = NO_JUMP; /* position of an eventual PUSHINT */ + if (ISJUMP(previous) || need_value(fs, v->u.l.f, OP_JMPONF) + || need_value(fs, v->u.l.t, OP_JMPONT)) { + /* expression needs values */ + if (ISJUMP(previous)) + luaK_concat(fs, &v->u.l.t, fs->pc-1); /* put `previous' in t. list */ + else { + j = code_label(fs, OP_JMP, NO_JUMP); /* to jump over both pushes */ + /* correct stack for compiler and symbolic execution */ + luaK_adjuststack(fs, 1); + } + p_nil = code_label(fs, OP_PUSHNILJMP, 0); + p_1 = code_label(fs, OP_PUSHINT, 1); + luaK_patchlist(fs, j, luaK_getlabel(fs)); + } + final = luaK_getlabel(fs); + luaK_patchlistaux(fs, v->u.l.f, p_nil, OP_JMPONF, final); + luaK_patchlistaux(fs, v->u.l.t, p_1, OP_JMPONT, final); + v->u.l.f = v->u.l.t = NO_JUMP; + } + } +} + + +void luaK_prefix (LexState *ls, UnOpr op, expdesc *v) { + FuncState *fs = ls->fs; + if (op == OPR_MINUS) { + luaK_tostack(ls, v, 1); + luaK_code0(fs, OP_MINUS); + } + else { /* op == NOT */ + Instruction *previous; + discharge1(fs, v); + previous = &fs->f->code[fs->pc-1]; + if (ISJUMP(GET_OPCODE(*previous))) + SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous))); + else + luaK_code0(fs, OP_NOT); + /* interchange true and false lists */ + { int temp = v->u.l.f; v->u.l.f = v->u.l.t; v->u.l.t = temp; } + } +} + + +void luaK_infix (LexState *ls, BinOpr op, expdesc *v) { + FuncState *fs = ls->fs; + switch (op) { + case OPR_AND: + luaK_goiftrue(fs, v, 1); + break; + case OPR_OR: + luaK_goiffalse(fs, v, 1); + break; + default: + luaK_tostack(ls, v, 1); /* all other binary operators need a value */ + } +} + + + +static const struct { + OpCode opcode; /* opcode for each binary operator */ + int arg; /* default argument for the opcode */ +} codes[] = { /* ORDER OPR */ + {OP_ADD, 0}, {OP_SUB, 0}, {OP_MULT, 0}, {OP_DIV, 0}, + {OP_POW, 0}, {OP_CONCAT, 2}, + {OP_JMPNE, NO_JUMP}, {OP_JMPEQ, NO_JUMP}, + {OP_JMPLT, NO_JUMP}, {OP_JMPLE, NO_JUMP}, + {OP_JMPGT, NO_JUMP}, {OP_JMPGE, NO_JUMP} +}; + + +void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2) { + FuncState *fs = ls->fs; + switch (op) { + case OPR_AND: { + LUA_ASSERT(v1->u.l.t == NO_JUMP, "list must be closed"); + discharge1(fs, v2); + v1->u.l.t = v2->u.l.t; + luaK_concat(fs, &v1->u.l.f, v2->u.l.f); + break; + } + case OPR_OR: { + LUA_ASSERT(v1->u.l.f == NO_JUMP, "list must be closed"); + discharge1(fs, v2); + v1->u.l.f = v2->u.l.f; + luaK_concat(fs, &v1->u.l.t, v2->u.l.t); + break; + } + default: { + luaK_tostack(ls, v2, 1); /* `v2' must be a value */ + luaK_code1(fs, codes[op].opcode, codes[op].arg); + } + } +} + + +static void codelineinfo (FuncState *fs) { + Proto *f = fs->f; + LexState *ls = fs->ls; + if (ls->lastline > fs->lastline) { + luaM_growvector(fs->L, f->lineinfo, f->nlineinfo, 2, int, + "line info overflow", MAX_INT); + if (ls->lastline > fs->lastline+1) + f->lineinfo[f->nlineinfo++] = -(ls->lastline - (fs->lastline+1)); + f->lineinfo[f->nlineinfo++] = fs->pc; + fs->lastline = ls->lastline; + } +} + + +int luaK_code0 (FuncState *fs, OpCode o) { + return luaK_code2(fs, o, 0, 0); +} + + +int luaK_code1 (FuncState *fs, OpCode o, int arg1) { + return luaK_code2(fs, o, arg1, 0); +} + + +int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) { + Instruction i = previous_instruction(fs); + int delta = luaK_opproperties[o].push - luaK_opproperties[o].pop; + int optm = 0; /* 1 when there is an optimization */ + switch (o) { + case OP_CLOSURE: { + delta = -arg2+1; + break; + } + case OP_SETTABLE: { + delta = -arg2; + break; + } + case OP_SETLIST: { + if (arg2 == 0) return NO_JUMP; /* nothing to do */ + delta = -arg2; + break; + } + case OP_SETMAP: { + if (arg1 == 0) return NO_JUMP; /* nothing to do */ + delta = -2*arg1; + break; + } + case OP_RETURN: { + if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET) { + SET_OPCODE(i, OP_TAILCALL); + SETARG_B(i, arg1); + optm = 1; + } + break; + } + case OP_PUSHNIL: { + if (arg1 == 0) return NO_JUMP; /* nothing to do */ + delta = arg1; + switch(GET_OPCODE(i)) { + case OP_PUSHNIL: SETARG_U(i, GETARG_U(i)+arg1); optm = 1; break; + default: break; + } + break; + } + case OP_POP: { + if (arg1 == 0) return NO_JUMP; /* nothing to do */ + delta = -arg1; + switch(GET_OPCODE(i)) { + case OP_SETTABLE: SETARG_B(i, GETARG_B(i)+arg1); optm = 1; break; + default: break; + } + break; + } + case OP_GETTABLE: { + switch(GET_OPCODE(i)) { + case OP_PUSHSTRING: /* `t.x' */ + SET_OPCODE(i, OP_GETDOTTED); + optm = 1; + break; + case OP_GETLOCAL: /* `t[i]' */ + SET_OPCODE(i, OP_GETINDEXED); + optm = 1; + break; + default: break; + } + break; + } + case OP_ADD: { + switch(GET_OPCODE(i)) { + case OP_PUSHINT: SET_OPCODE(i, OP_ADDI); optm = 1; break; /* `a+k' */ + default: break; + } + break; + } + case OP_SUB: { + switch(GET_OPCODE(i)) { + case OP_PUSHINT: /* `a-k' */ + i = CREATE_S(OP_ADDI, -GETARG_S(i)); + optm = 1; + break; + default: break; + } + break; + } + case OP_CONCAT: { + delta = -arg1+1; + switch(GET_OPCODE(i)) { + case OP_CONCAT: /* `a..b..c' */ + SETARG_U(i, GETARG_U(i)+1); + optm = 1; + break; + default: break; + } + break; + } + case OP_MINUS: { + switch(GET_OPCODE(i)) { + case OP_PUSHINT: /* `-k' */ + SETARG_S(i, -GETARG_S(i)); + optm = 1; + break; + case OP_PUSHNUM: /* `-k' */ + SET_OPCODE(i, OP_PUSHNEGNUM); + optm = 1; + break; + default: break; + } + break; + } + case OP_JMPNE: { + if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a~=nil' */ + i = CREATE_S(OP_JMPT, NO_JUMP); + optm = 1; + } + break; + } + case OP_JMPEQ: { + if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a==nil' */ + i = CREATE_0(OP_NOT); + delta = -1; /* just undo effect of previous PUSHNIL */ + optm = 1; + } + break; + } + case OP_JMPT: + case OP_JMPONT: { + switch (GET_OPCODE(i)) { + case OP_NOT: { + i = CREATE_S(OP_JMPF, NO_JUMP); + optm = 1; + break; + } + case OP_PUSHINT: { + if (o == OP_JMPT) { /* JMPONT must keep original integer value */ + i = CREATE_S(OP_JMP, NO_JUMP); + optm = 1; + } + break; + } + case OP_PUSHNIL: { + if (GETARG_U(i) == 1) { + fs->pc--; /* erase previous instruction */ + luaK_deltastack(fs, -1); /* correct stack */ + return NO_JUMP; + } + break; + } + default: break; + } + break; + } + case OP_JMPF: + case OP_JMPONF: { + switch (GET_OPCODE(i)) { + case OP_NOT: { + i = CREATE_S(OP_JMPT, NO_JUMP); + optm = 1; + break; + } + case OP_PUSHINT: { /* `while 1 do ...' */ + fs->pc--; /* erase previous instruction */ + luaK_deltastack(fs, -1); /* correct stack */ + return NO_JUMP; + } + case OP_PUSHNIL: { /* `repeat ... until nil' */ + if (GETARG_U(i) == 1) { + i = CREATE_S(OP_JMP, NO_JUMP); + optm = 1; + } + break; + } + default: break; + } + break; + } + case OP_GETDOTTED: + case OP_GETINDEXED: + case OP_TAILCALL: + case OP_ADDI: { + LUA_INTERNALERROR("instruction used only for optimizations"); + break; + } + default: { + LUA_ASSERT(delta != VD, "invalid delta"); + break; + } + } + luaK_deltastack(fs, delta); + if (optm) { /* optimize: put instruction in place of last one */ + fs->f->code[fs->pc-1] = i; /* change previous instruction */ + return fs->pc-1; /* do not generate new instruction */ + } + /* else build new instruction */ + switch ((enum Mode)luaK_opproperties[o].mode) { + case iO: i = CREATE_0(o); break; + case iU: i = CREATE_U(o, arg1); break; + case iS: i = CREATE_S(o, arg1); break; + case iAB: i = CREATE_AB(o, arg1, arg2); break; + } + codelineinfo(fs); + /* put new instruction in code array */ + luaM_growvector(fs->L, fs->f->code, fs->pc, 1, Instruction, + "code size overflow", MAX_INT); + fs->f->code[fs->pc] = i; + return fs->pc++; +} + + +const struct OpProperties luaK_opproperties[NUM_OPCODES] = { + {iO, 0, 0}, /* OP_END */ + {iU, 0, 0}, /* OP_RETURN */ + {iAB, 0, 0}, /* OP_CALL */ + {iAB, 0, 0}, /* OP_TAILCALL */ + {iU, VD, 0}, /* OP_PUSHNIL */ + {iU, VD, 0}, /* OP_POP */ + {iS, 1, 0}, /* OP_PUSHINT */ + {iU, 1, 0}, /* OP_PUSHSTRING */ + {iU, 1, 0}, /* OP_PUSHNUM */ + {iU, 1, 0}, /* OP_PUSHNEGNUM */ + {iU, 1, 0}, /* OP_PUSHUPVALUE */ + {iU, 1, 0}, /* OP_GETLOCAL */ + {iU, 1, 0}, /* OP_GETGLOBAL */ + {iO, 1, 2}, /* OP_GETTABLE */ + {iU, 1, 1}, /* OP_GETDOTTED */ + {iU, 1, 1}, /* OP_GETINDEXED */ + {iU, 2, 1}, /* OP_PUSHSELF */ + {iU, 1, 0}, /* OP_CREATETABLE */ + {iU, 0, 1}, /* OP_SETLOCAL */ + {iU, 0, 1}, /* OP_SETGLOBAL */ + {iAB, VD, 0}, /* OP_SETTABLE */ + {iAB, VD, 0}, /* OP_SETLIST */ + {iU, VD, 0}, /* OP_SETMAP */ + {iO, 1, 2}, /* OP_ADD */ + {iS, 1, 1}, /* OP_ADDI */ + {iO, 1, 2}, /* OP_SUB */ + {iO, 1, 2}, /* OP_MULT */ + {iO, 1, 2}, /* OP_DIV */ + {iO, 1, 2}, /* OP_POW */ + {iU, VD, 0}, /* OP_CONCAT */ + {iO, 1, 1}, /* OP_MINUS */ + {iO, 1, 1}, /* OP_NOT */ + {iS, 0, 2}, /* OP_JMPNE */ + {iS, 0, 2}, /* OP_JMPEQ */ + {iS, 0, 2}, /* OP_JMPLT */ + {iS, 0, 2}, /* OP_JMPLE */ + {iS, 0, 2}, /* OP_JMPGT */ + {iS, 0, 2}, /* OP_JMPGE */ + {iS, 0, 1}, /* OP_JMPT */ + {iS, 0, 1}, /* OP_JMPF */ + {iS, 0, 1}, /* OP_JMPONT */ + {iS, 0, 1}, /* OP_JMPONF */ + {iS, 0, 0}, /* OP_JMP */ + {iO, 0, 0}, /* OP_PUSHNILJMP */ + {iS, 0, 0}, /* OP_FORPREP */ + {iS, 0, 3}, /* OP_FORLOOP */ + {iS, 2, 0}, /* OP_LFORPREP */ + {iS, 0, 3}, /* OP_LFORLOOP */ + {iAB, VD, 0} /* OP_CLOSURE */ +}; + diff --git a/src/lcode.h b/src/lcode.h new file mode 100644 index 00000000..3f0a209a --- /dev/null +++ b/src/lcode.h @@ -0,0 +1,70 @@ +/* +** $Id: lcode.h,v 1.16 2000/08/09 14:49:13 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums +*/ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MULT, OPR_DIV, OPR_POW, + OPR_CONCAT, + OPR_NE, OPR_EQ, OPR_LT, OPR_LE, OPR_GT, OPR_GE, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_NOUNOPR } UnOpr; + + +enum Mode {iO, iU, iS, iAB}; /* instruction format */ + +#define VD 100 /* flag for variable delta */ + +extern const struct OpProperties { + char mode; + unsigned char push; + unsigned char pop; +} luaK_opproperties[]; + + +void luaK_error (LexState *ls, const char *msg); +int luaK_code0 (FuncState *fs, OpCode o); +int luaK_code1 (FuncState *fs, OpCode o, int arg1); +int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2); +int luaK_jump (FuncState *fs); +void luaK_patchlist (FuncState *fs, int list, int target); +void luaK_concat (FuncState *fs, int *l1, int l2); +void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue); +int luaK_getlabel (FuncState *fs); +void luaK_deltastack (FuncState *fs, int delta); +void luaK_kstr (LexState *ls, int c); +void luaK_number (FuncState *fs, Number f); +void luaK_adjuststack (FuncState *fs, int n); +int luaK_lastisopen (FuncState *fs); +void luaK_setcallreturns (FuncState *fs, int nresults); +void luaK_tostack (LexState *ls, expdesc *v, int onlyone); +void luaK_storevar (LexState *ls, const expdesc *var); +void luaK_prefix (LexState *ls, UnOpr op, expdesc *v); +void luaK_infix (LexState *ls, BinOpr op, expdesc *v); +void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2); + + +#endif diff --git a/src/ldebug.c b/src/ldebug.c new file mode 100644 index 00000000..a5a2ab1d --- /dev/null +++ b/src/ldebug.c @@ -0,0 +1,466 @@ +/* +** $Id: ldebug.c,v 1.50 2000/10/30 12:38:50 roberto Exp $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + + +#include <stdlib.h> + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "luadebug.h" + + + +static const char *getfuncname (lua_State *L, StkId f, const char **name); + + +static void setnormalized (TObject *d, const TObject *s) { + if (ttype(s) == LUA_TMARK) { + clvalue(d) = infovalue(s)->func; + ttype(d) = LUA_TFUNCTION; + } + else *d = *s; +} + + +static int isLmark (StkId o) { + return (o && ttype(o) == LUA_TMARK && !infovalue(o)->func->isC); +} + + +LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func) { + lua_Hook oldhook = L->callhook; + L->callhook = func; + return oldhook; +} + + +LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func) { + lua_Hook oldhook = L->linehook; + L->linehook = func; + return oldhook; +} + + +static StkId aux_stackedfunction (lua_State *L, int level, StkId top) { + int i; + for (i = (top-1) - L->stack; i>=0; i--) { + if (is_T_MARK(L->stack[i].ttype)) { + if (level == 0) + return L->stack+i; + level--; + } + } + return NULL; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + StkId f = aux_stackedfunction(L, level, L->top); + if (f == NULL) return 0; /* there is no such level */ + else { + ar->_func = f; + return 1; + } +} + + +static int nups (StkId f) { + switch (ttype(f)) { + case LUA_TFUNCTION: + return clvalue(f)->nupvalues; + case LUA_TMARK: + return infovalue(f)->func->nupvalues; + default: + return 0; + } +} + + +int luaG_getline (int *lineinfo, int pc, int refline, int *prefi) { + int refi; + if (lineinfo == NULL || pc == -1) + return -1; /* no line info or function is not active */ + refi = prefi ? *prefi : 0; + if (lineinfo[refi] < 0) + refline += -lineinfo[refi++]; + LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info"); + while (lineinfo[refi] > pc) { + refline--; + refi--; + if (lineinfo[refi] < 0) + refline -= -lineinfo[refi--]; + LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info"); + } + for (;;) { + int nextline = refline + 1; + int nextref = refi + 1; + if (lineinfo[nextref] < 0) + nextline += -lineinfo[nextref++]; + LUA_ASSERT(lineinfo[nextref] >= 0, "invalid line info"); + if (lineinfo[nextref] > pc) + break; + refline = nextline; + refi = nextref; + } + if (prefi) *prefi = refi; + return refline; +} + + +static int currentpc (StkId f) { + CallInfo *ci = infovalue(f); + LUA_ASSERT(isLmark(f), "function has no pc"); + if (ci->pc) + return (*ci->pc - ci->func->f.l->code) - 1; + else + return -1; /* function is not active */ +} + + +static int currentline (StkId f) { + if (!isLmark(f)) + return -1; /* only active lua functions have current-line information */ + else { + CallInfo *ci = infovalue(f); + int *lineinfo = ci->func->f.l->lineinfo; + return luaG_getline(lineinfo, currentpc(f), 1, NULL); + } +} + + + +static Proto *getluaproto (StkId f) { + return (isLmark(f) ? infovalue(f)->func->f.l : NULL); +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + const char *name; + StkId f = ar->_func; + Proto *fp = getluaproto(f); + if (!fp) return NULL; /* `f' is not a Lua function? */ + name = luaF_getlocalname(fp, n, currentpc(f)); + if (!name) return NULL; + luaA_pushobject(L, (f+1)+(n-1)); /* push value */ + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + const char *name; + StkId f = ar->_func; + Proto *fp = getluaproto(f); + L->top--; /* pop new value */ + if (!fp) return NULL; /* `f' is not a Lua function? */ + name = luaF_getlocalname(fp, n, currentpc(f)); + if (!name || name[0] == '(') return NULL; /* `(' starts private locals */ + *((f+1)+(n-1)) = *L->top; + return name; +} + + +static void infoLproto (lua_Debug *ar, Proto *f) { + ar->source = f->source->str; + ar->linedefined = f->lineDefined; + ar->what = "Lua"; +} + + +static void funcinfo (lua_State *L, lua_Debug *ar, StkId func) { + Closure *cl = NULL; + switch (ttype(func)) { + case LUA_TFUNCTION: + cl = clvalue(func); + break; + case LUA_TMARK: + cl = infovalue(func)->func; + break; + default: + lua_error(L, "value for `lua_getinfo' is not a function"); + } + if (cl->isC) { + ar->source = "=C"; + ar->linedefined = -1; + ar->what = "C"; + } + else + infoLproto(ar, cl->f.l); + luaO_chunkid(ar->short_src, ar->source, sizeof(ar->short_src)); + if (ar->linedefined == 0) + ar->what = "main"; +} + + +static const char *travtagmethods (lua_State *L, const TObject *o) { + if (ttype(o) == LUA_TFUNCTION) { + int e; + for (e=0; e<TM_N; e++) { + int t; + for (t=0; t<=L->last_tag; t++) + if (clvalue(o) == luaT_gettm(L, t, e)) + return luaT_eventname[e]; + } + } + return NULL; +} + + +static const char *travglobals (lua_State *L, const TObject *o) { + Hash *g = L->gt; + int i; + for (i=0; i<g->size; i++) { + if (luaO_equalObj(o, val(node(g, i))) && + ttype(key(node(g, i))) == LUA_TSTRING) + return tsvalue(key(node(g, i)))->str; + } + return NULL; +} + + +static void getname (lua_State *L, StkId f, lua_Debug *ar) { + TObject o; + setnormalized(&o, f); + /* try to find a name for given function */ + if ((ar->name = travglobals(L, &o)) != NULL) + ar->namewhat = "global"; + /* not found: try tag methods */ + else if ((ar->name = travtagmethods(L, &o)) != NULL) + ar->namewhat = "tag-method"; + else ar->namewhat = ""; /* not found at all */ +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + StkId func; + int isactive = (*what != '>'); + if (isactive) + func = ar->_func; + else { + what++; /* skip the '>' */ + func = L->top - 1; + } + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(L, ar, func); + break; + } + case 'l': { + ar->currentline = currentline(func); + break; + } + case 'u': { + ar->nups = nups(func); + break; + } + case 'n': { + ar->namewhat = (isactive) ? getfuncname(L, func, &ar->name) : NULL; + if (ar->namewhat == NULL) + getname(L, func, ar); + break; + } + case 'f': { + setnormalized(L->top, func); + incr_top; /* push function */ + break; + } + default: return 0; /* invalid option */ + } + } + if (!isactive) L->top--; /* pop function */ + return 1; +} + + +/* +** {====================================================== +** Symbolic Execution +** ======================================================= +*/ + + +static int pushpc (int *stack, int pc, int top, int n) { + while (n--) + stack[top++] = pc-1; + return top; +} + + +static Instruction luaG_symbexec (const Proto *pt, int lastpc, int stackpos) { + int stack[MAXSTACK]; /* stores last instruction that changed a stack entry */ + const Instruction *code = pt->code; + int top = pt->numparams; + int pc = 0; + if (pt->is_vararg) /* varargs? */ + top++; /* `arg' */ + while (pc < lastpc) { + const Instruction i = code[pc++]; + LUA_ASSERT(0 <= top && top <= pt->maxstacksize, "wrong stack"); + switch (GET_OPCODE(i)) { + case OP_RETURN: { + LUA_ASSERT(top >= GETARG_U(i), "wrong stack"); + top = GETARG_U(i); + break; + } + case OP_TAILCALL: { + LUA_ASSERT(top >= GETARG_A(i), "wrong stack"); + top = GETARG_B(i); + break; + } + case OP_CALL: { + int nresults = GETARG_B(i); + if (nresults == MULT_RET) nresults = 1; + LUA_ASSERT(top >= GETARG_A(i), "wrong stack"); + top = pushpc(stack, pc, GETARG_A(i), nresults); + break; + } + case OP_PUSHNIL: { + top = pushpc(stack, pc, top, GETARG_U(i)); + break; + } + case OP_POP: { + top -= GETARG_U(i); + break; + } + case OP_SETTABLE: + case OP_SETLIST: { + top -= GETARG_B(i); + break; + } + case OP_SETMAP: { + top -= 2*GETARG_U(i); + break; + } + case OP_CONCAT: { + top -= GETARG_U(i); + stack[top++] = pc-1; + break; + } + case OP_CLOSURE: { + top -= GETARG_B(i); + stack[top++] = pc-1; + break; + } + case OP_JMPONT: + case OP_JMPONF: { + int newpc = pc + GETARG_S(i); + /* jump is forward and do not skip `lastpc'? */ + if (pc < newpc && newpc <= lastpc) { + stack[top-1] = pc-1; /* value comes from `and'/`or' */ + pc = newpc; /* do the jump */ + } + else + top--; /* do not jump; pop value */ + break; + } + default: { + OpCode op = GET_OPCODE(i); + LUA_ASSERT(luaK_opproperties[op].push != VD, + "invalid opcode for default"); + top -= luaK_opproperties[op].pop; + LUA_ASSERT(top >= 0, "wrong stack"); + top = pushpc(stack, pc, top, luaK_opproperties[op].push); + } + } + } + return code[stack[stackpos]]; +} + + +static const char *getobjname (lua_State *L, StkId obj, const char **name) { + StkId func = aux_stackedfunction(L, 0, obj); + if (!isLmark(func)) + return NULL; /* not an active Lua function */ + else { + Proto *p = infovalue(func)->func->f.l; + int pc = currentpc(func); + int stackpos = obj - (func+1); /* func+1 == function base */ + Instruction i = luaG_symbexec(p, pc, stackpos); + LUA_ASSERT(pc != -1, "function must be active"); + switch (GET_OPCODE(i)) { + case OP_GETGLOBAL: { + *name = p->kstr[GETARG_U(i)]->str; + return "global"; + } + case OP_GETLOCAL: { + *name = luaF_getlocalname(p, GETARG_U(i)+1, pc); + LUA_ASSERT(*name, "local must exist"); + return "local"; + } + case OP_PUSHSELF: + case OP_GETDOTTED: { + *name = p->kstr[GETARG_U(i)]->str; + return "field"; + } + default: + return NULL; /* no useful name found */ + } + } +} + + +static const char *getfuncname (lua_State *L, StkId f, const char **name) { + StkId func = aux_stackedfunction(L, 0, f); /* calling function */ + if (!isLmark(func)) + return NULL; /* not an active Lua function */ + else { + Proto *p = infovalue(func)->func->f.l; + int pc = currentpc(func); + Instruction i; + if (pc == -1) return NULL; /* function is not activated */ + i = p->code[pc]; + switch (GET_OPCODE(i)) { + case OP_CALL: case OP_TAILCALL: + return getobjname(L, (func+1)+GETARG_A(i), name); + default: + return NULL; /* no useful name found */ + } + } +} + + +/* }====================================================== */ + + +void luaG_typeerror (lua_State *L, StkId o, const char *op) { + const char *name; + const char *kind = getobjname(L, o, &name); + const char *t = luaO_typename(o); + if (kind) + luaO_verror(L, "attempt to %.30s %.20s `%.40s' (a %.10s value)", + op, kind, name, t); + else + luaO_verror(L, "attempt to %.30s a %.10s value", op, t); +} + + +void luaG_binerror (lua_State *L, StkId p1, int t, const char *op) { + if (ttype(p1) == t) p1++; + LUA_ASSERT(ttype(p1) != t, "must be an error"); + luaG_typeerror(L, p1, op); +} + + +void luaG_ordererror (lua_State *L, StkId top) { + const char *t1 = luaO_typename(top-2); + const char *t2 = luaO_typename(top-1); + if (t1[2] == t2[2]) + luaO_verror(L, "attempt to compare two %.10s values", t1); + else + luaO_verror(L, "attempt to compare %.10s with %.10s", t1, t2); +} + diff --git a/src/ldebug.h b/src/ldebug.h new file mode 100644 index 00000000..d4b2d3b0 --- /dev/null +++ b/src/ldebug.h @@ -0,0 +1,21 @@ +/* +** $Id: ldebug.h,v 1.7 2000/10/05 12:14:08 roberto Exp $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" +#include "luadebug.h" + + +void luaG_typeerror (lua_State *L, StkId o, const char *op); +void luaG_binerror (lua_State *L, StkId p1, int t, const char *op); +int luaG_getline (int *lineinfo, int pc, int refline, int *refi); +void luaG_ordererror (lua_State *L, StkId top); + + +#endif @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 1.45b 1999/06/22 20:37:23 roberto Exp $ +** $Id: ldo.c,v 1.109 2000/10/30 12:38:50 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -10,378 +10,376 @@ #include <stdlib.h> #include <string.h> -#include "lauxlib.h" +#include "lua.h" + +#include "ldebug.h" #include "ldo.h" -#include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" +#include "ltable.h" #include "ltm.h" -#include "lua.h" -#include "luadebug.h" #include "lundump.h" #include "lvm.h" #include "lzio.h" +/* space to handle stack overflow errors */ +#define EXTRA_STACK (2*LUA_MINSTACK) -#ifndef STACK_LIMIT -#define STACK_LIMIT 6000 /* arbitrary limit */ -#endif - - - -#define STACK_UNIT 128 - -#ifdef DEBUG -#undef STACK_UNIT -#define STACK_UNIT 2 -#endif - - -void luaD_init (void) { - L->stack.stack = luaM_newvector(STACK_UNIT, TObject); - L->stack.top = L->stack.stack; - L->stack.last = L->stack.stack+(STACK_UNIT-1); +void luaD_init (lua_State *L, int stacksize) { + L->stack = luaM_newvector(L, stacksize+EXTRA_STACK, TObject); + L->nblocks += stacksize*sizeof(TObject); + L->stack_last = L->stack+(stacksize-1); + L->stacksize = stacksize; + L->Cbase = L->top = L->stack; } -void luaD_checkstack (int n) { - struct Stack *S = &L->stack; - if (S->last-S->top <= n) { - StkId top = S->top-S->stack; - int stacksize = (S->last-S->stack)+STACK_UNIT+n; - luaM_reallocvector(S->stack, stacksize, TObject); - S->last = S->stack+(stacksize-1); - S->top = S->stack + top; - if (stacksize >= STACK_LIMIT) { /* stack overflow? */ - if (lua_stackedfunction(100) == LUA_NOOBJECT) /* 100 funcs on stack? */ - lua_error("Lua2C - C2Lua overflow"); /* doesn't look like a rec. loop */ - else - lua_error("stack size overflow"); +void luaD_checkstack (lua_State *L, int n) { + if (L->stack_last - L->top <= n) { /* stack overflow? */ + if (L->stack_last-L->stack > (L->stacksize-1)) { + /* overflow while handling overflow */ + luaD_breakrun(L, LUA_ERRERR); /* break run without error message */ + } + else { + L->stack_last += EXTRA_STACK; /* to be used by error message */ + lua_error(L, "stack overflow"); } } } +static void restore_stack_limit (lua_State *L) { + if (L->top - L->stack < L->stacksize - 1) + L->stack_last = L->stack + (L->stacksize-1); +} + + /* -** Adjust stack. Set top to the given value, pushing NILs if needed. +** Adjust stack. Set top to base+extra, pushing NILs if needed. +** (we cannot add base+extra unless we are sure it fits in the stack; +** otherwise the result of such operation on pointers is undefined) */ -void luaD_adjusttop (StkId newtop) { - int diff = newtop-(L->stack.top-L->stack.stack); +void luaD_adjusttop (lua_State *L, StkId base, int extra) { + int diff = extra-(L->top-base); if (diff <= 0) - L->stack.top += diff; + L->top = base+extra; else { - luaD_checkstack(diff); + luaD_checkstack(L, diff); while (diff--) - ttype(L->stack.top++) = LUA_T_NIL; + ttype(L->top++) = LUA_TNIL; } } /* -** Open a hole below "nelems" from the L->stack.top. +** Open a hole inside the stack at `pos' */ -void luaD_openstack (int nelems) { - luaO_memup(L->stack.top-nelems+1, L->stack.top-nelems, - nelems*sizeof(TObject)); +static void luaD_openstack (lua_State *L, StkId pos) { + int i = L->top-pos; + while (i--) pos[i+1] = pos[i]; incr_top; } -void luaD_lineHook (int line) { - struct C_Lua_Stack oldCLS = L->Cstack; - StkId old_top = L->Cstack.lua2C = L->Cstack.base = L->stack.top-L->stack.stack; - L->Cstack.num = 0; - (*L->linehook)(line); - L->stack.top = L->stack.stack+old_top; - L->Cstack = oldCLS; +static void dohook (lua_State *L, lua_Debug *ar, lua_Hook hook) { + StkId old_Cbase = L->Cbase; + StkId old_top = L->Cbase = L->top; + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + L->allowhooks = 0; /* cannot call hooks inside a hook */ + (*hook)(L, ar); + LUA_ASSERT(L->allowhooks == 0, "invalid allow"); + L->allowhooks = 1; + L->top = old_top; + L->Cbase = old_Cbase; } -void luaD_callHook (StkId base, TProtoFunc *tf, int isreturn) { - struct C_Lua_Stack oldCLS = L->Cstack; - StkId old_top = L->Cstack.lua2C = L->Cstack.base = L->stack.top-L->stack.stack; - L->Cstack.num = 0; - if (isreturn) - (*L->callhook)(LUA_NOOBJECT, "(return)", 0); - else { - TObject *f = L->stack.stack+base-1; - if (tf) - (*L->callhook)(Ref(f), tf->source->str, tf->lineDefined); - else - (*L->callhook)(Ref(f), "(C)", -1); +void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook) { + if (L->allowhooks) { + lua_Debug ar; + ar._func = func; + ar.event = "line"; + ar.currentline = line; + dohook(L, &ar, linehook); } - L->stack.top = L->stack.stack+old_top; - L->Cstack = oldCLS; } -/* -** Call a C function. -** Cstack.num is the number of arguments; Cstack.lua2C points to the -** first argument. Returns an index to the first result from C. -*/ -static StkId callC (lua_CFunction f, StkId base) { - struct C_Lua_Stack *cls = &L->Cstack; - struct C_Lua_Stack oldCLS = *cls; - StkId firstResult; - int numarg = (L->stack.top-L->stack.stack) - base; - cls->num = numarg; - cls->lua2C = base; - cls->base = base+numarg; /* == top-stack */ - if (L->callhook) - luaD_callHook(base, NULL, 0); - (*f)(); /* do the actual call */ - if (L->callhook) /* func may have changed callhook */ - luaD_callHook(base, NULL, 1); - firstResult = cls->base; - *cls = oldCLS; - return firstResult; +static void luaD_callHook (lua_State *L, StkId func, lua_Hook callhook, + const char *event) { + if (L->allowhooks) { + lua_Debug ar; + ar._func = func; + ar.event = event; + infovalue(func)->pc = NULL; /* function is not active */ + dohook(L, &ar, callhook); + } } -static StkId callCclosure (struct Closure *cl, lua_CFunction f, StkId base) { - TObject *pbase; - int nup = cl->nelems; /* number of upvalues */ - luaD_checkstack(nup); - pbase = L->stack.stack+base; /* care: previous call may change this */ - /* open space for upvalues as extra arguments */ - luaO_memup(pbase+nup, pbase, (L->stack.top-pbase)*sizeof(TObject)); - /* copy upvalues into stack */ - memcpy(pbase, cl->consts+1, nup*sizeof(TObject)); - L->stack.top += nup; - return callC(f, base); +static StkId callCclosure (lua_State *L, const struct Closure *cl, StkId base) { + int nup = cl->nupvalues; /* number of upvalues */ + StkId old_Cbase = L->Cbase; + int n; + L->Cbase = base; /* new base for C function */ + luaD_checkstack(L, nup+LUA_MINSTACK); /* ensure minimum stack size */ + for (n=0; n<nup; n++) /* copy upvalues as extra arguments */ + *(L->top++) = cl->upvalue[n]; + n = (*cl->f.c)(L); /* do the actual call */ + L->Cbase = old_Cbase; /* restore old C base */ + return L->top - n; /* return index of first result */ } -void luaD_callTM (TObject *f, int nParams, int nResults) { - luaD_openstack(nParams); - *(L->stack.top-nParams-1) = *f; - luaD_calln(nParams, nResults); +void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults) { + StkId base = L->top - nParams; + luaD_openstack(L, base); + clvalue(base) = f; + ttype(base) = LUA_TFUNCTION; + luaD_call(L, base, nResults); } /* -** Call a function (C or Lua). The parameters must be on the stack, -** between [top-nArgs,top). The function to be called is right below the -** arguments. -** When returns, the results are on the stack, between [top-nArgs-1,top). -** The number of results is nResults, unless nResults=MULT_RET. -*/ -void luaD_calln (int nArgs, int nResults) { - struct Stack *S = &L->stack; /* to optimize */ - StkId base = (S->top-S->stack)-nArgs; - TObject *func = S->stack+base-1; +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, the results are on the stack, starting at the original +** function position. +** The number of results is nResults, unless nResults=LUA_MULTRET. +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + lua_Hook callhook; StkId firstResult; - int i; - switch (ttype(func)) { - case LUA_T_CPROTO: - ttype(func) = LUA_T_CMARK; - firstResult = callC(fvalue(func), base); - break; - case LUA_T_PROTO: - ttype(func) = LUA_T_PMARK; - firstResult = luaV_execute(NULL, tfvalue(func), base); - break; - case LUA_T_CLOSURE: { - Closure *c = clvalue(func); - TObject *proto = &(c->consts[0]); - ttype(func) = LUA_T_CLMARK; - firstResult = (ttype(proto) == LUA_T_CPROTO) ? - callCclosure(c, fvalue(proto), base) : - luaV_execute(c, tfvalue(proto), base); - break; - } - default: { /* func is not a function */ - /* Check the tag method for invalid functions */ - TObject *im = luaT_getimbyObj(func, IM_FUNCTION); - if (ttype(im) == LUA_T_NIL) - lua_error("call expression not a function"); - luaD_callTM(im, (S->top-S->stack)-(base-1), nResults); - return; + CallInfo ci; + Closure *cl; + if (ttype(func) != LUA_TFUNCTION) { + /* `func' is not a function; check the `function' tag method */ + Closure *tm = luaT_gettmbyObj(L, func, TM_FUNCTION); + if (tm == NULL) + luaG_typeerror(L, func, "call"); + luaD_openstack(L, func); + clvalue(func) = tm; /* tag method is the new function to be called */ + ttype(func) = LUA_TFUNCTION; + } + cl = clvalue(func); + ci.func = cl; + infovalue(func) = &ci; + ttype(func) = LUA_TMARK; + callhook = L->callhook; + if (callhook) + luaD_callHook(L, func, callhook, "call"); + firstResult = (cl->isC ? callCclosure(L, cl, func+1) : + luaV_execute(L, cl, func+1)); + if (callhook) /* same hook that was active at entry */ + luaD_callHook(L, func, callhook, "return"); + LUA_ASSERT(ttype(func) == LUA_TMARK, "invalid tag"); + /* move results to `func' (to erase parameters and function) */ + if (nResults == LUA_MULTRET) { + while (firstResult < L->top) /* copy all results */ + *func++ = *firstResult++; + L->top = func; + } + else { /* copy at most `nResults' */ + for (; nResults > 0 && firstResult < L->top; nResults--) + *func++ = *firstResult++; + L->top = func; + for (; nResults > 0; nResults--) { /* if there are not enough results */ + ttype(L->top) = LUA_TNIL; /* adjust the stack */ + incr_top; /* must check stack space */ } } - /* adjust the number of results */ - if (nResults == MULT_RET) - nResults = (S->top-S->stack)-firstResult; - else - luaD_adjusttop(firstResult+nResults); - /* move results to base-1 (to erase parameters and function) */ - base--; - for (i=0; i<nResults; i++) - *(S->stack+base+i) = *(S->stack+firstResult+i); - S->top -= firstResult-base; + luaC_checkGC(L); } /* -** Traverse all objects on L->stack.stack +** Execute a protected call. */ -void luaD_travstack (int (*fn)(TObject *)) -{ - StkId i; - for (i = (L->stack.top-1)-L->stack.stack; i>=0; i--) - fn(L->stack.stack+i); +struct CallS { /* data to `f_call' */ + StkId func; + int nresults; +}; + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = (struct CallS *)ud; + luaD_call(L, c->func, c->nresults); } - -static void message (char *s) { - TObject *em = &(luaS_new("_ERRORMESSAGE")->u.s.globalval); - if (ttype(em) == LUA_T_PROTO || ttype(em) == LUA_T_CPROTO || - ttype(em) == LUA_T_CLOSURE) { - *L->stack.top = *em; - incr_top; - lua_pushstring(s); - luaD_calln(1, 0); - } -} - -/* -** Reports an error, and jumps up to the available recover label -*/ -void lua_error (char *s) { - if (s) message(s); - if (L->errorJmp) - longjmp(L->errorJmp->b, 1); - else { - message("exit(1). Unable to recover.\n"); - exit(1); - } -} - - -/* -** Execute a protected call. Assumes that function is at L->Cstack.base and -** parameters are on top of it. Leave nResults on the stack. -*/ -int luaD_protectedrun (void) { - volatile struct C_Lua_Stack oldCLS = L->Cstack; - struct lua_longjmp myErrorJmp; - volatile int status; - struct lua_longjmp *volatile oldErr = L->errorJmp; - L->errorJmp = &myErrorJmp; - if (setjmp(myErrorJmp.b) == 0) { - StkId base = L->Cstack.base; - luaD_calln((L->stack.top-L->stack.stack)-base-1, MULT_RET); - L->Cstack.lua2C = base; /* position of the new results */ - L->Cstack.num = (L->stack.top-L->stack.stack) - base; - L->Cstack.base = base + L->Cstack.num; /* incorporate results on stack */ - status = 0; - } - else { /* an error occurred: restore L->Cstack and L->stack.top */ - L->Cstack = oldCLS; - L->stack.top = L->stack.stack+L->Cstack.base; - status = 1; - } - L->errorJmp = oldErr; +LUA_API int lua_call (lua_State *L, int nargs, int nresults) { + StkId func = L->top - (nargs+1); /* function to be called */ + struct CallS c; + int status; + c.func = func; c.nresults = nresults; + status = luaD_runprotected(L, f_call, &c); + if (status != 0) /* an error occurred? */ + L->top = func; /* remove parameters from the stack */ return status; } /* -** returns 0 = chunk loaded; 1 = error; 2 = no more chunks to load +** Execute a protected parser. */ -static int protectedparser (ZIO *z, int bin) { - volatile struct C_Lua_Stack oldCLS = L->Cstack; - struct lua_longjmp myErrorJmp; - volatile int status; - TProtoFunc *volatile tf; - struct lua_longjmp *volatile oldErr = L->errorJmp; - L->errorJmp = &myErrorJmp; - if (setjmp(myErrorJmp.b) == 0) { - tf = bin ? luaU_undump1(z) : luaY_parser(z); - status = 0; - } - else { /* an error occurred: restore L->Cstack and L->stack.top */ - L->Cstack = oldCLS; - L->stack.top = L->stack.stack+L->Cstack.base; - tf = NULL; - status = 1; - } - L->errorJmp = oldErr; - if (status) return 1; /* error code */ - if (tf == NULL) return 2; /* 'natural' end */ - luaD_adjusttop(L->Cstack.base+1); /* one slot for the pseudo-function */ - L->stack.stack[L->Cstack.base].ttype = LUA_T_PROTO; - L->stack.stack[L->Cstack.base].value.tf = tf; - luaV_closure(0); - return 0; -} - +struct ParserS { /* data to `f_parser' */ + ZIO *z; + int bin; +}; -static int do_main (ZIO *z, int bin) { - int status; - int debug = L->debug; /* save debug status */ - do { - long old_blocks = (luaC_checkGC(), L->nblocks); - status = protectedparser(z, bin); - if (status == 1) return 1; /* error */ - else if (status == 2) return 0; /* 'natural' end */ - else { - unsigned long newelems2 = 2*(L->nblocks-old_blocks); - L->GCthreshold += newelems2; - status = luaD_protectedrun(); - L->GCthreshold -= newelems2; - } - } while (bin && status == 0); - L->debug = debug; /* restore debug status */ - return status; +static void f_parser (lua_State *L, void *ud) { + struct ParserS *p = (struct ParserS *)ud; + Proto *tf = p->bin ? luaU_undump(L, p->z) : luaY_parser(L, p->z); + luaV_Lclosure(L, tf, 0); } -void luaD_gcIM (TObject *o) -{ - TObject *im = luaT_getimbyObj(o, IM_GC); - if (ttype(im) != LUA_T_NIL) { - *L->stack.top = *o; - incr_top; - luaD_callTM(im, 1, 0); +static int protectedparser (lua_State *L, ZIO *z, int bin) { + struct ParserS p; + unsigned long old_blocks; + int status; + p.z = z; p.bin = bin; + luaC_checkGC(L); + old_blocks = L->nblocks; + status = luaD_runprotected(L, f_parser, &p); + if (status == 0) { + /* add new memory to threshold (as it probably will stay) */ + L->GCthreshold += (L->nblocks - old_blocks); } + else if (status == LUA_ERRRUN) /* an error occurred: correct error code */ + status = LUA_ERRSYNTAX; + return status; } -#define MAXFILENAME 260 /* maximum part of a file name kept */ - -int lua_dofile (char *filename) { +static int parse_file (lua_State *L, const char *filename) { ZIO z; int status; - int c; - int bin; - char source[MAXFILENAME]; + int bin; /* flag for file mode */ + int c; /* look ahead char */ FILE *f = (filename == NULL) ? stdin : fopen(filename, "r"); - if (f == NULL) - return 2; - luaL_filesource(source, filename, sizeof(source)); + if (f == NULL) return LUA_ERRFILE; /* unable to open file */ c = fgetc(f); ungetc(c, f); bin = (c == ID_CHUNK); if (bin && f != stdin) { f = freopen(filename, "rb", f); /* set binary mode */ - if (f == NULL) return 2; + if (f == NULL) return LUA_ERRFILE; /* unable to reopen file */ } - luaZ_Fopen(&z, f, source); - status = do_main(&z, bin); + lua_pushstring(L, "@"); + lua_pushstring(L, (filename == NULL) ? "(stdin)" : filename); + lua_concat(L, 2); + filename = lua_tostring(L, -1); /* filename = '@'..filename */ + lua_pop(L, 1); /* OK: there is no GC during parser */ + luaZ_Fopen(&z, f, filename); + status = protectedparser(L, &z, bin); if (f != stdin) fclose(f); return status; } -int lua_dostring (char *str) { - return lua_dobuffer(str, strlen(str), str); +LUA_API int lua_dofile (lua_State *L, const char *filename) { + int status = parse_file(L, filename); + if (status == 0) /* parse OK? */ + status = lua_call(L, 0, LUA_MULTRET); /* call main */ + return status; } -int lua_dobuffer (char *buff, int size, char *name) { +static int parse_buffer (lua_State *L, const char *buff, size_t size, + const char *name) { ZIO z; if (!name) name = "?"; luaZ_mopen(&z, buff, size, name); - return do_main(&z, buff[0]==ID_CHUNK); + return protectedparser(L, &z, buff[0]==ID_CHUNK); } + +LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name) { + int status = parse_buffer(L, buff, size, name); + if (status == 0) /* parse OK? */ + status = lua_call(L, 0, LUA_MULTRET); /* call main */ + return status; +} + + +LUA_API int lua_dostring (lua_State *L, const char *str) { + return lua_dobuffer(L, str, strlen(str), str); +} + + +/* +** {====================================================== +** Error-recover functions (based on long jumps) +** ======================================================= +*/ + +/* chain list of long jump buffers */ +struct lua_longjmp { + jmp_buf b; + struct lua_longjmp *previous; + volatile int status; /* error code */ +}; + + +static void message (lua_State *L, const char *s) { + const TObject *em = luaH_getglobal(L, LUA_ERRORMESSAGE); + if (ttype(em) == LUA_TFUNCTION) { + *L->top = *em; + incr_top; + lua_pushstring(L, s); + luaD_call(L, L->top-2, 0); + } +} + + +/* +** Reports an error, and jumps up to the available recovery label +*/ +LUA_API void lua_error (lua_State *L, const char *s) { + if (s) message(L, s); + luaD_breakrun(L, LUA_ERRRUN); +} + + +void luaD_breakrun (lua_State *L, int errcode) { + if (L->errorJmp) { + L->errorJmp->status = errcode; + longjmp(L->errorJmp->b, 1); + } + else { + if (errcode != LUA_ERRMEM) + message(L, "unable to recover; exiting\n"); + exit(EXIT_FAILURE); + } +} + + +int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud) { + StkId oldCbase = L->Cbase; + StkId oldtop = L->top; + struct lua_longjmp lj; + int allowhooks = L->allowhooks; + lj.status = 0; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + if (setjmp(lj.b) == 0) + (*f)(L, ud); + else { /* an error occurred: restore the state */ + L->allowhooks = allowhooks; + L->Cbase = oldCbase; + L->top = oldtop; + restore_stack_limit(L); + } + L->errorJmp = lj.previous; /* restore old error handler */ + return lj.status; +} + +/* }====================================================== */ + @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 1.6 1999/06/22 20:37:23 roberto Exp $ +** $Id: ldo.h,v 1.28 2000/10/06 12:45:25 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -12,35 +12,22 @@ #include "lstate.h" -#define MULT_RET 255 - - - /* ** macro to increment stack top. ** There must be always an empty slot at the L->stack.top */ -#define incr_top { if (L->stack.top >= L->stack.last) luaD_checkstack(1); \ - L->stack.top++; } - - -/* macros to convert from lua_Object to (TObject *) and back */ +#define incr_top {if (L->top == L->stack_last) luaD_checkstack(L, 1); L->top++;} -#define Address(lo) ((lo)+L->stack.stack-1) -#define Ref(st) ((st)-L->stack.stack+1) +void luaD_init (lua_State *L, int stacksize); +void luaD_adjusttop (lua_State *L, StkId base, int extra); +void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook); +void luaD_call (lua_State *L, StkId func, int nResults); +void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults); +void luaD_checkstack (lua_State *L, int n); -void luaD_init (void); -void luaD_adjusttop (StkId newtop); -void luaD_openstack (int nelems); -void luaD_lineHook (int line); -void luaD_callHook (StkId base, TProtoFunc *tf, int isreturn); -void luaD_calln (int nArgs, int nResults); -void luaD_callTM (TObject *f, int nParams, int nResults); -int luaD_protectedrun (void); -void luaD_gcIM (TObject *o); -void luaD_travstack (int (*fn)(TObject *)); -void luaD_checkstack (int n); +void luaD_breakrun (lua_State *L, int errcode); +int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud); #endif diff --git a/src/lfunc.c b/src/lfunc.c index 7494e2cd..6841ef71 100644 --- a/src/lfunc.c +++ b/src/lfunc.c @@ -1,5 +1,5 @@ /* -** $Id: lfunc.c,v 1.10 1999/03/04 21:17:26 roberto Exp $ +** $Id: lfunc.c,v 1.34 2000/10/30 12:20:29 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ @@ -7,92 +7,103 @@ #include <stdlib.h> +#include "lua.h" + #include "lfunc.h" #include "lmem.h" #include "lstate.h" -#define gcsizeproto(p) 5 /* approximate "weight" for a prototype */ -#define gcsizeclosure(c) 1 /* approximate "weight" for a closure */ +#define sizeclosure(n) ((int)sizeof(Closure) + (int)sizeof(TObject)*((n)-1)) -Closure *luaF_newclosure (int nelems) -{ - Closure *c = (Closure *)luaM_malloc(sizeof(Closure)+nelems*sizeof(TObject)); - luaO_insertlist(&(L->rootcl), (GCnode *)c); - L->nblocks += gcsizeclosure(c); - c->nelems = nelems; +Closure *luaF_newclosure (lua_State *L, int nelems) { + int size = sizeclosure(nelems); + Closure *c = (Closure *)luaM_malloc(L, size); + c->next = L->rootcl; + L->rootcl = c; + c->mark = c; + c->nupvalues = nelems; + L->nblocks += size; return c; } -TProtoFunc *luaF_newproto (void) -{ - TProtoFunc *f = luaM_new(TProtoFunc); +Proto *luaF_newproto (lua_State *L) { + Proto *f = luaM_new(L, Proto); + f->knum = NULL; + f->nknum = 0; + f->kstr = NULL; + f->nkstr = 0; + f->kproto = NULL; + f->nkproto = 0; f->code = NULL; + f->ncode = 0; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->marked = 0; + f->lineinfo = NULL; + f->nlineinfo = 0; + f->nlocvars = 0; + f->locvars = NULL; f->lineDefined = 0; f->source = NULL; - f->consts = NULL; - f->nconsts = 0; - f->locvars = NULL; - luaO_insertlist(&(L->rootproto), (GCnode *)f); - L->nblocks += gcsizeproto(f); + f->next = L->rootproto; /* chain in list of protos */ + L->rootproto = f; return f; } +static size_t protosize (Proto *f) { + return sizeof(Proto) + + f->nknum*sizeof(Number) + + f->nkstr*sizeof(TString *) + + f->nkproto*sizeof(Proto *) + + f->ncode*sizeof(Instruction) + + f->nlocvars*sizeof(struct LocVar) + + f->nlineinfo*sizeof(int); +} -static void freefunc (TProtoFunc *f) -{ - luaM_free(f->code); - luaM_free(f->locvars); - luaM_free(f->consts); - luaM_free(f); + +void luaF_protook (lua_State *L, Proto *f, int pc) { + f->ncode = pc; /* signal that proto was properly created */ + L->nblocks += protosize(f); } -void luaF_freeproto (TProtoFunc *l) -{ - while (l) { - TProtoFunc *next = (TProtoFunc *)l->head.next; - L->nblocks -= gcsizeproto(l); - freefunc(l); - l = next; - } +void luaF_freeproto (lua_State *L, Proto *f) { + if (f->ncode > 0) /* function was properly created? */ + L->nblocks -= protosize(f); + luaM_free(L, f->code); + luaM_free(L, f->locvars); + luaM_free(L, f->kstr); + luaM_free(L, f->knum); + luaM_free(L, f->kproto); + luaM_free(L, f->lineinfo); + luaM_free(L, f); } -void luaF_freeclosure (Closure *l) -{ - while (l) { - Closure *next = (Closure *)l->head.next; - L->nblocks -= gcsizeclosure(l); - luaM_free(l); - l = next; - } +void luaF_freeclosure (lua_State *L, Closure *c) { + L->nblocks -= sizeclosure(c->nupvalues); + luaM_free(L, c); } /* -** Look for n-th local variable at line "line" in function "func". +** Look for n-th local variable at line `line' in function `func'. ** Returns NULL if not found. */ -char *luaF_getlocalname (TProtoFunc *func, int local_number, int line) -{ - int count = 0; - char *varname = NULL; - LocVar *lv = func->locvars; - if (lv == NULL) - return NULL; - for (; lv->line != -1 && lv->line < line; lv++) { - if (lv->varname) { /* register */ - if (++count == local_number) - varname = lv->varname->str; +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; i<f->nlocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return f->locvars[i].varname->str; } - else /* unregister */ - if (--count < local_number) - varname = NULL; } - return varname; + return NULL; /* not found */ } diff --git a/src/lfunc.h b/src/lfunc.h index cade80a2..32afbc5d 100644 --- a/src/lfunc.h +++ b/src/lfunc.h @@ -1,6 +1,6 @@ /* -** $Id: lfunc.h,v 1.5 1997/12/15 16:17:20 roberto Exp $ -** Lua Function structures +** $Id: lfunc.h,v 1.13 2000/09/29 12:42:13 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ @@ -12,12 +12,13 @@ -TProtoFunc *luaF_newproto (void); -Closure *luaF_newclosure (int nelems); -void luaF_freeproto (TProtoFunc *l); -void luaF_freeclosure (Closure *l); +Proto *luaF_newproto (lua_State *L); +void luaF_protook (lua_State *L, Proto *f, int pc); +Closure *luaF_newclosure (lua_State *L, int nelems); +void luaF_freeproto (lua_State *L, Proto *f); +void luaF_freeclosure (lua_State *L, Closure *c); -char *luaF_getlocalname (TProtoFunc *func, int local_number, int line); +const char *luaF_getlocalname (const Proto *func, int local_number, int pc); #endif @@ -1,9 +1,10 @@ /* -** $Id: lgc.c,v 1.23 1999/03/04 21:17:26 roberto Exp $ +** $Id: lgc.c,v 1.72 2000/10/26 12:47:05 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ +#include "lua.h" #include "ldo.h" #include "lfunc.h" @@ -14,262 +15,339 @@ #include "lstring.h" #include "ltable.h" #include "ltm.h" -#include "lua.h" +typedef struct GCState { + Hash *tmark; /* list of marked tables to be visited */ + Closure *cmark; /* list of marked closures to be visited */ +} GCState; -static int markobject (TObject *o); +static void markobject (GCState *st, TObject *o); -/* -** ======================================================= -** REF mechanism -** ======================================================= -*/ +/* mark a string; marks larger than 1 cannot be changed */ +#define strmark(s) {if ((s)->marked == 0) (s)->marked = 1;} -int luaC_ref (TObject *o, int lock) { - int ref; - if (ttype(o) == LUA_T_NIL) - ref = -1; /* special ref for nil */ - else { - for (ref=0; ref<L->refSize; ref++) - if (L->refArray[ref].status == FREE) - break; - if (ref == L->refSize) { /* no more empty spaces? */ - luaM_growvector(L->refArray, L->refSize, 1, struct ref, refEM, MAX_INT); - L->refSize++; - } - L->refArray[ref].o = *o; - L->refArray[ref].status = lock ? LOCK : HOLD; + + +static void protomark (Proto *f) { + if (!f->marked) { + int i; + f->marked = 1; + strmark(f->source); + for (i=0; i<f->nkstr; i++) + strmark(f->kstr[i]); + for (i=0; i<f->nkproto; i++) + protomark(f->kproto[i]); + for (i=0; i<f->nlocvars; i++) /* mark local-variable names */ + strmark(f->locvars[i].varname); } - return ref; } -void lua_unref (int ref) -{ - if (ref >= 0 && ref < L->refSize) - L->refArray[ref].status = FREE; +static void markstack (lua_State *L, GCState *st) { + StkId o; + for (o=L->stack; o<L->top; o++) + markobject(st, o); } -TObject* luaC_getref (int ref) -{ - if (ref == -1) - return &luaO_nilobject; - if (ref >= 0 && ref < L->refSize && - (L->refArray[ref].status == LOCK || L->refArray[ref].status == HOLD)) - return &L->refArray[ref].o; - else - return NULL; +static void marklock (lua_State *L, GCState *st) { + int i; + for (i=0; i<L->refSize; i++) { + if (L->refArray[i].st == LOCK) + markobject(st, &L->refArray[i].o); + } } -static void travlock (void) -{ - int i; - for (i=0; i<L->refSize; i++) - if (L->refArray[i].status == LOCK) - markobject(&L->refArray[i].o); +static void markclosure (GCState *st, Closure *cl) { + if (!ismarked(cl)) { + if (!cl->isC) + protomark(cl->f.l); + cl->mark = st->cmark; /* chain it for later traversal */ + st->cmark = cl; + } +} + + +static void marktagmethods (lua_State *L, GCState *st) { + int e; + for (e=0; e<TM_N; e++) { + int t; + for (t=0; t<=L->last_tag; t++) { + Closure *cl = luaT_gettm(L, t, e); + if (cl) markclosure(st, cl); + } + } +} + + +static void markobject (GCState *st, TObject *o) { + switch (ttype(o)) { + case LUA_TUSERDATA: case LUA_TSTRING: + strmark(tsvalue(o)); + break; + case LUA_TMARK: + markclosure(st, infovalue(o)->func); + break; + case LUA_TFUNCTION: + markclosure(st, clvalue(o)); + break; + case LUA_TTABLE: { + if (!ismarked(hvalue(o))) { + hvalue(o)->mark = st->tmark; /* chain it in list of marked */ + st->tmark = hvalue(o); + } + break; + } + default: break; /* numbers, etc */ + } +} + + +static void markall (lua_State *L) { + GCState st; + st.cmark = NULL; + st.tmark = L->gt; /* put table of globals in mark list */ + L->gt->mark = NULL; + marktagmethods(L, &st); /* mark tag methods */ + markstack(L, &st); /* mark stack objects */ + marklock(L, &st); /* mark locked objects */ + for (;;) { /* mark tables and closures */ + if (st.cmark) { + int i; + Closure *f = st.cmark; /* get first closure from list */ + st.cmark = f->mark; /* remove it from list */ + for (i=0; i<f->nupvalues; i++) /* mark its upvalues */ + markobject(&st, &f->upvalue[i]); + } + else if (st.tmark) { + int i; + Hash *h = st.tmark; /* get first table from list */ + st.tmark = h->mark; /* remove it from list */ + for (i=0; i<h->size; i++) { + Node *n = node(h, i); + if (ttype(key(n)) != LUA_TNIL) { + if (ttype(val(n)) == LUA_TNIL) + luaH_remove(h, key(n)); /* dead element; try to remove it */ + markobject(&st, &n->key); + markobject(&st, &n->val); + } + } + } + else break; /* nothing else to mark */ + } } -static int ismarked (TObject *o) -{ +static int hasmark (const TObject *o) { /* valid only for locked objects */ switch (o->ttype) { - case LUA_T_STRING: case LUA_T_USERDATA: - return o->value.ts->head.marked; - case LUA_T_ARRAY: - return o->value.a->head.marked; - case LUA_T_CLOSURE: - return o->value.cl->head.marked; - case LUA_T_PROTO: - return o->value.tf->head.marked; -#ifdef DEBUG - case LUA_T_LINE: case LUA_T_CLMARK: - case LUA_T_CMARK: case LUA_T_PMARK: - LUA_INTERNALERROR("invalid type"); -#endif - default: /* nil, number or cproto */ + case LUA_TSTRING: case LUA_TUSERDATA: + return tsvalue(o)->marked; + case LUA_TTABLE: + return ismarked(hvalue(o)); + case LUA_TFUNCTION: + return ismarked(clvalue(o)); + default: /* number */ return 1; } } -static void invalidaterefs (void) -{ +/* macro for internal debugging; check if a link of free refs is valid */ +#define VALIDLINK(L, st,n) (NONEXT <= (st) && (st) < (n)) + +static void invalidaterefs (lua_State *L) { + int n = L->refSize; int i; - for (i=0; i<L->refSize; i++) - if (L->refArray[i].status == HOLD && !ismarked(&L->refArray[i].o)) - L->refArray[i].status = COLLECTED; + for (i=0; i<n; i++) { + struct Ref *r = &L->refArray[i]; + if (r->st == HOLD && !hasmark(&r->o)) + r->st = COLLECTED; + LUA_ASSERT((r->st == LOCK && hasmark(&r->o)) || + (r->st == HOLD && hasmark(&r->o)) || + r->st == COLLECTED || + r->st == NONEXT || + (r->st < n && VALIDLINK(L, L->refArray[r->st].st, n)), + "inconsistent ref table"); + } + LUA_ASSERT(VALIDLINK(L, L->refFree, n), "inconsistent ref table"); } -void luaC_hashcallIM (Hash *l) -{ - TObject t; - ttype(&t) = LUA_T_ARRAY; - for (; l; l=(Hash *)l->head.next) { - avalue(&t) = l; - luaD_gcIM(&t); +static void collectproto (lua_State *L) { + Proto **p = &L->rootproto; + Proto *next; + while ((next = *p) != NULL) { + if (next->marked) { + next->marked = 0; + p = &next->next; + } + else { + *p = next->next; + luaF_freeproto(L, next); + } } } -void luaC_strcallIM (TaggedString *l) -{ - TObject o; - ttype(&o) = LUA_T_USERDATA; - for (; l; l=(TaggedString *)l->head.next) - if (l->constindex == -1) { /* is userdata? */ - tsvalue(&o) = l; - luaD_gcIM(&o); +static void collectclosure (lua_State *L) { + Closure **p = &L->rootcl; + Closure *next; + while ((next = *p) != NULL) { + if (ismarked(next)) { + next->mark = next; /* unmark */ + p = &next->next; + } + else { + *p = next->next; + luaF_freeclosure(L, next); } + } } - -static GCnode *listcollect (GCnode *l) -{ - GCnode *frees = NULL; - while (l) { - GCnode *next = l->next; - l->marked = 0; - while (next && !next->marked) { - l->next = next->next; - next->next = frees; - frees = next; - next = l->next; +static void collecttable (lua_State *L) { + Hash **p = &L->roottable; + Hash *next; + while ((next = *p) != NULL) { + if (ismarked(next)) { + next->mark = next; /* unmark */ + p = &next->next; + } + else { + *p = next->next; + luaH_free(L, next); } - l = next; } - return frees; } -static void strmark (TaggedString *s) -{ - if (!s->head.marked) - s->head.marked = 1; +static void checktab (lua_State *L, stringtable *tb) { + if (tb->nuse < (lint32)(tb->size/4) && tb->size > 10) + luaS_resize(L, tb, tb->size/2); /* table is too big */ } -static void protomark (TProtoFunc *f) { - if (!f->head.marked) { - int i; - f->head.marked = 1; - strmark(f->source); - for (i=0; i<f->nconsts; i++) - markobject(&f->consts[i]); +static void collectstrings (lua_State *L, int all) { + int i; + for (i=0; i<L->strt.size; i++) { /* for each list */ + TString **p = &L->strt.hash[i]; + TString *next; + while ((next = *p) != NULL) { + if (next->marked && !all) { /* preserve? */ + if (next->marked < FIXMARK) /* does not change FIXMARKs */ + next->marked = 0; + p = &next->nexthash; + } + else { /* collect */ + *p = next->nexthash; + L->strt.nuse--; + L->nblocks -= sizestring(next->len); + luaM_free(L, next); + } + } } + checktab(L, &L->strt); } -static void closuremark (Closure *f) -{ - if (!f->head.marked) { - int i; - f->head.marked = 1; - for (i=f->nelems; i>=0; i--) - markobject(&f->consts[i]); +static void collectudata (lua_State *L, int all) { + int i; + for (i=0; i<L->udt.size; i++) { /* for each list */ + TString **p = &L->udt.hash[i]; + TString *next; + while ((next = *p) != NULL) { + LUA_ASSERT(next->marked <= 1, "udata cannot be fixed"); + if (next->marked && !all) { /* preserve? */ + next->marked = 0; + p = &next->nexthash; + } + else { /* collect */ + int tag = next->u.d.tag; + *p = next->nexthash; + next->nexthash = L->TMtable[tag].collected; /* chain udata */ + L->TMtable[tag].collected = next; + L->nblocks -= sizestring(next->len); + L->udt.nuse--; + } + } } + checktab(L, &L->udt); } -static void hashmark (Hash *h) -{ - if (!h->head.marked) { - int i; - h->head.marked = 1; - for (i=0; i<nhash(h); i++) { - Node *n = node(h,i); - if (ttype(ref(n)) != LUA_T_NIL) { - markobject(&n->ref); - markobject(&n->val); - } - } +#define MINBUFFER 256 +static void checkMbuffer (lua_State *L) { + if (L->Mbuffsize > MINBUFFER*2) { /* is buffer too big? */ + size_t newsize = L->Mbuffsize/2; /* still larger than MINBUFFER */ + L->nblocks += (newsize - L->Mbuffsize)*sizeof(char); + L->Mbuffsize = newsize; + luaM_reallocvector(L, L->Mbuffer, newsize, char); } } -static void globalmark (void) -{ - TaggedString *g; - for (g=(TaggedString *)L->rootglobal.next; g; g=(TaggedString *)g->head.next){ - LUA_ASSERT(g->constindex >= 0, "userdata in global list"); - if (g->u.s.globalval.ttype != LUA_T_NIL) { - markobject(&g->u.s.globalval); - strmark(g); /* cannot collect non nil global variables */ - } +static void callgcTM (lua_State *L, const TObject *o) { + Closure *tm = luaT_gettmbyObj(L, o, TM_GC); + if (tm != NULL) { + int oldah = L->allowhooks; + L->allowhooks = 0; /* stop debug hooks during GC tag methods */ + luaD_checkstack(L, 2); + clvalue(L->top) = tm; + ttype(L->top) = LUA_TFUNCTION; + *(L->top+1) = *o; + L->top += 2; + luaD_call(L, L->top-2, 0); + L->allowhooks = oldah; /* restore hooks */ } } -static int markobject (TObject *o) -{ - switch (ttype(o)) { - case LUA_T_USERDATA: case LUA_T_STRING: - strmark(tsvalue(o)); - break; - case LUA_T_ARRAY: - hashmark(avalue(o)); - break; - case LUA_T_CLOSURE: case LUA_T_CLMARK: - closuremark(o->value.cl); - break; - case LUA_T_PROTO: case LUA_T_PMARK: - protomark(o->value.tf); - break; - default: break; /* numbers, cprotos, etc */ +static void callgcTMudata (lua_State *L) { + int tag; + TObject o; + ttype(&o) = LUA_TUSERDATA; + L->GCthreshold = 2*L->nblocks; /* avoid GC during tag methods */ + for (tag=L->last_tag; tag>=0; tag--) { /* for each tag (in reverse order) */ + TString *udata; + while ((udata = L->TMtable[tag].collected) != NULL) { + L->TMtable[tag].collected = udata->nexthash; /* remove it from list */ + tsvalue(&o) = udata; + callgcTM(L, &o); + luaM_free(L, udata); + } } - return 0; } - -static void markall (void) -{ - luaD_travstack(markobject); /* mark stack objects */ - globalmark(); /* mark global variable values and names */ - travlock(); /* mark locked objects */ - luaT_travtagmethods(markobject); /* mark fallbacks */ +void luaC_collect (lua_State *L, int all) { + collectudata(L, all); + callgcTMudata(L); + collectstrings(L, all); + collecttable(L); + collectproto(L); + collectclosure(L); } -long lua_collectgarbage (long limit) -{ - unsigned long recovered = L->nblocks; /* to subtract nblocks after gc */ - Hash *freetable; - TaggedString *freestr; - TProtoFunc *freefunc; - Closure *freeclos; - markall(); - invalidaterefs(); - freestr = luaS_collector(); - freetable = (Hash *)listcollect(&(L->roottable)); - freefunc = (TProtoFunc *)listcollect(&(L->rootproto)); - freeclos = (Closure *)listcollect(&(L->rootcl)); - L->GCthreshold *= 4; /* to avoid GC during GC */ - luaC_hashcallIM(freetable); /* GC tag methods for tables */ - luaC_strcallIM(freestr); /* GC tag methods for userdata */ - luaD_gcIM(&luaO_nilobject); /* GC tag method for nil (signal end of GC) */ - luaH_free(freetable); - luaS_free(freestr); - luaF_freeproto(freefunc); - luaF_freeclosure(freeclos); - recovered = recovered-L->nblocks; - L->GCthreshold = (limit == 0) ? 2*L->nblocks : L->nblocks+limit; - return recovered; +static void luaC_collectgarbage (lua_State *L) { + markall(L); + invalidaterefs(L); /* check unlocked references */ + luaC_collect(L, 0); + checkMbuffer(L); + L->GCthreshold = 2*L->nblocks; /* set new threshold */ + callgcTM(L, &luaO_nilobject); } -void luaC_checkGC (void) -{ +void luaC_checkGC (lua_State *L) { if (L->nblocks >= L->GCthreshold) - lua_collectgarbage(0); + luaC_collectgarbage(L); } @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 1.4 1997/12/01 20:31:25 roberto Exp $ +** $Id: lgc.h,v 1.8 2000/10/02 14:47:43 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -11,11 +11,8 @@ #include "lobject.h" -void luaC_checkGC (void); -TObject* luaC_getref (int ref); -int luaC_ref (TObject *o, int lock); -void luaC_hashcallIM (Hash *l); -void luaC_strcallIM (TaggedString *l); +void luaC_collect (lua_State *L, int all); +void luaC_checkGC (lua_State *L); #endif diff --git a/src/lib/Makefile b/src/lib/Makefile index 5d8664c9..081b8867 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -1,4 +1,4 @@ -# makefile for lua standard library +# makefile for Lua standard library LUA= ../.. @@ -7,8 +7,8 @@ include $(LUA)/config # actually only used in liolib.c EXTRA_DEFS= $(POPEN) -OBJS= linit.o ldblib.o liolib.o lmathlib.o lstrlib.o -SRCS= linit.c ldblib.c liolib.c lmathlib.c lstrlib.c +OBJS= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o lstrlib.o +SRCS= lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c lstrlib.c T= $(LIB)/liblualib.a diff --git a/src/lib/README b/src/lib/README index e8e599c8..c04a12e2 100644 --- a/src/lib/README +++ b/src/lib/README @@ -1,4 +1,6 @@ This is the standard Lua library. It is implemented entirely on top of the official Lua API as declared in lua.h, -using src/lauxlib.c, which contains several useful functions. -The code can be read as an example of how to export C functions to Lua. +using lauxlib.c, which contains several useful functions for writing libraries. +We encourage developers to use lauxlib.c in their own libraries. +The code of the standard library can be read as an example of how to export +C functions to Lua. diff --git a/src/lib/lauxlib.c b/src/lib/lauxlib.c new file mode 100644 index 00000000..4bdaeeff --- /dev/null +++ b/src/lib/lauxlib.c @@ -0,0 +1,216 @@ +/* +** $Id: lauxlib.c,v 1.43 2000/10/30 13:07:48 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +** With care, these functions can be used by other libraries. +*/ + +#include "lua.h" + +#include "lauxlib.h" +#include "luadebug.h" + + + +LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { + int i; + for (i=0; list[i]; i++) + if (strcmp(list[i], name) == 0) + return i; + return -1; /* name not found */ +} + +LUALIB_API void luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + lua_getstack(L, 0, &ar); + lua_getinfo(L, "n", &ar); + if (ar.name == NULL) + ar.name = "?"; + luaL_verror(L, "bad argument #%d to `%.50s' (%.100s)", + narg, ar.name, extramsg); +} + + +static void type_error (lua_State *L, int narg, int t) { + char buff[50]; + sprintf(buff, "%.8s expected, got %.8s", lua_typename(L, t), + lua_typename(L, lua_type(L, narg))); + luaL_argerror(L, narg, buff); +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (space > lua_stackspace(L)) + luaL_verror(L, "stack overflow (%.30s)", mes); +} + + +LUALIB_API void luaL_checktype(lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + type_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_check_lstr (lua_State *L, int narg, size_t *len) { + const char *s = lua_tostring(L, narg); + if (!s) type_error(L, narg, LUA_TSTRING); + if (len) *len = lua_strlen(L, narg); + return s; +} + + +LUALIB_API const char *luaL_opt_lstr (lua_State *L, int narg, const char *def, size_t *len) { + if (lua_isnull(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_check_lstr(L, narg, len); +} + + +LUALIB_API double luaL_check_number (lua_State *L, int narg) { + double d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + type_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API double luaL_opt_number (lua_State *L, int narg, double def) { + if (lua_isnull(L, narg)) return def; + else return luaL_check_number(L, narg); +} + + +LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n) { + int i; + for (i=0; i<n; i++) + lua_register(L, l[i].name, l[i].func); +} + + +LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...) { + char buff[500]; + va_list argp; + va_start(argp, fmt); + vsprintf(buff, fmt, argp); + va_end(argp); + lua_error(L, buff); +} + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define buffempty(B) ((B)->p == (B)->buffer) +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->level++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->level > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->level - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->level); + if (toget >= 2) { + lua_concat(L, toget); + B->level = B->level - toget + 1; + } + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_putchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + if (B->level == 0) + lua_pushlstring(B->L, NULL, 0); + else if (B->level > 1) + lua_concat(B->L, B->level); + B->level = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl = lua_strlen(L, -1); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->level++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->level = 0; +} + +/* }====================================================== */ diff --git a/src/lib/lbaselib.c b/src/lib/lbaselib.c new file mode 100644 index 00000000..1ff475f9 --- /dev/null +++ b/src/lib/lbaselib.c @@ -0,0 +1,651 @@ +/* +** $Id: lbaselib.c,v 1.17 2000/11/06 13:45:18 roberto Exp $ +** Basic library +** See Copyright Notice in lua.h +*/ + + + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "luadebug.h" +#include "lualib.h" + + + +/* +** If your system does not support `stderr', redefine this function, or +** redefine _ERRORMESSAGE so that it won't need _ALERT. +*/ +static int luaB__ALERT (lua_State *L) { + fputs(luaL_check_string(L, 1), stderr); + return 0; +} + + +/* +** Basic implementation of _ERRORMESSAGE. +** The library `liolib' redefines _ERRORMESSAGE for better error information. +*/ +static int luaB__ERRORMESSAGE (lua_State *L) { + luaL_checktype(L, 1, LUA_TSTRING); + lua_getglobal(L, LUA_ALERT); + if (lua_isfunction(L, -1)) { /* avoid error loop if _ALERT is not defined */ + lua_Debug ar; + lua_pushstring(L, "error: "); + lua_pushvalue(L, 1); + if (lua_getstack(L, 1, &ar)) { + lua_getinfo(L, "Sl", &ar); + if (ar.source && ar.currentline > 0) { + char buff[100]; + sprintf(buff, "\n <%.70s: line %d>", ar.short_src, ar.currentline); + lua_pushstring(L, buff); + lua_concat(L, 2); + } + } + lua_pushstring(L, "\n"); + lua_concat(L, 3); + lua_rawcall(L, 1, 0); + } + return 0; +} + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_rawcall(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + lua_error(L, "`tostring' must return a string to `print'"); + if (i>1) fputs("\t", stdout); + fputs(s, stdout); + lua_pop(L, 1); /* pop result */ + } + fputs("\n", stdout); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_opt_int(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_check_string(L, 1); + char *s2; + unsigned long n; + luaL_arg_check(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)*s2)) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + lua_error(L, luaL_opt_string(L, 1, NULL)); + return 0; /* to avoid warnings */ +} + +static int luaB_setglobal (lua_State *L) { + luaL_checkany(L, 2); + lua_setglobal(L, luaL_check_string(L, 1)); + return 0; +} + +static int luaB_getglobal (lua_State *L) { + lua_getglobal(L, luaL_check_string(L, 1)); + return 1; +} + +static int luaB_tag (lua_State *L) { + luaL_checkany(L, 1); + lua_pushnumber(L, lua_tag(L, 1)); + return 1; +} + +static int luaB_settag (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, 1); /* push table */ + lua_settag(L, luaL_check_int(L, 2)); + return 1; /* return table */ +} + +static int luaB_newtag (lua_State *L) { + lua_pushnumber(L, lua_newtag(L)); + return 1; +} + +static int luaB_copytagmethods (lua_State *L) { + lua_pushnumber(L, lua_copytagmethods(L, luaL_check_int(L, 1), + luaL_check_int(L, 2))); + return 1; +} + +static int luaB_globals (lua_State *L) { + lua_getglobals(L); /* value to be returned */ + if (!lua_isnull(L, 1)) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, 1); /* new table of globals */ + lua_setglobals(L); + } + return 1; +} + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_rawget(L, -2); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_rawset(L, -3); + return 1; +} + +static int luaB_settagmethod (lua_State *L) { + int tag = luaL_check_int(L, 1); + const char *event = luaL_check_string(L, 2); + luaL_arg_check(L, lua_isfunction(L, 3) || lua_isnil(L, 3), 3, + "function or nil expected"); + if (strcmp(event, "gc") == 0) + lua_error(L, "deprecated use: cannot set the `gc' tag method from Lua"); + lua_gettagmethod(L, tag, event); + lua_pushvalue(L, 3); + lua_settagmethod(L, tag, event); + return 1; +} + + +static int luaB_gettagmethod (lua_State *L) { + int tag = luaL_check_int(L, 1); + const char *event = luaL_check_string(L, 2); + if (strcmp(event, "gc") == 0) + lua_error(L, "deprecated use: cannot get the `gc' tag method from Lua"); + lua_gettagmethod(L, tag, event); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushnumber(L, lua_getgccount(L)); + lua_pushnumber(L, lua_getgcthreshold(L)); + return 2; +} + + +static int luaB_collectgarbage (lua_State *L) { + lua_setgcthreshold(L, luaL_opt_int(L, 1, 0)); + return 0; +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int passresults (lua_State *L, int status, int oldtop) { + static const char *const errornames[] = + {"ok", "run-time error", "file error", "syntax error", + "memory error", "error in error handling"}; + if (status == 0) { + int nresults = lua_gettop(L) - oldtop; + if (nresults > 0) + return nresults; /* results are already on the stack */ + else { + lua_pushuserdata(L, NULL); /* at least one result to signal no errors */ + return 1; + } + } + else { /* error */ + lua_pushnil(L); + lua_pushstring(L, errornames[status]); /* error code */ + return 2; + } +} + +static int luaB_dostring (lua_State *L) { + int oldtop = lua_gettop(L); + size_t l; + const char *s = luaL_check_lstr(L, 1, &l); + if (*s == '\27') /* binary files start with ESC... */ + lua_error(L, "`dostring' cannot run pre-compiled code"); + return passresults(L, lua_dobuffer(L, s, l, luaL_opt_string(L, 2, s)), oldtop); +} + + +static int luaB_dofile (lua_State *L) { + int oldtop = lua_gettop(L); + const char *fname = luaL_opt_string(L, 1, NULL); + return passresults(L, lua_dofile(L, fname), oldtop); +} + + +static int luaB_call (lua_State *L) { + int oldtop; + const char *options = luaL_opt_string(L, 3, ""); + int err = 0; /* index of old error method */ + int i, status; + int n; + luaL_checktype(L, 2, LUA_TTABLE); + n = lua_getn(L, 2); + if (!lua_isnull(L, 4)) { /* set new error method */ + lua_getglobal(L, LUA_ERRORMESSAGE); + err = lua_gettop(L); /* get index */ + lua_pushvalue(L, 4); + lua_setglobal(L, LUA_ERRORMESSAGE); + } + oldtop = lua_gettop(L); /* top before function-call preparation */ + /* push function */ + lua_pushvalue(L, 1); + luaL_checkstack(L, n, "too many arguments"); + for (i=0; i<n; i++) /* push arg[1...n] */ + lua_rawgeti(L, 2, i+1); + status = lua_call(L, n, LUA_MULTRET); + if (err != 0) { /* restore old error method */ + lua_pushvalue(L, err); + lua_setglobal(L, LUA_ERRORMESSAGE); + } + if (status != 0) { /* error in call? */ + if (strchr(options, 'x')) + lua_pushnil(L); /* return nil to signal the error */ + else + lua_error(L, NULL); /* propagate error without additional messages */ + return 1; + } + if (strchr(options, 'p')) /* pack results? */ + lua_error(L, "deprecated option `p' in `call'"); + return lua_gettop(L) - oldtop; /* results are already on the stack */ +} + + +static int luaB_tostring (lua_State *L) { + char buff[64]; + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + return 1; + case LUA_TSTRING: + lua_pushvalue(L, 1); + return 1; + case LUA_TTABLE: + sprintf(buff, "table: %p", lua_topointer(L, 1)); + break; + case LUA_TFUNCTION: + sprintf(buff, "function: %p", lua_topointer(L, 1)); + break; + case LUA_TUSERDATA: + sprintf(buff, "userdata(%d): %p", lua_tag(L, 1), lua_touserdata(L, 1)); + break; + case LUA_TNIL: + lua_pushstring(L, "nil"); + return 1; + default: + luaL_argerror(L, 1, "value expected"); + } + lua_pushstring(L, buff); + return 1; +} + + +static int luaB_foreachi (lua_State *L) { + int n, i; + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + n = lua_getn(L, 1); + for (i=1; i<=n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushnumber(L, i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_rawcall(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int luaB_foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first index */ + for (;;) { + if (lua_next(L, 1) == 0) + return 0; + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_rawcall(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (lua_isnil(L, 1)) + luaL_verror(L, "assertion failed! %.90s", luaL_opt_string(L, 2, "")); + return 0; +} + + +static int luaB_getn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnumber(L, lua_getn(L, 1)); + return 1; +} + + +static int luaB_tinsert (lua_State *L) { + int v = lua_gettop(L); /* last argument: to be inserted */ + int n, pos; + luaL_checktype(L, 1, LUA_TTABLE); + n = lua_getn(L, 1); + if (v == 2) /* called with only 2 arguments */ + pos = n+1; + else + pos = luaL_check_int(L, 2); /* 2nd argument is the position */ + lua_pushstring(L, "n"); + lua_pushnumber(L, n+1); + lua_rawset(L, 1); /* t.n = n+1 */ + for (; n>=pos; n--) { + lua_rawgeti(L, 1, n); + lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ + } + lua_pushvalue(L, v); + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int luaB_tremove (lua_State *L) { + int pos, n; + luaL_checktype(L, 1, LUA_TTABLE); + n = lua_getn(L, 1); + pos = luaL_opt_int(L, 2, n); + if (n <= 0) return 0; /* table is "empty" */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos<n; pos++) { + lua_rawgeti(L, 1, pos+1); + lua_rawseti(L, 1, pos); /* a[pos] = a[pos+1] */ + } + lua_pushstring(L, "n"); + lua_pushnumber(L, n-1); + lua_rawset(L, 1); /* t.n = n-1 */ + lua_pushnil(L); + lua_rawseti(L, 1, n); /* t[n] = nil */ + return 1; +} + + + + +/* +** {====================================================== +** Quicksort +** (based on `Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +*/ + + +static void set2 (lua_State *L, int i, int j) { + lua_rawseti(L, 1, i); + lua_rawseti(L, 1, j); +} + +static int sort_comp (lua_State *L, int a, int b) { + /* WARNING: the caller (auxsort) must ensure stack space */ + if (!lua_isnil(L, 2)) { /* function? */ + int res; + lua_pushvalue(L, 2); + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */ + lua_rawcall(L, 2, 1); + res = !lua_isnil(L, -1); + lua_pop(L, 1); + return res; + } + else /* a < b? */ + return lua_lessthan(L, a, b); +} + +static void auxsort (lua_State *L, int l, int u) { + while (l < u) { /* for tail recursion */ + int i, j; + /* sort elements a[l], a[(l+u)/2] and a[u] */ + lua_rawgeti(L, 1, l); + lua_rawgeti(L, 1, u); + if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ + set2(L, l, u); /* swap a[l] - a[u] */ + else + lua_pop(L, 2); + if (u-l == 1) break; /* only 2 elements */ + i = (l+u)/2; + lua_rawgeti(L, 1, i); + lua_rawgeti(L, 1, l); + if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */ + set2(L, i, l); + else { + lua_pop(L, 1); /* remove a[l] */ + lua_rawgeti(L, 1, u); + if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */ + set2(L, i, u); + else + lua_pop(L, 2); + } + if (u-l == 2) break; /* only 3 elements */ + lua_rawgeti(L, 1, i); /* Pivot */ + lua_pushvalue(L, -1); + lua_rawgeti(L, 1, u-1); + set2(L, i, u-1); + /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ + i = l; j = u-1; + for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */ + /* repeat ++i until a[i] >= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) lua_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j<l) lua_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + if (j<i) { + lua_pop(L, 3); /* pop pivot, a[i], a[j] */ + break; + } + set2(L, i, j); + } + lua_rawgeti(L, 1, u-1); + lua_rawgeti(L, 1, i); + set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */ + /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ + /* adjust so that smaller "half" is in [j..i] and larger one in [l..u] */ + if (i-l < u-i) { + j=l; i=i-1; l=i+2; + } + else { + j=i+1; i=u; u=j-2; + } + auxsort(L, j, i); /* call recursively the smaller one */ + } /* repeat the routine for the larger one */ +} + +static int luaB_sort (lua_State *L) { + int n; + luaL_checktype(L, 1, LUA_TTABLE); + n = lua_getn(L, 1); + if (!lua_isnull(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_settop(L, 2); /* make sure there is two arguments */ + auxsort(L, 1, n); + return 0; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Deprecated functions to manipulate global environment. +** ======================================================= +*/ + + +#define num_deprecated 4 + +static const struct luaL_reg deprecated_names [num_deprecated] = { + {"foreachvar", luaB_foreach}, + {"nextvar", luaB_next}, + {"rawgetglobal", luaB_rawget}, + {"rawsetglobal", luaB_rawset} +}; + + +#ifdef LUA_DEPRECATEDFUNCS + +/* +** call corresponding function inserting `globals' as first argument +*/ +static int deprecated_func (lua_State *L) { + lua_insert(L, 1); /* upvalue is the function to be called */ + lua_getglobals(L); + lua_insert(L, 2); /* table of globals is 1o argument */ + lua_rawcall(L, lua_gettop(L)-1, LUA_MULTRET); + return lua_gettop(L); /* return all results */ +} + + +static void deprecated_funcs (lua_State *L) { + int i; + for (i=0; i<num_deprecated; i++) { + lua_pushcfunction(L, deprecated_names[i].func); + lua_pushcclosure(L, deprecated_func, 1); + lua_setglobal(L, deprecated_names[i].name); + } +} + + +#else + +/* +** gives an explicit error in any attempt to call a deprecated function +*/ +static int deprecated_func (lua_State *L) { + luaL_verror(L, "function `%.20s' is deprecated", lua_tostring(L, -1)); + return 0; /* to avoid warnings */ +} + + +static void deprecated_funcs (lua_State *L) { + int i; + for (i=0; i<num_deprecated; i++) { + lua_pushstring(L, deprecated_names[i].name); + lua_pushcclosure(L, deprecated_func, 1); + lua_setglobal(L, deprecated_names[i].name); + } +} + +#endif + +/* }====================================================== */ + +static const struct luaL_reg base_funcs[] = { + {LUA_ALERT, luaB__ALERT}, + {LUA_ERRORMESSAGE, luaB__ERRORMESSAGE}, + {"call", luaB_call}, + {"collectgarbage", luaB_collectgarbage}, + {"copytagmethods", luaB_copytagmethods}, + {"dofile", luaB_dofile}, + {"dostring", luaB_dostring}, + {"error", luaB_error}, + {"foreach", luaB_foreach}, + {"foreachi", luaB_foreachi}, + {"gcinfo", luaB_gcinfo}, + {"getglobal", luaB_getglobal}, + {"gettagmethod", luaB_gettagmethod}, + {"globals", luaB_globals}, + {"newtag", luaB_newtag}, + {"next", luaB_next}, + {"print", luaB_print}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"rawgettable", luaB_rawget}, /* for compatibility */ + {"rawsettable", luaB_rawset}, /* for compatibility */ + {"setglobal", luaB_setglobal}, + {"settag", luaB_settag}, + {"settagmethod", luaB_settagmethod}, + {"tag", luaB_tag}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"assert", luaB_assert}, + {"getn", luaB_getn}, + {"sort", luaB_sort}, + {"tinsert", luaB_tinsert}, + {"tremove", luaB_tremove} +}; + + + +LUALIB_API void lua_baselibopen (lua_State *L) { + luaL_openl(L, base_funcs); + lua_pushstring(L, LUA_VERSION); + lua_setglobal(L, "_VERSION"); + deprecated_funcs(L); +} + diff --git a/src/lib/ldblib.c b/src/lib/ldblib.c index 388a2f2d..636dbe05 100644 --- a/src/lib/ldblib.c +++ b/src/lib/ldblib.c @@ -1,217 +1,188 @@ /* -** $Id: ldblib.c,v 1.5 1999/03/04 21:17:26 roberto Exp $ +** $Id: ldblib.c,v 1.29 2000/11/06 17:58:38 roberto Exp $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include "lauxlib.h" #include "lua.h" + +#include "lauxlib.h" #include "luadebug.h" #include "lualib.h" -static void settabss (lua_Object t, char *i, char *v) { - lua_pushobject(t); - lua_pushstring(i); - lua_pushstring(v); - lua_settable(); +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, i); + lua_pushstring(L, v); + lua_settable(L, -3); } -static void settabsi (lua_Object t, char *i, int v) { - lua_pushobject(t); - lua_pushstring(i); - lua_pushnumber(v); - lua_settable(); +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushstring(L, i); + lua_pushnumber(L, v); + lua_settable(L, -3); } -static lua_Object getfuncinfo (lua_Object func) { - lua_Object result = lua_createtable(); - char *str; - int line; - lua_funcinfo(func, &str, &line); - if (line == -1) /* C function? */ - settabss(result, "kind", "C"); - else if (line == 0) { /* "main"? */ - settabss(result, "kind", "chunk"); - settabss(result, "source", str); +static int getinfo (lua_State *L) { + lua_Debug ar; + const char *options = luaL_opt_string(L, 2, "flnSu"); + char buff[20]; + if (lua_isnumber(L, 1)) { + if (!lua_getstack(L, (int)lua_tonumber(L, 1), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; } - else { /* Lua function */ - settabss(result, "kind", "Lua"); - settabsi(result, "def_line", line); - settabss(result, "source", str); } - if (line != 0) { /* is it not a "main"? */ - char *kind = lua_getobjname(func, &str); - if (*kind) { - settabss(result, "name", str); - settabss(result, "where", kind); + else if (lua_isfunction(L, 1)) { + lua_pushvalue(L, 1); + sprintf(buff, ">%.10s", options); + options = buff; + } + else + luaL_argerror(L, 1, "function or level expected"); + if (!lua_getinfo(L, options, &ar)) + luaL_argerror(L, 2, "invalid option"); + lua_newtable(L); + for (; *options; options++) { + switch (*options) { + case 'S': + settabss(L, "source", ar.source); + if (ar.source) + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabss(L, "what", ar.what); + break; + case 'l': + settabsi(L, "currentline", ar.currentline); + break; + case 'u': + settabsi(L, "nups", ar.nups); + break; + case 'n': + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + break; + case 'f': + lua_pushstring(L, "func"); + lua_pushvalue(L, -3); + lua_settable(L, -3); + break; } } - return result; + return 1; /* return table */ } - - -static void getstack (void) { - lua_Object func = lua_stackedfunction(luaL_check_int(1)); - if (func == LUA_NOOBJECT) /* level out of range? */ - return; - else { - lua_Object result = getfuncinfo(func); - int currline = lua_currentline(func); - if (currline > 0) - settabsi(result, "current", currline); - lua_pushobject(result); - lua_pushstring("func"); - lua_pushobject(func); - lua_settable(); /* result.func = func */ - lua_pushobject(result); + + +static int getlocal (lua_State *L) { + lua_Debug ar; + const char *name; + if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */ + luaL_argerror(L, 1, "level out of range"); + name = lua_getlocal(L, &ar, luaL_check_int(L, 2)); + if (name) { + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; } -} - - -static void funcinfo (void) { - lua_pushobject(getfuncinfo(luaL_functionarg(1))); -} - - -static int findlocal (lua_Object func, int arg) { - lua_Object v = lua_getparam(arg); - if (lua_isnumber(v)) - return (int)lua_getnumber(v); else { - char *name = luaL_check_string(arg); - int i = 0; - int result = -1; - char *vname; - while (lua_getlocal(func, ++i, &vname) != LUA_NOOBJECT) { - if (strcmp(name, vname) == 0) - result = i; /* keep looping to get the last var with this name */ - } - if (result == -1) - luaL_verror("no local variable `%.50s' at given level", name); - return result; + lua_pushnil(L); + return 1; } } -static void getlocal (void) { - lua_Object func = lua_stackedfunction(luaL_check_int(1)); - lua_Object val; - char *name; - if (func == LUA_NOOBJECT) /* level out of range? */ - return; /* return nil */ - else if (lua_getparam(2) != LUA_NOOBJECT) { /* 2nd argument? */ - if ((val = lua_getlocal(func, findlocal(func, 2), &name)) != LUA_NOOBJECT) { - lua_pushobject(val); - lua_pushstring(name); - } - /* else return nil */ - } - else { /* collect all locals in a table */ - lua_Object result = lua_createtable(); - int i; - for (i=1; ;i++) { - if ((val = lua_getlocal(func, i, &name)) == LUA_NOOBJECT) - break; - lua_pushobject(result); - lua_pushstring(name); - lua_pushobject(val); - lua_settable(); /* result[name] = value */ - } - lua_pushobject(result); - } +static int setlocal (lua_State *L) { + lua_Debug ar; + if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */ + luaL_argerror(L, 1, "level out of range"); + luaL_checkany(L, 3); + lua_pushstring(L, lua_setlocal(L, &ar, luaL_check_int(L, 2))); + return 1; } -static void setlocal (void) { - lua_Object func = lua_stackedfunction(luaL_check_int(1)); - int numvar; - luaL_arg_check(func != LUA_NOOBJECT, 1, "level out of range"); - numvar = findlocal(func, 2); - lua_pushobject(luaL_nonnullarg(3)); - if (!lua_setlocal(func, numvar)) - lua_error("no such local variable"); -} +/* dummy variables (to define unique addresses) */ +static char key1, key2; +#define KEY_CALLHOOK (&key1) +#define KEY_LINEHOOK (&key2) -static int linehook = -1; /* Lua reference to line hook function */ -static int callhook = -1; /* Lua reference to call hook function */ +static void hookf (lua_State *L, void *key) { + lua_getregistry(L); + lua_pushuserdata(L, key); + lua_gettable(L, -2); + if (lua_isfunction(L, -1)) { + lua_pushvalue(L, 1); + lua_rawcall(L, 1, 0); + } + else + lua_pop(L, 1); /* pop result from gettable */ + lua_pop(L, 1); /* pop table */ +} -static void dohook (int ref) { - lua_LHFunction oldlinehook = lua_setlinehook(NULL); - lua_CHFunction oldcallhook = lua_setcallhook(NULL); - lua_callfunction(lua_getref(ref)); - lua_setlinehook(oldlinehook); - lua_setcallhook(oldcallhook); +static void callf (lua_State *L, lua_Debug *ar) { + lua_pushstring(L, ar->event); + hookf(L, KEY_CALLHOOK); } -static void linef (int line) { - lua_pushnumber(line); - dohook(linehook); +static void linef (lua_State *L, lua_Debug *ar) { + lua_pushnumber(L, ar->currentline); + hookf(L, KEY_LINEHOOK); } -static void callf (lua_Function func, char *file, int line) { - if (func != LUA_NOOBJECT) { - lua_pushobject(func); - lua_pushstring(file); - lua_pushnumber(line); - } - dohook(callhook); +static void sethook (lua_State *L, void *key, lua_Hook hook, + lua_Hook (*sethookf)(lua_State * L, lua_Hook h)) { + lua_settop(L, 1); + if (lua_isnil(L, 1)) + (*sethookf)(L, NULL); + else if (lua_isfunction(L, 1)) + (*sethookf)(L, hook); + else + luaL_argerror(L, 1, "function expected"); + lua_getregistry(L); + lua_pushuserdata(L, key); + lua_pushvalue(L, -1); /* dup key */ + lua_gettable(L, -3); /* get old value */ + lua_pushvalue(L, -2); /* key (again) */ + lua_pushvalue(L, 1); + lua_settable(L, -5); /* set new value */ } -static void setcallhook (void) { - lua_Object f = lua_getparam(1); - lua_unref(callhook); - if (f == LUA_NOOBJECT) { - callhook = -1; - lua_setcallhook(NULL); - } - else { - lua_pushobject(f); - callhook = lua_ref(1); - lua_setcallhook(callf); - } +static int setcallhook (lua_State *L) { + sethook(L, KEY_CALLHOOK, callf, lua_setcallhook); + return 1; } -static void setlinehook (void) { - lua_Object f = lua_getparam(1); - lua_unref(linehook); - if (f == LUA_NOOBJECT) { - linehook = -1; - lua_setlinehook(NULL); - } - else { - lua_pushobject(f); - linehook = lua_ref(1); - lua_setlinehook(linef); - } +static int setlinehook (lua_State *L) { + sethook(L, KEY_LINEHOOK, linef, lua_setlinehook); + return 1; } -static struct luaL_reg dblib[] = { - {"funcinfo", funcinfo}, +static const struct luaL_reg dblib[] = { {"getlocal", getlocal}, - {"getstack", getstack}, + {"getinfo", getinfo}, {"setcallhook", setcallhook}, {"setlinehook", setlinehook}, {"setlocal", setlocal} }; -void lua_dblibopen (void) { - luaL_openlib(dblib, (sizeof(dblib)/sizeof(dblib[0]))); +LUALIB_API void lua_dblibopen (lua_State *L) { + luaL_openl(L, dblib); } diff --git a/src/lib/linit.c b/src/lib/linit.c deleted file mode 100644 index be57aae7..00000000 --- a/src/lib/linit.c +++ /dev/null @@ -1,17 +0,0 @@ -/* -** $Id: linit.c,v 1.1 1999/01/08 16:49:32 roberto Exp $ -** Initialization of libraries for lua.c -** See Copyright Notice in lua.h -*/ - -#include "lua.h" -#include "lualib.h" - - -void lua_userinit (void) { - lua_iolibopen(); - lua_strlibopen(); - lua_mathlibopen(); - lua_dblibopen(); -} - diff --git a/src/lib/liolib.c b/src/lib/liolib.c index d833cec5..70f8057a 100644 --- a/src/lib/liolib.c +++ b/src/lib/liolib.c @@ -1,67 +1,78 @@ /* -** $Id: liolib.c,v 1.41 1999/06/23 13:48:39 roberto Exp $ +** $Id: liolib.c,v 1.91 2000/10/31 13:10:24 roberto Exp $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ -#include <errno.h> +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> -#include "lauxlib.h" #include "lua.h" + +#include "lauxlib.h" #include "luadebug.h" #include "lualib.h" #ifndef OLD_ANSI +#include <errno.h> #include <locale.h> +#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s)) +#define free(b) if (b) (free)(b) #else /* no support for locale and for strerror: fake them */ -#define setlocale(a,b) 0 +#define setlocale(a,b) ((void)a, strcmp((b),"C")==0?"C":NULL) #define LC_ALL 0 #define LC_COLLATE 0 #define LC_CTYPE 0 #define LC_MONETARY 0 #define LC_NUMERIC 0 #define LC_TIME 0 -#define strerror(e) "(no error message provided by operating system)" +#define strerror(e) "generic I/O error" +#define errno (-1) #endif -#define IOTAG 1 - -#define FIRSTARG 2 /* 1st is upvalue */ - -#define CLOSEDTAG(tag) ((tag)-1) /* assume that CLOSEDTAG = iotag-1 */ - - -#define FINPUT "_INPUT" -#define FOUTPUT "_OUTPUT" - #ifdef POPEN -FILE *popen(); -int pclose(); -#define CLOSEFILE(f) {if (pclose(f) == -1) fclose(f);} +/* FILE *popen(); +int pclose(); */ +#define CLOSEFILE(L, f) ((pclose(f) == -1) ? fclose(f) : 0) #else /* no support for popen */ #define popen(x,y) NULL /* that is, popen always fails */ -#define CLOSEFILE(f) {fclose(f);} +#define CLOSEFILE(L, f) (fclose(f)) #endif +#define INFILE 0 +#define OUTFILE 1 + +typedef struct IOCtrl { + int ref[2]; /* ref for strings _INPUT/_OUTPUT */ + int iotag; /* tag for file handles */ + int closedtag; /* tag for closed handles */ +} IOCtrl; + + + +static const char *const filenames[] = {"_INPUT", "_OUTPUT"}; -static void pushresult (int i) { - if (i) - lua_pushuserdata(NULL); + +static int pushresult (lua_State *L, int i) { + if (i) { + lua_pushuserdata(L, NULL); + return 1; + } else { - lua_pushnil(); - lua_pushstring(strerror(errno)); - lua_pushnumber(errno); + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushnumber(L, errno); + return 3;; } } @@ -72,138 +83,138 @@ static void pushresult (int i) { ** ======================================================= */ -static int gettag (void) { - return (int)lua_getnumber(lua_getparam(IOTAG)); -} - -static int ishandle (lua_Object f) { - if (lua_isuserdata(f)) { - int tag = gettag(); - if (lua_tag(f) == CLOSEDTAG(tag)) - lua_error("cannot access a closed file"); - return lua_tag(f) == tag; +static FILE *gethandle (lua_State *L, IOCtrl *ctrl, int f) { + void *p = lua_touserdata(L, f); + if (p != NULL) { /* is `f' a userdata ? */ + int ftag = lua_tag(L, f); + if (ftag == ctrl->iotag) /* does it have the correct tag? */ + return (FILE *)p; + else if (ftag == ctrl->closedtag) + lua_error(L, "cannot access a closed file"); + /* else go through */ } - else return 0; + return NULL; } -static FILE *getfilebyname (char *name) { - lua_Object f = lua_getglobal(name); - if (!ishandle(f)) - luaL_verror("global variable `%.50s' is not a file handle", name); - return lua_getuserdata(f); -} - - -static FILE *getfile (int arg) { - lua_Object f = lua_getparam(arg); - return (ishandle(f)) ? lua_getuserdata(f) : NULL; +static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) { + FILE *f = gethandle(L, ctrl, arg); + luaL_arg_check(L, f, arg, "invalid file handle"); + return f; } -static FILE *getnonullfile (int arg) { - FILE *f = getfile(arg); - luaL_arg_check(f, arg, "invalid file handle"); +static FILE *getfilebyref (lua_State *L, IOCtrl *ctrl, int inout) { + FILE *f; + lua_getglobals(L); + lua_getref(L, ctrl->ref[inout]); + lua_rawget(L, -2); + f = gethandle(L, ctrl, -1); + if (f == NULL) + luaL_verror(L, "global variable `%.10s' is not a file handle", + filenames[inout]); return f; } -static FILE *getfileparam (char *name, int *arg) { - FILE *f = getfile(*arg); - if (f) { - (*arg)++; - return f; - } - else - return getfilebyname(name); +static void setfilebyname (lua_State *L, IOCtrl *ctrl, FILE *f, + const char *name) { + lua_pushusertag(L, f, ctrl->iotag); + lua_setglobal(L, name); } -static void closefile (FILE *f) { - if (f != stdin && f != stdout) { - int tag = gettag(); - CLOSEFILE(f); - lua_pushusertag(f, tag); - lua_settag(CLOSEDTAG(tag)); - } -} +#define setfile(L,ctrl,f,inout) (setfilebyname(L,ctrl,f,filenames[inout])) -static void io_close (void) { - closefile(getnonullfile(FIRSTARG)); +static int setreturn (lua_State *L, IOCtrl *ctrl, FILE *f, int inout) { + if (f == NULL) + return pushresult(L, 0); + else { + setfile(L, ctrl, f, inout); + lua_pushusertag(L, f, ctrl->iotag); + return 1; + } } -static void gc_close (void) { - FILE *f = getnonullfile(FIRSTARG); - if (f != stdin && f != stdout && f != stderr) { - CLOSEFILE(f); +static int closefile (lua_State *L, IOCtrl *ctrl, FILE *f) { + if (f == stdin || f == stdout || f == stderr) + return 1; + else { + lua_pushusertag(L, f, ctrl->iotag); + lua_settag(L, ctrl->closedtag); + return (CLOSEFILE(L, f) == 0); } } -static void io_open (void) { - FILE *f = fopen(luaL_check_string(FIRSTARG), luaL_check_string(FIRSTARG+1)); - if (f) lua_pushusertag(f, gettag()); - else pushresult(0); +static int io_close (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + lua_pop(L, 1); /* remove upvalue */ + return pushresult(L, closefile(L, ctrl, getnonullfile(L, ctrl, 1))); } -static void setfile (FILE *f, char *name, int tag) { - lua_pushusertag(f, tag); - lua_setglobal(name); +static int file_collect (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + FILE *f = getnonullfile(L, ctrl, 1); + if (f != stdin && f != stdout && f != stderr) + CLOSEFILE(L, f); + return 0; } -static void setreturn (FILE *f, char *name) { - if (f == NULL) - pushresult(0); - else { - int tag = gettag(); - setfile(f, name, tag); - lua_pushusertag(f, tag); +static int io_open (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + FILE *f; + lua_pop(L, 1); /* remove upvalue */ + f = fopen(luaL_check_string(L, 1), luaL_check_string(L, 2)); + if (f) { + lua_pushusertag(L, f, ctrl->iotag); + return 1; } + else + return pushresult(L, 0); } -static void io_readfrom (void) { + +static int io_fromto (lua_State *L, int inout, const char *mode) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *current; - lua_Object f = lua_getparam(FIRSTARG); - if (f == LUA_NOOBJECT) { - closefile(getfilebyname(FINPUT)); - current = stdin; + lua_pop(L, 1); /* remove upvalue */ + if (lua_isnull(L, 1)) { + closefile(L, ctrl, getfilebyref(L, ctrl, inout)); + current = (inout == 0) ? stdin : stdout; } - else if (lua_tag(f) == gettag()) /* deprecated option */ - current = lua_getuserdata(f); + else if (lua_tag(L, 1) == ctrl->iotag) /* deprecated option */ + current = (FILE *)lua_touserdata(L, 1); else { - char *s = luaL_check_string(FIRSTARG); - current = (*s == '|') ? popen(s+1, "r") : fopen(s, "r"); + const char *s = luaL_check_string(L, 1); + current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode); } - setreturn(current, FINPUT); + return setreturn(L, ctrl, current, inout); } -static void io_writeto (void) { - FILE *current; - lua_Object f = lua_getparam(FIRSTARG); - if (f == LUA_NOOBJECT) { - closefile(getfilebyname(FOUTPUT)); - current = stdout; - } - else if (lua_tag(f) == gettag()) /* deprecated option */ - current = lua_getuserdata(f); - else { - char *s = luaL_check_string(FIRSTARG); - current = (*s == '|') ? popen(s+1,"w") : fopen(s, "w"); - } - setreturn(current, FOUTPUT); +static int io_readfrom (lua_State *L) { + return io_fromto(L, INFILE, "r"); } -static void io_appendto (void) { - FILE *current = fopen(luaL_check_string(FIRSTARG), "a"); - setreturn(current, FOUTPUT); +static int io_writeto (lua_State *L) { + return io_fromto(L, OUTFILE, "w"); +} + + +static int io_appendto (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + FILE *current; + lua_pop(L, 1); /* remove upvalue */ + current = fopen(luaL_check_string(L, 1), "a"); + return setreturn(L, ctrl, current, OUTFILE); } @@ -215,6 +226,9 @@ static void io_appendto (void) { */ + +#ifdef LUA_COMPAT_READPATTERN + /* ** We cannot lookahead without need, because this can lock stdin. ** This flag signals when we need to read a next char. @@ -222,9 +236,11 @@ static void io_appendto (void) { #define NEED_OTHER (EOF-1) /* just some flag different from EOF */ -static int read_pattern (FILE *f, char *p) { +static int read_pattern (lua_State *L, FILE *f, const char *p) { int inskip = 0; /* {skip} level */ int c = NEED_OTHER; + luaL_Buffer b; + luaL_buffinit(L, &b); while (*p != '\0') { switch (*p) { case '{': @@ -232,17 +248,17 @@ static int read_pattern (FILE *f, char *p) { p++; continue; case '}': - if (!inskip) lua_error("unbalanced braces in read pattern"); + if (!inskip) lua_error(L, "unbalanced braces in read pattern"); inskip--; p++; continue; default: { - char *ep = luaI_classend(p); /* get what is next */ + const char *ep = luaI_classend(L, p); /* get what is next */ int m; /* match result */ if (c == NEED_OTHER) c = getc(f); m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep); if (m) { - if (!inskip) luaL_addchar(c); + if (!inskip) luaL_putchar(&b, c); c = NEED_OTHER; } switch (*ep) { @@ -253,7 +269,7 @@ static int read_pattern (FILE *f, char *p) { while (m) { /* reads the same item until it fails */ c = getc(f); m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep); - if (m && !inskip) luaL_addchar(c); + if (m && !inskip) luaL_putchar(&b, c); } /* go through to continue reading the pattern */ case '?': /* optional */ @@ -267,116 +283,210 @@ static int read_pattern (FILE *f, char *p) { } } break_while: if (c != NEED_OTHER) ungetc(c, f); + luaL_pushresult(&b); /* close buffer */ return (*p == '\0'); } +#else + +#define read_pattern(L, f, p) (lua_error(L, "read patterns are deprecated"), 0) -static int read_number (FILE *f) { +#endif + + +static int read_number (lua_State *L, FILE *f) { double d; if (fscanf(f, "%lf", &d) == 1) { - lua_pushnumber(d); + lua_pushnumber(L, d); return 1; } else return 0; /* read fails */ } -#define HUNK_LINE 1024 -#define HUNK_FILE BUFSIZ +static int read_word (lua_State *L, FILE *f) { + int c; + luaL_Buffer b; + luaL_buffinit(L, &b); + do { c = fgetc(f); } while (isspace(c)); /* skip spaces */ + while (c != EOF && !isspace(c)) { + luaL_putchar(&b, c); + c = fgetc(f); + } + ungetc(c, f); + luaL_pushresult(&b); /* close buffer */ + return (lua_strlen(L, -1) > 0); +} -static int read_line (FILE *f) { - /* equivalent to: return read_pattern(f, "[^\n]*{\n}"); */ - int n; - char *b; - do { - b = luaL_openspace(HUNK_LINE); - if (!fgets(b, HUNK_LINE, f)) return 0; /* read fails */ - n = strlen(b); - luaL_addsize(n); - } while (b[n-1] != '\n'); - luaL_addsize(-1); /* remove '\n' */ - return 1; + +static int read_line (lua_State *L, FILE *f) { + int n = 0; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + char *p = luaL_prepbuffer(&b); + if (!fgets(p, LUAL_BUFFERSIZE, f)) /* read fails? */ + break; + n = strlen(p); + if (p[n-1] != '\n') + luaL_addsize(&b, n); + else { + luaL_addsize(&b, n-1); /* do not add the `\n' */ + break; + } + } + luaL_pushresult(&b); /* close buffer */ + return (n > 0); /* read something? */ } -static void read_file (FILE *f) { - /* equivalent to: return read_pattern(f, ".*"); */ - int n; - do { - char *b = luaL_openspace(HUNK_FILE); - n = fread(b, sizeof(char), HUNK_FILE, f); - luaL_addsize(n); - } while (n==HUNK_FILE); +static void read_file (lua_State *L, FILE *f) { + size_t len = 0; + size_t size = BUFSIZ; + char *buffer = NULL; + for (;;) { + char *newbuffer = (char *)realloc(buffer, size); + if (newbuffer == NULL) { + free(buffer); + lua_error(L, "not enough memory to read a file"); + } + buffer = newbuffer; + len += fread(buffer+len, sizeof(char), size-len, f); + if (len < size) break; /* did not read all it could */ + size *= 2; + } + lua_pushlstring(L, buffer, len); + free(buffer); } -static void io_read (void) { - static char *options[] = {"*n", "*l", "*a", ".*", "*w", NULL}; - int arg = FIRSTARG; - FILE *f = getfileparam(FINPUT, &arg); - char *p = luaL_opt_string(arg++, "*l"); - do { /* repeat for each part */ - long l; +static int read_chars (lua_State *L, FILE *f, size_t n) { + char *buffer; + size_t n1; + char statbuff[BUFSIZ]; + if (n <= BUFSIZ) + buffer = statbuff; + else { + buffer = (char *)malloc(n); + if (buffer == NULL) + lua_error(L, "not enough memory to read a file"); + } + n1 = fread(buffer, sizeof(char), n, f); + lua_pushlstring(L, buffer, n1); + if (buffer != statbuff) free(buffer); + return (n1 > 0 || n == 0); +} + + +static int io_read (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + int lastarg = lua_gettop(L) - 1; + int firstarg = 1; + FILE *f = gethandle(L, ctrl, firstarg); + int n; + if (f) firstarg++; + else f = getfilebyref(L, ctrl, INFILE); /* get _INPUT */ + lua_pop(L, 1); + if (firstarg > lastarg) { /* no arguments? */ + lua_settop(L, 0); /* erase upvalue and other eventual garbage */ + firstarg = lastarg = 1; /* correct indices */ + lua_pushstring(L, "*l"); /* push default argument */ + } + else /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, lastarg-firstarg+1+LUA_MINSTACK, "too many arguments"); + for (n = firstarg; n<=lastarg; n++) { int success; - luaL_resetbuffer(); - switch (luaL_findstring(p, options)) { - case 0: /* number */ - if (!read_number(f)) return; /* read fails */ - continue; /* number is already pushed; avoid the "pushstring" */ - case 1: /* line */ - success = read_line(f); - break; - case 2: case 3: /* file */ - read_file(f); - success = 1; /* always success */ - break; - case 4: /* word */ - success = read_pattern(f, "{%s*}%S+"); - break; - default: - success = read_pattern(f, p); + if (lua_isnumber(L, n)) + success = read_chars(L, f, (size_t)lua_tonumber(L, n)); + else { + const char *p = luaL_check_string(L, n); + if (p[0] != '*') + success = read_pattern(L, f, p); /* deprecated! */ + else { + switch (p[1]) { + case 'n': /* number */ + if (!read_number(L, f)) goto endloop; /* read fails */ + continue; /* number is already pushed; avoid the "pushstring" */ + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_file(L, f); + success = 1; /* always success */ + break; + case 'w': /* word */ + success = read_word(L, f); + break; + default: + luaL_argerror(L, n, "invalid format"); + success = 0; /* to avoid warnings */ + } + } } - l = luaL_getsize(); - if (!success && l==0) return; /* read fails */ - lua_pushlstring(luaL_buffer(), l); - } while ((p = luaL_opt_string(arg++, NULL)) != NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + break; /* read fails */ + } + } endloop: + return n - firstarg; } /* }====================================================== */ -static void io_write (void) { - int arg = FIRSTARG; - FILE *f = getfileparam(FOUTPUT, &arg); +static int io_write (lua_State *L) { + int lastarg = lua_gettop(L) - 1; + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + int arg = 1; int status = 1; - char *s; - long l; - while ((s = luaL_opt_lstr(arg++, NULL, &l)) != NULL) - status = status && ((long)fwrite(s, 1, l, f) == l); - pushresult(status); + FILE *f = gethandle(L, ctrl, arg); + if (f) arg++; + else f = getfilebyref(L, ctrl, OUTFILE); /* get _OUTPUT */ + for (; arg <= lastarg; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { /* LUA_NUMBER */ + /* optimization: could be done exactly as for strings */ + status = status && fprintf(f, "%.16g", lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_check_lstr(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + pushresult(L, status); + return 1; } -static void io_seek (void) { - static int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; - static char *modenames[] = {"set", "cur", "end", NULL}; - FILE *f = getnonullfile(FIRSTARG); - int op = luaL_findstring(luaL_opt_string(FIRSTARG+1, "cur"), modenames); - long offset = luaL_opt_long(FIRSTARG+2, 0); - luaL_arg_check(op != -1, FIRSTARG+1, "invalid mode"); +static int io_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + FILE *f; + int op; + long offset; + lua_pop(L, 1); /* remove upvalue */ + f = getnonullfile(L, ctrl, 1); + op = luaL_findstring(luaL_opt_string(L, 2, "cur"), modenames); + offset = luaL_opt_long(L, 3, 0); + luaL_arg_check(L, op != -1, 2, "invalid mode"); op = fseek(f, offset, mode[op]); if (op) - pushresult(0); /* error */ - else - lua_pushnumber(ftell(f)); + return pushresult(L, 0); /* error */ + else { + lua_pushnumber(L, ftell(f)); + return 1; + } } -static void io_flush (void) { - FILE *f = getfile(FIRSTARG); - luaL_arg_check(f || lua_getparam(FIRSTARG) == LUA_NOOBJECT, FIRSTARG, - "invalid file handle"); - pushresult(fflush(f) == 0); +static int io_flush (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); + FILE *f; + lua_pop(L, 1); /* remove upvalue */ + f = gethandle(L, ctrl, 1); + luaL_arg_check(L, f || lua_isnull(L, 1), 1, "invalid file handle"); + return pushresult(L, fflush(f) == 0); } /* }====================================================== */ @@ -388,145 +498,165 @@ static void io_flush (void) { ** ======================================================= */ -static void io_execute (void) { - lua_pushnumber(system(luaL_check_string(1))); +static int io_execute (lua_State *L) { + lua_pushnumber(L, system(luaL_check_string(L, 1))); + return 1; } -static void io_remove (void) { - pushresult(remove(luaL_check_string(1)) == 0); +static int io_remove (lua_State *L) { + return pushresult(L, remove(luaL_check_string(L, 1)) == 0); } -static void io_rename (void) { - pushresult(rename(luaL_check_string(1), - luaL_check_string(2)) == 0); +static int io_rename (lua_State *L) { + return pushresult(L, rename(luaL_check_string(L, 1), + luaL_check_string(L, 2)) == 0); } -static void io_tmpname (void) { - lua_pushstring(tmpnam(NULL)); +static int io_tmpname (lua_State *L) { + lua_pushstring(L, tmpnam(NULL)); + return 1; } -static void io_getenv (void) { - lua_pushstring(getenv(luaL_check_string(1))); /* if NULL push nil */ +static int io_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_check_string(L, 1))); /* if NULL push nil */ + return 1; } -static void io_clock (void) { - lua_pushnumber(((double)clock())/CLOCKS_PER_SEC); +static int io_clock (lua_State *L) { + lua_pushnumber(L, ((double)clock())/CLOCKS_PER_SEC); + return 1; } -static void io_date (void) { +static int io_date (lua_State *L) { char b[256]; - char *s = luaL_opt_string(1, "%c"); - struct tm *tm; + const char *s = luaL_opt_string(L, 1, "%c"); + struct tm *stm; time_t t; - time(&t); tm = localtime(&t); - if (strftime(b,sizeof(b),s,tm)) - lua_pushstring(b); + time(&t); stm = localtime(&t); + if (strftime(b, sizeof(b), s, stm)) + lua_pushstring(L, b); else - lua_error("invalid `date' format"); + lua_error(L, "invalid `date' format"); + return 1; } -static void setloc (void) { - static int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, - LC_TIME}; - static char *catnames[] = {"all", "collate", "ctype", "monetary", +static int setloc (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", "numeric", "time", NULL}; - int op = luaL_findstring(luaL_opt_string(2, "all"), catnames); - luaL_arg_check(op != -1, 2, "invalid option"); - lua_pushstring(setlocale(cat[op], luaL_check_string(1))); + int op = luaL_findstring(luaL_opt_string(L, 2, "all"), catnames); + luaL_arg_check(L, op != -1, 2, "invalid option"); + lua_pushstring(L, setlocale(cat[op], luaL_check_string(L, 1))); + return 1; } -static void io_exit (void) { - lua_Object o = lua_getparam(1); - exit(lua_isnumber(o) ? (int)lua_getnumber(o) : 1); +static int io_exit (lua_State *L) { + exit(luaL_opt_int(L, 1, EXIT_SUCCESS)); + return 0; /* to avoid warnings */ } /* }====================================================== */ -static void io_debug (void) { +static int io_debug (lua_State *L) { for (;;) { char buffer[250]; fprintf(stderr, "lua_debug> "); if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0) - return; - lua_dostring(buffer); + return 0; + lua_dostring(L, buffer); + lua_settop(L, 0); /* remove eventual returns */ } } +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ -#define MESSAGESIZE 150 -#define MAXMESSAGE (MESSAGESIZE*10) - - -#define MAXSRC 60 - - -static void errorfb (void) { - char buff[MAXMESSAGE]; +static int errorfb (lua_State *L) { int level = 1; /* skip level 0 (it's this function) */ - lua_Object func; - sprintf(buff, "lua error: %.200s\n", lua_getstring(lua_getparam(1))); - while ((func = lua_stackedfunction(level++)) != LUA_NOOBJECT) { - char *name; - int currentline; - char *chunkname; - char buffchunk[MAXSRC]; - int linedefined; - lua_funcinfo(func, &chunkname, &linedefined); - luaL_chunkid(buffchunk, chunkname, sizeof(buffchunk)); - if (level == 2) strcat(buff, "Active Stack:\n"); - strcat(buff, " "); - if (strlen(buff) > MAXMESSAGE-MESSAGESIZE) { - strcat(buff, "...\n"); - break; /* buffer is full */ + int firstpart = 1; /* still before eventual `...' */ + lua_Debug ar; + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addstring(&b, "error: "); + luaL_addstring(&b, luaL_check_string(L, 1)); + luaL_addstring(&b, "\n"); + while (lua_getstack(L, level++, &ar)) { + char buff[120]; /* enough to fit following `sprintf's */ + if (level == 2) + luaL_addstring(&b, "stack traceback:\n"); + else if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + luaL_addstring(&b, " ...\n"); /* too many levels */ + while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; } - switch (*lua_getobjname(func, &name)) { - case 'g': - sprintf(buff+strlen(buff), "function `%.50s'", name); + sprintf(buff, "%4d: ", level-1); + luaL_addstring(&b, buff); + lua_getinfo(L, "Snl", &ar); + switch (*ar.namewhat) { + case 'g': case 'l': /* global, local */ + sprintf(buff, "function `%.50s'", ar.name); break; - case 't': - sprintf(buff+strlen(buff), "`%.50s' tag method", name); + case 'f': /* field */ + sprintf(buff, "method `%.50s'", ar.name); + break; + case 't': /* tag method */ + sprintf(buff, "`%.50s' tag method", ar.name); break; default: { - if (linedefined == 0) - sprintf(buff+strlen(buff), "main of %.70s", buffchunk); - else if (linedefined < 0) - sprintf(buff+strlen(buff), "%.70s", buffchunk); + if (*ar.what == 'm') /* main? */ + sprintf(buff, "main of %.70s", ar.short_src); + else if (*ar.what == 'C') /* C function? */ + sprintf(buff, "%.70s", ar.short_src); else - sprintf(buff+strlen(buff), "function <%d:%.70s>", - linedefined, buffchunk); - chunkname = NULL; + sprintf(buff, "function <%d:%.70s>", ar.linedefined, ar.short_src); + ar.source = NULL; /* do not print source again */ } } - if ((currentline = lua_currentline(func)) > 0) - sprintf(buff+strlen(buff), " at line %d", currentline); - if (chunkname) - sprintf(buff+strlen(buff), " [%.70s]", buffchunk); - strcat(buff, "\n"); + luaL_addstring(&b, buff); + if (ar.currentline > 0) { + sprintf(buff, " at line %d", ar.currentline); + luaL_addstring(&b, buff); + } + if (ar.source) { + sprintf(buff, " [%.70s]", ar.short_src); + luaL_addstring(&b, buff); + } + luaL_addstring(&b, "\n"); } - func = lua_rawgetglobal("_ALERT"); - if (lua_isfunction(func)) { /* avoid error loop if _ALERT is not defined */ - lua_pushstring(buff); - lua_callfunction(func); + luaL_pushresult(&b); + lua_getglobal(L, LUA_ALERT); + if (lua_isfunction(L, -1)) { /* avoid loop if _ALERT is not defined */ + lua_pushvalue(L, -2); /* error message */ + lua_rawcall(L, 1, 0); } + return 0; } -static struct luaL_reg iolib[] = { - {"_ERRORMESSAGE", errorfb}, +static const struct luaL_reg iolib[] = { + {LUA_ERRORMESSAGE, errorfb}, {"clock", io_clock}, {"date", io_date}, {"debug", io_debug}, @@ -540,7 +670,7 @@ static struct luaL_reg iolib[] = { }; -static struct luaL_reg iolibtag[] = { +static const struct luaL_reg iolibtag[] = { {"appendto", io_appendto}, {"closefile", io_close}, {"flush", io_flush}, @@ -553,31 +683,36 @@ static struct luaL_reg iolibtag[] = { }; -static void openwithtags (void) { - int i; - int iotag = lua_newtag(); - lua_newtag(); /* alloc CLOSEDTAG: assume that CLOSEDTAG = iotag-1 */ +static void openwithcontrol (lua_State *L) { + IOCtrl *ctrl = (IOCtrl *)lua_newuserdata(L, sizeof(IOCtrl)); + unsigned int i; + ctrl->iotag = lua_newtag(L); + ctrl->closedtag = lua_newtag(L); for (i=0; i<sizeof(iolibtag)/sizeof(iolibtag[0]); i++) { - /* put iotag as upvalue for these functions */ - lua_pushnumber(iotag); - lua_pushcclosure(iolibtag[i].func, 1); - lua_setglobal(iolibtag[i].name); + /* put `ctrl' as upvalue for these functions */ + lua_pushvalue(L, -1); + lua_pushcclosure(L, iolibtag[i].func, 1); + lua_setglobal(L, iolibtag[i].name); } + /* create references to variable names */ + lua_pushstring(L, filenames[INFILE]); + ctrl->ref[INFILE] = lua_ref(L, 1); + lua_pushstring(L, filenames[OUTFILE]); + ctrl->ref[OUTFILE] = lua_ref(L, 1); /* predefined file handles */ - setfile(stdin, FINPUT, iotag); - setfile(stdout, FOUTPUT, iotag); - setfile(stdin, "_STDIN", iotag); - setfile(stdout, "_STDOUT", iotag); - setfile(stderr, "_STDERR", iotag); - /* close file when collected */ - lua_pushnumber(iotag); - lua_pushcclosure(gc_close, 1); - lua_settagmethod(iotag, "gc"); -} - -void lua_iolibopen (void) { - /* register lib functions */ - luaL_openlib(iolib, (sizeof(iolib)/sizeof(iolib[0]))); - openwithtags(); + setfile(L, ctrl, stdin, INFILE); + setfile(L, ctrl, stdout, OUTFILE); + setfilebyname(L, ctrl, stdin, "_STDIN"); + setfilebyname(L, ctrl, stdout, "_STDOUT"); + setfilebyname(L, ctrl, stderr, "_STDERR"); + /* close files when collected */ + lua_pushcclosure(L, file_collect, 1); /* pops `ctrl' from stack */ + lua_settagmethod(L, ctrl->iotag, "gc"); +} + + +LUALIB_API void lua_iolibopen (lua_State *L) { + luaL_openl(L, iolib); + openwithcontrol(L); } diff --git a/src/lib/lmathlib.c b/src/lib/lmathlib.c index 19cb11c2..c062cf49 100644 --- a/src/lib/lmathlib.c +++ b/src/lib/lmathlib.c @@ -1,6 +1,6 @@ /* -** $Id: lmathlib.c,v 1.17 1999/07/07 17:54:08 roberto Exp $ -** Lua standard mathematical library +** $Id: lmathlib.c,v 1.32 2000/10/31 13:10:24 roberto Exp $ +** Standard mathematical library ** See Copyright Notice in lua.h */ @@ -8,14 +8,15 @@ #include <stdlib.h> #include <math.h> -#include "lauxlib.h" #include "lua.h" + +#include "lauxlib.h" #include "lualib.h" #undef PI -#define PI (3.14159265358979323846) -#define RADIANS_PER_DEGREE (PI/180.0) +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) @@ -32,139 +33,173 @@ #endif -static void math_abs (void) { - lua_pushnumber(fabs(luaL_check_number(1))); +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_check_number(L, 1))); + return 1; } -static void math_sin (void) { - lua_pushnumber(sin(TORAD(luaL_check_number(1)))); +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(TORAD(luaL_check_number(L, 1)))); + return 1; } -static void math_cos (void) { - lua_pushnumber(cos(TORAD(luaL_check_number(1)))); +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(TORAD(luaL_check_number(L, 1)))); + return 1; } -static void math_tan (void) { - lua_pushnumber(tan(TORAD(luaL_check_number(1)))); +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(TORAD(luaL_check_number(L, 1)))); + return 1; } -static void math_asin (void) { - lua_pushnumber(FROMRAD(asin(luaL_check_number(1)))); +static int math_asin (lua_State *L) { + lua_pushnumber(L, FROMRAD(asin(luaL_check_number(L, 1)))); + return 1; } -static void math_acos (void) { - lua_pushnumber(FROMRAD(acos(luaL_check_number(1)))); +static int math_acos (lua_State *L) { + lua_pushnumber(L, FROMRAD(acos(luaL_check_number(L, 1)))); + return 1; } -static void math_atan (void) { - lua_pushnumber(FROMRAD(atan(luaL_check_number(1)))); +static int math_atan (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan(luaL_check_number(L, 1)))); + return 1; } -static void math_atan2 (void) { - lua_pushnumber(FROMRAD(atan2(luaL_check_number(1), luaL_check_number(2)))); +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan2(luaL_check_number(L, 1), luaL_check_number(L, 2)))); + return 1; } -static void math_ceil (void) { - lua_pushnumber(ceil(luaL_check_number(1))); +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_check_number(L, 1))); + return 1; } -static void math_floor (void) { - lua_pushnumber(floor(luaL_check_number(1))); +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_check_number(L, 1))); + return 1; } -static void math_mod (void) { - lua_pushnumber(fmod(luaL_check_number(1), luaL_check_number(2))); +static int math_mod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_check_number(L, 1), luaL_check_number(L, 2))); + return 1; } -static void math_sqrt (void) { - lua_pushnumber(sqrt(luaL_check_number(1))); +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_check_number(L, 1))); + return 1; } -static void math_pow (void) { - lua_pushnumber(pow(luaL_check_number(1), luaL_check_number(2))); +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_check_number(L, 1), luaL_check_number(L, 2))); + return 1; } -static void math_log (void) { - lua_pushnumber(log(luaL_check_number(1))); +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_check_number(L, 1))); + return 1; } -static void math_log10 (void) { - lua_pushnumber(log10(luaL_check_number(1))); +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_check_number(L, 1))); + return 1; } -static void math_exp (void) { - lua_pushnumber(exp(luaL_check_number(1))); +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_check_number(L, 1))); + return 1; } -static void math_deg (void) { - lua_pushnumber(luaL_check_number(1)/RADIANS_PER_DEGREE); +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_check_number(L, 1)/RADIANS_PER_DEGREE); + return 1; } -static void math_rad (void) { - lua_pushnumber(luaL_check_number(1)*RADIANS_PER_DEGREE); +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_check_number(L, 1)*RADIANS_PER_DEGREE); + return 1; } -static void math_frexp (void) { +static int math_frexp (lua_State *L) { int e; - lua_pushnumber(frexp(luaL_check_number(1), &e)); - lua_pushnumber(e); + lua_pushnumber(L, frexp(luaL_check_number(L, 1), &e)); + lua_pushnumber(L, e); + return 2; } -static void math_ldexp (void) { - lua_pushnumber(ldexp(luaL_check_number(1), luaL_check_int(2))); +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_check_number(L, 1), luaL_check_int(L, 2))); + return 1; } -static void math_min (void) { - int i = 1; - double dmin = luaL_check_number(i); - while (lua_getparam(++i) != LUA_NOOBJECT) { - double d = luaL_check_number(i); +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + double dmin = luaL_check_number(L, 1); + int i; + for (i=2; i<=n; i++) { + double d = luaL_check_number(L, i); if (d < dmin) dmin = d; } - lua_pushnumber(dmin); + lua_pushnumber(L, dmin); + return 1; } -static void math_max (void) { - int i = 1; - double dmax = luaL_check_number(i); - while (lua_getparam(++i) != LUA_NOOBJECT) { - double d = luaL_check_number(i); +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + double dmax = luaL_check_number(L, 1); + int i; + for (i=2; i<=n; i++) { + double d = luaL_check_number(L, i); if (d > dmax) dmax = d; } - lua_pushnumber(dmax); + lua_pushnumber(L, dmax); + return 1; } -static void math_random (void) { +static int math_random (lua_State *L) { /* the '%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) "rand()" may return a value bigger than RAND_MAX */ + some systems (SunOS!) "rand()" may return a value larger than RAND_MAX */ double r = (double)(rand()%RAND_MAX) / (double)RAND_MAX; - int l = luaL_opt_int(1, 0); - if (l == 0) - lua_pushnumber(r); - else { - int u = luaL_opt_int(2, 0); - if (u == 0) { - u = l; - l = 1; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_check_int(L, 1); + luaL_arg_check(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, (int)(r*u)+1); /* integer between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_check_int(L, 1); + int u = luaL_check_int(L, 2); + luaL_arg_check(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, (int)(r*(u-l+1))+l); /* integer between `l' and `u' */ + break; } - luaL_arg_check(l<=u, 1, "interval is empty"); - lua_pushnumber((int)(r*(u-l+1))+l); + default: lua_error(L, "wrong number of arguments"); } + return 1; } -static void math_randomseed (void) { - srand(luaL_check_int(1)); +static int math_randomseed (lua_State *L) { + srand(luaL_check_int(L, 1)); + return 0; } -static struct luaL_reg mathlib[] = { +static const struct luaL_reg mathlib[] = { {"abs", math_abs}, {"sin", math_sin}, {"cos", math_cos}, @@ -193,11 +228,11 @@ static struct luaL_reg mathlib[] = { /* ** Open math library */ -void lua_mathlibopen (void) { - luaL_openlib(mathlib, (sizeof(mathlib)/sizeof(mathlib[0]))); - lua_pushcfunction(math_pow); - lua_pushnumber(0); /* to get its tag */ - lua_settagmethod(lua_tag(lua_pop()), "pow"); - lua_pushnumber(PI); lua_setglobal("PI"); +LUALIB_API void lua_mathlibopen (lua_State *L) { + luaL_openl(L, mathlib); + lua_pushcfunction(L, math_pow); + lua_settagmethod(L, LUA_TNUMBER, "pow"); + lua_pushnumber(L, PI); + lua_setglobal(L, "PI"); } diff --git a/src/lib/lstrlib.c b/src/lib/lstrlib.c index b47e21d3..8f286982 100644 --- a/src/lib/lstrlib.c +++ b/src/lib/lstrlib.c @@ -1,112 +1,111 @@ /* -** $Id: lstrlib.c,v 1.32 1999/06/17 17:04:03 roberto Exp $ -** Standard library for strings and pattern-matching +** $Id: lstrlib.c,v 1.56 2000/10/27 16:15:53 roberto Exp $ +** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ #include <ctype.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "lauxlib.h" #include "lua.h" -#include "lualib.h" - - - -static void addnchar (char *s, int n) -{ - char *b = luaL_openspace(n); - memcpy(b, s, n); - luaL_addsize(n); -} +#include "lauxlib.h" +#include "lualib.h" -static void str_len (void) -{ - long l; - luaL_check_lstr(1, &l); - lua_pushnumber(l); -} -static void closeandpush (void) { - lua_pushlstring(luaL_buffer(), luaL_getsize()); +static int str_len (lua_State *L) { + size_t l; + luaL_check_lstr(L, 1, &l); + lua_pushnumber(L, l); + return 1; } -static long posrelat (long pos, long len) { +static long posrelat (long pos, size_t len) { /* relative string position: negative means back from end */ - return (pos>=0) ? pos : len+pos+1; + return (pos>=0) ? pos : (long)len+pos+1; } -static void str_sub (void) { - long l; - char *s = luaL_check_lstr(1, &l); - long start = posrelat(luaL_check_long(2), l); - long end = posrelat(luaL_opt_long(3, -1), l); +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_check_lstr(L, 1, &l); + long start = posrelat(luaL_check_long(L, 2), l); + long end = posrelat(luaL_opt_long(L, 3, -1), l); if (start < 1) start = 1; - if (end > l) end = l; + if (end > (long)l) end = l; if (start <= end) - lua_pushlstring(s+start-1, end-start+1); - else lua_pushstring(""); + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushstring(L, ""); + return 1; } -static void str_lower (void) { - long l; - int i; - char *s = luaL_check_lstr(1, &l); - luaL_resetbuffer(); +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_check_lstr(L, 1, &l); + luaL_buffinit(L, &b); for (i=0; i<l; i++) - luaL_addchar(tolower((unsigned char)(s[i]))); - closeandpush(); + luaL_putchar(&b, tolower((unsigned char)(s[i]))); + luaL_pushresult(&b); + return 1; } -static void str_upper (void) { - long l; - int i; - char *s = luaL_check_lstr(1, &l); - luaL_resetbuffer(); +static int str_upper (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_check_lstr(L, 1, &l); + luaL_buffinit(L, &b); for (i=0; i<l; i++) - luaL_addchar(toupper((unsigned char)(s[i]))); - closeandpush(); + luaL_putchar(&b, toupper((unsigned char)(s[i]))); + luaL_pushresult(&b); + return 1; } -static void str_rep (void) -{ - long l; - char *s = luaL_check_lstr(1, &l); - int n = luaL_check_int(2); - luaL_resetbuffer(); +static int str_rep (lua_State *L) { + size_t l; + luaL_Buffer b; + const char *s = luaL_check_lstr(L, 1, &l); + int n = luaL_check_int(L, 2); + luaL_buffinit(L, &b); while (n-- > 0) - addnchar(s, l); - closeandpush(); + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; } -static void str_byte (void) { - long l; - char *s = luaL_check_lstr(1, &l); - long pos = posrelat(luaL_opt_long(2, 1), l); - luaL_arg_check(0<pos && pos<=l, 2, "out of range"); - lua_pushnumber((unsigned char)s[pos-1]); +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_check_lstr(L, 1, &l); + long pos = posrelat(luaL_opt_long(L, 2, 1), l); + luaL_arg_check(L, 0<pos && (size_t)pos<=l, 2, "out of range"); + lua_pushnumber(L, (unsigned char)s[pos-1]); + return 1; } -static void str_char (void) { - int i = 0; - luaL_resetbuffer(); - while (lua_getparam(++i) != LUA_NOOBJECT) { - double c = luaL_check_number(i); - luaL_arg_check((unsigned char)c == c, i, "invalid value"); - luaL_addchar((unsigned char)c); +static int str_char (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i=1; i<=n; i++) { + int c = luaL_check_int(L, i); + luaL_arg_check(L, (unsigned char)c == c, i, "invalid value"); + luaL_putchar(&b, (unsigned char)c); } - closeandpush(); + luaL_pushresult(&b); + return 1; } @@ -117,63 +116,53 @@ static void str_char (void) { ** ======================================================= */ -#ifndef MAX_CAPT -#define MAX_CAPT 32 /* arbitrary limit */ +#ifndef MAX_CAPTURES +#define MAX_CAPTURES 32 /* arbitrary limit */ #endif struct Capture { - char *src_end; /* end ('\0') of source string */ + const char *src_end; /* end ('\0') of source string */ int level; /* total number of captures (finished or unfinished) */ struct { - char *init; - int len; /* -1 signals unfinished capture */ - } capture[MAX_CAPT]; + const char *init; + long len; /* -1 signals unfinished capture */ + } capture[MAX_CAPTURES]; }; -#define ESC '%' -#define SPECIALS "^$*+?.([%-" +#define ESC '%' +#define SPECIALS "^$*+?.([%-" -static void push_captures (struct Capture *cap) { - int i; - for (i=0; i<cap->level; i++) { - int l = cap->capture[i].len; - if (l == -1) lua_error("unfinished capture"); - lua_pushlstring(cap->capture[i].init, l); - } -} - - -static int check_cap (int l, struct Capture *cap) { +static int check_capture (lua_State *L, int l, struct Capture *cap) { l -= '1'; if (!(0 <= l && l < cap->level && cap->capture[l].len != -1)) - lua_error("invalid capture index"); + lua_error(L, "invalid capture index"); return l; } -static int capture_to_close (struct Capture *cap) { +static int capture_to_close (lua_State *L, struct Capture *cap) { int level = cap->level; for (level--; level>=0; level--) if (cap->capture[level].len == -1) return level; - lua_error("invalid pattern capture"); + lua_error(L, "invalid pattern capture"); return 0; /* to avoid warnings */ } -char *luaI_classend (char *p) { +const char *luaI_classend (lua_State *L, const char *p) { switch (*p++) { case ESC: - if (*p == '\0') - luaL_verror("incorrect pattern (ends with `%c')", ESC); + if (*p == '\0') lua_error(L, "malformed pattern (ends with `%')"); return p+1; case '[': if (*p == '^') p++; - if (*p == ']') p++; - p = strchr(p, ']'); - if (!p) lua_error("incorrect pattern (missing `]')"); + do { /* look for a ']' */ + if (*p == '\0') lua_error(L, "malformed pattern (missing `]')"); + if (*(p++) == ESC && *p != '\0') p++; /* skip escapes (e.g. '%]') */ + } while (*p != ']'); return p+1; default: return p; @@ -181,7 +170,7 @@ char *luaI_classend (char *p) { } -static int matchclass (int c, int cl) { +static int match_class (int c, int cl) { int res; switch (tolower(cl)) { case 'a' : res = isalpha(c); break; @@ -201,36 +190,36 @@ static int matchclass (int c, int cl) { -static int matchbracketclass (int c, char *p, char *end) { +static int matchbracketclass (int c, const char *p, const char *endclass) { int sig = 1; if (*(p+1) == '^') { sig = 0; p++; /* skip the '^' */ } - while (++p < end) { + while (++p < endclass) { if (*p == ESC) { p++; - if ((p < end) && matchclass(c, (unsigned char)*p)) + if (match_class(c, (unsigned char)*p)) return sig; } - else if ((*(p+1) == '-') && (p+2 < end)) { + else if ((*(p+1) == '-') && (p+2 < endclass)) { p+=2; if ((int)(unsigned char)*(p-2) <= c && c <= (int)(unsigned char)*p) return sig; } - else if ((unsigned char)*p == c) return sig; + else if ((int)(unsigned char)*p == c) return sig; } return !sig; } -int luaI_singlematch (int c, char *p, char *ep) { +int luaI_singlematch (int c, const char *p, const char *ep) { switch (*p) { case '.': /* matches any char */ return 1; case ESC: - return matchclass(c, (unsigned char)*(p+1)); + return match_class(c, (unsigned char)*(p+1)); case '[': return matchbracketclass(c, p, ep-1); default: @@ -239,12 +228,14 @@ int luaI_singlematch (int c, char *p, char *ep) { } -static char *match (char *s, char *p, struct Capture *cap); +static const char *match (lua_State *L, const char *s, const char *p, + struct Capture *cap); -static char *matchbalance (char *s, char *p, struct Capture *cap) { +static const char *matchbalance (lua_State *L, const char *s, const char *p, + struct Capture *cap) { if (*p == 0 || *(p+1) == 0) - lua_error("unbalanced pattern"); + lua_error(L, "unbalanced pattern"); if (*s != *p) return NULL; else { int b = *p; @@ -261,13 +252,14 @@ static char *matchbalance (char *s, char *p, struct Capture *cap) { } -static char *max_expand (char *s, char *p, char *ep, struct Capture *cap) { - int i = 0; /* counts maximum expand for item */ +static const char *max_expand (lua_State *L, const char *s, const char *p, + const char *ep, struct Capture *cap) { + long i = 0; /* counts maximum expand for item */ while ((s+i)<cap->src_end && luaI_singlematch((unsigned char)*(s+i), p, ep)) i++; - /* keeps trying to match mith the maximum repetitions */ + /* keeps trying to match with the maximum repetitions */ while (i>=0) { - char *res = match((s+i), ep+1, cap); + const char *res = match(L, (s+i), ep+1, cap); if (res) return res; i--; /* else didn't match; reduce 1 repetition to try again */ } @@ -275,9 +267,10 @@ static char *max_expand (char *s, char *p, char *ep, struct Capture *cap) { } -static char *min_expand (char *s, char *p, char *ep, struct Capture *cap) { +static const char *min_expand (lua_State *L, const char *s, const char *p, + const char *ep, struct Capture *cap) { for (;;) { - char *res = match(s, ep+1, cap); + const char *res = match(L, s, ep+1, cap); if (res != NULL) return res; else if (s<cap->src_end && luaI_singlematch((unsigned char)*s, p, ep)) @@ -287,56 +280,60 @@ static char *min_expand (char *s, char *p, char *ep, struct Capture *cap) { } -static char *start_capt (char *s, char *p, struct Capture *cap) { - char *res; +static const char *start_capture (lua_State *L, const char *s, const char *p, + struct Capture *cap) { + const char *res; int level = cap->level; - if (level >= MAX_CAPT) lua_error("too many captures"); + if (level >= MAX_CAPTURES) lua_error(L, "too many captures"); cap->capture[level].init = s; cap->capture[level].len = -1; cap->level = level+1; - if ((res=match(s, p+1, cap)) == NULL) /* match failed? */ + if ((res=match(L, s, p+1, cap)) == NULL) /* match failed? */ cap->level--; /* undo capture */ return res; } -static char *end_capt (char *s, char *p, struct Capture *cap) { - int l = capture_to_close(cap); - char *res; +static const char *end_capture (lua_State *L, const char *s, const char *p, + struct Capture *cap) { + int l = capture_to_close(L, cap); + const char *res; cap->capture[l].len = s - cap->capture[l].init; /* close capture */ - if ((res = match(s, p+1, cap)) == NULL) /* match failed? */ + if ((res = match(L, s, p+1, cap)) == NULL) /* match failed? */ cap->capture[l].len = -1; /* undo capture */ return res; } -static char *match_capture (char *s, int level, struct Capture *cap) { - int l = check_cap(level, cap); - int len = cap->capture[l].len; - if (cap->src_end-s >= len && +static const char *match_capture (lua_State *L, const char *s, int level, + struct Capture *cap) { + int l = check_capture(L, level, cap); + size_t len = cap->capture[l].len; + if ((size_t)(cap->src_end-s) >= len && memcmp(cap->capture[l].init, s, len) == 0) return s+len; else return NULL; } -static char *match (char *s, char *p, struct Capture *cap) { +static const char *match (lua_State *L, const char *s, const char *p, + struct Capture *cap) { init: /* using goto's to optimize tail recursion */ switch (*p) { case '(': /* start capture */ - return start_capt(s, p, cap); + return start_capture(L, s, p, cap); case ')': /* end capture */ - return end_capt(s, p, cap); + return end_capture(L, s, p, cap); case ESC: /* may be %[0-9] or %b */ if (isdigit((unsigned char)(*(p+1)))) { /* capture? */ - s = match_capture(s, *(p+1), cap); + s = match_capture(L, s, *(p+1), cap); if (s == NULL) return NULL; - p+=2; goto init; /* else return match(p+2, s, cap) */ + p+=2; goto init; /* else return match(L, s, p+2, cap) */ } else if (*(p+1) == 'b') { /* balanced string? */ - s = matchbalance(s, p+2, cap); + s = matchbalance(L, s, p+2, cap); if (s == NULL) return NULL; - p+=4; goto init; /* else return match(p+4, s, cap); */ + p+=4; goto init; /* else return match(L, s, p+4, cap); */ } else goto dflt; /* case default */ case '\0': /* end of pattern */ @@ -346,178 +343,208 @@ static char *match (char *s, char *p, struct Capture *cap) { return (s == cap->src_end) ? s : NULL; /* check end of string */ else goto dflt; default: dflt: { /* it is a pattern item */ - char *ep = luaI_classend(p); /* points to what is next */ + const char *ep = luaI_classend(L, p); /* points to what is next */ int m = s<cap->src_end && luaI_singlematch((unsigned char)*s, p, ep); switch (*ep) { case '?': { /* optional */ - char *res; - if (m && ((res=match(s+1, ep+1, cap)) != NULL)) + const char *res; + if (m && ((res=match(L, s+1, ep+1, cap)) != NULL)) return res; - p=ep+1; goto init; /* else return match(s, ep+1, cap); */ + p=ep+1; goto init; /* else return match(L, s, ep+1, cap); */ } case '*': /* 0 or more repetitions */ - return max_expand(s, p, ep, cap); + return max_expand(L, s, p, ep, cap); case '+': /* 1 or more repetitions */ - return (m ? max_expand(s+1, p, ep, cap) : NULL); + return (m ? max_expand(L, s+1, p, ep, cap) : NULL); case '-': /* 0 or more repetitions (minimum) */ - return min_expand(s, p, ep, cap); + return min_expand(L, s, p, ep, cap); default: if (!m) return NULL; - s++; p=ep; goto init; /* else return match(s+1, ep, cap); */ + s++; p=ep; goto init; /* else return match(L, s+1, ep, cap); */ + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; } } + return NULL; /* not found */ } } -static void str_find (void) { - long l; - char *s = luaL_check_lstr(1, &l); - char *p = luaL_check_string(2); - long init = posrelat(luaL_opt_long(3, 1), l) - 1; +static int push_captures (lua_State *L, struct Capture *cap) { + int i; + luaL_checkstack(L, cap->level, "too many captures"); + for (i=0; i<cap->level; i++) { + int l = cap->capture[i].len; + if (l == -1) lua_error(L, "unfinished capture"); + lua_pushlstring(L, cap->capture[i].init, l); + } + return cap->level; /* number of strings pushed */ +} + + +static int str_find (lua_State *L) { + size_t l1, l2; + const char *s = luaL_check_lstr(L, 1, &l1); + const char *p = luaL_check_lstr(L, 2, &l2); + long init = posrelat(luaL_opt_long(L, 3, 1), l1) - 1; struct Capture cap; - luaL_arg_check(0 <= init && init <= l, 3, "out of range"); - if (lua_getparam(4) != LUA_NOOBJECT || - strpbrk(p, SPECIALS) == NULL) { /* no special characters? */ - char *s2 = strstr(s+init, p); + luaL_arg_check(L, 0 <= init && (size_t)init <= l1, 3, "out of range"); + if (lua_gettop(L) > 3 || /* extra argument? */ + strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); if (s2) { - lua_pushnumber(s2-s+1); - lua_pushnumber(s2-s+strlen(p)); - return; + lua_pushnumber(L, s2-s+1); + lua_pushnumber(L, s2-s+l2); + return 2; } } else { int anchor = (*p == '^') ? (p++, 1) : 0; - char *s1=s+init; - cap.src_end = s+l; + const char *s1=s+init; + cap.src_end = s+l1; do { - char *res; + const char *res; cap.level = 0; - if ((res=match(s1, p, &cap)) != NULL) { - lua_pushnumber(s1-s+1); /* start */ - lua_pushnumber(res-s); /* end */ - push_captures(&cap); - return; + if ((res=match(L, s1, p, &cap)) != NULL) { + lua_pushnumber(L, s1-s+1); /* start */ + lua_pushnumber(L, res-s); /* end */ + return push_captures(L, &cap) + 2; } } while (s1++<cap.src_end && !anchor); } - lua_pushnil(); /* if arrives here, it didn't find */ + lua_pushnil(L); /* not found */ + return 1; } -static void add_s (lua_Object newp, struct Capture *cap) { - if (lua_isstring(newp)) { - char *news = lua_getstring(newp); - int l = lua_strlen(newp); - int i; +static void add_s (lua_State *L, luaL_Buffer *b, struct Capture *cap) { + if (lua_isstring(L, 3)) { + const char *news = lua_tostring(L, 3); + size_t l = lua_strlen(L, 3); + size_t i; for (i=0; i<l; i++) { if (news[i] != ESC) - luaL_addchar(news[i]); + luaL_putchar(b, news[i]); else { i++; /* skip ESC */ if (!isdigit((unsigned char)news[i])) - luaL_addchar(news[i]); + luaL_putchar(b, news[i]); else { - int level = check_cap(news[i], cap); - addnchar(cap->capture[level].init, cap->capture[level].len); + int level = check_capture(L, news[i], cap); + luaL_addlstring(b, cap->capture[level].init, cap->capture[level].len); } } } } else { /* is a function */ - lua_Object res; - int status; - int oldbuff; - lua_beginblock(); - push_captures(cap); - /* function may use buffer, so save it and create a new one */ - oldbuff = luaL_newbuffer(0); - status = lua_callfunction(newp); - /* restore old buffer */ - luaL_oldbuffer(oldbuff); - if (status != 0) { - lua_endblock(); - lua_error(NULL); - } - res = lua_getresult(1); - if (lua_isstring(res)) - addnchar(lua_getstring(res), lua_strlen(res)); - lua_endblock(); + int n; + lua_pushvalue(L, 3); + n = push_captures(L, cap); + lua_rawcall(L, n, 1); + if (lua_isstring(L, -1)) + luaL_addvalue(b); /* add return to accumulated result */ + else + lua_pop(L, 1); /* function result is not a string: pop it */ } } -static void str_gsub (void) { - long srcl; - char *src = luaL_check_lstr(1, &srcl); - char *p = luaL_check_string(2); - lua_Object newp = lua_getparam(3); - int max_s = luaL_opt_int(4, srcl+1); +static int str_gsub (lua_State *L) { + size_t srcl; + const char *src = luaL_check_lstr(L, 1, &srcl); + const char *p = luaL_check_string(L, 2); + int max_s = luaL_opt_int(L, 4, srcl+1); int anchor = (*p == '^') ? (p++, 1) : 0; int n = 0; struct Capture cap; - luaL_arg_check(lua_isstring(newp) || lua_isfunction(newp), 3, - "string or function expected"); - luaL_resetbuffer(); + luaL_Buffer b; + luaL_arg_check(L, + lua_gettop(L) >= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), + 3, "string or function expected"); + luaL_buffinit(L, &b); cap.src_end = src+srcl; while (n < max_s) { - char *e; + const char *e; cap.level = 0; - e = match(src, p, &cap); + e = match(L, src, p, &cap); if (e) { n++; - add_s(newp, &cap); + add_s(L, &b, &cap); } if (e && e>src) /* non empty match? */ src = e; /* skip it */ else if (src < cap.src_end) - luaL_addchar(*src++); + luaL_putchar(&b, *src++); else break; if (anchor) break; } - addnchar(src, cap.src_end-src); - closeandpush(); - lua_pushnumber(n); /* number of substitutions */ + luaL_addlstring(&b, src, cap.src_end-src); + luaL_pushresult(&b); + lua_pushnumber(L, n); /* number of substitutions */ + return 2; } /* }====================================================== */ -static void luaI_addquoted (int arg) { - long l; - char *s = luaL_check_lstr(arg, &l); - luaL_addchar('"'); +static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_check_lstr(L, arg, &l); + luaL_putchar(b, '"'); while (l--) { switch (*s) { case '"': case '\\': case '\n': - luaL_addchar('\\'); - luaL_addchar(*s); + luaL_putchar(b, '\\'); + luaL_putchar(b, *s); break; - case '\0': addnchar("\\000", 4); break; - default: luaL_addchar(*s); + case '\0': luaL_addlstring(b, "\\000", 4); break; + default: luaL_putchar(b, *s); } s++; } - luaL_addchar('"'); + luaL_putchar(b, '"'); } +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 /* maximum size of each format specification (such as '%-099.99d') */ -#define MAX_FORMAT 20 /* arbitrary limit */ +#define MAX_FORMAT 20 -static void str_format (void) { +static int str_format (lua_State *L) { int arg = 1; - char *strfrmt = luaL_check_string(arg); - luaL_resetbuffer(); + const char *strfrmt = luaL_check_string(L, arg); + luaL_Buffer b; + luaL_buffinit(L, &b); while (*strfrmt) { if (*strfrmt != '%') - luaL_addchar(*strfrmt++); + luaL_putchar(&b, *strfrmt++); else if (*++strfrmt == '%') - luaL_addchar(*strfrmt++); /* %% */ + luaL_putchar(&b, *strfrmt++); /* %% */ else { /* format item */ struct Capture cap; char form[MAX_FORMAT]; /* to store the format ('%...') */ - char *buff; /* to store the formatted item */ - char *initf = strfrmt; + char buff[MAX_ITEM]; /* to store the formatted item */ + const char *initf = strfrmt; form[0] = '%'; if (isdigit((unsigned char)*initf) && *(initf+1) == '$') { arg = *initf - '0'; @@ -526,33 +553,33 @@ static void str_format (void) { arg++; cap.src_end = strfrmt+strlen(strfrmt)+1; cap.level = 0; - strfrmt = match(initf, "[-+ #0]*(%d*)%.?(%d*)", &cap); + strfrmt = match(L, initf, "[-+ #0]*(%d*)%.?(%d*)", &cap); if (cap.capture[0].len > 2 || cap.capture[1].len > 2 || /* < 100? */ strfrmt-initf > MAX_FORMAT-2) - lua_error("invalid format (width or precision too long)"); + lua_error(L, "invalid format (width or precision too long)"); strncpy(form+1, initf, strfrmt-initf+1); /* +1 to include conversion */ form[strfrmt-initf+2] = 0; - buff = luaL_openspace(512); /* 512 > size of format('%99.99f', -1e308) */ switch (*strfrmt++) { case 'c': case 'd': case 'i': - sprintf(buff, form, luaL_check_int(arg)); + sprintf(buff, form, luaL_check_int(L, arg)); break; case 'o': case 'u': case 'x': case 'X': - sprintf(buff, form, (unsigned int)luaL_check_number(arg)); + sprintf(buff, form, (unsigned int)luaL_check_number(L, arg)); break; case 'e': case 'E': case 'f': case 'g': case 'G': - sprintf(buff, form, luaL_check_number(arg)); + sprintf(buff, form, luaL_check_number(L, arg)); break; case 'q': - luaI_addquoted(arg); + luaI_addquoted(L, &b, arg); continue; /* skip the "addsize" at the end */ case 's': { - long l; - char *s = luaL_check_lstr(arg, &l); + size_t l; + const char *s = luaL_check_lstr(L, arg, &l); if (cap.capture[1].len == 0 && l >= 100) { - /* no precision and string is too big to be formatted; + /* no precision and string is too long to be formatted; keep original string */ - addnchar(s, l); + lua_pushvalue(L, arg); + luaL_addvalue(&b); continue; /* skip the "addsize" at the end */ } else { @@ -561,16 +588,17 @@ static void str_format (void) { } } default: /* also treat cases 'pnLlh' */ - lua_error("invalid option in `format'"); + lua_error(L, "invalid option in `format'"); } - luaL_addsize(strlen(buff)); + luaL_addlstring(&b, buff, strlen(buff)); } } - closeandpush(); /* push the result */ + luaL_pushresult(&b); + return 1; } -static struct luaL_reg strlib[] = { +static const struct luaL_reg strlib[] = { {"strlen", str_len}, {"strsub", str_sub}, {"strlower", str_lower}, @@ -588,7 +616,6 @@ static struct luaL_reg strlib[] = { /* ** Open string library */ -void strlib_open (void) -{ - luaL_openlib(strlib, (sizeof(strlib)/sizeof(strlib[0]))); +LUALIB_API void lua_strlibopen (lua_State *L) { + luaL_openl(L, strlib); } @@ -1,71 +1,85 @@ /* -** $Id: llex.c,v 1.36 1999/06/17 17:04:03 roberto Exp $ +** $Id: llex.c,v 1.72 2000/10/20 16:39:03 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #include <ctype.h> +#include <stdio.h> #include <string.h> -#include "lauxlib.h" +#include "lua.h" + #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" +#include "ltable.h" #include "luadebug.h" #include "lzio.h" -#define next(LS) (LS->current = zgetc(LS->lex_z)) - +#define next(LS) (LS->current = zgetc(LS->z)) -#define save(c) luaL_addchar(c) -#define save_and_next(LS) (save(LS->current), next(LS)) -char *reserved [] = {"and", "do", "else", "elseif", "end", "function", - "if", "local", "nil", "not", "or", "repeat", "return", "then", - "until", "while"}; +/* ORDER RESERVED */ +static const char *const token2string [] = { + "and", "break", "do", "else", "elseif", "end", "for", + "function", "if", "local", "nil", "not", "or", "repeat", "return", "then", + "until", "while", "", "..", "...", "==", ">=", "<=", "~=", "", "", "<eof>"}; -void luaX_init (void) { +void luaX_init (lua_State *L) { int i; - for (i=0; i<(sizeof(reserved)/sizeof(reserved[0])); i++) { - TaggedString *ts = luaS_new(reserved[i]); - ts->head.marked = FIRST_RESERVED+i; /* reserved word (always > 255) */ + for (i=0; i<NUM_RESERVED; i++) { + TString *ts = luaS_new(L, token2string[i]); + ts->marked = (unsigned char)(RESERVEDMARK+i); /* reserved word */ } } #define MAXSRC 80 -void luaX_syntaxerror (LexState *ls, char *s, char *token) { + +void luaX_checklimit (LexState *ls, int val, int limit, const char *msg) { + if (val > limit) { + char buff[100]; + sprintf(buff, "too many %.50s (limit=%d)", msg, limit); + luaX_error(ls, buff, ls->t.token); + } +} + + +void luaX_syntaxerror (LexState *ls, const char *s, const char *token) { char buff[MAXSRC]; - luaL_chunkid(buff, zname(ls->lex_z), sizeof(buff)); - if (token[0] == '\0') - token = "<eof>"; - luaL_verror("%.100s;\n last token read: `%.50s' at line %d in %.80s", + luaO_chunkid(buff, ls->source->str, sizeof(buff)); + luaO_verror(ls->L, "%.99s;\n last token read: `%.30s' at line %d in %.80s", s, token, ls->linenumber, buff); } -void luaX_error (LexState *ls, char *s) { - save('\0'); - luaX_syntaxerror(ls, s, luaL_buffer()); +void luaX_error (LexState *ls, const char *s, int token) { + char buff[TOKEN_LEN]; + luaX_token2str(token, buff); + if (buff[0] == '\0') + luaX_syntaxerror(ls, s, ls->L->Mbuffer); + else + luaX_syntaxerror(ls, s, buff); } void luaX_token2str (int token, char *s) { - if (token < 255) { + if (token < 256) { s[0] = (char)token; s[1] = '\0'; } else - strcpy(s, reserved[token-FIRST_RESERVED]); + strcpy(s, token2string[token-FIRST_RESERVED]); } @@ -76,200 +90,199 @@ static void luaX_invalidchar (LexState *ls, int c) { } -static void firstline (LexState *LS) -{ - int c = zgetc(LS->lex_z); - if (c == '#') - while ((c=zgetc(LS->lex_z)) != '\n' && c != EOZ) /* skip first line */; - zungetc(LS->lex_z); +static void inclinenumber (LexState *LS) { + next(LS); /* skip '\n' */ + ++LS->linenumber; + luaX_checklimit(LS, LS->linenumber, MAX_INT, "lines in a chunk"); } -void luaX_setinput (LexState *LS, ZIO *z) -{ - LS->current = '\n'; - LS->linenumber = 0; - LS->iflevel = 0; - LS->ifstate[0].skip = 0; - LS->ifstate[0].elsepart = 1; /* to avoid a free $else */ - LS->lex_z = z; +void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source) { + LS->L = L; + LS->lookahead.token = TK_EOS; /* no look-ahead token */ + LS->z = z; LS->fs = NULL; - firstline(LS); - luaL_resetbuffer(); + LS->linenumber = 1; + LS->lastline = 1; + LS->source = source; + next(LS); /* read first char */ + if (LS->current == '#') { + do { /* skip first line */ + next(LS); + } while (LS->current != '\n' && LS->current != EOZ); + } } /* ** ======================================================= -** PRAGMAS +** LEXICAL ANALYZER ** ======================================================= */ -#ifndef PRAGMASIZE -#define PRAGMASIZE 80 /* arbitrary limit */ -#endif -static void skipspace (LexState *LS) { - while (LS->current == ' ' || LS->current == '\t' || LS->current == '\r') - next(LS); -} +/* use Mbuffer to store names, literal strings and numbers */ +#define EXTRABUFF 128 +#define checkbuffer(L, n, len) if ((len)+(n) > L->Mbuffsize) \ + luaO_openspace(L, (len)+(n)+EXTRABUFF) -static int checkcond (LexState *LS, char *buff) { - static char *opts[] = {"nil", "1", NULL}; - int i = luaL_findstring(buff, opts); - if (i >= 0) return i; - else if (isalpha((unsigned char)buff[0]) || buff[0] == '_') - return luaS_globaldefined(buff); - else { - luaX_syntaxerror(LS, "invalid $if condition", buff); - return 0; /* to avoid warnings */ - } -} +#define save(L, c, l) (L->Mbuffer[l++] = (char)c) +#define save_and_next(L, LS, l) (save(L, LS->current, l), next(LS)) -static void readname (LexState *LS, char *buff) { - int i = 0; - skipspace(LS); - while (isalnum(LS->current) || LS->current == '_') { - if (i >= PRAGMASIZE) { - buff[PRAGMASIZE] = 0; - luaX_syntaxerror(LS, "pragma too long", buff); - } - buff[i++] = (char)LS->current; - next(LS); - } - buff[i] = 0; +static const char *readname (LexState *LS) { + lua_State *L = LS->L; + size_t l = 0; + checkbuffer(L, 10, l); + do { + checkbuffer(L, 10, l); + save_and_next(L, LS, l); + } while (isalnum(LS->current) || LS->current == '_'); + save(L, '\0', l); + return L->Mbuffer; } -static void inclinenumber (LexState *LS); - - -static void ifskip (LexState *LS) { - while (LS->ifstate[LS->iflevel].skip) { - if (LS->current == '\n') - inclinenumber(LS); - else if (LS->current == EOZ) - luaX_error(LS, "input ends inside a $if"); - else next(LS); +/* LUA_NUMBER */ +static void read_number (LexState *LS, int comma, SemInfo *seminfo) { + lua_State *L = LS->L; + size_t l = 0; + checkbuffer(L, 10, l); + if (comma) save(L, '.', l); + while (isdigit(LS->current)) { + checkbuffer(L, 10, l); + save_and_next(L, LS, l); } -} - - -static void inclinenumber (LexState *LS) { - static char *pragmas [] = - {"debug", "nodebug", "endinput", "end", "ifnot", "if", "else", NULL}; - next(LS); /* skip '\n' */ - ++LS->linenumber; - if (LS->current == '$') { /* is a pragma? */ - char buff[PRAGMASIZE+1]; - int ifnot = 0; - int skip = LS->ifstate[LS->iflevel].skip; - next(LS); /* skip $ */ - readname(LS, buff); - switch (luaL_findstring(buff, pragmas)) { - case 0: /* debug */ - if (!skip) L->debug = 1; - break; - case 1: /* nodebug */ - if (!skip) L->debug = 0; - break; - case 2: /* endinput */ - if (!skip) { - LS->current = EOZ; - LS->iflevel = 0; /* to allow $endinput inside a $if */ - } - break; - case 3: /* end */ - if (LS->iflevel-- == 0) - luaX_syntaxerror(LS, "unmatched $end", "$end"); - break; - case 4: /* ifnot */ - ifnot = 1; - /* go through */ - case 5: /* if */ - if (LS->iflevel == MAX_IFS-1) - luaX_syntaxerror(LS, "too many nested $ifs", "$if"); - readname(LS, buff); - LS->iflevel++; - LS->ifstate[LS->iflevel].elsepart = 0; - LS->ifstate[LS->iflevel].condition = checkcond(LS, buff) ? !ifnot : ifnot; - LS->ifstate[LS->iflevel].skip = skip || !LS->ifstate[LS->iflevel].condition; - break; - case 6: /* else */ - if (LS->ifstate[LS->iflevel].elsepart) - luaX_syntaxerror(LS, "unmatched $else", "$else"); - LS->ifstate[LS->iflevel].elsepart = 1; - LS->ifstate[LS->iflevel].skip = LS->ifstate[LS->iflevel-1].skip || - LS->ifstate[LS->iflevel].condition; - break; - default: - luaX_syntaxerror(LS, "unknown pragma", buff); + if (LS->current == '.') { + save_and_next(L, LS, l); + if (LS->current == '.') { + save_and_next(L, LS, l); + save(L, '\0', l); + luaX_error(LS, "ambiguous syntax" + " (decimal point x string concatenation)", TK_NUMBER); } - skipspace(LS); - if (LS->current == '\n') /* pragma must end with a '\n' ... */ - inclinenumber(LS); - else if (LS->current != EOZ) /* or eof */ - luaX_syntaxerror(LS, "invalid pragma format", buff); - ifskip(LS); } + while (isdigit(LS->current)) { + checkbuffer(L, 10, l); + save_and_next(L, LS, l); + } + if (LS->current == 'e' || LS->current == 'E') { + save_and_next(L, LS, l); /* read 'E' */ + if (LS->current == '+' || LS->current == '-') + save_and_next(L, LS, l); /* optional exponent sign */ + while (isdigit(LS->current)) { + checkbuffer(L, 10, l); + save_and_next(L, LS, l); + } + } + save(L, '\0', l); + if (!luaO_str2d(L->Mbuffer, &seminfo->r)) + luaX_error(LS, "malformed number", TK_NUMBER); } - -/* -** ======================================================= -** LEXICAL ANALIZER -** ======================================================= -*/ - - - -static int read_long_string (LexState *LS) { +static void read_long_string (LexState *LS, SemInfo *seminfo) { + lua_State *L = LS->L; int cont = 0; + size_t l = 0; + checkbuffer(L, 10, l); + save(L, '[', l); /* save first '[' */ + save_and_next(L, LS, l); /* pass the second '[' */ for (;;) { + checkbuffer(L, 10, l); switch (LS->current) { case EOZ: - luaX_error(LS, "unfinished long string"); - return EOS; /* to avoid warnings */ + save(L, '\0', l); + luaX_error(LS, "unfinished long string", TK_STRING); + break; /* to avoid warnings */ case '[': - save_and_next(LS); + save_and_next(L, LS, l); if (LS->current == '[') { cont++; - save_and_next(LS); + save_and_next(L, LS, l); } continue; case ']': - save_and_next(LS); + save_and_next(L, LS, l); if (LS->current == ']') { if (cont == 0) goto endloop; cont--; - save_and_next(LS); + save_and_next(L, LS, l); } continue; case '\n': - save('\n'); + save(L, '\n', l); inclinenumber(LS); continue; default: - save_and_next(LS); + save_and_next(L, LS, l); } } endloop: - save_and_next(LS); /* skip the second ']' */ - LS->seminfo.ts = luaS_newlstr(L->Mbuffer+(L->Mbuffbase+2), - L->Mbuffnext-L->Mbuffbase-4); - return STRING; + save_and_next(L, LS, l); /* skip the second ']' */ + save(L, '\0', l); + seminfo->ts = luaS_newlstr(L, L->Mbuffer+2, l-5); } -int luaX_lex (LexState *LS) { - luaL_resetbuffer(); +static void read_string (LexState *LS, int del, SemInfo *seminfo) { + lua_State *L = LS->L; + size_t l = 0; + checkbuffer(L, 10, l); + save_and_next(L, LS, l); + while (LS->current != del) { + checkbuffer(L, 10, l); + switch (LS->current) { + case EOZ: case '\n': + save(L, '\0', l); + luaX_error(LS, "unfinished string", TK_STRING); + break; /* to avoid warnings */ + case '\\': + next(LS); /* do not save the '\' */ + switch (LS->current) { + case 'a': save(L, '\a', l); next(LS); break; + case 'b': save(L, '\b', l); next(LS); break; + case 'f': save(L, '\f', l); next(LS); break; + case 'n': save(L, '\n', l); next(LS); break; + case 'r': save(L, '\r', l); next(LS); break; + case 't': save(L, '\t', l); next(LS); break; + case 'v': save(L, '\v', l); next(LS); break; + case '\n': save(L, '\n', l); inclinenumber(LS); break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + int c = 0; + int i = 0; + do { + c = 10*c + (LS->current-'0'); + next(LS); + } while (++i<3 && isdigit(LS->current)); + if (c != (unsigned char)c) { + save(L, '\0', l); + luaX_error(LS, "escape sequence too large", TK_STRING); + } + save(L, c, l); + break; + } + default: /* handles \\, \", \', and \? */ + save_and_next(L, LS, l); + } + break; + default: + save_and_next(L, LS, l); + } + } + save_and_next(L, LS, l); /* skip delimiter */ + save(L, '\0', l); + seminfo->ts = luaS_newlstr(L, L->Mbuffer+1, l-3); +} + + +int luaX_lex (LexState *LS, SemInfo *seminfo) { for (;;) { switch (LS->current) { - case ' ': case '\t': case '\r': /* CR: to avoid problems with DOS */ + case ' ': case '\t': case '\r': /* `\r' to avoid problems with DOS */ next(LS); continue; @@ -277,159 +290,89 @@ int luaX_lex (LexState *LS) { inclinenumber(LS); continue; + case '$': + luaX_error(LS, "unexpected `$' (pragmas are no longer supported)", '$'); + break; + case '-': - save_and_next(LS); + next(LS); if (LS->current != '-') return '-'; do { next(LS); } while (LS->current != '\n' && LS->current != EOZ); - luaL_resetbuffer(); continue; case '[': - save_and_next(LS); + next(LS); if (LS->current != '[') return '['; else { - save_and_next(LS); /* pass the second '[' */ - return read_long_string(LS); + read_long_string(LS, seminfo); + return TK_STRING; } case '=': - save_and_next(LS); + next(LS); if (LS->current != '=') return '='; - else { save_and_next(LS); return EQ; } + else { next(LS); return TK_EQ; } case '<': - save_and_next(LS); + next(LS); if (LS->current != '=') return '<'; - else { save_and_next(LS); return LE; } + else { next(LS); return TK_LE; } case '>': - save_and_next(LS); + next(LS); if (LS->current != '=') return '>'; - else { save_and_next(LS); return GE; } + else { next(LS); return TK_GE; } case '~': - save_and_next(LS); + next(LS); if (LS->current != '=') return '~'; - else { save_and_next(LS); return NE; } + else { next(LS); return TK_NE; } case '"': - case '\'': { - int del = LS->current; - save_and_next(LS); - while (LS->current != del) { - switch (LS->current) { - case EOZ: - case '\n': - luaX_error(LS, "unfinished string"); - return EOS; /* to avoid warnings */ - case '\\': - next(LS); /* do not save the '\' */ - switch (LS->current) { - case 'a': save('\a'); next(LS); break; - case 'b': save('\b'); next(LS); break; - case 'f': save('\f'); next(LS); break; - case 'n': save('\n'); next(LS); break; - case 'r': save('\r'); next(LS); break; - case 't': save('\t'); next(LS); break; - case 'v': save('\v'); next(LS); break; - case '\n': save('\n'); inclinenumber(LS); break; - default : { - if (isdigit(LS->current)) { - int c = 0; - int i = 0; - do { - c = 10*c + (LS->current-'0'); - next(LS); - } while (++i<3 && isdigit(LS->current)); - if (c != (unsigned char)c) - luaX_error(LS, "escape sequence too large"); - save(c); - } - else { /* handles \, ", ', and ? */ - save(LS->current); - next(LS); - } - break; - } - } - break; - default: - save_and_next(LS); - } - } - save_and_next(LS); /* skip delimiter */ - LS->seminfo.ts = luaS_newlstr(L->Mbuffer+(L->Mbuffbase+1), - L->Mbuffnext-L->Mbuffbase-2); - return STRING; - } + case '\'': + read_string(LS, LS->current, seminfo); + return TK_STRING; case '.': - save_and_next(LS); - if (LS->current == '.') - { - save_and_next(LS); - if (LS->current == '.') - { - save_and_next(LS); - return DOTS; /* ... */ + next(LS); + if (LS->current == '.') { + next(LS); + if (LS->current == '.') { + next(LS); + return TK_DOTS; /* ... */ } - else return CONC; /* .. */ + else return TK_CONCAT; /* .. */ } else if (!isdigit(LS->current)) return '.'; - goto fraction; /* LS->current is a digit: goes through to number */ + else { + read_number(LS, 1, seminfo); + return TK_NUMBER; + } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - do { - save_and_next(LS); - } while (isdigit(LS->current)); - if (LS->current == '.') { - save_and_next(LS); - if (LS->current == '.') { - save('.'); - luaX_error(LS, - "ambiguous syntax (decimal point x string concatenation)"); - } - } - fraction: - while (isdigit(LS->current)) - save_and_next(LS); - if (toupper(LS->current) == 'E') { - save_and_next(LS); /* read 'E' */ - save_and_next(LS); /* read '+', '-' or first digit */ - while (isdigit(LS->current)) - save_and_next(LS); - } - save('\0'); - LS->seminfo.r = luaO_str2d(L->Mbuffer+L->Mbuffbase); - if (LS->seminfo.r < 0) - luaX_error(LS, "invalid numeric format"); - return NUMBER; + read_number(LS, 0, seminfo); + return TK_NUMBER; case EOZ: - if (LS->iflevel > 0) - luaX_error(LS, "input ends inside a $if"); - return EOS; + return TK_EOS; + + case '_': goto tname; default: - if (LS->current != '_' && !isalpha(LS->current)) { + if (!isalpha(LS->current)) { int c = LS->current; if (iscntrl(c)) luaX_invalidchar(LS, c); - save_and_next(LS); + next(LS); return c; } - else { /* identifier or reserved word */ - TaggedString *ts; - do { - save_and_next(LS); - } while (isalnum(LS->current) || LS->current == '_'); - save('\0'); - ts = luaS_new(L->Mbuffer+L->Mbuffbase); - if (ts->head.marked >= FIRST_RESERVED) - return ts->head.marked; /* reserved word */ - LS->seminfo.ts = ts; - return NAME; + tname: { /* identifier or reserved word */ + TString *ts = luaS_new(LS->L, readname(LS)); + if (ts->marked >= RESERVEDMARK) /* reserved word? */ + return ts->marked-RESERVEDMARK+FIRST_RESERVED; + seminfo->ts = ts; + return TK_NAME; } } } @@ -1,5 +1,5 @@ /* -** $Id: llex.h,v 1.12 1999/06/17 17:04:03 roberto Exp $ +** $Id: llex.h,v 1.31 2000/09/27 17:41:58 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -11,53 +11,61 @@ #include "lzio.h" -#define FIRST_RESERVED 260 +#define FIRST_RESERVED 257 -/* maximum length of a reserved word (+1 for terminal 0) */ +/* maximum length of a reserved word (+1 for final 0) */ #define TOKEN_LEN 15 + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ enum RESERVED { /* terminal symbols denoted by reserved words */ - AND = FIRST_RESERVED, - DO, ELSE, ELSEIF, END, FUNCTION, IF, LOCAL, NIL, NOT, OR, - REPEAT, RETURN, THEN, UNTIL, WHILE, + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FOR, TK_FUNCTION, TK_IF, TK_LOCAL, + TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_UNTIL, TK_WHILE, /* other terminal symbols */ - NAME, CONC, DOTS, EQ, GE, LE, NE, NUMBER, STRING, EOS}; + TK_NAME, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, + TK_STRING, TK_EOS +}; +/* number of reserved words */ +#define NUM_RESERVED ((int)(TK_WHILE-FIRST_RESERVED+1)) -#ifndef MAX_IFS -#define MAX_IFS 5 /* arbitrary limit */ -#endif -/* "ifstate" keeps the state of each nested $if the lexical is dealing with. */ +typedef union { + Number r; + TString *ts; +} SemInfo; /* semantics information */ -struct ifState { - int elsepart; /* true if it's in the $else part */ - int condition; /* true if $if condition is true */ - int skip; /* true if part must be skipped */ -}; + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; typedef struct LexState { - int current; /* look ahead character */ - int token; /* look ahead token */ - struct FuncState *fs; /* 'FuncState' is private for the parser */ - union { - real r; - TaggedString *ts; - } seminfo; /* semantics information */ - struct zio *lex_z; /* input stream */ + int current; /* current character */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* `FuncState' is private to the parser */ + struct lua_State *L; + struct zio *z; /* input stream */ int linenumber; /* input line counter */ - int iflevel; /* level of nested $if's (for lexical analysis) */ - struct ifState ifstate[MAX_IFS]; + int lastline; /* line of last token `consumed' */ + TString *source; /* current source name */ } LexState; -void luaX_init (void); -void luaX_setinput (LexState *LS, ZIO *z); -int luaX_lex (LexState *LS); -void luaX_syntaxerror (LexState *ls, char *s, char *token); -void luaX_error (LexState *ls, char *s); +void luaX_init (lua_State *L); +void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source); +int luaX_lex (LexState *LS, SemInfo *seminfo); +void luaX_checklimit (LexState *ls, int val, int limit, const char *msg); +void luaX_syntaxerror (LexState *ls, const char *s, const char *token); +void luaX_error (LexState *ls, const char *s, int token); void luaX_token2str (int token, char *s); diff --git a/src/llimits.h b/src/llimits.h new file mode 100644 index 00000000..b3f5de47 --- /dev/null +++ b/src/llimits.h @@ -0,0 +1,204 @@ +/* +** $Id: llimits.h,v 1.19 2000/10/26 12:47:05 roberto Exp $ +** Limits, basic types, and some other "installation-dependent" definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include <limits.h> +#include <stddef.h> + + + +/* +** try to find number of bits in an integer +*/ +#ifndef BITS_INT +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define BITS_INT 16 +#else +#if INT_MAX > 2147483640L +/* machine has at least 32 bits */ +#define BITS_INT 32 +#else +#error "you must define BITS_INT with number of bits in an integer" +#endif +#endif +#endif + + +/* +** Define the type `number' of Lua +** GREP LUA_NUMBER to change that +*/ +#ifndef LUA_NUM_TYPE +#define LUA_NUM_TYPE double +#endif + +typedef LUA_NUM_TYPE Number; + +/* function to convert a Number to a string */ +#define NUMBER_FMT "%.16g" /* LUA_NUMBER */ +#define lua_number2str(s,n) sprintf((s), NUMBER_FMT, (n)) + +/* function to convert a string to a Number */ +#define lua_str2number(s,p) strtod((s), (p)) + + + +typedef unsigned long lint32; /* unsigned int with at least 32 bits */ + + +#define MAX_SIZET ((size_t)(~(size_t)0)-2) + + +#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ + +/* +** conversion of pointer to int (for hashing only) +** (the shift removes bits that are usually 0 because of alignment) +*/ +#define IntPoint(p) (((unsigned long)(p)) >> 3) + + + +#define MINPOWER2 4 /* minimum size for "growing" vectors */ + + + +#ifndef DEFAULT_STACK_SIZE +#define DEFAULT_STACK_SIZE 1024 +#endif + + + +/* type to ensure maximum alignment */ +union L_Umaxalign { double d; char *s; long l; }; + + + +/* +** type for virtual-machine instructions +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +** For a very small machine, you may change that to 2 bytes (and adjust +** the following limits accordingly) +*/ +typedef unsigned long Instruction; + + +/* +** size and position of opcode arguments. +** For an instruction with 2 bytes, size is 16, and size_b can be 5 +** (accordingly, size_u will be 10, and size_a will be 5) +*/ +#define SIZE_INSTRUCTION 32 +#define SIZE_B 9 + +#define SIZE_OP 6 +#define SIZE_U (SIZE_INSTRUCTION-SIZE_OP) +#define POS_U SIZE_OP +#define POS_B SIZE_OP +#define SIZE_A (SIZE_INSTRUCTION-(SIZE_OP+SIZE_B)) +#define POS_A (SIZE_OP+SIZE_B) + + +/* +** limits for opcode arguments. +** we use (signed) int to manipulate most arguments, +** so they must fit in BITS_INT-1 bits (-1 for sign) +*/ +#if SIZE_U < BITS_INT-1 +#define MAXARG_U ((1<<SIZE_U)-1) +#define MAXARG_S (MAXARG_U>>1) /* `S' is signed */ +#else +#define MAXARG_U MAX_INT +#define MAXARG_S MAX_INT +#endif + +#if SIZE_A < BITS_INT-1 +#define MAXARG_A ((1<<SIZE_A)-1) +#else +#define MAXARG_A MAX_INT +#endif + +#if SIZE_B < BITS_INT-1 +#define MAXARG_B ((1<<SIZE_B)-1) +#else +#define MAXARG_B MAX_INT +#endif + + +/* maximum stack size in a function */ +#ifndef MAXSTACK +#define MAXSTACK 250 +#endif + +#if MAXSTACK > MAXARG_B +#undef MAXSTACK +#define MAXSTACK MAXARG_B +#endif + + +/* maximum number of local variables */ +#ifndef MAXLOCALS +#define MAXLOCALS 200 /* arbitrary limit (<MAXSTACK) */ +#endif +#if MAXLOCALS>=MAXSTACK +#undef MAXLOCALS +#define MAXLOCALS (MAXSTACK-1) +#endif + + +/* maximum number of upvalues */ +#ifndef MAXUPVALUES +#define MAXUPVALUES 32 /* arbitrary limit (<=MAXARG_B) */ +#endif +#if MAXUPVALUES>MAXARG_B +#undef MAXUPVALUES +#define MAXUPVALUES MAXARG_B +#endif + + +/* maximum number of variables in the left side of an assignment */ +#ifndef MAXVARSLH +#define MAXVARSLH 100 /* arbitrary limit (<MULT_RET) */ +#endif +#if MAXVARSLH>=MULT_RET +#undef MAXVARSLH +#define MAXVARSLH (MULT_RET-1) +#endif + + +/* maximum number of parameters in a function */ +#ifndef MAXPARAMS +#define MAXPARAMS 100 /* arbitrary limit (<MAXLOCALS) */ +#endif +#if MAXPARAMS>=MAXLOCALS +#undef MAXPARAMS +#define MAXPARAMS (MAXLOCALS-1) +#endif + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 64 +#if LFIELDS_PER_FLUSH>(MAXSTACK/4) +#undef LFIELDS_PER_FLUSH +#define LFIELDS_PER_FLUSH (MAXSTACK/4) +#endif + +/* number of record items to accumulate before a SETMAP instruction */ +/* (each item counts 2 elements on the stack: an index and a value) */ +#define RFIELDS_PER_FLUSH (LFIELDS_PER_FLUSH/2) + + +/* maximum lookback to find a real constant (for code generation) */ +#ifndef LOOKBACKNUMS +#define LOOKBACKNUMS 20 /* arbitrary constant */ +#endif + + +#endif @@ -1,5 +1,5 @@ /* -** $Id: lmem.c,v 1.17 1999/05/24 17:51:05 roberto Exp $ +** $Id: lmem.c,v 1.39 2000/10/30 16:29:59 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -7,134 +7,144 @@ #include <stdlib.h> -#include "lmem.h" -#include "lstate.h" #include "lua.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" -/* -** real ANSI systems do not need these tests; -** but some systems (Sun OS) are not that ANSI... -*/ -#ifdef OLD_ANSI -#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s)) -#define free(b) if (b) (free)(b) -#endif - - -#define MINSIZE 8 /* minimum size for "growing" vectors */ - - - - -static unsigned long power2 (unsigned long n) { - unsigned long p = MINSIZE; - while (p<=n) p<<=1; - return p; -} - - -void *luaM_growaux (void *block, unsigned long nelems, int inc, int size, - char *errormsg, unsigned long limit) { - unsigned long newn = nelems+inc; - if (newn >= limit) lua_error(errormsg); - if ((newn ^ nelems) <= nelems || /* still the same power of 2 limit? */ - (nelems > 0 && newn < MINSIZE)) /* or block already is MINSIZE? */ - return block; /* do not need to reallocate */ - else /* it crossed a power of 2 boundary; grow to next power */ - return luaM_realloc(block, power2(newn)*size); -} -#ifndef DEBUG +#ifdef LUA_DEBUG /* -** generic allocation routine. +** {====================================================================== +** Controlled version for realloc. +** ======================================================================= */ -void *luaM_realloc (void *block, unsigned long size) { - size_t s = (size_t)size; - if (s != size) - lua_error("memory allocation error: block too big"); - if (size == 0) { - free(block); /* block may be NULL, that is OK for free */ - return NULL; - } - block = realloc(block, s); - if (block == NULL) - lua_error(memEM); - return block; -} +#include <assert.h> +#include <limits.h> +#include <string.h> -#else -/* DEBUG */ +#define realloc(b, s) debug_realloc(b, s) +#define malloc(b) debug_realloc(NULL, b) +#define free(b) debug_realloc(b, 0) -#include <string.h> +/* ensures maximum alignment for HEADER */ +#define HEADER (sizeof(union L_Umaxalign)) -#define HEADER (sizeof(double)) #define MARKSIZE 16 - -#define MARK 55 +#define MARK 0x55 /* 01010101 (a nice pattern) */ #define blocksize(b) ((unsigned long *)((char *)(b) - HEADER)) -unsigned long numblocks = 0; -unsigned long totalmem = 0; +unsigned long memdebug_numblocks = 0; +unsigned long memdebug_total = 0; +unsigned long memdebug_maxmem = 0; +unsigned long memdebug_memlimit = LONG_MAX; static void *checkblock (void *block) { - if (block == NULL) - return NULL; - else { - unsigned long *b = blocksize(block); - unsigned long size = *b; - int i; - for (i=0;i<MARKSIZE;i++) - LUA_ASSERT(*(((char *)b)+HEADER+size+i) == MARK+i, "corrupted block"); - numblocks--; - totalmem -= size; - return b; - } + unsigned long *b = blocksize(block); + unsigned long size = *b; + int i; + for (i=0;i<MARKSIZE;i++) + assert(*(((char *)b)+HEADER+size+i) == MARK+i); /* corrupted block? */ + memdebug_numblocks--; + memdebug_total -= size; + return b; } static void freeblock (void *block) { - if (block) - memset(block, -1, *blocksize(block)); /* erase block */ - free(checkblock(block)); + if (block) { + size_t size = *blocksize(block); + block = checkblock(block); + memset(block, -1, size+HEADER+MARKSIZE); /* erase block */ + (free)(block); /* free original block */ + } } -void *luaM_realloc (void *block, unsigned long size) { - unsigned long realsize = HEADER+size+MARKSIZE; - if (realsize != (size_t)realsize) - lua_error("memory allocation error: block too big"); +static void *debug_realloc (void *block, size_t size) { if (size == 0) { freeblock(block); return NULL; } + else if (memdebug_total+size > memdebug_memlimit) + return NULL; /* to test memory allocation errors */ else { - char *newblock = malloc(realsize); + size_t realsize = HEADER+size+MARKSIZE; + char *newblock = (char *)(malloc)(realsize); /* alloc a new block */ int i; + if (realsize < size) return NULL; /* overflow! */ + if (newblock == NULL) return NULL; if (block) { - unsigned long oldsize = *blocksize(block); + size_t oldsize = *blocksize(block); if (oldsize > size) oldsize = size; memcpy(newblock+HEADER, block, oldsize); freeblock(block); /* erase (and check) old copy */ } - if (newblock == NULL) - lua_error(memEM); - totalmem += size; - numblocks++; + memdebug_total += size; + if (memdebug_total > memdebug_maxmem) memdebug_maxmem = memdebug_total; + memdebug_numblocks++; *(unsigned long *)newblock = size; for (i=0;i<MARKSIZE;i++) - *(newblock+HEADER+size+i) = MARK+i; + *(newblock+HEADER+size+i) = (char)(MARK+i); return newblock+HEADER; } } +/* }====================================================================== */ +#endif + + + +/* +** Real ISO (ANSI) systems do not need these tests; +** but some systems (Sun OS) are not that ISO... +*/ +#ifdef OLD_ANSI +#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s)) +#define free(b) if (b) (free)(b) #endif + + +void *luaM_growaux (lua_State *L, void *block, size_t nelems, + int inc, size_t size, const char *errormsg, size_t limit) { + size_t newn = nelems+inc; + if (nelems >= limit-inc) lua_error(L, errormsg); + if ((newn ^ nelems) <= nelems || /* still the same power-of-2 limit? */ + (nelems > 0 && newn < MINPOWER2)) /* or block already is MINPOWER2? */ + return block; /* do not need to reallocate */ + else /* it crossed a power-of-2 boundary; grow to next power */ + return luaM_realloc(L, block, luaO_power2(newn)*size); +} + + +/* +** generic allocation routine. +*/ +void *luaM_realloc (lua_State *L, void *block, lint32 size) { + if (size == 0) { + free(block); /* block may be NULL; that is OK for free */ + return NULL; + } + else if (size >= MAX_SIZET) + lua_error(L, "memory allocation error: block too big"); + block = realloc(block, size); + if (block == NULL) { + if (L) + luaD_breakrun(L, LUA_ERRMEM); /* break run without error message */ + else return NULL; /* error before creating state! */ + } + return block; +} + + @@ -1,5 +1,5 @@ /* -** $Id: lmem.h,v 1.8 1999/02/26 15:48:55 roberto Exp $ +** $Id: lmem.h,v 1.16 2000/10/30 16:29:59 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -8,32 +8,33 @@ #define lmem_h -#include <stdlib.h> +#include <stddef.h> -/* memory error messages */ -#define codeEM "code size overflow" -#define constantEM "constant table overflow" -#define refEM "reference table overflow" -#define tableEM "table overflow" -#define memEM "not enough memory" -#define arrEM "internal array bigger than `int' limit" +#include "llimits.h" +#include "lua.h" -void *luaM_realloc (void *oldblock, unsigned long size); -void *luaM_growaux (void *block, unsigned long nelems, int inc, int size, - char *errormsg, unsigned long limit); +void *luaM_realloc (lua_State *L, void *oldblock, lint32 size); +void *luaM_growaux (lua_State *L, void *block, size_t nelems, + int inc, size_t size, const char *errormsg, + size_t limit); -#define luaM_free(b) luaM_realloc((b), 0) -#define luaM_malloc(t) luaM_realloc(NULL, (t)) -#define luaM_new(t) ((t *)luaM_malloc(sizeof(t))) -#define luaM_newvector(n,t) ((t *)luaM_malloc((n)*sizeof(t))) -#define luaM_growvector(v,nelems,inc,t,e,l) \ - ((v)=(t *)luaM_growaux(v,nelems,inc,sizeof(t),e,l)) -#define luaM_reallocvector(v,n,t) ((v)=(t *)luaM_realloc(v,(n)*sizeof(t))) +#define luaM_free(L, b) luaM_realloc(L, (b), 0) +#define luaM_malloc(L, t) luaM_realloc(L, NULL, (t)) +#define luaM_new(L, t) ((t *)luaM_malloc(L, sizeof(t))) +#define luaM_newvector(L, n,t) ((t *)luaM_malloc(L, (n)*(lint32)sizeof(t))) +#define luaM_growvector(L, v,nelems,inc,t,e,l) \ + ((v)=(t *)luaM_growaux(L, v,nelems,inc,sizeof(t),e,l)) -#ifdef DEBUG -extern unsigned long numblocks; -extern unsigned long totalmem; +#define luaM_reallocvector(L, v,n,t) \ + ((v)=(t *)luaM_realloc(L, v,(n)*(lint32)sizeof(t))) + + +#ifdef LUA_DEBUG +extern unsigned long memdebug_numblocks; +extern unsigned long memdebug_total; +extern unsigned long memdebug_maxmem; +extern unsigned long memdebug_memlimit; #endif diff --git a/src/lobject.c b/src/lobject.c index 0225e2d8..e787fbe8 100644 --- a/src/lobject.c +++ b/src/lobject.c @@ -1,129 +1,125 @@ /* -** $Id: lobject.c,v 1.19 1999/04/13 19:28:49 roberto Exp $ +** $Id: lobject.c,v 1.55 2000/10/20 16:36:32 roberto Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ #include <ctype.h> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> -#include "lobject.h" #include "lua.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" -char *luaO_typenames[] = { /* ORDER LUA_T */ - "userdata", "number", "string", "table", "function", "function", - "nil", "function", "mark", "mark", "mark", "line", NULL -}; -TObject luaO_nilobject = {LUA_T_NIL, {NULL}}; +const TObject luaO_nilobject = {LUA_TNIL, {NULL}}; +const char *const luaO_typenames[] = { + "userdata", "nil", "number", "string", "table", "function" +}; -/* hash dimensions values */ -static long dimensions[] = - {5L, 11L, 23L, 47L, 97L, 197L, 397L, 797L, 1597L, 3203L, 6421L, - 12853L, 25717L, 51437L, 102811L, 205619L, 411233L, 822433L, - 1644817L, 3289613L, 6579211L, 13158023L, MAX_INT}; -int luaO_redimension (int oldsize) -{ - int i; - for (i=0; dimensions[i]<MAX_INT; i++) { - if (dimensions[i] > oldsize) - return dimensions[i]; - } - lua_error("tableEM"); - return 0; /* to avoid warnings */ +/* +** returns smaller power of 2 larger than `n' (minimum is MINPOWER2) +*/ +lint32 luaO_power2 (lint32 n) { + lint32 p = MINPOWER2; + while (p<=n) p<<=1; + return p; } -int luaO_equalval (TObject *t1, TObject *t2) { +int luaO_equalObj (const TObject *t1, const TObject *t2) { + if (ttype(t1) != ttype(t2)) return 0; switch (ttype(t1)) { - case LUA_T_NIL: return 1; - case LUA_T_NUMBER: return nvalue(t1) == nvalue(t2); - case LUA_T_STRING: case LUA_T_USERDATA: return svalue(t1) == svalue(t2); - case LUA_T_ARRAY: return avalue(t1) == avalue(t2); - case LUA_T_PROTO: return tfvalue(t1) == tfvalue(t2); - case LUA_T_CPROTO: return fvalue(t1) == fvalue(t2); - case LUA_T_CLOSURE: return t1->value.cl == t2->value.cl; + case LUA_TNUMBER: + return nvalue(t1) == nvalue(t2); + case LUA_TSTRING: case LUA_TUSERDATA: + return tsvalue(t1) == tsvalue(t2); + case LUA_TTABLE: + return hvalue(t1) == hvalue(t2); + case LUA_TFUNCTION: + return clvalue(t1) == clvalue(t2); default: - LUA_INTERNALERROR("invalid type"); - return 0; /* UNREACHABLE */ + LUA_ASSERT(ttype(t1) == LUA_TNIL, "invalid type"); + return 1; /* LUA_TNIL */ } } -void luaO_insertlist (GCnode *root, GCnode *node) -{ - node->next = root->next; - root->next = node; - node->marked = 0; +char *luaO_openspace (lua_State *L, size_t n) { + if (n > L->Mbuffsize) { + luaM_reallocvector(L, L->Mbuffer, n, char); + L->nblocks += (n - L->Mbuffsize)*sizeof(char); + L->Mbuffsize = n; + } + return L->Mbuffer; } -#ifdef OLD_ANSI -void luaO_memup (void *dest, void *src, int size) { - while (size--) - ((char *)dest)[size]=((char *)src)[size]; -} - -void luaO_memdown (void *dest, void *src, int size) { - int i; - for (i=0; i<size; i++) - ((char *)dest)[i]=((char *)src)[i]; +int luaO_str2d (const char *s, Number *result) { /* LUA_NUMBER */ + char *endptr; + Number res = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* no conversion */ + while (isspace((unsigned char)*endptr)) endptr++; + if (*endptr != '\0') return 0; /* invalid trailing characters? */ + *result = res; + return 1; } -#endif +/* maximum length of a string format for `luaO_verror' */ +#define MAX_VERROR 280 -static double expten (unsigned int e) { - double exp = 10.0; - double res = 1.0; - for (; e; e>>=1) { - if (e & 1) res *= exp; - exp *= exp; - } - return res; +/* this function needs to handle only '%d' and '%.XXs' formats */ +void luaO_verror (lua_State *L, const char *fmt, ...) { + va_list argp; + char buff[MAX_VERROR]; /* to hold formatted message */ + va_start(argp, fmt); + vsprintf(buff, fmt, argp); + va_end(argp); + lua_error(L, buff); } -double luaO_str2d (char *s) { /* LUA_NUMBER */ - double a = 0.0; - int point = 0; - while (isdigit((unsigned char)*s)) { - a = 10.0*a + (*(s++)-'0'); +void luaO_chunkid (char *out, const char *source, int bufflen) { + if (*source == '=') { + strncpy(out, source+1, bufflen); /* remove first char */ + out[bufflen-1] = '\0'; /* ensures null termination */ } - if (*s == '.') { - s++; - while (isdigit((unsigned char)*s)) { - a = 10.0*a + (*(s++)-'0'); - point++; + else { + if (*source == '@') { + int l; + source++; /* skip the `@' */ + bufflen -= sizeof("file `...%s'"); + l = strlen(source); + if (l>bufflen) { + source += (l-bufflen); /* get last part of file name */ + sprintf(out, "file `...%.99s'", source); + } + else + sprintf(out, "file `%.99s'", source); } - } - if (toupper((unsigned char)*s) == 'E') { - int e = 0; - int sig = 1; - s++; - if (*s == '-') { - s++; - sig = -1; + else { + int len = strcspn(source, "\n"); /* stop at first newline */ + bufflen -= sizeof("string \"%.*s...\""); + if (len > bufflen) len = bufflen; + if (source[len] != '\0') { /* must truncate? */ + strcpy(out, "string \""); + out += strlen(out); + strncpy(out, source, len); + strcpy(out+len, "...\""); + } + else + sprintf(out, "string \"%.99s\"", source); } - else if (*s == '+') s++; - if (!isdigit((unsigned char)*s)) return -1; /* no digit in the exponent? */ - do { - e = 10*e + (*(s++)-'0'); - } while (isdigit((unsigned char)*s)); - point -= sig*e; } - while (isspace((unsigned char)*s)) s++; - if (*s != '\0') return -1; /* invalid trailing characters? */ - if (point > 0) - a /= expten(point); - else if (point < 0) - a *= expten(-point); - return a; } - diff --git a/src/lobject.h b/src/lobject.h index f3b21477..cb232c77 100644 --- a/src/lobject.h +++ b/src/lobject.h @@ -1,5 +1,5 @@ /* -** $Id: lobject.h,v 1.28 1999/03/16 16:43:27 roberto Exp $ +** $Id: lobject.h,v 1.82 2000/10/30 17:49:19 roberto Exp $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ @@ -8,197 +8,197 @@ #define lobject_h -#include <limits.h> - +#include "llimits.h" #include "lua.h" -#ifdef DEBUG -#include "lauxlib.h" -#define LUA_INTERNALERROR(s) \ - luaL_verror("INTERNAL ERROR - %s [%s:%d]",(s),__FILE__,__LINE__) -#define LUA_ASSERT(c,s) { if (!(c)) LUA_INTERNALERROR(s); } +#ifdef LUA_DEBUG +#undef NDEBUG +#include <assert.h> +#define LUA_INTERNALERROR(s) assert(((void)s,0)) +#define LUA_ASSERT(c,s) assert(((void)s,(c))) #else -#define LUA_INTERNALERROR(s) /* empty */ -#define LUA_ASSERT(c,s) /* empty */ +#define LUA_INTERNALERROR(s) /* empty */ +#define LUA_ASSERT(c,s) /* empty */ #endif -/* -** "real" is the type "number" of Lua -** GREP LUA_NUMBER to change that -*/ -#ifndef LUA_NUM_TYPE -#define LUA_NUM_TYPE double +#ifdef LUA_DEBUG +/* to avoid warnings, and make sure value is really unused */ +#define UNUSED(x) (x=0, (void)(x)) +#else +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ #endif -typedef LUA_NUM_TYPE real; - -#define Byte lua_Byte /* some systems have Byte as a predefined type */ -typedef unsigned char Byte; /* unsigned 8 bits */ - +/* mark for closures active in the stack */ +#define LUA_TMARK 6 -#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ -typedef unsigned int IntPoint; /* unsigned with same size as a pointer (for hashing) */ +/* tags for values visible from Lua == first user-created tag */ +#define NUM_TAGS 6 -/* -** Lua TYPES -** WARNING: if you change the order of this enumeration, -** grep "ORDER LUA_T" -*/ -typedef enum { - LUA_T_USERDATA = 0, /* tag default for userdata */ - LUA_T_NUMBER = -1, /* fixed tag for numbers */ - LUA_T_STRING = -2, /* fixed tag for strings */ - LUA_T_ARRAY = -3, /* tag default for tables (or arrays) */ - LUA_T_PROTO = -4, /* fixed tag for functions */ - LUA_T_CPROTO = -5, /* fixed tag for Cfunctions */ - LUA_T_NIL = -6, /* last "pre-defined" tag */ - LUA_T_CLOSURE = -7, - LUA_T_CLMARK = -8, /* mark for closures */ - LUA_T_PMARK = -9, /* mark for Lua prototypes */ - LUA_T_CMARK = -10, /* mark for C prototypes */ - LUA_T_LINE = -11 -} lua_Type; - -#define NUM_TAGS 7 +/* check whether `t' is a mark */ +#define is_T_MARK(t) ((t) == LUA_TMARK) typedef union { - lua_CFunction f; /* LUA_T_CPROTO, LUA_T_CMARK */ - real n; /* LUA_T_NUMBER */ - struct TaggedString *ts; /* LUA_T_STRING, LUA_T_USERDATA */ - struct TProtoFunc *tf; /* LUA_T_PROTO, LUA_T_PMARK */ - struct Closure *cl; /* LUA_T_CLOSURE, LUA_T_CLMARK */ - struct Hash *a; /* LUA_T_ARRAY */ - int i; /* LUA_T_LINE */ + struct TString *ts; /* LUA_TSTRING, LUA_TUSERDATA */ + struct Closure *cl; /* LUA_TFUNCTION */ + struct Hash *a; /* LUA_TTABLE */ + struct CallInfo *i; /* LUA_TLMARK */ + Number n; /* LUA_TNUMBER */ } Value; -typedef struct TObject { - lua_Type ttype; +/* Macros to access values */ +#define ttype(o) ((o)->ttype) +#define nvalue(o) ((o)->value.n) +#define tsvalue(o) ((o)->value.ts) +#define clvalue(o) ((o)->value.cl) +#define hvalue(o) ((o)->value.a) +#define infovalue(o) ((o)->value.i) +#define svalue(o) (tsvalue(o)->str) + + +typedef struct lua_TObject { + int ttype; Value value; } TObject; - /* -** generic header for garbage collector lists +** String headers for string table */ -typedef struct GCnode { - struct GCnode *next; - int marked; -} GCnode; - /* -** String headers for string table +** most `malloc' libraries allocate memory in blocks of 8 bytes. TSPACK +** tries to make sizeof(TString) a multiple of this granularity, to reduce +** waste of space. */ +#define TSPACK ((int)sizeof(int)) -typedef struct TaggedString { - GCnode head; - unsigned long hash; - int constindex; /* hint to reuse constants (= -1 if this is a userdata) */ +typedef struct TString { union { - struct { - TObject globalval; - long len; /* if this is a string, here is its length */ + struct { /* for strings */ + unsigned long hash; + int constindex; /* hint to reuse constants */ } s; - struct { + struct { /* for userdata */ int tag; - void *v; /* if this is a userdata, here is its value */ + void *value; } d; } u; - char str[1]; /* \0 byte already reserved */ -} TaggedString; - - + size_t len; + struct TString *nexthash; /* chain for hash table */ + int marked; + char str[TSPACK]; /* variable length string!! must be the last field! */ +} TString; /* ** Function Prototypes */ -typedef struct TProtoFunc { - GCnode head; - struct TObject *consts; - int nconsts; - Byte *code; /* ends with opcode ENDCODE */ +typedef struct Proto { + Number *knum; /* Number numbers used by the function */ + int nknum; /* size of `knum' */ + struct TString **kstr; /* strings used by the function */ + int nkstr; /* size of `kstr' */ + struct Proto **kproto; /* functions defined inside the function */ + int nkproto; /* size of `kproto' */ + Instruction *code; + int ncode; /* size of `code'; when 0 means an incomplete `Proto' */ + short numparams; + short is_vararg; + short maxstacksize; + short marked; + struct Proto *next; + /* debug information */ + int *lineinfo; /* map from opcodes to source lines */ + int nlineinfo; /* size of `lineinfo' */ + int nlocvars; + struct LocVar *locvars; /* information about local variables */ int lineDefined; - TaggedString *source; - struct LocVar *locvars; /* ends with line = -1 */ -} TProtoFunc; + TString *source; +} Proto; + typedef struct LocVar { - TaggedString *varname; /* NULL signals end of scope */ - int line; + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ } LocVar; - - - -/* Macros to access structure members */ -#define ttype(o) ((o)->ttype) -#define nvalue(o) ((o)->value.n) -#define svalue(o) ((o)->value.ts->str) -#define tsvalue(o) ((o)->value.ts) -#define clvalue(o) ((o)->value.cl) -#define avalue(o) ((o)->value.a) -#define fvalue(o) ((o)->value.f) -#define tfvalue(o) ((o)->value.tf) - -#define protovalue(o) ((o)->value.cl->consts) - - /* ** Closures */ typedef struct Closure { - GCnode head; - int nelems; /* not included the first one (always the prototype) */ - TObject consts[1]; /* at least one for prototype */ + union { + lua_CFunction c; /* C functions */ + struct Proto *l; /* Lua functions */ + } f; + struct Closure *next; + struct Closure *mark; /* marked closures (point to itself when not marked) */ + short isC; /* 0 for Lua functions, 1 for C functions */ + short nupvalues; + TObject upvalue[1]; } Closure; +#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->isC) + -typedef struct node { - TObject ref; +typedef struct Node { + TObject key; TObject val; + struct Node *next; /* for chaining */ } Node; typedef struct Hash { - GCnode head; Node *node; - int nhash; - int nuse; int htag; + int size; + Node *firstfree; /* this position is free; all positions after it are full */ + struct Hash *next; + struct Hash *mark; /* marked tables (point to itself when not marked) */ } Hash; -extern char *luaO_typenames[]; +/* unmarked tables and closures are represented by pointing `mark' to +** themselves +*/ +#define ismarked(x) ((x)->mark != (x)) + -#define luaO_typename(o) luaO_typenames[-ttype(o)] +/* +** informations about a call (for debugging) +*/ +typedef struct CallInfo { + struct Closure *func; /* function being called */ + const Instruction **pc; /* current pc of called function */ + int lastpc; /* last pc traced */ + int line; /* current line */ + int refi; /* current index in `lineinfo' */ +} CallInfo; -extern TObject luaO_nilobject; +extern const TObject luaO_nilobject; +extern const char *const luaO_typenames[]; -#define luaO_equalObj(t1,t2) ((ttype(t1) != ttype(t2)) ? 0 \ - : luaO_equalval(t1,t2)) -int luaO_equalval (TObject *t1, TObject *t2); -int luaO_redimension (int oldsize); -void luaO_insertlist (GCnode *root, GCnode *node); -double luaO_str2d (char *s); -#ifdef OLD_ANSI -void luaO_memup (void *dest, void *src, int size); -void luaO_memdown (void *dest, void *src, int size); -#else -#include <string.h> -#define luaO_memup(d,s,n) memmove(d,s,n) -#define luaO_memdown(d,s,n) memmove(d,s,n) -#endif +#define luaO_typename(o) (luaO_typenames[ttype(o)]) + + +lint32 luaO_power2 (lint32 n); +char *luaO_openspace (lua_State *L, size_t n); + +int luaO_equalObj (const TObject *t1, const TObject *t2); +int luaO_str2d (const char *s, Number *result); + +void luaO_verror (lua_State *L, const char *fmt, ...); +void luaO_chunkid (char *out, const char *source, int len); + #endif diff --git a/src/lopcodes.h b/src/lopcodes.h index 6a59b39e..2df72ce7 100644 --- a/src/lopcodes.h +++ b/src/lopcodes.h @@ -1,5 +1,5 @@ /* -** $Id: lopcodes.h,v 1.33 1999/06/17 17:04:03 roberto Exp $ +** $Id: lopcodes.h,v 1.68 2000/10/24 16:05:59 roberto Exp $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ @@ -7,132 +7,162 @@ #ifndef lopcodes_h #define lopcodes_h +#include "llimits.h" -/* -** NOTICE: variants of the same opcode must be consecutive: First, those -** with word parameter, then with byte parameter. -*/ +/*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. Moreover, + an instruction can have 0, 1, or 2 arguments. Instructions can + have the following types: + type 0: no arguments + type 1: 1 unsigned argument in the higher bits (called `U') + type 2: 1 signed argument in the higher bits (`S') + type 3: 1st unsigned argument in the higher bits (`A') + 2nd unsigned argument in the middle bits (`B') -typedef enum { -/* name parm before after side effect ------------------------------------------------------------------------------*/ -ENDCODE,/* - - (return) */ -RETCODE,/* b - (return) */ + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. -CALL,/* b c v_c...v_1 f r_b...r_1 f(v1,...,v_c) */ + The size of each argument is defined in `llimits.h'. The usual is an + instruction with 32 bits, U arguments with 26 bits (32-6), B arguments + with 9 bits, and A arguments with 17 bits (32-6-9). For small + installations, the instruction size can be 16, so U has 10 bits, + and A and B have 5 bits each. +===========================================================================*/ -TAILCALL,/* b c v_c...v_1 f (return) f(v1,...,v_c) */ -PUSHNIL,/* b - nil_0...nil_b */ -POP,/* b a_b...a_1 - */ -PUSHNUMBERW,/* w - (float)w */ -PUSHNUMBER,/* b - (float)b */ -PUSHNUMBERNEGW,/* w - (float)-w */ -PUSHNUMBERNEG,/* b - (float)-b */ +/* creates a mask with `n' 1 bits at position `p' */ +#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p) -PUSHCONSTANTW,/*w - CNST[w] */ -PUSHCONSTANT,/* b - CNST[b] */ +/* creates a mask with `n' 0 bits at position `p' */ +#define MASK0(n,p) (~MASK1(n,p)) -PUSHUPVALUE,/* b - Closure[b] */ +/* +** the following macros help to manipulate instructions +*/ -PUSHLOCAL,/* b - LOC[b] */ +#define CREATE_0(o) ((Instruction)(o)) +#define GET_OPCODE(i) ((OpCode)((i)&MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,0)) | (Instruction)(o))) -GETGLOBALW,/* w - VAR[CNST[w]] */ -GETGLOBAL,/* b - VAR[CNST[b]] */ +#define CREATE_U(o,u) ((Instruction)(o) | ((Instruction)(u)<<POS_U)) +#define GETARG_U(i) ((int)((i)>>POS_U)) +#define SETARG_U(i,u) ((i) = (((i)&MASK0(SIZE_U,POS_U)) | \ + ((Instruction)(u)<<POS_U))) -GETTABLE,/* - i t t[i] */ +#define CREATE_S(o,s) CREATE_U((o),(s)+MAXARG_S) +#define GETARG_S(i) (GETARG_U(i)-MAXARG_S) +#define SETARG_S(i,s) SETARG_U((i),(s)+MAXARG_S) -GETDOTTEDW,/* w t t[CNST[w]] */ -GETDOTTED,/* b t t[CNST[b]] */ -PUSHSELFW,/* w t t t[CNST[w]] */ -PUSHSELF,/* b t t t[CNST[b]] */ +#define CREATE_AB(o,a,b) ((Instruction)(o) | ((Instruction)(a)<<POS_A) \ + | ((Instruction)(b)<<POS_B)) +#define GETARG_A(i) ((int)((i)>>POS_A)) +#define SETARG_A(i,a) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ + ((Instruction)(a)<<POS_A))) +#define GETARG_B(i) ((int)(((i)>>POS_B) & MASK1(SIZE_B,0))) +#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ + ((Instruction)(b)<<POS_B))) -CREATEARRAYW,/* w - newarray(size = w) */ -CREATEARRAY,/* b - newarray(size = b) */ -SETLOCAL,/* b x - LOC[b]=x */ +/* +** K = U argument used as index to `kstr' +** J = S argument used as jump offset (relative to pc of next instruction) +** L = unsigned argument used as index of local variable +** N = U argument used as index to `knum' +*/ -SETGLOBALW,/* w x - VAR[CNST[w]]=x */ -SETGLOBAL,/* b x - VAR[CNST[b]]=x */ +typedef enum { +/*---------------------------------------------------------------------- +name args stack before stack after side effects +------------------------------------------------------------------------*/ +OP_END,/* - - (return) no results */ +OP_RETURN,/* U v_n-v_x(at u) (return) returns v_x-v_n */ -SETTABLEPOP,/* - v i t - t[i]=v */ +OP_CALL,/* A B v_n-v_1 f(at a) r_b-r_1 f(v1,...,v_n) */ +OP_TAILCALL,/* A B v_n-v_1 f(at a) (return) f(v1,...,v_n) */ -SETTABLE,/* b v a_b...a_1 i t a_b...a_1 i t t[i]=v */ +OP_PUSHNIL,/* U - nil_1-nil_u */ +OP_POP,/* U a_u-a_1 - */ -SETLISTW,/* w c v_c...v_1 t t t[i+w*FPF]=v_i */ -SETLIST,/* b c v_c...v_1 t t t[i+b*FPF]=v_i */ +OP_PUSHINT,/* S - (Number)s */ +OP_PUSHSTRING,/* K - KSTR[k] */ +OP_PUSHNUM,/* N - KNUM[n] */ +OP_PUSHNEGNUM,/* N - -KNUM[n] */ -SETMAP,/* b v_b k_b ...v_0 k_0 t t t[k_i]=v_i */ +OP_PUSHUPVALUE,/* U - Closure[u] */ -NEQOP,/* - y x (x~=y)? 1 : nil */ -EQOP,/* - y x (x==y)? 1 : nil */ -LTOP,/* - y x (x<y)? 1 : nil */ -LEOP,/* - y x (x<y)? 1 : nil */ -GTOP,/* - y x (x>y)? 1 : nil */ -GEOP,/* - y x (x>=y)? 1 : nil */ -ADDOP,/* - y x x+y */ -SUBOP,/* - y x x-y */ -MULTOP,/* - y x x*y */ -DIVOP,/* - y x x/y */ -POWOP,/* - y x x^y */ -CONCOP,/* - y x x..y */ -MINUSOP,/* - x -x */ -NOTOP,/* - x (x==nil)? 1 : nil */ +OP_GETLOCAL,/* L - LOC[l] */ +OP_GETGLOBAL,/* K - VAR[KSTR[k]] */ -ONTJMPW,/* w x (x!=nil)? x : - (x!=nil)? PC+=w */ -ONTJMP,/* b x (x!=nil)? x : - (x!=nil)? PC+=b */ -ONFJMPW,/* w x (x==nil)? x : - (x==nil)? PC+=w */ -ONFJMP,/* b x (x==nil)? x : - (x==nil)? PC+=b */ -JMPW,/* w - - PC+=w */ -JMP,/* b - - PC+=b */ -IFFJMPW,/* w x - (x==nil)? PC+=w */ -IFFJMP,/* b x - (x==nil)? PC+=b */ -IFTUPJMPW,/* w x - (x!=nil)? PC-=w */ -IFTUPJMP,/* b x - (x!=nil)? PC-=b */ -IFFUPJMPW,/* w x - (x==nil)? PC-=w */ -IFFUPJMP,/* b x - (x==nil)? PC-=b */ +OP_GETTABLE,/* - i t t[i] */ +OP_GETDOTTED,/* K t t[KSTR[k]] */ +OP_GETINDEXED,/* L t t[LOC[l]] */ +OP_PUSHSELF,/* K t t t[KSTR[k]] */ -CLOSUREW,/* w c v_c...v_1 closure(CNST[w], v_c...v_1) */ -CLOSURE,/* b c v_c...v_1 closure(CNST[b], v_c...v_1) */ +OP_CREATETABLE,/* U - newarray(size = u) */ -SETLINEW,/* w - - LINE=w */ -SETLINE,/* b - - LINE=b */ +OP_SETLOCAL,/* L x - LOC[l]=x */ +OP_SETGLOBAL,/* K x - VAR[KSTR[k]]=x */ +OP_SETTABLE,/* A B v a_a-a_1 i t (pops b values) t[i]=v */ -LONGARGW,/* w (add w*(1<<16) to arg of next instruction) */ -LONGARG,/* b (add b*(1<<16) to arg of next instruction) */ +OP_SETLIST,/* A B v_b-v_1 t t t[i+a*FPF]=v_i */ +OP_SETMAP,/* U v_u k_u - v_1 k_1 t t t[k_i]=v_i */ -CHECKSTACK /* b (assert #temporaries == b; only for internal debuging!) */ +OP_ADD,/* - y x x+y */ +OP_ADDI,/* S x x+s */ +OP_SUB,/* - y x x-y */ +OP_MULT,/* - y x x*y */ +OP_DIV,/* - y x x/y */ +OP_POW,/* - y x x^y */ +OP_CONCAT,/* U v_u-v_1 v1..-..v_u */ +OP_MINUS,/* - x -x */ +OP_NOT,/* - x (x==nil)? 1 : nil */ -} OpCode; +OP_JMPNE,/* J y x - (x~=y)? PC+=s */ +OP_JMPEQ,/* J y x - (x==y)? PC+=s */ +OP_JMPLT,/* J y x - (x<y)? PC+=s */ +OP_JMPLE,/* J y x - (x<y)? PC+=s */ +OP_JMPGT,/* J y x - (x>y)? PC+=s */ +OP_JMPGE,/* J y x - (x>=y)? PC+=s */ +OP_JMPT,/* J x - (x~=nil)? PC+=s */ +OP_JMPF,/* J x - (x==nil)? PC+=s */ +OP_JMPONT,/* J x (x~=nil)? x : - (x~=nil)? PC+=s */ +OP_JMPONF,/* J x (x==nil)? x : - (x==nil)? PC+=s */ +OP_JMP,/* J - - PC+=s */ -#define RFIELDS_PER_FLUSH 32 /* records (SETMAP) */ -#define LFIELDS_PER_FLUSH 64 /* FPF - lists (SETLIST) */ +OP_PUSHNILJMP,/* - - nil PC++; */ -#define ZEROVARARG 128 +OP_FORPREP,/* J */ +OP_FORLOOP,/* J */ +OP_LFORPREP,/* J */ +OP_LFORLOOP,/* J */ -/* maximum value of an arg of 3 bytes; must fit in an "int" */ -#if MAX_INT < (1<<24) -#define MAX_ARG MAX_INT -#else -#define MAX_ARG ((1<<24)-1) -#endif +OP_CLOSURE/* A B v_b-v_1 closure(KPROTO[a], v_1-v_b) */ -/* maximum value of a word of 2 bytes; cannot be bigger than MAX_ARG */ -#if MAX_ARG < (1<<16) -#define MAX_WORD MAX_ARG -#else -#define MAX_WORD ((1<<16)-1) -#endif +} OpCode; + +#define NUM_OPCODES ((int)OP_CLOSURE+1) -/* maximum value of a byte */ -#define MAX_BYTE ((1<<8)-1) +#define ISJUMP(o) (OP_JMPNE <= (o) && (o) <= OP_JMP) + + + +/* special code to fit a LUA_MULTRET inside an argB */ +#define MULT_RET 255 /* (<=MAXARG_B) */ +#if MULT_RET>MAXARG_B +#undef MULT_RET +#define MULT_RET MAXARG_B +#endif #endif diff --git a/src/lparser.c b/src/lparser.c index b0aa13f5..b792c956 100644 --- a/src/lparser.c +++ b/src/lparser.c @@ -1,5 +1,5 @@ /* -** $Id: lparser.c,v 1.37a 1999/06/17 17:04:03 roberto Exp $ +** $Id: lparser.c,v 1.116 2000/10/27 11:39:52 roberto Exp $ ** LL(1) Parser and code generator for Lua ** See Copyright Notice in lua.h */ @@ -8,1385 +8,1117 @@ #include <stdio.h> #include <string.h> -#include "lauxlib.h" -#include "ldo.h" +#include "lua.h" + +#include "lcode.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 "lua.h" -#include "luadebug.h" -#include "lzio.h" - - - -/* size of a "normal" jump instruction: OpCode + 1 byte */ -#define JMPSIZE 2 - -/* maximum number of local variables */ -#ifndef MAXLOCALS -#define MAXLOCALS 200 /* arbitrary limit (<256) */ -#endif - - -/* maximum number of upvalues */ -#ifndef MAXUPVALUES -#define MAXUPVALUES 32 /* arbitrary limit (<256) */ -#endif - - -/* maximum number of variables in the left side of an assignment */ -#ifndef MAXVARSLH -#define MAXVARSLH 100 /* arbitrary limit (<255) */ -#endif - - -/* maximum number of parameters in a function */ -#ifndef MAXPARAMS -#define MAXPARAMS 100 /* arbitrary limit (<ZEROVARARG) */ -#endif - - -/* -** Variable descriptor: -** must include an "exp" option because LL(1) cannot distinguish -** between variables, upvalues and function calls on first sight. -*/ -typedef enum { - VGLOBAL, /* info is constant index of global name */ - VLOCAL, /* info is stack index */ - VDOT, /* info is constant index of index name */ - VINDEXED, /* no info (table and index are on the stack) */ - VEXP /* info is pc index of "nparam" of a call (or 0 if exp is closed) */ -} varkind; - -typedef struct vardesc { - varkind k; - int info; -} vardesc; - - -/* -** Expression List descriptor: -** tells number of expressions in the list, -** and, if last expression is open (a function call), -** where is its pc index of "nparam" -*/ -typedef struct listdesc { - int n; - int pc; /* 0 if last expression is closed */ -} listdesc; /* ** Constructors descriptor: -** "n" indicates number of elements, and "k" signals whether +** `n' indicates number of elements, and `k' signals whether ** it is a list constructor (k = 0) or a record constructor (k = 1) ** or empty (k = ';' or '}') */ -typedef struct constdesc { +typedef struct Constdesc { int n; int k; -} constdesc; +} Constdesc; + + +typedef struct Breaklabel { + struct Breaklabel *previous; /* chain */ + int breaklist; + int stacklevel; +} Breaklabel; -/* state needed to generate code for a given function */ -typedef struct FuncState { - TProtoFunc *f; /* current function header */ - struct FuncState *prev; /* enclosuring function */ - int pc; /* next position to code */ - int stacksize; /* number of values on activation register */ - int maxstacksize; /* maximum number of values on activation register */ - int nlocalvar; /* number of active local variables */ - int nupvalues; /* number of upvalues */ - int nvars; /* number of entries in f->locvars (-1 if no debug information) */ - int lastsetline; /* line where last SETLINE was issued */ - vardesc upvalues[MAXUPVALUES]; /* upvalues */ - TaggedString *localvar[MAXLOCALS]; /* store local variable names */ -} 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 chunk (LexState *ls); static void constructor (LexState *ls); -static void decinit (LexState *ls, listdesc *d); -static void exp0 (LexState *ls, vardesc *v); +static void expr (LexState *ls, expdesc *v); 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 statlist (LexState *ls); -static void var_or_func (LexState *ls, vardesc *v); -static void var_or_func_tail (LexState *ls, vardesc *v); - - - -static void checklimit (LexState *ls, int val, int limit, char *msg) { - if (val > limit) { - char buff[100]; - sprintf(buff, "too many %s (limit=%d)", msg, limit); - luaX_error(ls, buff); - } -} - -static void check_pc (FuncState *fs, int n) { - luaM_growvector(fs->f->code, fs->pc, n, Byte, codeEM, MAX_INT); -} - - -static void code_byte (FuncState *fs, Byte c) { - check_pc(fs, 1); - fs->f->code[fs->pc++] = c; -} -static void deltastack (LexState *ls, int delta) { - FuncState *fs = ls->fs; - fs->stacksize += delta; - if (fs->stacksize > fs->maxstacksize) { - if (fs->stacksize > MAX_BYTE) - luaX_error(ls, "function/expression too complex"); - fs->maxstacksize = fs->stacksize; - } -} - - -static void code_oparg_at (LexState *ls, int pc, OpCode op, - int arg, int delta) { - Byte *code = ls->fs->f->code; - deltastack(ls, delta); - if (arg <= MAX_BYTE) { - code[pc] = (Byte)op; - code[pc+1] = (Byte)arg; - } - else if (arg > MAX_ARG) - luaX_error(ls, "code too long"); - else { /* MAX_BYTE < arg < MAX_ARG */ - if (arg > MAX_WORD) { - code[pc] = (Byte)LONGARG; - code[pc+1] = (Byte)(arg>>16); - pc += 2; - } - code[pc] = (Byte)(op-1); /* opcode for word argument */ - code[pc+1] = (Byte)((arg&0xFFFF)>>8); - code[pc+2] = (Byte)(arg&0xFF); +static void next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ } + else + ls->t.token = luaX_lex(ls, &ls->t.seminfo); /* read next token */ } -static int codesize (int arg) { - if (arg <= MAX_BYTE) return 2; /* opcode + 1 byte */ - else if (arg <= MAX_WORD) return 3; /* opcode + 1 word (2 bytes) */ - else return 5; /* LONGARG + 1 byte + opcode + 1 word (2 bytes) */ +static void lookahead (LexState *ls) { + LUA_ASSERT(ls->lookahead.token == TK_EOS, "two look-aheads"); + ls->lookahead.token = luaX_lex(ls, &ls->lookahead.seminfo); } -static int fix_opcode (LexState *ls, int pc, OpCode op, int arg) { - int tomove = codesize(arg)-2; - if (tomove > 0) { /* need to open space? */ - FuncState *fs = ls->fs; - TProtoFunc *f = fs->f; - check_pc(fs, tomove); - luaO_memup(f->code+pc+tomove, f->code+pc, fs->pc-pc); - fs->pc += tomove; - } - code_oparg_at(ls, pc, op, arg, 0); - return tomove; +static void error_expected (LexState *ls, int token) { + char buff[100], t[TOKEN_LEN]; + luaX_token2str(token, t); + sprintf(buff, "`%.20s' expected", t); + luaK_error(ls, buff); } -static void code_oparg (LexState *ls, OpCode op, int arg, int delta) { - int size = codesize(arg); - check_pc(ls->fs, size); - code_oparg_at(ls, ls->fs->pc, op, arg, delta); - ls->fs->pc += size; +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); + next(ls); } -static void code_opcode (LexState *ls, OpCode op, int delta) { - deltastack(ls, delta); - code_byte(ls->fs, (Byte)op); +static void check_condition (LexState *ls, int c, const char *msg) { + if (!c) luaK_error(ls, msg); } -static void code_constant (LexState *ls, int c) { - code_oparg(ls, PUSHCONSTANT, c, 1); +static int optional (LexState *ls, int c) { + if (ls->t.token == c) { + next(ls); + return 1; + } + else return 0; } -static int next_constant (FuncState *fs) { - TProtoFunc *f = fs->f; - luaM_growvector(f->consts, f->nconsts, 1, TObject, constantEM, MAX_ARG); - return f->nconsts++; +static void check_match (LexState *ls, int what, int who, int where) { + if (ls->t.token != what) { + if (where == ls->linenumber) + error_expected(ls, what); + else { + char buff[100]; + char t_what[TOKEN_LEN], t_who[TOKEN_LEN]; + luaX_token2str(what, t_what); + luaX_token2str(who, t_who); + sprintf(buff, "`%.20s' expected (to close `%.20s' at line %d)", + t_what, t_who, where); + luaK_error(ls, buff); + } + } + next(ls); } -static int string_constant (FuncState *fs, TaggedString *s) { - TProtoFunc *f = fs->f; - int c = s->constindex; - if (!(c < f->nconsts && - ttype(&f->consts[c]) == LUA_T_STRING && tsvalue(&f->consts[c]) == s)) { - c = next_constant(fs); - ttype(&f->consts[c]) = LUA_T_STRING; - tsvalue(&f->consts[c]) = s; - s->constindex = c; /* hint for next time */ +static int string_constant (FuncState *fs, TString *s) { + Proto *f = fs->f; + int c = s->u.s.constindex; + if (c >= f->nkstr || f->kstr[c] != s) { + luaM_growvector(fs->L, f->kstr, f->nkstr, 1, TString *, + "constant table overflow", MAXARG_U); + c = f->nkstr++; + f->kstr[c] = s; + s->u.s.constindex = c; /* hint for next time */ } return c; } -static void code_string (LexState *ls, TaggedString *s) { - code_constant(ls, string_constant(ls->fs, s)); +static void code_string (LexState *ls, TString *s) { + luaK_kstr(ls, string_constant(ls->fs, s)); } -#define LIM 20 -static int real_constant (FuncState *fs, real r) { - /* check whether 'r' has appeared within the last LIM entries */ - TObject *cnt = fs->f->consts; - int c = fs->f->nconsts; - int lim = c < LIM ? 0 : c-LIM; - while (--c >= lim) { - if (ttype(&cnt[c]) == LUA_T_NUMBER && nvalue(&cnt[c]) == r) - return c; - } - /* not found; create a new entry */ - c = next_constant(fs); - cnt = fs->f->consts; /* 'next_constant' may have reallocated this vector */ - ttype(&cnt[c]) = LUA_T_NUMBER; - nvalue(&cnt[c]) = r; - return c; -} - - -static void code_number (LexState *ls, real f) { - real af = (f<0) ? -f : f; - if (0 <= af && af <= (real)MAX_WORD && (int)af == af) { - /* abs(f) has a short integer value */ - code_oparg(ls, (f<0) ? PUSHNUMBERNEG : PUSHNUMBER, (int)af, 1); - } - else - code_constant(ls, real_constant(ls->fs, f)); +static TString *str_checkname (LexState *ls) { + TString *ts; + check_condition(ls, (ls->t.token == TK_NAME), "<name> expected"); + ts = ls->t.seminfo.ts; + next(ls); + return ts; } -static void flush_record (LexState *ls, int n) { - if (n > 0) - code_oparg(ls, SETMAP, n-1, -2*n); +static int checkname (LexState *ls) { + return string_constant(ls->fs, str_checkname(ls)); } -static void flush_list (LexState *ls, int m, int n) { - if (n > 0) { - code_oparg(ls, SETLIST, m, -n); - code_byte(ls->fs, (Byte)n); - } +static int luaI_registerlocalvar (LexState *ls, TString *varname) { + Proto *f = ls->fs->f; + luaM_growvector(ls->L, f->locvars, f->nlocvars, 1, LocVar, "", MAX_INT); + f->locvars[f->nlocvars].varname = varname; + return f->nlocvars++; } -static void luaI_registerlocalvar (FuncState *fs, TaggedString *varname, - int line) { - if (fs->nvars != -1) { /* debug information? */ - TProtoFunc *f = fs->f; - luaM_growvector(f->locvars, fs->nvars, 1, LocVar, "", MAX_INT); - f->locvars[fs->nvars].varname = varname; - f->locvars[fs->nvars].line = line; - fs->nvars++; - } +static void new_localvar (LexState *ls, TString *name, int n) { + FuncState *fs = ls->fs; + luaX_checklimit(ls, fs->nactloc+n+1, MAXLOCALS, "local variables"); + fs->actloc[fs->nactloc+n] = luaI_registerlocalvar(ls, name); } -static void luaI_unregisterlocalvar (FuncState *fs, int line) { - luaI_registerlocalvar(fs, NULL, line); +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + while (nvars--) + fs->f->locvars[fs->actloc[fs->nactloc++]].startpc = fs->pc; } -static void store_localvar (LexState *ls, TaggedString *name, int n) { +static void removelocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; - checklimit(ls, fs->nlocalvar+n+1, MAXLOCALS, "local variables"); - fs->localvar[fs->nlocalvar+n] = name; - luaI_registerlocalvar(fs, name, ls->linenumber); + while (nvars--) + fs->f->locvars[fs->actloc[--fs->nactloc]].endpc = fs->pc; } -static void add_localvar (LexState *ls, TaggedString *name) { - store_localvar(ls, name, 0); - ls->fs->nlocalvar++; +static void new_localvarstr (LexState *ls, const char *name, int n) { + new_localvar(ls, luaS_newfixed(ls->L, name), n); } -static void correctvarlines (LexState *ls, int nvars) { - FuncState *fs = ls->fs; - if (fs->nvars != -1) { /* debug information? */ - for (; nvars; nvars--) { /* correct line information */ - fs->f->locvars[fs->nvars-nvars].line = fs->lastsetline; +static int search_local (LexState *ls, TString *n, expdesc *var) { + FuncState *fs; + int level = 0; + for (fs=ls->fs; fs; fs=fs->prev) { + int i; + for (i=fs->nactloc-1; i >= 0; i--) { + if (n == fs->f->locvars[fs->actloc[i]].varname) { + var->k = VLOCAL; + var->u.index = i; + return level; + } } + level++; /* `var' not found; check outer level */ } + var->k = VGLOBAL; /* not found in any level; must be global */ + return -1; } -static int aux_localname (FuncState *fs, TaggedString *n) { - int i; - for (i=fs->nlocalvar-1; i >= 0; i--) - if (n == fs->localvar[i]) return i; /* local var index */ - return -1; /* not found */ -} - - -static void singlevar (LexState *ls, TaggedString *n, vardesc *var, int prev) { - FuncState *fs = prev ? ls->fs->prev : ls->fs; - int i = aux_localname(fs, n); - if (i >= 0) { /* local value? */ - var->k = VLOCAL; - var->info = i; - } - else { - FuncState *level = fs; - while ((level = level->prev) != NULL) /* check shadowing */ - if (aux_localname(level, n) >= 0) - luaX_syntaxerror(ls, "cannot access a variable in outer scope", n->str); - var->k = VGLOBAL; - var->info = string_constant(fs, n); - } +static void singlevar (LexState *ls, TString *n, expdesc *var) { + int level = search_local(ls, n, var); + if (level >= 1) /* neither local (0) nor global (-1)? */ + luaX_syntaxerror(ls, "cannot access a variable in outer scope", n->str); + else if (level == -1) /* global? */ + var->u.index = string_constant(ls->fs, n); } -static int indexupvalue (LexState *ls, TaggedString *n) { +static int indexupvalue (LexState *ls, expdesc *v) { FuncState *fs = ls->fs; - vardesc v; int i; - singlevar(ls, n, &v, 1); for (i=0; i<fs->nupvalues; i++) { - if (fs->upvalues[i].k == v.k && fs->upvalues[i].info == v.info) + if (fs->upvalues[i].k == v->k && fs->upvalues[i].u.index == v->u.index) return i; } /* new one */ - ++(fs->nupvalues); - checklimit(ls, fs->nupvalues, MAXUPVALUES, "upvalues"); - fs->upvalues[i] = v; /* i = fs->nupvalues - 1 */ - return i; -} - - -static void pushupvalue (LexState *ls, TaggedString *n) { - if (ls->fs->prev == NULL) - luaX_syntaxerror(ls, "cannot access upvalue in main", n->str); - if (aux_localname(ls->fs, n) >= 0) - luaX_syntaxerror(ls, "cannot access an upvalue in current scope", n->str); - code_oparg(ls, PUSHUPVALUE, indexupvalue(ls, n), 1); -} - - - -static void check_debugline (LexState *ls) { - if (L->debug && ls->linenumber != ls->fs->lastsetline) { - code_oparg(ls, SETLINE, ls->linenumber, 0); - ls->fs->lastsetline = ls->linenumber; - } -} - - -static void adjuststack (LexState *ls, int n) { - if (n > 0) - code_oparg(ls, POP, n, -n); - else if (n < 0) - code_oparg(ls, PUSHNIL, (-n)-1, -n); + luaX_checklimit(ls, fs->nupvalues+1, MAXUPVALUES, "upvalues"); + fs->upvalues[fs->nupvalues] = *v; + return fs->nupvalues++; } -static void close_exp (LexState *ls, int pc, int nresults) { - if (pc > 0) { /* expression is an open function call? */ - Byte *code = ls->fs->f->code; - code[pc-1] = (Byte)nresults; /* set nresults */ - /* push results, pop params (at code[pc]) and function */ - deltastack(ls, nresults-(code[pc]+1)); +static void pushupvalue (LexState *ls, TString *n) { + FuncState *fs = ls->fs; + expdesc v; + int level = search_local(ls, n, &v); + if (level == -1) { /* global? */ + if (fs->prev == NULL) + luaX_syntaxerror(ls, "cannot access upvalue in main", n->str); + v.u.index = string_constant(fs->prev, n); } -#ifdef DEBUG - code_oparg(ls, CHECKSTACK, ls->fs->stacksize, 0); -#endif + else if (level != 1) + luaX_syntaxerror(ls, + "upvalue must be global or local to immediately outer scope", n->str); + luaK_code1(fs, OP_PUSHUPVALUE, indexupvalue(ls, &v)); } -static void adjust_mult_assign (LexState *ls, int nvars, listdesc *d) { - int diff = d->n - nvars; - if (d->pc == 0) { /* list is closed */ - /* push or pop eventual difference between list lengths */ - adjuststack(ls, diff); - } - else { /* must correct function call */ +static void adjust_mult_assign (LexState *ls, int nvars, int nexps) { + FuncState *fs = ls->fs; + int diff = nexps - nvars; + if (nexps > 0 && luaK_lastisopen(fs)) { /* list ends in a function call */ diff--; /* do not count function call itself */ if (diff <= 0) { /* more variables than values? */ - /* function call must provide extra values */ - close_exp(ls, d->pc, -diff); - } - else { /* more values than variables */ - close_exp(ls, d->pc, 0); /* call should provide no value */ - adjuststack(ls, diff); /* pop eventual extra values */ + luaK_setcallreturns(fs, -diff); /* function call provide extra values */ + diff = 0; /* no more difference */ } + else /* more values than variables */ + luaK_setcallreturns(fs, 0); /* call should provide no value */ } + /* push or pop eventual difference between list lengths */ + luaK_adjuststack(fs, diff); } -static void code_args (LexState *ls, int nparams, int dots) { +static void code_params (LexState *ls, int nparams, int dots) { FuncState *fs = ls->fs; - fs->nlocalvar += nparams; /* "self" may already be there */ - checklimit(ls, fs->nlocalvar, MAXPARAMS, "parameters"); - nparams = fs->nlocalvar; - if (!dots) { - fs->f->code[1] = (Byte)nparams; /* fill-in arg information */ - deltastack(ls, nparams); - } - else { - fs->f->code[1] = (Byte)(nparams+ZEROVARARG); - deltastack(ls, nparams+1); - add_localvar(ls, luaS_new("arg")); + adjustlocalvars(ls, nparams); + luaX_checklimit(ls, fs->nactloc, MAXPARAMS, "parameters"); + fs->f->numparams = fs->nactloc; /* `self' could be there already */ + fs->f->is_vararg = dots; + if (dots) { + new_localvarstr(ls, "arg", 0); + adjustlocalvars(ls, 1); } + luaK_deltastack(fs, fs->nactloc); /* count parameters in the stack */ } -static void unloaddot (LexState *ls, vardesc *v) { - /* dotted variables <a.x> must be stored like regular indexed vars <a["x"]> */ - if (v->k == VDOT) { - code_constant(ls, v->info); - v->k = VINDEXED; - } +static void enterbreak (FuncState *fs, Breaklabel *bl) { + bl->stacklevel = fs->stacklevel; + bl->breaklist = NO_JUMP; + bl->previous = fs->bl; + fs->bl = bl; } -static void lua_pushvar (LexState *ls, vardesc *var) { - switch (var->k) { - case VLOCAL: - code_oparg(ls, PUSHLOCAL, var->info, 1); - break; - case VGLOBAL: - code_oparg(ls, GETGLOBAL, var->info, 1); - break; - case VDOT: - code_oparg(ls, GETDOTTED, var->info, 0); - break; - case VINDEXED: - code_opcode(ls, GETTABLE, -1); - break; - case VEXP: - close_exp(ls, var->info, 1); /* function must return 1 value */ - break; - } - var->k = VEXP; - var->info = 0; /* now this is a closed expression */ -} - - -static void storevar (LexState *ls, vardesc *var) { - switch (var->k) { - case VLOCAL: - code_oparg(ls, SETLOCAL, var->info, -1); - break; - case VGLOBAL: - code_oparg(ls, SETGLOBAL, var->info, -1); - break; - case VINDEXED: - code_opcode(ls, SETTABLEPOP, -3); - break; - default: - LUA_INTERNALERROR("invalid var kind to store"); - } -} - - -static int fix_jump (LexState *ls, int pc, OpCode op, int n) { - /* jump is relative to position following jump instruction */ - return fix_opcode(ls, pc, op, n-(pc+JMPSIZE)); -} - - -static void fix_upjmp (LexState *ls, OpCode op, int pos) { - int delta = ls->fs->pc+JMPSIZE - pos; /* jump is relative */ - code_oparg(ls, op, delta+(codesize(delta)-2), 0); +static void leavebreak (FuncState *fs, Breaklabel *bl) { + fs->bl = bl->previous; + LUA_ASSERT(bl->stacklevel == fs->stacklevel, "wrong levels"); + luaK_patchlist(fs, bl->breaklist, luaK_getlabel(fs)); } -static void codeIf (LexState *ls, int thenAdd, int elseAdd) { - FuncState *fs = ls->fs; - int elseinit = elseAdd+JMPSIZE; - if (fs->pc == elseinit) { /* no else part? */ - fs->pc -= JMPSIZE; - elseinit = fs->pc; - } - else - elseinit += fix_jump(ls, elseAdd, JMP, fs->pc); - fix_jump(ls, thenAdd, IFFJMP, elseinit); -} - - -static void func_onstack (LexState *ls, FuncState *func) { +static void pushclosure (LexState *ls, FuncState *func) { FuncState *fs = ls->fs; + Proto *f = fs->f; int i; - int c = next_constant(fs); - ttype(&fs->f->consts[c]) = LUA_T_PROTO; - fs->f->consts[c].value.tf = func->f; - if (func->nupvalues == 0) - code_constant(ls, c); - else { - for (i=0; i<func->nupvalues; i++) - lua_pushvar(ls, &func->upvalues[i]); - deltastack(ls, 1); /* CLOSURE puts one extra element (before poping) */ - code_oparg(ls, CLOSURE, c, -func->nupvalues); - code_byte(fs, (Byte)func->nupvalues); - } + for (i=0; i<func->nupvalues; i++) + luaK_tostack(ls, &func->upvalues[i], 1); + luaM_growvector(ls->L, f->kproto, f->nkproto, 1, Proto *, + "constant table overflow", MAXARG_A); + f->kproto[f->nkproto++] = func->f; + luaK_code2(fs, OP_CLOSURE, f->nkproto-1, func->nupvalues); } -static void init_state (LexState *ls, FuncState *fs, TaggedString *source) { - TProtoFunc *f = luaF_newproto(); +static void open_func (LexState *ls, FuncState *fs) { + Proto *f = luaF_newproto(ls->L); fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + fs->L = ls->L; ls->fs = fs; - fs->stacksize = 0; - fs->maxstacksize = 0; - fs->nlocalvar = 0; + fs->stacklevel = 0; + fs->nactloc = 0; fs->nupvalues = 0; - fs->lastsetline = 0; + fs->bl = NULL; fs->f = f; - f->source = source; + f->source = ls->source; fs->pc = 0; + fs->lasttarget = 0; + fs->lastline = 0; + fs->jlt = NO_JUMP; f->code = NULL; - fs->nvars = (L->debug) ? 0 : -1; /* flag no debug information? */ - code_byte(fs, 0); /* to be filled with maxstacksize */ - code_byte(fs, 0); /* to be filled with arg information */ - /* push function (to avoid GC) */ - tfvalue(L->stack.top) = f; ttype(L->stack.top) = LUA_T_PROTO; - incr_top; + f->maxstacksize = 0; + f->numparams = 0; /* default for main chunk */ + f->is_vararg = 0; /* default for main chunk */ } static void close_func (LexState *ls) { + lua_State *L = ls->L; FuncState *fs = ls->fs; - TProtoFunc *f = fs->f; - code_opcode(ls, ENDCODE, 0); - f->code[0] = (Byte)fs->maxstacksize; - luaM_reallocvector(f->code, fs->pc, Byte); - luaM_reallocvector(f->consts, f->nconsts, TObject); - if (fs->nvars != -1) { /* debug information? */ - luaI_registerlocalvar(fs, NULL, -1); /* flag end of vector */ - luaM_reallocvector(f->locvars, fs->nvars, LocVar); - } + Proto *f = fs->f; + luaK_code0(fs, OP_END); + luaK_getlabel(fs); /* close eventual list of pending jumps */ + luaM_reallocvector(L, f->code, fs->pc, Instruction); + luaM_reallocvector(L, f->kstr, f->nkstr, TString *); + luaM_reallocvector(L, f->knum, f->nknum, Number); + luaM_reallocvector(L, f->kproto, f->nkproto, Proto *); + removelocalvars(ls, fs->nactloc); + luaM_reallocvector(L, f->locvars, f->nlocvars, LocVar); + luaM_reallocvector(L, f->lineinfo, f->nlineinfo+1, int); + f->lineinfo[f->nlineinfo++] = MAX_INT; /* end flag */ + luaF_protook(L, f, fs->pc); /* proto is ok now */ ls->fs = fs->prev; - L->stack.top--; /* pop function */ + LUA_ASSERT(fs->bl == NULL, "wrong list end"); } - -static int expfollow [] = {ELSE, ELSEIF, THEN, IF, WHILE, REPEAT, DO, NAME, - LOCAL, FUNCTION, END, UNTIL, RETURN, ')', ']', '}', ';', EOS, ',', 0}; - - -static int is_in (int tok, int *toks) { - int *t; - for (t=toks; *t; t++) - if (*t == tok) return t-toks; - return -1; -} - - -static void next (LexState *ls) { - ls->token = luaX_lex(ls); -} - - -static void error_expected (LexState *ls, int token) { - char buff[100], t[TOKEN_LEN]; - luaX_token2str(token, t); - sprintf(buff, "`%s' expected", t); - luaX_error(ls, buff); -} - - -static void error_unexpected (LexState *ls) { - luaX_error(ls, "unexpected token"); -} - - -static void error_unmatched (LexState *ls, int what, int who, int where) { - if (where == ls->linenumber) - error_expected(ls, what); - else { - char buff[100]; - char t_what[TOKEN_LEN], t_who[TOKEN_LEN]; - luaX_token2str(what, t_what); - luaX_token2str(who, t_who); - sprintf(buff, "`%s' expected (to close `%s' at line %d)", - t_what, t_who, where); - luaX_error(ls, buff); - } -} - -static void check (LexState *ls, int c) { - if (ls->token != c) - error_expected(ls, c); - next(ls); -} - -static void check_match (LexState *ls, int what, int who, int where) { - if (ls->token != what) - error_unmatched(ls, what, who, where); - check_debugline(ls); /* to 'mark' the 'what' */ - next(ls); -} - -static int checkname (LexState *ls) { - int sc; - if (ls->token != NAME) - luaX_error(ls, "`NAME' expected"); - sc = string_constant(ls->fs, ls->seminfo.ts); - next(ls); - return sc; -} - - -static TaggedString *str_checkname (LexState *ls) { - int i = checkname(ls); /* this call may realloc `f->consts' */ - return tsvalue(&ls->fs->f->consts[i]); -} - - -static int optional (LexState *ls, int c) { - if (ls->token == c) { - next(ls); - return 1; - } - else return 0; -} - - -TProtoFunc *luaY_parser (ZIO *z) { +Proto *luaY_parser (lua_State *L, ZIO *z) { struct LexState lexstate; struct FuncState funcstate; - luaX_setinput(&lexstate, z); - init_state(&lexstate, &funcstate, luaS_new(zname(z))); + luaX_setinput(L, &lexstate, z, luaS_new(L, zname(z))); + open_func(&lexstate, &funcstate); next(&lexstate); /* read first token */ chunk(&lexstate); - if (lexstate.token != EOS) - luaX_error(&lexstate, "<eof> expected"); + check_condition(&lexstate, (lexstate.t.token == TK_EOS), "<eof> expected"); close_func(&lexstate); + LUA_ASSERT(funcstate.prev == NULL, "wrong list end"); + LUA_ASSERT(funcstate.nupvalues == 0, "no upvalues in main"); return funcstate.f; } /*============================================================*/ -/* GRAMAR RULES */ +/* GRAMMAR RULES */ /*============================================================*/ -static void chunk (LexState *ls) { - /* chunk -> statlist ret */ - statlist(ls); - ret(ls); -} -static void statlist (LexState *ls) { - /* statlist -> { stat [;] } */ - while (stat(ls)) { - LUA_ASSERT(ls->fs->stacksize == ls->fs->nlocalvar, - "stack size != # local vars"); - optional(ls, ';'); +static int explist1 (LexState *ls) { + /* explist1 -> expr { ',' expr } */ + int n = 1; /* at least one expression */ + expdesc v; + expr(ls, &v); + while (ls->t.token == ',') { + luaK_tostack(ls, &v, 1); /* gets only 1 value from previous expression */ + next(ls); /* skip comma */ + expr(ls, &v); + n++; } + luaK_tostack(ls, &v, 0); /* keep open number of values of last expression */ + return n; } -static int stat (LexState *ls) { - int line = ls->linenumber; /* may be needed for error messages */ - FuncState *fs = ls->fs; - switch (ls->token) { - case IF: /* stat -> IF ifpart END */ - ifpart(ls, line); - return 1; - case WHILE: { /* stat -> WHILE cond DO block END */ - TProtoFunc *f = fs->f; - int while_init = fs->pc; - int cond_end, cond_size; +static void funcargs (LexState *ls, int slf) { + FuncState *fs = ls->fs; + int slevel = fs->stacklevel - slf - 1; /* where is func in the stack */ + switch (ls->t.token) { + case '(': { /* funcargs -> '(' [ explist1 ] ')' */ + int line = ls->linenumber; + int nargs = 0; 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(fs, 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); - return 1; + if (ls->t.token != ')') /* arg list not empty? */ + nargs = explist1(ls); + check_match(ls, ')', '(', line); +#ifdef LUA_COMPAT_ARGRET + if (nargs > 0) /* arg list is not empty? */ + luaK_setcallreturns(fs, 1); /* last call returns only 1 value */ +#else + UNUSED(nargs); /* to avoid warnings */ +#endif + break; } - - case DO: { /* stat -> DO block END */ - next(ls); - block(ls); - check_match(ls, END, DO, line); - return 1; + case '{': { /* funcargs -> constructor */ + constructor(ls); + break; } - - case REPEAT: { /* stat -> REPEAT block UNTIL exp1 */ - int repeat_init = fs->pc; + case TK_STRING: { /* funcargs -> STRING */ + code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */ next(ls); - block(ls); - check_match(ls, UNTIL, REPEAT, line); - exp1(ls); - fix_upjmp(ls, IFFUPJMP, repeat_init); - deltastack(ls, -1); /* pops condition */ - return 1; + break; } - - case FUNCTION: { /* stat -> 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; + default: { + luaK_error(ls, "function arguments expected"); + break; } + } + fs->stacklevel = slevel; /* call will remove function and arguments */ + luaK_code2(fs, OP_CALL, slevel, MULT_RET); +} - case LOCAL: { /* stat -> LOCAL localnamelist decinit */ - listdesc d; - int nvars; - check_debugline(ls); - next(ls); - nvars = localnamelist(ls); - decinit(ls, &d); - fs->nlocalvar += nvars; - correctvarlines(ls, nvars); /* vars will be alive only after decinit */ - adjust_mult_assign(ls, nvars, &d); - return 1; - } - case NAME: case '%': { /* 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); +static void var_or_func_tail (LexState *ls, expdesc *v) { + for (;;) { + switch (ls->t.token) { + case '.': { /* var_or_func_tail -> '.' NAME */ + next(ls); + luaK_tostack(ls, v, 1); /* `v' must be on stack */ + luaK_kstr(ls, checkname(ls)); + v->k = VINDEXED; + break; + } + case '[': { /* var_or_func_tail -> '[' exp1 ']' */ + next(ls); + luaK_tostack(ls, v, 1); /* `v' must be on stack */ + v->k = VINDEXED; + exp1(ls); + check(ls, ']'); + break; } - else { /* stat -> ['%'] NAME assignment */ - int left = assignment(ls, &v, 1); - adjuststack(ls, left); /* remove eventual 'garbage' left on stack */ + case ':': { /* var_or_func_tail -> ':' NAME funcargs */ + int name; + next(ls); + name = checkname(ls); + luaK_tostack(ls, v, 1); /* `v' must be on stack */ + luaK_code1(ls->fs, OP_PUSHSELF, name); + funcargs(ls, 1); + v->k = VEXP; + v->u.l.t = v->u.l.f = NO_JUMP; + break; } - return 1; + case '(': case TK_STRING: case '{': { /* var_or_func_tail -> funcargs */ + luaK_tostack(ls, v, 1); /* `v' must be on stack */ + funcargs(ls, 0); + v->k = VEXP; + v->u.l.t = v->u.l.f = NO_JUMP; + break; + } + default: return; /* should be follow... */ } + } +} - 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 */ +static void var_or_func (LexState *ls, expdesc *v) { + /* var_or_func -> ['%'] NAME var_or_func_tail */ + if (optional(ls, '%')) { /* upvalue? */ + pushupvalue(ls, str_checkname(ls)); + v->k = VEXP; + v->u.l.t = v->u.l.f = NO_JUMP; } + else /* variable name */ + singlevar(ls, str_checkname(ls), v); + var_or_func_tail(ls, v); } -static int SaveWord (LexState *ls) { - int res = ls->fs->pc; - check_pc(ls->fs, JMPSIZE); - ls->fs->pc += JMPSIZE; /* open space */ - return res; -} -static int SaveWordPop (LexState *ls) { - deltastack(ls, -1); /* pop condition */ - return SaveWord(ls); -} -static int cond (LexState *ls) { - /* cond -> exp1 */ +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +static void recfield (LexState *ls) { + /* recfield -> (NAME | '['exp1']') = exp1 */ + switch (ls->t.token) { + case TK_NAME: { + luaK_kstr(ls, checkname(ls)); + break; + } + case '[': { + next(ls); + exp1(ls); + check(ls, ']'); + break; + } + default: luaK_error(ls, "<name> or `[' expected"); + } + check(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(fs, 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 { ',' recfield } [','] */ + FuncState *fs = ls->fs; + int n = 1; /* at least one element */ + recfield(ls); + while (ls->t.token == ',') { next(ls); - lua_pushvar(ls, v); - code_constant(ls, checkname(ls)); - v->k = VINDEXED; + if (ls->t.token == ';' || ls->t.token == '}') + break; + recfield(ls); + n++; + if (n%RFIELDS_PER_FLUSH == 0) + luaK_code1(fs, OP_SETMAP, RFIELDS_PER_FLUSH); } - return needself; -} - -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_new("self")); - parlist(ls); - check(ls, ')'); - chunk(ls); - check_match(ls, END, FUNCTION, line); - close_func(ls); - func_onstack(ls, &newfs); + luaK_code1(fs, OP_SETMAP, n%RFIELDS_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 int listfields (LexState *ls) { + /* listfields -> exp1 { ',' exp1 } [','] */ + FuncState *fs = ls->fs; + int n = 1; /* at least one element */ + exp1(ls); + while (ls->t.token == ',') { + next(ls); + if (ls->t.token == ';' || ls->t.token == '}') + break; + exp1(ls); + n++; + luaX_checklimit(ls, n/LFIELDS_PER_FLUSH, MAXARG_A, + "`item groups' in a list initializer"); + if (n%LFIELDS_PER_FLUSH == 0) + luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH); } - codeIf(ls, c, e); + luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH); + return n; } -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; + +static void constructor_part (LexState *ls, Constdesc *cd) { + switch (ls->t.token) { + case ';': case '}': { /* constructor_part -> empty */ + cd->n = 0; + cd->k = ls->t.token; + break; + } + case TK_NAME: { /* may be listfields or recfields */ + lookahead(ls); + if (ls->lookahead.token != '=') /* expression? */ + goto case_default; + /* else go through to recfields */ + } + case '[': { /* constructor_part -> recfields */ + cd->n = recfields(ls); + cd->k = 1; /* record */ + break; + } + default: { /* constructor_part -> listfields */ + case_default: + cd->n = listfields(ls); + cd->k = 0; /* list */ + break; } - else - code_oparg(ls, RETCODE, ls->fs->nlocalvar, 0); - ls->fs->stacksize = ls->fs->nlocalvar; /* removes all temp values */ - optional(ls, ';'); } } -/* -** For parsing expressions, we use a classic stack with priorities. -** 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. -*/ - -#define INDNOT 0 -#define INDMINUS 1 - -/* code of first binary operator */ -#define FIRSTBIN 2 - -/* code for power operator (last operator) -** '^' needs special treatment because it is right associative -*/ -#define POW 13 - -static int binop [] = {EQ, NE, '>', '<', LE, GE, CONC, - '+', '-', '*', '/', '^', 0}; - -static int priority [POW+1] = {5, 5, 1, 1, 1, 1, 1, 1, 2, 3, 3, 4, 4, 6}; - -static OpCode opcodes [POW+1] = {NOTOP, MINUSOP, EQOP, NEQOP, GTOP, LTOP, - LEOP, GEOP, CONCOP, ADDOP, SUBOP, MULTOP, DIVOP, POWOP}; - -#define MAXOPS 20 /* op's stack size (arbitrary limit) */ - -typedef struct stack_op { - int ops[MAXOPS]; - int top; -} 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, "ill-formed expression"); +static void constructor (LexState *ls) { + /* constructor -> '{' constructor_part [';' constructor_part] '}' */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_code1(fs, OP_CREATETABLE, 0); + int nelems; + Constdesc cd; + check(ls, '{'); + constructor_part(ls, &cd); + nelems = cd.n; + if (optional(ls, ';')) { + Constdesc other_cd; + constructor_part(ls, &other_cd); + check_condition(ls, (cd.k != other_cd.k), "invalid constructor syntax"); + nelems += other_cd.n; + } + check_match(ls, '}', '{', line); + luaX_checklimit(ls, nelems, MAXARG_U, "elements in a table constructor"); + SETARG_U(fs->f->code[pc], nelems); /* set initial table size */ } +/* }====================================================================== */ -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) { - if (s->top >= MAXOPS) - luaX_error(ls, "expression too complex"); - s->ops[s->top++] = op; -} +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ -static void pop_to (LexState *ls, stack_op *s, int prio) { - int op; - while (s->top > 0 && priority[(op=s->ops[s->top-1])] >= prio) { - code_opcode(ls, opcodes[op], op<FIRSTBIN?0:-1); - s->top--; - } -} -static void simpleexp (LexState *ls, vardesc *v, stack_op *s) { - check_debugline(ls); - switch (ls->token) { - case NUMBER: { /* simpleexp -> NUMBER */ - real r = ls->seminfo.r; +static void simpleexp (LexState *ls, expdesc *v) { + FuncState *fs = ls->fs; + switch (ls->t.token) { + case TK_NUMBER: { /* simpleexp -> NUMBER */ + Number r = ls->t.seminfo.r; next(ls); - /* dirty trick: check whether it is a -NUMBER not followed by '^' */ - /* (because the priority of '^' is closer than '-'...) */ - if (s->top > 0 && s->ops[s->top-1] == INDMINUS && ls->token != '^') { - s->top--; /* remove '-' from stack */ - r = -r; - } - code_number(ls, r); + luaK_number(fs, r); break; } - - case STRING: /* simpleexp -> STRING */ - code_string(ls, ls->seminfo.ts); /* must use 'seminfo' before "next" */ + case TK_STRING: { /* simpleexp -> STRING */ + code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */ next(ls); break; - - case NIL: /* simpleexp -> NIL */ - adjuststack(ls, -1); + } + case TK_NIL: { /* simpleexp -> NIL */ + luaK_adjuststack(fs, -1); next(ls); break; - - case '{': /* simpleexp -> constructor */ + } + case '{': { /* simpleexp -> constructor */ constructor(ls); break; - - case FUNCTION: /* simpleexp -> FUNCTION body */ + } + case TK_FUNCTION: { /* simpleexp -> FUNCTION body */ next(ls); body(ls, 0, ls->linenumber); break; - - case '(': /* simpleexp -> '(' exp0 ')' */ + } + case '(': { /* simpleexp -> '(' expr ')' */ next(ls); - exp0(ls, v); + expr(ls, v); check(ls, ')'); return; - - case NAME: case '%': + } + case TK_NAME: case '%': { var_or_func(ls, v); return; - - default: - luaX_error(ls, "<expression> expected"); + } + default: { + luaK_error(ls, "<expression> expected"); return; + } } - v->k = VEXP; v->info = 0; + v->k = VEXP; + v->u.l.t = v->u.l.f = NO_JUMP; } -static void prefixexp (LexState *ls, vardesc *v, stack_op *s) { - /* prefixexp -> {NOT | '-'} simpleexp */ - while (ls->token == NOT || ls->token == '-') { - push(ls, s, (ls->token==NOT)?INDNOT:INDMINUS); - next(ls); - } - simpleexp(ls, v, s); +static void exp1 (LexState *ls) { + expdesc v; + expr(ls, &v); + luaK_tostack(ls, &v, 1); } -static void exp2 (LexState *ls, vardesc *v) { - stack_op s; - int op; - s.top = 0; - prefixexp(ls, v, &s); - while ((op = is_in(ls->token, binop)) >= 0) { - op += FIRSTBIN; - lua_pushvar(ls, v); - /* '^' is right associative, so must 'simulate' a higher priority */ - pop_to(ls, &s, (op == POW)?priority[op]+1:priority[op]); - push(ls, &s, op); - next(ls); - prefixexp(ls, v, &s); - lua_pushvar(ls, v); - } - if (s.top > 0) { - lua_pushvar(ls, v); - pop_to(ls, &s, 0); +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + default: return OPR_NOUNOPR; } } -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 */ +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MULT; + case '/': return OPR_DIV; + 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; } - else /* variable name */ - singlevar(ls, str_checkname(ls), v, 0); - var_or_func_tail(ls, v); } -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; +static const struct { + char left; /* left priority for each binary operator */ + char right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {5, 5}, {5, 5}, {6, 6}, {6, 6}, /* arithmetic */ + {9, 8}, {4, 3}, /* power and concat (right associative) */ + {2, 2}, {2, 2}, /* equality */ + {2, 2}, {2, 2}, {2, 2}, {2, 2}, /* order */ + {1, 1}, {1, 1} /* logical */ +}; - 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; +#define UNARY_PRIORITY 7 /* priority for unary operators */ - 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... */ - } +/* +** subexpr -> (simplexep | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { + BinOpr op; + UnOpr uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { + next(ls); + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls, uop, v); + } + 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; + next(ls); + luaK_infix(ls, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls, op, v, &v2); + op = nextop; } + return op; /* return first untreated operator */ } -static int funcparams (LexState *ls, int slf) { - FuncState *fs = ls->fs; - int nparams = 1; /* in cases STRING and constructor */ - 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, 1); - nparams = e.n; - break; - } - case '{': /* funcparams -> constructor */ - constructor(ls); - break; +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, -1); +} - 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(fs, CALL); - code_byte(fs, 0); /* save space for nresult */ - code_byte(fs, (Byte)(nparams+slf)); - return fs->pc-1; -} - -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); +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +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; } } -static void explist1 (LexState *ls, listdesc *d) { - vardesc v; - exp0(ls, &v); - d->n = 1; - while (ls->token == ',') { - d->n++; - lua_pushvar(ls, &v); + +static void block (LexState *ls) { + /* block -> chunk */ + FuncState *fs = ls->fs; + int nactloc = fs->nactloc; + chunk(ls); + luaK_adjuststack(fs, fs->nactloc - nactloc); /* remove local variables */ + removelocalvars(ls, fs->nactloc - nactloc); +} + + +static int assignment (LexState *ls, expdesc *v, int nvars) { + int left = 0; /* number of values left in the stack after assignment */ + luaX_checklimit(ls, nvars, MAXVARSLH, "variables in a multiple assignment"); + if (ls->t.token == ',') { /* assignment -> ',' NAME assignment */ + expdesc nv; next(ls); - exp0(ls, &v); + var_or_func(ls, &nv); + check_condition(ls, (nv.k != VEXP), "syntax error"); + left = assignment(ls, &nv, nvars+1); + } + else { /* assignment -> '=' explist1 */ + int nexps; + check(ls, '='); + nexps = explist1(ls); + adjust_mult_assign(ls, nvars, nexps); } - if (v.k == VEXP) - d->pc = v.info; - else { - lua_pushvar(ls, &v); - d->pc = 0; + if (v->k != VINDEXED) + luaK_storevar(ls, v); + else { /* there may be garbage between table-index and value */ + luaK_code2(ls->fs, OP_SETTABLE, left+nvars+2, 1); + left += 2; } + 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; +static void cond (LexState *ls, expdesc *v) { + /* cond -> exp */ + expr(ls, v); /* read condition */ + luaK_goiftrue(ls->fs, v, 0); +} - 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; + int while_init = luaK_getlabel(fs); + expdesc v; + Breaklabel bl; + enterbreak(fs, &bl); + next(ls); + cond(ls, &v); + check(ls, TK_DO); + block(ls); + luaK_patchlist(fs, luaK_jump(fs), while_init); + luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs)); + check_match(ls, TK_END, TK_WHILE, line); + leavebreak(fs, &bl); +} - 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 cond */ + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + expdesc v; + Breaklabel bl; + enterbreak(fs, &bl); + next(ls); + block(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + cond(ls, &v); + luaK_patchlist(fs, v.u.l.f, repeat_init); + leavebreak(fs, &bl); } -static int localnamelist (LexState *ls) { - /* localnamelist -> NAME {',' NAME} */ - int i = 1; - store_localvar(ls, str_checkname(ls), 0); - while (ls->token == ',') { - next(ls); - store_localvar(ls, str_checkname(ls), i++); + +static void forbody (LexState *ls, int nvar, OpCode prepfor, OpCode loopfor) { + /* forbody -> DO block END */ + FuncState *fs = ls->fs; + int prep = luaK_code1(fs, prepfor, NO_JUMP); + int blockinit = luaK_getlabel(fs); + check(ls, TK_DO); + adjustlocalvars(ls, nvar); /* scope for control variables */ + block(ls); + luaK_patchlist(fs, luaK_code1(fs, loopfor, NO_JUMP), blockinit); + luaK_patchlist(fs, prep, luaK_getlabel(fs)); + removelocalvars(ls, nvar); +} + + +static void fornum (LexState *ls, TString *varname) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + check(ls, '='); + exp1(ls); /* initial value */ + check(ls, ','); + exp1(ls); /* limit */ + if (optional(ls, ',')) + exp1(ls); /* optional step */ + else + luaK_code1(fs, OP_PUSHINT, 1); /* default step */ + new_localvar(ls, varname, 0); + new_localvarstr(ls, "(limit)", 1); + new_localvarstr(ls, "(step)", 2); + forbody(ls, 3, OP_FORPREP, OP_FORLOOP); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME,NAME IN exp1 forbody */ + TString *valname; + check(ls, ','); + valname = str_checkname(ls); + /* next test is dirty, but avoids `in' being a reserved word */ + check_condition(ls, + (ls->t.token == TK_NAME && ls->t.seminfo.ts == luaS_new(ls->L, "in")), + "`in' expected"); + next(ls); /* skip `in' */ + exp1(ls); /* table */ + new_localvarstr(ls, "(table)", 0); + new_localvar(ls, indexname, 1); + new_localvar(ls, valname, 2); + forbody(ls, 3, OP_LFORPREP, OP_LFORLOOP); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> fornum | forlist */ + FuncState *fs = ls->fs; + TString *varname; + Breaklabel bl; + enterbreak(fs, &bl); + next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname); break; + case ',': forlist(ls, varname); break; + default: luaK_error(ls, "`=' or `,' expected"); } - return i; + check_match(ls, TK_END, TK_FOR, line); + leavebreak(fs, &bl); } -static void decinit (LexState *ls, listdesc *d) { - /* decinit -> ['=' explist1] */ - if (ls->token == '=') { - next(ls); - explist1(ls, d); + +static void test_then_block (LexState *ls, expdesc *v) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + next(ls); /* skip IF or ELSEIF */ + cond(ls, v); + check(ls, TK_THEN); + block(ls); /* `then' part */ +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + expdesc v; + int escapelist = NO_JUMP; + test_then_block(ls, &v); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs)); + test_then_block(ls, &v); /* ELSEIF cond THEN block */ } - else { - d->n = 0; - d->pc = 0; + if (ls->t.token == TK_ELSE) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs)); + next(ls); /* skip ELSE */ + block(ls); /* `else' part */ } + else + luaK_concat(fs, &escapelist, v.u.l.f); + luaK_patchlist(fs, escapelist, luaK_getlabel(fs)); + check_match(ls, TK_END, TK_IF, line); +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME {',' NAME} ['=' explist1] */ + int nvars = 0; + int nexps; + do { + next(ls); /* skip LOCAL or ',' */ + new_localvar(ls, str_checkname(ls), nvars++); + } while (ls->t.token == ','); + if (optional(ls, '=')) + nexps = explist1(ls); + else + nexps = 0; + adjust_mult_assign(ls, nvars, nexps); + adjustlocalvars(ls, nvars); } -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 int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME [':' NAME | '.' NAME] */ + int needself = 0; + singlevar(ls, str_checkname(ls), v); + if (ls->t.token == ':' || ls->t.token == '.') { + needself = (ls->t.token == ':'); next(ls); - var_or_func(ls, &nv); - if (nv.k == VEXP) - luaX_error(ls, "syntax error"); - left = assignment(ls, &nv, nvars+1); - } - else { /* assignment -> '=' explist1 */ - listdesc d; - check(ls, '='); - explist1(ls, &d); - adjust_mult_assign(ls, nvars, &d); + luaK_tostack(ls, v, 1); + luaK_kstr(ls, checkname(ls)); + v->k = VINDEXED; } - if (v->k != VINDEXED || left+(nvars-1) == 0) { - /* global/local var or indexed var without values in between */ - storevar(ls, v); + return needself; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int needself; + expdesc v; + next(ls); /* skip FUNCTION */ + needself = funcname(ls, &v); + body(ls, needself, line); + luaK_storevar(ls, &v); +} + + +static void namestat (LexState *ls) { + /* stat -> func | ['%'] NAME assignment */ + FuncState *fs = ls->fs; + expdesc v; + var_or_func(ls, &v); + if (v.k == VEXP) { /* stat -> func */ + check_condition(ls, luaK_lastisopen(fs), "syntax error"); /* an upvalue? */ + luaK_setcallreturns(fs, 0); /* call statement uses no results */ } - 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 */ + else { /* stat -> ['%'] NAME assignment */ + int left = assignment(ls, &v, 1); + luaK_adjuststack(fs, left); /* remove eventual garbage left on stack */ } - return left; } -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; - } - check_match(ls, '}', '{', line); - fix_opcode(ls, pc, CREATEARRAY, nelems); +static void retstat (LexState *ls) { + /* stat -> RETURN explist */ + FuncState *fs = ls->fs; + next(ls); /* skip RETURN */ + if (!block_follow(ls->t.token)) + explist1(ls); /* optional return values */ + luaK_code1(fs, OP_RETURN, ls->fs->nactloc); + fs->stacklevel = fs->nactloc; /* removes all temp values */ } -static void part (LexState *ls, constdesc *cd) { - switch (ls->token) { - case ';': case '}': /* part -> empty */ - cd->n = 0; - cd->k = ls->token; - return; - 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]); - 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; - } +static void breakstat (LexState *ls) { + /* stat -> BREAK [NAME] */ + FuncState *fs = ls->fs; + int currentlevel = fs->stacklevel; + Breaklabel *bl = fs->bl; + if (!bl) + luaK_error(ls, "no loop to break"); + next(ls); /* skip BREAK */ + luaK_adjuststack(fs, currentlevel - bl->stacklevel); + luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); + /* correct stack for compiler and symbolic execution */ + luaK_adjuststack(fs, bl->stacklevel - currentlevel); +} - case '[': /* part -> recfield recfields */ - recfield(ls); - cd->n = recfields(ls); - cd->k = 1; /* record */ - break; - default: /* part -> exp1 listfields */ - exp1(ls); - cd->n = listfields(ls); - cd->k = 0; /* list */ - break; +static int stat (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + switch (ls->t.token) { + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + return 0; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + return 0; + } + case TK_DO: { /* stat -> DO block END */ + next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + return 0; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + return 0; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + return 0; + } + case TK_FUNCTION: { /* stat -> funcstat */ + funcstat(ls, line); + return 0; + } + case TK_LOCAL: { /* stat -> localstat */ + localstat(ls); + return 0; + } + case TK_NAME: case '%': { /* stat -> namestat */ + namestat(ls); + return 0; + } + case TK_RETURN: { /* stat -> retstat */ + retstat(ls); + return 1; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + breakstat(ls); + return 1; /* must be last statement */ + } + default: { + luaK_error(ls, "<statement> expected"); + return 0; /* to avoid warnings */ + } } } -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); + +static void parlist (LexState *ls) { + /* parlist -> [ param { ',' param } ] */ + int nparams = 0; + int dots = 0; + if (ls->t.token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_DOTS: next(ls); dots = 1; break; + case TK_NAME: new_localvar(ls, str_checkname(ls), nparams++); break; + default: luaK_error(ls, "<name> or `...' expected"); + } + } while (!dots && optional(ls, ',')); } - flush_record(ls, n%RFIELDS_PER_FLUSH); - return n; + code_params(ls, nparams, dots); } -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 body (LexState *ls, int needself, int line) { + /* body -> '(' parlist ')' chunk END */ + FuncState new_fs; + open_func(ls, &new_fs); + new_fs.f->lineDefined = line; + check(ls, '('); + if (needself) { + new_localvarstr(ls, "self", 0); + adjustlocalvars(ls, 1); } - flush_list(ls, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH); - return n; + parlist(ls); + check(ls, ')'); + chunk(ls); + check_match(ls, TK_END, TK_FUNCTION, line); + close_func(ls); + pushclosure(ls, &new_fs); } -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 [';'] } */ + int islast = 0; + while (!islast && !block_follow(ls->t.token)) { + islast = stat(ls); + optional(ls, ';'); + LUA_ASSERT(ls->fs->stacklevel == ls->fs->nactloc, + "stack size != # local vars"); } - check(ls, '='); - exp1(ls); } diff --git a/src/lparser.h b/src/lparser.h index 9825ec57..445acea6 100644 --- a/src/lparser.h +++ b/src/lparser.h @@ -1,5 +1,5 @@ /* -** $Id: lparser.h,v 1.3 1999/02/25 19:13:56 roberto Exp $ +** $Id: lparser.h,v 1.26 2000/10/09 13:47:46 roberto Exp $ ** LL(1) Parser and code generator for Lua ** See Copyright Notice in lua.h */ @@ -11,10 +11,50 @@ #include "lzio.h" -void luaY_codedebugline (int line); -TProtoFunc *luaY_parser (ZIO *z); -void luaY_error (char *s); -void luaY_syntaxerror (char *s, char *token); +/* +** Expression descriptor +*/ + +typedef enum { + VGLOBAL, + VLOCAL, + VINDEXED, + VEXP +} expkind; + +typedef struct expdesc { + expkind k; + union { + int index; /* VGLOBAL: `kstr' index of global name; VLOCAL: stack index */ + struct { + int t; /* patch list of `exit when true' */ + int f; /* patch list of `exit when false' */ + } l; + } u; +} expdesc; + + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct lua_State *L; /* copy of the Lua state */ + int pc; /* next position to code */ + int lasttarget; /* `pc' of last `jump target' */ + int jlt; /* list of jumps to `lasttarget' */ + short stacklevel; /* number of values on activation register */ + short nactloc; /* number of active local variables */ + short nupvalues; /* number of upvalues */ + int lastline; /* line where last `lineinfo' was generated */ + struct Breaklabel *bl; /* chain of breakable blocks */ + expdesc upvalues[MAXUPVALUES]; /* upvalues */ + int actloc[MAXLOCALS]; /* local-variable stack (indices to locvars) */ +} FuncState; + + +Proto *luaY_parser (lua_State *L, ZIO *z); #endif diff --git a/src/lstate.c b/src/lstate.c index 3b98d729..586c1085 100644 --- a/src/lstate.c +++ b/src/lstate.c @@ -1,13 +1,15 @@ /* -** $Id: lstate.c,v 1.12 1999/05/11 20:08:20 roberto Exp $ +** $Id: lstate.c,v 1.48 2000/10/30 16:29:59 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ -#include "lbuiltin.h" +#include <stdio.h> + +#include "lua.h" + #include "ldo.h" -#include "lfunc.h" #include "lgc.h" #include "llex.h" #include "lmem.h" @@ -17,70 +19,103 @@ #include "ltm.h" -lua_State *lua_state = NULL; +#ifdef LUA_DEBUG +static lua_State *lua_state = NULL; +void luaB_opentests (lua_State *L); +#endif -void lua_open (void) -{ - if (lua_state) return; - lua_state = luaM_new(lua_State); - L->Cstack.base = 0; - L->Cstack.lua2C = 0; - L->Cstack.num = 0; - L->errorJmp = NULL; +/* +** built-in implementation for ERRORMESSAGE. In a "correct" environment +** ERRORMESSAGE should have an external definition, and so this function +** would not be used. +*/ +static int errormessage (lua_State *L) { + const char *s = lua_tostring(L, 1); + if (s == NULL) s = "(no message)"; + fprintf(stderr, "error: %s\n", s); + return 0; +} + + +/* +** open parts that may cause memory-allocation errors +*/ +static void f_luaopen (lua_State *L, void *ud) { + int stacksize = *(int *)ud; + if (stacksize == 0) + stacksize = DEFAULT_STACK_SIZE; + else + stacksize += LUA_MINSTACK; + L->gt = luaH_new(L, 10); /* table of globals */ + luaD_init(L, stacksize); + luaS_init(L); + luaX_init(L); + luaT_init(L); + lua_newtable(L); + lua_ref(L, 1); /* create registry */ + lua_register(L, LUA_ERRORMESSAGE, errormessage); +#ifdef LUA_DEBUG + luaB_opentests(L); + if (lua_state == NULL) lua_state = L; /* keep first state to be opened */ +#endif + LUA_ASSERT(lua_gettop(L) == 0, "wrong API stack"); +} + + +LUA_API lua_State *lua_open (int stacksize) { + lua_State *L = luaM_new(NULL, lua_State); + if (L == NULL) return NULL; /* memory allocation error */ + L->stack = NULL; + L->strt.size = L->udt.size = 0; + L->strt.nuse = L->udt.nuse = 0; + L->strt.hash = NULL; + L->udt.hash = NULL; L->Mbuffer = NULL; - L->Mbuffbase = 0; L->Mbuffsize = 0; - L->Mbuffnext = 0; - L->Cblocks = NULL; - L->numCblocks = 0; - L->debug = 0; - L->callhook = NULL; - L->linehook = NULL; - L->rootproto.next = NULL; - L->rootproto.marked = 0; - L->rootcl.next = NULL; - L->rootcl.marked = 0; - L->rootglobal.next = NULL; - L->rootglobal.marked = 0; - L->roottable.next = NULL; - L->roottable.marked = 0; - L->IMtable = NULL; + L->rootproto = NULL; + L->rootcl = NULL; + L->roottable = NULL; + L->TMtable = NULL; + L->last_tag = -1; L->refArray = NULL; L->refSize = 0; - L->GCthreshold = GARBAGE_BLOCK; - L->nblocks = 0; - luaD_init(); - luaS_init(); - luaX_init(); - luaT_init(); - luaB_predefine(); + L->refFree = NONEXT; + L->nblocks = sizeof(lua_State); + L->GCthreshold = MAX_INT; /* to avoid GC during pre-definitions */ + L->callhook = NULL; + L->linehook = NULL; + L->allowhooks = 1; + L->errorJmp = NULL; + if (luaD_runprotected(L, f_luaopen, &stacksize) != 0) { + /* memory allocation error: free partial state */ + lua_close(L); + return NULL; + } + L->GCthreshold = 2*L->nblocks; + return L; } -void lua_close (void) -{ - TaggedString *alludata = luaS_collectudata(); - L->GCthreshold = MAX_INT; /* to avoid GC during GC */ - luaC_hashcallIM((Hash *)L->roottable.next); /* GC t.methods for tables */ - luaC_strcallIM(alludata); /* GC tag methods for userdata */ - luaD_gcIM(&luaO_nilobject); /* GC tag method for nil (signal end of GC) */ - luaH_free((Hash *)L->roottable.next); - luaF_freeproto((TProtoFunc *)L->rootproto.next); - luaF_freeclosure((Closure *)L->rootcl.next); - luaS_free(alludata); - luaS_freeall(); - luaM_free(L->stack.stack); - luaM_free(L->IMtable); - luaM_free(L->refArray); - luaM_free(L->Mbuffer); - luaM_free(L->Cblocks); - luaM_free(L); - L = NULL; -#ifdef DEBUG - printf("total de blocos: %ld\n", numblocks); - printf("total de memoria: %ld\n", totalmem); -#endif +LUA_API void lua_close (lua_State *L) { + LUA_ASSERT(L != lua_state || lua_gettop(L) == 0, "garbage in C stack"); + luaC_collect(L, 1); /* collect all elements */ + LUA_ASSERT(L->rootproto == NULL, "list should be empty"); + LUA_ASSERT(L->rootcl == NULL, "list should be empty"); + LUA_ASSERT(L->roottable == NULL, "list should be empty"); + luaS_freeall(L); + if (L->stack) + L->nblocks -= (L->stack_last - L->stack + 1)*sizeof(TObject); + luaM_free(L, L->stack); + L->nblocks -= (L->last_tag+1)*sizeof(struct TM); + luaM_free(L, L->TMtable); + L->nblocks -= (L->refSize)*sizeof(struct Ref); + luaM_free(L, L->refArray); + L->nblocks -= (L->Mbuffsize)*sizeof(char); + luaM_free(L, L->Mbuffer); + LUA_ASSERT(L->nblocks == sizeof(lua_State), "wrong count for nblocks"); + luaM_free(L, L); + LUA_ASSERT(L != lua_state || memdebug_numblocks == 0, "memory leak!"); + LUA_ASSERT(L != lua_state || memdebug_total == 0,"memory leak!"); } - diff --git a/src/lstate.h b/src/lstate.h index 168257dd..0c8f5521 100644 --- a/src/lstate.h +++ b/src/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 1.19 1999/05/11 20:08:20 roberto Exp $ +** $Id: lstate.h,v 1.41 2000/10/05 13:00:17 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ @@ -7,88 +7,71 @@ #ifndef lstate_h #define lstate_h -#include <setjmp.h> - #include "lobject.h" #include "lua.h" #include "luadebug.h" -#define GARBAGE_BLOCK 150 - -typedef int StkId; /* index to stack elements */ +typedef TObject *StkId; /* index to stack elements */ /* -** "jmp_buf" may be an array, so it is better to make sure it has an -** address (and not that it *is* an address...) +** marks for Reference array */ -struct lua_longjmp { - jmp_buf b; -}; +#define NONEXT -1 /* to end the free list */ +#define HOLD -2 +#define COLLECTED -3 +#define LOCK -4 -struct Stack { - TObject *top; - TObject *stack; - TObject *last; +struct Ref { + TObject o; + int st; /* can be LOCK, HOLD, COLLECTED, or next (for free list) */ }; -struct C_Lua_Stack { - StkId base; /* when Lua calls C or C calls Lua, points to */ - /* the first slot after the last parameter. */ - StkId lua2C; /* points to first element of "array" lua2C */ - int num; /* size of "array" lua2C */ -}; + +struct lua_longjmp; /* defined in ldo.c */ +struct TM; /* defined in ltm.h */ typedef struct stringtable { int size; - int nuse; /* number of elements (including EMPTYs) */ - TaggedString **hash; + lint32 nuse; /* number of elements */ + TString **hash; } stringtable; -enum Status {LOCK, HOLD, FREE, COLLECTED}; - -struct ref { - TObject o; - enum Status status; -}; - struct lua_State { /* thread-specific state */ - struct Stack stack; /* Lua stack */ - struct C_Lua_Stack Cstack; /* C2lua struct */ + StkId top; /* first free slot in the stack */ + StkId stack; /* stack base */ + StkId stack_last; /* last free slot in the stack */ + int stacksize; + StkId Cbase; /* base for current C function */ struct lua_longjmp *errorJmp; /* current error recover point */ char *Mbuffer; /* global buffer */ - int Mbuffbase; /* current first position of Mbuffer */ - int Mbuffsize; /* size of Mbuffer */ - int Mbuffnext; /* next position to fill in Mbuffer */ - struct C_Lua_Stack *Cblocks; - int numCblocks; /* number of nested Cblocks */ - int debug; - lua_CHFunction callhook; - lua_LHFunction linehook; + size_t Mbuffsize; /* size of Mbuffer */ /* global state */ - GCnode rootproto; /* list of all prototypes */ - GCnode rootcl; /* list of all closures */ - GCnode roottable; /* list of all tables */ - GCnode rootglobal; /* list of strings with global values */ - stringtable *string_root; /* array of hash tables for strings and udata */ - struct IM *IMtable; /* table for tag methods */ - int last_tag; /* last used tag in IMtable */ - struct ref *refArray; /* locked objects */ + Proto *rootproto; /* list of all prototypes */ + Closure *rootcl; /* list of all closures */ + Hash *roottable; /* list of all tables */ + stringtable strt; /* hash table for strings */ + stringtable udt; /* hash table for udata */ + Hash *gt; /* table for globals */ + struct TM *TMtable; /* table for tag methods */ + int last_tag; /* last used tag in TMtable */ + struct Ref *refArray; /* locked objects */ int refSize; /* size of refArray */ + int refFree; /* list of free positions in refArray */ unsigned long GCthreshold; - unsigned long nblocks; /* number of 'blocks' currently allocated */ + unsigned long nblocks; /* number of `bytes' currently allocated */ + lua_Hook callhook; + lua_Hook linehook; + int allowhooks; }; -#define L lua_state - - #endif diff --git a/src/lstring.c b/src/lstring.c index fa974ae9..e4c7e26c 100644 --- a/src/lstring.c +++ b/src/lstring.c @@ -1,5 +1,5 @@ /* -** $Id: lstring.c,v 1.19 1999/02/26 15:49:53 roberto Exp $ +** $Id: lstring.c,v 1.45 2000/10/30 17:49:19 roberto Exp $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -7,292 +7,149 @@ #include <string.h> +#include "lua.h" + #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" -#include "lua.h" - - -#define NUM_HASHSTR 31 -#define NUM_HASHUDATA 31 -#define NUM_HASHS (NUM_HASHSTR+NUM_HASHUDATA) -#define gcsizestring(l) (1+(l/64)) /* "weight" for a string with length 'l' */ +/* +** type equivalent to TString, but with maximum alignment requirements +*/ +union L_UTString { + TString ts; + union L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ +}; -static TaggedString EMPTY = {{NULL, 2}, 0L, 0, - {{{LUA_T_NIL, {NULL}}, 0L}}, {0}}; +void luaS_init (lua_State *L) { + L->strt.hash = luaM_newvector(L, 1, TString *); + L->udt.hash = luaM_newvector(L, 1, TString *); + L->nblocks += 2*sizeof(TString *); + L->strt.size = L->udt.size = 1; + L->strt.nuse = L->udt.nuse = 0; + L->strt.hash[0] = L->udt.hash[0] = NULL; +} -void luaS_init (void) { - int i; - L->string_root = luaM_newvector(NUM_HASHS, stringtable); - for (i=0; i<NUM_HASHS; i++) { - L->string_root[i].size = 0; - L->string_root[i].nuse = 0; - L->string_root[i].hash = NULL; - } +void luaS_freeall (lua_State *L) { + LUA_ASSERT(L->strt.nuse==0, "non-empty string table"); + L->nblocks -= (L->strt.size + L->udt.size)*sizeof(TString *); + luaM_free(L, L->strt.hash); + LUA_ASSERT(L->udt.nuse==0, "non-empty udata table"); + luaM_free(L, L->udt.hash); } -static unsigned long hash_s (char *s, long l) { - unsigned long h = 0; /* seed */ - while (l--) - h = h ^ ((h<<5)+(h>>2)+(unsigned char)*(s++)); +static unsigned long hash_s (const char *s, size_t l) { + unsigned long h = l; /* seed */ + size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */ + for (; l>=step; l-=step) + h = h ^ ((h<<5)+(h>>2)+(unsigned char)*(s++)); return h; } -static int newsize (stringtable *tb) { - int size = tb->size; - int realuse = 0; - int i; - /* count how many entries are really in use */ - for (i=0; i<size; i++) - if (tb->hash[i] != NULL && tb->hash[i] != &EMPTY) - realuse++; - return luaO_redimension((realuse+1)*2); /* +1 is the new element */ -} - -static void grow (stringtable *tb) { - int ns = newsize(tb); - TaggedString **newhash = luaM_newvector(ns, TaggedString *); +void luaS_resize (lua_State *L, stringtable *tb, int newsize) { + TString **newhash = luaM_newvector(L, newsize, TString *); int i; - for (i=0; i<ns; i++) - newhash[i] = NULL; + for (i=0; i<newsize; i++) newhash[i] = NULL; /* rehash */ - tb->nuse = 0; for (i=0; i<tb->size; i++) { - if (tb->hash[i] != NULL && tb->hash[i] != &EMPTY) { - unsigned long h = tb->hash[i]->hash; - int h1 = h%ns; - while (newhash[h1]) { - h1 += (h&(ns-2)) + 1; /* double hashing */ - if (h1 >= ns) h1 -= ns; - } - newhash[h1] = tb->hash[i]; - tb->nuse++; + TString *p = tb->hash[i]; + while (p) { /* for each node in the list */ + TString *next = p->nexthash; /* save next */ + unsigned long h = (tb == &L->strt) ? p->u.s.hash : IntPoint(p->u.d.value); + int h1 = h&(newsize-1); /* new position */ + LUA_ASSERT(h%newsize == (h&(newsize-1)), + "a&(x-1) == a%x, for x power of 2"); + p->nexthash = newhash[h1]; /* chain it in new position */ + newhash[h1] = p; + p = next; } } - luaM_free(tb->hash); - tb->size = ns; + luaM_free(L, tb->hash); + L->nblocks += (newsize - tb->size)*sizeof(TString *); + tb->size = newsize; tb->hash = newhash; } -static TaggedString *newone_s (char *str, long l, unsigned long h) { - TaggedString *ts = (TaggedString *)luaM_malloc(sizeof(TaggedString)+l); - memcpy(ts->str, str, l); - ts->str[l] = 0; /* ending 0 */ - ts->u.s.globalval.ttype = LUA_T_NIL; /* initialize global value */ - ts->u.s.len = l; - ts->constindex = 0; - L->nblocks += gcsizestring(l); - ts->head.marked = 0; - ts->head.next = (GCnode *)ts; /* signal it is in no list */ - ts->hash = h; - return ts; +static void newentry (lua_State *L, stringtable *tb, TString *ts, int h) { + ts->nexthash = tb->hash[h]; /* chain new entry */ + tb->hash[h] = ts; + tb->nuse++; + if (tb->nuse > (lint32)tb->size && tb->size < MAX_INT/2) /* too crowded? */ + luaS_resize(L, tb, tb->size*2); } -static TaggedString *newone_u (char *buff, int tag, unsigned long h) { - TaggedString *ts = luaM_new(TaggedString); - ts->u.d.v = buff; - ts->u.d.tag = (tag == LUA_ANYTAG) ? 0 : tag; - ts->constindex = -1; /* tag -> this is a userdata */ - L->nblocks++; - ts->head.marked = 0; - ts->head.next = (GCnode *)ts; /* signal it is in no list */ - ts->hash = h; - return ts; -} -static TaggedString *insert_s (char *str, long l, stringtable *tb) { - TaggedString *ts; + +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { unsigned long h = hash_s(str, l); - int size = tb->size; - int j = -1; - int h1; - if ((long)tb->nuse*3 >= (long)size*2) { - grow(tb); - size = tb->size; - } - h1 = h%size; - while ((ts = tb->hash[h1]) != NULL) { - if (ts == &EMPTY) - j = h1; - else if (ts->u.s.len == l && (memcmp(str, ts->str, l) == 0)) + int h1 = h & (L->strt.size-1); + TString *ts; + for (ts = L->strt.hash[h1]; ts; ts = ts->nexthash) { + if (ts->len == l && (memcmp(str, ts->str, l) == 0)) return ts; - h1 += (h&(size-2)) + 1; /* double hashing */ - if (h1 >= size) h1 -= size; } /* not found */ - if (j != -1) /* is there an EMPTY space? */ - h1 = j; - else - tb->nuse++; - ts = tb->hash[h1] = newone_s(str, l, h); + ts = (TString *)luaM_malloc(L, sizestring(l)); + ts->marked = 0; + ts->nexthash = NULL; + ts->len = l; + ts->u.s.hash = h; + ts->u.s.constindex = 0; + memcpy(ts->str, str, l); + ts->str[l] = 0; /* ending 0 */ + L->nblocks += sizestring(l); + newentry(L, &L->strt, ts, h1); /* insert it on table */ return ts; } -static TaggedString *insert_u (void *buff, int tag, stringtable *tb) { - TaggedString *ts; - unsigned long h = (unsigned long)buff; - int size = tb->size; - int j = -1; - int h1; - if ((long)tb->nuse*3 >= (long)size*2) { - grow(tb); - size = tb->size; - } - h1 = h%size; - while ((ts = tb->hash[h1]) != NULL) { - if (ts == &EMPTY) - j = h1; - else if ((tag == ts->u.d.tag || tag == LUA_ANYTAG) && buff == ts->u.d.v) - return ts; - h1 += (h&(size-2)) + 1; /* double hashing */ - if (h1 >= size) h1 -= size; - } - /* not found */ - if (j != -1) /* is there an EMPTY space? */ - h1 = j; - else - tb->nuse++; - ts = tb->hash[h1] = newone_u(buff, tag, h); +TString *luaS_newudata (lua_State *L, size_t s, void *udata) { + union L_UTString *uts = (union L_UTString *)luaM_malloc(L, + (lint32)sizeof(union L_UTString)+s); + TString *ts = &uts->ts; + ts->marked = 0; + ts->nexthash = NULL; + ts->len = s; + ts->u.d.tag = 0; + ts->u.d.value = (udata == NULL) ? uts+1 : udata; + L->nblocks += sizestring(s); + /* insert it on table */ + newentry(L, &L->udt, ts, IntPoint(ts->u.d.value) & (L->udt.size-1)); return ts; } -TaggedString *luaS_createudata (void *udata, int tag) { - int t = ((unsigned)udata%NUM_HASHUDATA)+NUM_HASHSTR; - return insert_u(udata, tag, &L->string_root[t]); -} - -TaggedString *luaS_newlstr (char *str, long l) { - int t = (l==0) ? 0 : ((int)((unsigned char)str[0]*l))%NUM_HASHSTR; - return insert_s(str, l, &L->string_root[t]); -} - -TaggedString *luaS_new (char *str) { - return luaS_newlstr(str, strlen(str)); -} - -TaggedString *luaS_newfixedstring (char *str) { - TaggedString *ts = luaS_new(str); - if (ts->head.marked == 0) - ts->head.marked = 2; /* avoid GC */ - return ts; -} - - -void luaS_free (TaggedString *l) { - while (l) { - TaggedString *next = (TaggedString *)l->head.next; - L->nblocks -= (l->constindex == -1) ? 1 : gcsizestring(l->u.s.len); - luaM_free(l); - l = next; - } -} - - -/* -** Garbage collection functions. -*/ - -static void remove_from_list (GCnode *l) { - while (l) { - GCnode *next = l->next; - while (next && !next->marked) - next = l->next = next->next; - l = next; - } -} - - -TaggedString *luaS_collector (void) { - TaggedString *frees = NULL; - int i; - remove_from_list(&(L->rootglobal)); - for (i=0; i<NUM_HASHS; i++) { - stringtable *tb = &L->string_root[i]; - int j; - for (j=0; j<tb->size; j++) { - TaggedString *t = tb->hash[j]; - if (t == NULL) continue; - if (t->head.marked == 1) - t->head.marked = 0; - else if (!t->head.marked) { - t->head.next = (GCnode *)frees; - frees = t; - tb->hash[j] = &EMPTY; - } - } - } - return frees; -} - - -TaggedString *luaS_collectudata (void) { - TaggedString *frees = NULL; - int i; - L->rootglobal.next = NULL; /* empty list of globals */ - for (i=NUM_HASHSTR; i<NUM_HASHS; i++) { - stringtable *tb = &L->string_root[i]; - int j; - for (j=0; j<tb->size; j++) { - TaggedString *t = tb->hash[j]; - if (t == NULL || t == &EMPTY) - continue; - LUA_ASSERT(t->constindex == -1, "must be userdata"); - t->head.next = (GCnode *)frees; - frees = t; - tb->hash[j] = &EMPTY; - } - } - return frees; -} - - -void luaS_freeall (void) { - int i; - for (i=0; i<NUM_HASHS; i++) { - stringtable *tb = &L->string_root[i]; - int j; - for (j=0; j<tb->size; j++) { - TaggedString *t = tb->hash[j]; - if (t == &EMPTY) continue; - luaM_free(t); - } - luaM_free(tb->hash); - } - luaM_free(L->string_root); -} - - -void luaS_rawsetglobal (TaggedString *ts, TObject *newval) { - ts->u.s.globalval = *newval; - if (ts->head.next == (GCnode *)ts) { /* is not in list? */ - ts->head.next = L->rootglobal.next; - L->rootglobal.next = (GCnode *)ts; +TString *luaS_createudata (lua_State *L, void *udata, int tag) { + int h1 = IntPoint(udata) & (L->udt.size-1); + TString *ts; + for (ts = L->udt.hash[h1]; ts; ts = ts->nexthash) { + if (udata == ts->u.d.value && (tag == ts->u.d.tag || tag == LUA_ANYTAG)) + return ts; } + /* not found */ + ts = luaS_newudata(L, 0, udata); + if (tag != LUA_ANYTAG) + ts->u.d.tag = tag; + return ts; } -char *luaS_travsymbol (int (*fn)(TObject *)) { - TaggedString *g; - for (g=(TaggedString *)L->rootglobal.next; g; g=(TaggedString *)g->head.next) - if (fn(&g->u.s.globalval)) - return g->str; - return NULL; +TString *luaS_new (lua_State *L, const char *str) { + return luaS_newlstr(L, str, strlen(str)); } -int luaS_globaldefined (char *name) { - TaggedString *ts = luaS_new(name); - return ts->u.s.globalval.ttype != LUA_T_NIL; +TString *luaS_newfixed (lua_State *L, const char *str) { + TString *ts = luaS_new(L, str); + if (ts->marked == 0) ts->marked = FIXMARK; /* avoid GC */ + return ts; } diff --git a/src/lstring.h b/src/lstring.h index 6b214a21..67ede68d 100644 --- a/src/lstring.h +++ b/src/lstring.h @@ -1,5 +1,5 @@ /* -** $Id: lstring.h,v 1.7 1998/03/06 16:54:42 roberto Exp $ +** $Id: lstring.h,v 1.24 2000/10/30 17:49:19 roberto Exp $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -9,20 +9,29 @@ #include "lobject.h" +#include "lstate.h" -void luaS_init (void); -TaggedString *luaS_createudata (void *udata, int tag); -TaggedString *luaS_collector (void); -void luaS_free (TaggedString *l); -TaggedString *luaS_newlstr (char *str, long l); -TaggedString *luaS_new (char *str); -TaggedString *luaS_newfixedstring (char *str); -void luaS_rawsetglobal (TaggedString *ts, TObject *newval); -char *luaS_travsymbol (int (*fn)(TObject *)); -int luaS_globaldefined (char *name); -TaggedString *luaS_collectudata (void); -void luaS_freeall (void); +/* +** any TString with mark>=FIXMARK is never collected. +** Marks>=RESERVEDMARK are used to identify reserved words. +*/ +#define FIXMARK 2 +#define RESERVEDMARK 3 + + +#define sizestring(l) ((long)sizeof(TString) + \ + ((long)(l+1)-TSPACK)*(long)sizeof(char)) + + +void luaS_init (lua_State *L); +void luaS_resize (lua_State *L, stringtable *tb, int newsize); +TString *luaS_newudata (lua_State *L, size_t s, void *udata); +TString *luaS_createudata (lua_State *L, void *udata, int tag); +void luaS_freeall (lua_State *L); +TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +TString *luaS_new (lua_State *L, const char *str); +TString *luaS_newfixed (lua_State *L, const char *str); #endif diff --git a/src/ltable.c b/src/ltable.c index d768ba0b..b28712d9 100644 --- a/src/ltable.c +++ b/src/ltable.c @@ -1,177 +1,303 @@ /* -** $Id: ltable.c,v 1.22 1999/05/21 19:41:49 roberto Exp $ +** $Id: ltable.c,v 1.58 2000/10/26 12:47:05 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ -#include <stdlib.h> -#include "lauxlib.h" +/* +** Implementation of tables (aka arrays, objects, or hash tables); +** uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** In other words, there are collisions only when two elements have the +** same main position (i.e. the same hash values for that table size). +** Because of that, the load factor of these tables can be 100% without +** performance penalties. +*/ + + +#include "lua.h" + #include "lmem.h" #include "lobject.h" #include "lstate.h" +#include "lstring.h" #include "ltable.h" -#include "lua.h" -#define gcsize(n) (1+(n/16)) +#define gcsize(L, n) (sizeof(Hash)+(n)*sizeof(Node)) -#define nuse(t) ((t)->nuse) -#define nodevector(t) ((t)->node) -#define TagDefault LUA_T_ARRAY; +#define TagDefault LUA_TTABLE -static long int hashindex (TObject *ref) { - long int h; - switch (ttype(ref)) { - case LUA_T_NUMBER: - h = (long int)nvalue(ref); - break; - case LUA_T_STRING: case LUA_T_USERDATA: - h = (IntPoint)tsvalue(ref); +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +Node *luaH_mainposition (const Hash *t, const TObject *key) { + unsigned long h; + switch (ttype(key)) { + case LUA_TNUMBER: + h = (unsigned long)(long)nvalue(key); break; - case LUA_T_ARRAY: - h = (IntPoint)avalue(ref); + case LUA_TSTRING: + h = tsvalue(key)->u.s.hash; break; - case LUA_T_PROTO: - h = (IntPoint)tfvalue(ref); + case LUA_TUSERDATA: + h = IntPoint(tsvalue(key)); break; - case LUA_T_CPROTO: - h = (IntPoint)fvalue(ref); + case LUA_TTABLE: + h = IntPoint(hvalue(key)); break; - case LUA_T_CLOSURE: - h = (IntPoint)clvalue(ref); + case LUA_TFUNCTION: + h = IntPoint(clvalue(key)); break; default: - lua_error("unexpected type to index table"); - h = 0; /* to avoid warnings */ + return NULL; /* invalid key */ } - return (h >= 0 ? h : -(h+1)); + LUA_ASSERT(h%(unsigned int)t->size == (h&((unsigned int)t->size-1)), + "a&(x-1) == a%x, for x power of 2"); + return &t->node[h&(t->size-1)]; +} + + +static const TObject *luaH_getany (lua_State *L, const Hash *t, + const TObject *key) { + Node *n = luaH_mainposition(t, key); + if (!n) + lua_error(L, "table index is nil"); + else do { + if (luaO_equalObj(key, &n->key)) + return &n->val; + n = n->next; + } while (n); + return &luaO_nilobject; /* key not found */ +} + + +/* specialized version for numbers */ +const TObject *luaH_getnum (const Hash *t, Number key) { + Node *n = &t->node[(unsigned long)(long)key&(t->size-1)]; + do { + if (ttype(&n->key) == LUA_TNUMBER && nvalue(&n->key) == key) + return &n->val; + n = n->next; + } while (n); + return &luaO_nilobject; /* key not found */ } -Node *luaH_present (Hash *t, TObject *key) { - int tsize = nhash(t); - long int h = hashindex(key); - int h1 = h%tsize; - Node *n = node(t, h1); - /* keep looking until an entry with "ref" equal to key or nil */ - while ((ttype(ref(n)) == ttype(key)) ? !luaO_equalval(key, ref(n)) - : ttype(ref(n)) != LUA_T_NIL) { - h1 += (h&(tsize-2)) + 1; /* double hashing */ - if (h1 >= tsize) h1 -= tsize; - n = node(t, h1); +/* specialized version for strings */ +const TObject *luaH_getstr (const Hash *t, TString *key) { + Node *n = &t->node[key->u.s.hash&(t->size-1)]; + do { + if (ttype(&n->key) == LUA_TSTRING && tsvalue(&n->key) == key) + return &n->val; + n = n->next; + } while (n); + return &luaO_nilobject; /* key not found */ +} + + +const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key) { + switch (ttype(key)) { + case LUA_TNUMBER: return luaH_getnum(t, nvalue(key)); + case LUA_TSTRING: return luaH_getstr(t, tsvalue(key)); + default: return luaH_getany(L, t, key); } - return n; } -void luaH_free (Hash *frees) { - while (frees) { - Hash *next = (Hash *)frees->head.next; - L->nblocks -= gcsize(frees->nhash); - luaM_free(nodevector(frees)); - luaM_free(frees); - frees = next; +Node *luaH_next (lua_State *L, const Hash *t, const TObject *key) { + int i; + if (ttype(key) == LUA_TNIL) + i = 0; /* first iteration */ + else { + const TObject *v = luaH_get(L, t, key); + if (v == &luaO_nilobject) + lua_error(L, "invalid key for `next'"); + i = (int)(((const char *)v - + (const char *)(&t->node[0].val)) / sizeof(Node)) + 1; + } + for (; i<t->size; i++) { + Node *n = node(t, i); + if (ttype(val(n)) != LUA_TNIL) + return n; } + return NULL; /* no more elements */ } -static Node *hashnodecreate (int nhash) { - Node *v = luaM_newvector(nhash, Node); +/* +** try to remove a key without value from a table. To avoid problems with +** hash, change `key' for a number with the same hash. +*/ +void luaH_remove (Hash *t, TObject *key) { + if (ttype(key) == LUA_TNUMBER || + (ttype(key) == LUA_TSTRING && tsvalue(key)->len <= 30)) + return; /* do not remove numbers nor small strings */ + else { + /* try to find a number `n' with the same hash as `key' */ + Node *mp = luaH_mainposition(t, key); + int n = mp - &t->node[0]; + /* make sure `n' is not in `t' */ + while (luaH_getnum(t, n) != &luaO_nilobject) { + if (n >= MAX_INT - t->size) + return; /* give up; (to avoid overflow) */ + n += t->size; + } + ttype(key) = LUA_TNUMBER; + nvalue(key) = n; + LUA_ASSERT(luaH_mainposition(t, key) == mp, "cannot change hash"); + } +} + + +static void setnodevector (lua_State *L, Hash *t, lint32 size) { int i; - for (i=0; i<nhash; i++) - ttype(ref(&v[i])) = ttype(val(&v[i])) = LUA_T_NIL; - return v; + if (size > MAX_INT) + lua_error(L, "table overflow"); + t->node = luaM_newvector(L, size, Node); + for (i=0; i<(int)size; i++) { + ttype(&t->node[i].key) = ttype(&t->node[i].val) = LUA_TNIL; + t->node[i].next = NULL; + } + L->nblocks += gcsize(L, size) - gcsize(L, t->size); + t->size = size; + t->firstfree = &t->node[size-1]; /* first free position to be used */ } -Hash *luaH_new (int nhash) { - Hash *t = luaM_new(Hash); - nhash = luaO_redimension(nhash*3/2); - nodevector(t) = hashnodecreate(nhash); - nhash(t) = nhash; - nuse(t) = 0; +Hash *luaH_new (lua_State *L, int size) { + Hash *t = luaM_new(L, Hash); t->htag = TagDefault; - luaO_insertlist(&(L->roottable), (GCnode *)t); - L->nblocks += gcsize(nhash); + t->next = L->roottable; + L->roottable = t; + t->mark = t; + t->size = 0; + L->nblocks += gcsize(L, 0); + t->node = NULL; + setnodevector(L, t, luaO_power2(size)); return t; } -static int newsize (Hash *t) { +void luaH_free (lua_State *L, Hash *t) { + L->nblocks -= gcsize(L, t->size); + luaM_free(L, t->node); + luaM_free(L, t); +} + + +static int numuse (const Hash *t) { Node *v = t->node; - int size = nhash(t); + int size = t->size; int realuse = 0; int i; for (i=0; i<size; i++) { - if (ttype(val(v+i)) != LUA_T_NIL) + if (ttype(&v[i].val) != LUA_TNIL) realuse++; } - return luaO_redimension((realuse+1)*2); /* +1 is the new element */ + return realuse; } -static void rehash (Hash *t) { - int nold = nhash(t); - Node *vold = nodevector(t); - int nnew = newsize(t); +static void rehash (lua_State *L, Hash *t) { + int oldsize = t->size; + Node *nold = t->node; + int nelems = numuse(t); int i; - nodevector(t) = hashnodecreate(nnew); - nhash(t) = nnew; - nuse(t) = 0; - for (i=0; i<nold; i++) { - Node *n = vold+i; - if (ttype(val(n)) != LUA_T_NIL) { - *luaH_present(t, ref(n)) = *n; /* copy old node to new hash */ - nuse(t)++; - } + LUA_ASSERT(nelems<=oldsize, "wrong count"); + if (nelems >= oldsize-oldsize/4) /* using more than 3/4? */ + setnodevector(L, t, (lint32)oldsize*2); + else if (nelems <= oldsize/4 && /* less than 1/4? */ + oldsize > MINPOWER2) + setnodevector(L, t, oldsize/2); + else + setnodevector(L, t, oldsize); + for (i=0; i<oldsize; i++) { + Node *old = nold+i; + if (ttype(&old->val) != LUA_TNIL) + *luaH_set(L, t, &old->key) = old->val; } - L->nblocks += gcsize(nnew)-gcsize(nold); - luaM_free(vold); + luaM_free(L, nold); /* free old array */ } -void luaH_set (Hash *t, TObject *ref, TObject *val) { - Node *n = luaH_present(t, ref); - if (ttype(ref(n)) != LUA_T_NIL) - *val(n) = *val; - else { - TObject buff; - buff = *val; /* rehash may invalidate this address */ - if ((long)nuse(t)*3L > (long)nhash(t)*2L) { - rehash(t); - n = luaH_present(t, ref); +/* +** inserts a key into a hash table; first, check whether key is +** already present; if not, check whether key's main position is free; +** if not, check whether colliding node is in its main position or not; +** if it is not, move colliding node to an empty place and put new key +** in its main position; otherwise (colliding node is in its main position), +** new key goes to an empty position. +*/ +TObject *luaH_set (lua_State *L, Hash *t, const TObject *key) { + Node *mp = luaH_mainposition(t, key); + Node *n = mp; + if (!mp) + lua_error(L, "table index is nil"); + do { /* check whether `key' is somewhere in the chain */ + if (luaO_equalObj(key, &n->key)) + return &n->val; /* that's all */ + else n = n->next; + } while (n); + /* `key' not found; must insert it */ + if (ttype(&mp->key) != LUA_TNIL) { /* main position is not free? */ + Node *othern; /* main position of colliding node */ + n = t->firstfree; /* get a free place */ + /* is colliding node out of its main position? (can only happens if + its position is after "firstfree") */ + if (mp > n && (othern=luaH_mainposition(t, &mp->key)) != mp) { + /* yes; move colliding node into free position */ + while (othern->next != mp) othern = othern->next; /* find previous */ + othern->next = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + mp->next = NULL; /* now `mp' is free */ } - nuse(t)++; - *ref(n) = *ref; - *val(n) = buff; + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + n->next = mp->next; /* chain new position */ + mp->next = n; + mp = n; + } + } + mp->key = *key; + for (;;) { /* correct `firstfree' */ + if (ttype(&t->firstfree->key) == LUA_TNIL) + return &mp->val; /* OK; table still has a free place */ + else if (t->firstfree == t->node) break; /* cannot decrement from here */ + else (t->firstfree)--; } + rehash(L, t); /* no more free places */ + return luaH_set(L, t, key); /* `rehash' invalidates this insertion */ } -int luaH_pos (Hash *t, TObject *r) { - Node *n = luaH_present(t, r); - luaL_arg_check(ttype(val(n)) != LUA_T_NIL, 2, "key not found"); - return n-(t->node); +TObject *luaH_setint (lua_State *L, Hash *t, int key) { + TObject index; + ttype(&index) = LUA_TNUMBER; + nvalue(&index) = key; + return luaH_set(L, t, &index); } -void luaH_setint (Hash *t, int ref, TObject *val) { - TObject index; - ttype(&index) = LUA_T_NUMBER; - nvalue(&index) = ref; - luaH_set(t, &index, val); +void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val) { + TObject *value, index; + ttype(&index) = LUA_TSTRING; + tsvalue(&index) = key; + value = luaH_set(L, t, &index); + ttype(value) = LUA_TNUMBER; + nvalue(value) = val; } -TObject *luaH_getint (Hash *t, int ref) { - TObject index; - ttype(&index) = LUA_T_NUMBER; - nvalue(&index) = ref; - return luaH_get(t, &index); +const TObject *luaH_getglobal (lua_State *L, const char *name) { + return luaH_getstr(L->gt, luaS_new(L, name)); } diff --git a/src/ltable.h b/src/ltable.h index 49b485f2..8ee41a81 100644 --- a/src/ltable.h +++ b/src/ltable.h @@ -1,5 +1,5 @@ /* -** $Id: ltable.h,v 1.11 1999/02/23 14:57:28 roberto Exp $ +** $Id: ltable.h,v 1.24 2000/08/31 14:08:27 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -10,21 +10,25 @@ #include "lobject.h" -#define node(t,i) (&(t)->node[i]) -#define ref(n) (&(n)->ref) +#define node(t,i) (&(t)->node[i]) +#define key(n) (&(n)->key) #define val(n) (&(n)->val) -#define nhash(t) ((t)->nhash) - -#define luaH_get(t,ref) (val(luaH_present((t), (ref)))) -#define luaH_move(t,from,to) (luaH_setint(t, to, luaH_getint(t, from))) - -Hash *luaH_new (int nhash); -void luaH_free (Hash *frees); -Node *luaH_present (Hash *t, TObject *key); -void luaH_set (Hash *t, TObject *ref, TObject *val); -int luaH_pos (Hash *t, TObject *r); -void luaH_setint (Hash *t, int ref, TObject *val); -TObject *luaH_getint (Hash *t, int ref); + +Hash *luaH_new (lua_State *L, int nhash); +void luaH_free (lua_State *L, Hash *t); +const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key); +const TObject *luaH_getnum (const Hash *t, Number key); +const TObject *luaH_getstr (const Hash *t, TString *key); +void luaH_remove (Hash *t, TObject *key); +TObject *luaH_set (lua_State *L, Hash *t, const TObject *key); +Node * luaH_next (lua_State *L, const Hash *t, const TObject *r); +TObject *luaH_setint (lua_State *L, Hash *t, int key); +void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val); +unsigned long luaH_hash (lua_State *L, const TObject *key); +const TObject *luaH_getglobal (lua_State *L, const char *name); + +/* exported only for debugging */ +Node *luaH_mainposition (const Hash *t, const TObject *key); #endif diff --git a/src/ltests.c b/src/ltests.c new file mode 100644 index 00000000..c27c7c81 --- /dev/null +++ b/src/ltests.c @@ -0,0 +1,543 @@ +/* +** $Id: ltests.c,v 1.54 2000/10/31 13:10:24 roberto Exp $ +** Internal Module for Debugging of the Lua Implementation +** See Copyright Notice in lua.h +*/ + + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#include "lua.h" + +#include "lapi.h" +#include "lauxlib.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "luadebug.h" +#include "lualib.h" + + +void luaB_opentests (lua_State *L); + + +/* +** The whole module only makes sense with LUA_DEBUG on +*/ +#ifdef LUA_DEBUG + + + +static void setnameval (lua_State *L, const char *name, int val) { + lua_pushstring(L, name); + lua_pushnumber(L, val); + lua_settable(L, -3); +} + + +/* +** {====================================================== +** Disassembler +** ======================================================= +*/ + + +static const char *const instrname[NUM_OPCODES] = { + "END", "RETURN", "CALL", "TAILCALL", "PUSHNIL", "POP", "PUSHINT", + "PUSHSTRING", "PUSHNUM", "PUSHNEGNUM", "PUSHUPVALUE", "GETLOCAL", + "GETGLOBAL", "GETTABLE", "GETDOTTED", "GETINDEXED", "PUSHSELF", + "CREATETABLE", "SETLOCAL", "SETGLOBAL", "SETTABLE", "SETLIST", "SETMAP", + "ADD", "ADDI", "SUB", "MULT", "DIV", "POW", "CONCAT", "MINUS", "NOT", + "JMPNE", "JMPEQ", "JMPLT", "JMPLE", "JMPGT", "JMPGE", "JMPT", "JMPF", + "JMPONT", "JMPONF", "JMP", "PUSHNILJMP", "FORPREP", "FORLOOP", "LFORPREP", + "LFORLOOP", "CLOSURE" +}; + + +static int pushop (lua_State *L, Proto *p, int pc) { + char buff[100]; + Instruction i = p->code[pc]; + OpCode o = GET_OPCODE(i); + const char *name = instrname[o]; + sprintf(buff, "%5d - ", luaG_getline(p->lineinfo, pc, 1, NULL)); + switch ((enum Mode)luaK_opproperties[o].mode) { + case iO: + sprintf(buff+8, "%-12s", name); + break; + case iU: + sprintf(buff+8, "%-12s%4u", name, GETARG_U(i)); + break; + case iS: + sprintf(buff+8, "%-12s%4d", name, GETARG_S(i)); + break; + case iAB: + sprintf(buff+8, "%-12s%4d %4d", name, GETARG_A(i), GETARG_B(i)); + break; + } + lua_pushstring(L, buff); + return (o != OP_END); +} + + +static int listcode (lua_State *L) { + int pc; + Proto *p; + int res; + luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), + 1, "Lua function expected"); + p = clvalue(luaA_index(L, 1))->f.l; + lua_newtable(L); + setnameval(L, "maxstack", p->maxstacksize); + setnameval(L, "numparams", p->numparams); + pc = 0; + do { + lua_pushnumber(L, pc+1); + res = pushop(L, p, pc++); + lua_settable(L, -3); + } while (res); + return 1; +} + + +static int liststrings (lua_State *L) { + Proto *p; + int i; + luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), + 1, "Lua function expected"); + p = clvalue(luaA_index(L, 1))->f.l; + lua_newtable(L); + for (i=0; i<p->nkstr; i++) { + lua_pushnumber(L, i+1); + lua_pushstring(L, p->kstr[i]->str); + lua_settable(L, -3); + } + return 1; +} + + +static int listlocals (lua_State *L) { + Proto *p; + int pc = luaL_check_int(L, 2) - 1; + int i = 0; + const char *name; + luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), + 1, "Lua function expected"); + p = clvalue(luaA_index(L, 1))->f.l; + while ((name = luaF_getlocalname(p, ++i, pc)) != NULL) + lua_pushstring(L, name); + return i-1; +} + +/* }====================================================== */ + + + +static int get_limits (lua_State *L) { + lua_newtable(L); + setnameval(L, "BITS_INT", BITS_INT); + setnameval(L, "LFPF", LFIELDS_PER_FLUSH); + setnameval(L, "MAXARG_A", MAXARG_A); + setnameval(L, "MAXARG_B", MAXARG_B); + setnameval(L, "MAXARG_S", MAXARG_S); + setnameval(L, "MAXARG_U", MAXARG_U); + setnameval(L, "MAXLOCALS", MAXLOCALS); + setnameval(L, "MAXPARAMS", MAXPARAMS); + setnameval(L, "MAXSTACK", MAXSTACK); + setnameval(L, "MAXUPVALUES", MAXUPVALUES); + setnameval(L, "MAXVARSLH", MAXVARSLH); + setnameval(L, "RFPF", RFIELDS_PER_FLUSH); + setnameval(L, "SIZE_A", SIZE_A); + setnameval(L, "SIZE_B", SIZE_B); + setnameval(L, "SIZE_OP", SIZE_OP); + setnameval(L, "SIZE_U", SIZE_U); + return 1; +} + + +static int mem_query (lua_State *L) { + if (lua_isnull(L, 1)) { + lua_pushnumber(L, memdebug_total); + lua_pushnumber(L, memdebug_numblocks); + lua_pushnumber(L, memdebug_maxmem); + return 3; + } + else { + memdebug_memlimit = luaL_check_int(L, 1); + return 0; + } +} + + +static int hash_query (lua_State *L) { + if (lua_isnull(L, 2)) { + luaL_arg_check(L, lua_tag(L, 1) == LUA_TSTRING, 1, "string expected"); + lua_pushnumber(L, tsvalue(luaA_index(L, 1))->u.s.hash); + } + else { + Hash *t; + luaL_checktype(L, 2, LUA_TTABLE); + t = hvalue(luaA_index(L, 2)); + lua_pushnumber(L, luaH_mainposition(t, luaA_index(L, 1)) - t->node); + } + return 1; +} + + +static int table_query (lua_State *L) { + const Hash *t; + int i = luaL_opt_int(L, 2, -1); + luaL_checktype(L, 1, LUA_TTABLE); + t = hvalue(luaA_index(L, 1)); + if (i == -1) { + lua_pushnumber(L, t->size); + lua_pushnumber(L, t->firstfree - t->node); + return 2; + } + else if (i < t->size) { + luaA_pushobject(L, &t->node[i].key); + luaA_pushobject(L, &t->node[i].val); + if (t->node[i].next) { + lua_pushnumber(L, t->node[i].next - t->node); + return 3; + } + else + return 2; + } + return 0; +} + + +static int string_query (lua_State *L) { + stringtable *tb = (*luaL_check_string(L, 1) == 's') ? &L->strt : &L->udt; + int s = luaL_opt_int(L, 2, 0) - 1; + if (s==-1) { + lua_pushnumber(L ,tb->nuse); + lua_pushnumber(L ,tb->size); + return 2; + } + else if (s < tb->size) { + TString *ts; + int n = 0; + for (ts = tb->hash[s]; ts; ts = ts->nexthash) { + ttype(L->top) = LUA_TSTRING; + tsvalue(L->top) = ts; + incr_top; + n++; + } + return n; + } + return 0; +} + + +static int tref (lua_State *L) { + luaL_checkany(L, 1); + lua_pushvalue(L, 1); + lua_pushnumber(L, lua_ref(L, luaL_opt_int(L, 2, 1))); + return 1; +} + +static int getref (lua_State *L) { + if (lua_getref(L, luaL_check_int(L, 1))) + return 1; + else + return 0; +} + +static int unref (lua_State *L) { + lua_unref(L, luaL_check_int(L, 1)); + return 0; +} + +static int newuserdata (lua_State *L) { + if (lua_isnumber(L, 2)) + lua_pushusertag(L, (void *)luaL_check_int(L, 1), luaL_check_int(L, 2)); + else + lua_newuserdata(L, luaL_check_int(L, 1)); + return 1; +} + +static int udataval (lua_State *L) { + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_pushnumber(L, (int)lua_touserdata(L, 1)); + return 1; +} + +static int newstate (lua_State *L) { + lua_State *L1 = lua_open(luaL_check_int(L, 1)); + if (L1) + lua_pushuserdata(L, L1); + else + lua_pushnil(L); + return 1; +} + +static int loadlib (lua_State *L) { + lua_State *L1 = (lua_State *)lua_touserdata(L, 1); + switch (*luaL_check_string(L, 2)) { + case 'm': lua_mathlibopen(L1); break; + case 's': lua_strlibopen(L1); break; + case 'i': lua_iolibopen(L1); break; + case 'd': lua_dblibopen(L1); break; + case 'b': lua_baselibopen(L1); break; + default: luaL_argerror(L, 2, "invalid option"); + } + return 0; +} + +static int closestate (lua_State *L) { + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_close((lua_State *)lua_touserdata(L, 1)); + return 0; +} + +static int doremote (lua_State *L) { + lua_State *L1; + const char *code = luaL_check_string(L, 2); + int status; + luaL_checktype(L, 1, LUA_TUSERDATA); + L1 = (lua_State *)lua_touserdata(L, 1); + status = lua_dostring(L1, code); + if (status != 0) { + lua_pushnil(L); + lua_pushnumber(L, status); + return 2; + } + else { + int i = 0; + while (!lua_isnull(L1, ++i)) + lua_pushstring(L, lua_tostring(L1, i)); + return i-1; + } +} + +static int settagmethod (lua_State *L) { + int tag = luaL_check_int(L, 1); + const char *event = luaL_check_string(L, 2); + luaL_checkany(L, 3); + lua_gettagmethod(L, tag, event); + lua_pushvalue(L, 3); + lua_settagmethod(L, tag, event); + return 1; +} + +static int pushbool (lua_State *L, int b) { + if (b) lua_pushnumber(L, 1); + else lua_pushnil(L); + return 1; +} + +static int equal (lua_State *L) { + return pushbool(L, lua_equal(L, 1, 2)); +} + + + +/* +** {====================================================== +** function to test the API with C. It interprets a kind of "assembler" +** language with calls to the API, so the test can be driven by Lua code +** ======================================================= +*/ + +static const char *const delimits = " \t\n,;"; + +static void skip (const char **pc) { + while (**pc != '\0' && strchr(delimits, **pc)) (*pc)++; +} + +static int getnum (lua_State *L, const char **pc) { + int res = 0; + int sig = 1; + skip(pc); + if (**pc == '.') { + res = (int)lua_tonumber(L, -1); + lua_pop(L, 1); + (*pc)++; + return res; + } + else if (**pc == '-') { + sig = -1; + (*pc)++; + } + while (isdigit(**pc)) res = res*10 + (*(*pc)++) - '0'; + return sig*res; +} + +static const char *getname (char *buff, const char **pc) { + int i = 0; + skip(pc); + while (**pc != '\0' && !strchr(delimits, **pc)) + buff[i++] = *(*pc)++; + buff[i] = '\0'; + return buff; +} + + +#define EQ(s1) (strcmp(s1, inst) == 0) + +#define getnum ((getnum)(L, &pc)) +#define getname ((getname)(buff, &pc)) + + +static int testC (lua_State *L) { + char buff[30]; + const char *pc = luaL_check_string(L, 1); + for (;;) { + const char *inst = getname; + if EQ("") return 0; + else if EQ("isnumber") { + lua_pushnumber(L, lua_isnumber(L, getnum)); + } + else if EQ("isstring") { + lua_pushnumber(L, lua_isstring(L, getnum)); + } + else if EQ("istable") { + lua_pushnumber(L, lua_istable(L, getnum)); + } + else if EQ("iscfunction") { + lua_pushnumber(L, lua_iscfunction(L, getnum)); + } + else if EQ("isfunction") { + lua_pushnumber(L, lua_isfunction(L, getnum)); + } + else if EQ("isuserdata") { + lua_pushnumber(L, lua_isuserdata(L, getnum)); + } + else if EQ("isnil") { + lua_pushnumber(L, lua_isnil(L, getnum)); + } + else if EQ("isnull") { + lua_pushnumber(L, lua_isnull(L, getnum)); + } + else if EQ("tonumber") { + lua_pushnumber(L, lua_tonumber(L, getnum)); + } + else if EQ("tostring") { + lua_pushstring(L, lua_tostring(L, getnum)); + } + else if EQ("tonumber") { + lua_pushnumber(L, lua_tonumber(L, getnum)); + } + else if EQ("strlen") { + lua_pushnumber(L, lua_strlen(L, getnum)); + } + else if EQ("tocfunction") { + lua_pushcfunction(L, lua_tocfunction(L, getnum)); + } + else if EQ("return") { + return getnum; + } + else if EQ("gettop") { + lua_pushnumber(L, lua_gettop(L)); + } + else if EQ("settop") { + lua_settop(L, getnum); + } + else if EQ("pop") { + lua_pop(L, getnum); + } + else if EQ("pushnum") { + lua_pushnumber(L, getnum); + } + else if EQ("pushvalue") { + lua_pushvalue(L, getnum); + } + else if EQ("remove") { + lua_remove(L, getnum); + } + else if EQ("insert") { + lua_insert(L, getnum); + } + else if EQ("gettable") { + lua_gettable(L, getnum); + } + else if EQ("settable") { + lua_settable(L, getnum); + } + else if EQ("next") { + lua_next(L, -2); + } + else if EQ("concat") { + lua_concat(L, getnum); + } + else if EQ("rawcall") { + int narg = getnum; + int nres = getnum; + lua_rawcall(L, narg, nres); + } + else if EQ("call") { + int narg = getnum; + int nres = getnum; + lua_call(L, narg, nres); + } + else if EQ("dostring") { + lua_dostring(L, luaL_check_string(L, getnum)); + } + else if EQ("settagmethod") { + int tag = getnum; + const char *event = getname; + lua_settagmethod(L, tag, event); + } + else if EQ("gettagmethod") { + int tag = getnum; + const char *event = getname; + lua_gettagmethod(L, tag, event); + } + else if EQ("type") { + lua_pushstring(L, lua_typename(L, lua_type(L, getnum))); + } + else luaL_verror(L, "unknown instruction %.30s", buff); + } + return 0; +} + +/* }====================================================== */ + + + +static const struct luaL_reg tests_funcs[] = { + {"hash", hash_query}, + {"limits", get_limits}, + {"listcode", listcode}, + {"liststrings", liststrings}, + {"listlocals", listlocals}, + {"loadlib", loadlib}, + {"querystr", string_query}, + {"querytab", table_query}, + {"testC", testC}, + {"ref", tref}, + {"getref", getref}, + {"unref", unref}, + {"newuserdata", newuserdata}, + {"udataval", udataval}, + {"newstate", newstate}, + {"closestate", closestate}, + {"doremote", doremote}, + {"settagmethod", settagmethod}, + {"equal", equal}, + {"totalmem", mem_query} +}; + + +void luaB_opentests (lua_State *L) { + lua_newtable(L); + lua_getglobals(L); + lua_pushvalue(L, -2); + lua_setglobals(L); + luaL_openl(L, tests_funcs); /* open functions inside new table */ + lua_setglobals(L); /* restore old table of globals */ + lua_setglobal(L, "T"); /* set new table as global T */ +} + +#endif @@ -1,5 +1,5 @@ /* -** $Id: ltm.c,v 1.25 1999/05/21 19:41:49 roberto Exp $ +** $Id: ltm.c,v 1.56 2000/10/31 13:10:24 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -8,241 +8,156 @@ #include <stdio.h> #include <string.h> -#include "lauxlib.h" +#include "lua.h" + +#include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "ltm.h" -char *luaT_eventname[] = { /* ORDER IM */ - "gettable", "settable", "index", "getglobal", "setglobal", "add", - "sub", "mul", "div", "pow", "unm", "lt", "le", "gt", "ge", - "concat", "gc", "function", NULL +const char *const luaT_eventname[] = { /* ORDER TM */ + "gettable", "settable", "index", "getglobal", "setglobal", "add", "sub", + "mul", "div", "pow", "unm", "lt", "concat", "gc", "function", + "le", "gt", "ge", /* deprecated options!! */ + NULL }; -static int luaI_checkevent (char *name, char *list[]) { - int e = luaL_findstring(name, list); +static int findevent (const char *name) { + int i; + for (i=0; luaT_eventname[i]; i++) + if (strcmp(luaT_eventname[i], name) == 0) + return i; + return -1; /* name not found */ +} + + +static int luaI_checkevent (lua_State *L, const char *name, int t) { + int e = findevent(name); + if (e >= TM_N) + luaO_verror(L, "event `%.50s' is deprecated", name); + if (e == TM_GC && t == LUA_TTABLE) + luaO_verror(L, "event `gc' for tables is deprecated"); if (e < 0) - luaL_verror("`%.50s' is not a valid event name", name); + luaO_verror(L, "`%.50s' is not a valid event name", name); return e; } -/* events in LUA_T_NIL are all allowed, since this is used as a +/* events in LUA_TNIL are all allowed, since this is used as a * 'placeholder' for "default" fallbacks */ -static char luaT_validevents[NUM_TAGS][IM_N] = { /* ORDER LUA_T, ORDER IM */ -{1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_T_USERDATA */ -{1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, /* LUA_T_NUMBER */ -{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, /* LUA_T_STRING */ -{0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* LUA_T_ARRAY */ -{1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, /* LUA_T_PROTO */ -{1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, /* LUA_T_CPROTO */ -{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} /* LUA_T_NIL */ +/* ORDER LUA_T, ORDER TM */ +static const char luaT_validevents[NUM_TAGS][TM_N] = { + {1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TUSERDATA */ + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* LUA_TNIL */ + {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, /* LUA_TNUMBER */ + {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, /* LUA_TSTRING */ + {0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TTABLE */ + {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0} /* LUA_TFUNCTION */ }; -static int luaT_validevent (int t, int e) { /* ORDER LUA_T */ - return (t < LUA_T_NIL) ? 1 : luaT_validevents[-t][e]; +int luaT_validevent (int t, int e) { /* ORDER LUA_T */ + return (t >= NUM_TAGS) ? 1 : luaT_validevents[t][e]; } -static void init_entry (int tag) { +static void init_entry (lua_State *L, int tag) { int i; - for (i=0; i<IM_N; i++) - ttype(luaT_getim(tag, i)) = LUA_T_NIL; + for (i=0; i<TM_N; i++) + luaT_gettm(L, tag, i) = NULL; + L->TMtable[tag].collected = NULL; } -void luaT_init (void) { +void luaT_init (lua_State *L) { int t; - L->last_tag = -(NUM_TAGS-1); - luaM_growvector(L->IMtable, 0, NUM_TAGS, struct IM, arrEM, MAX_INT); - for (t=L->last_tag; t<=0; t++) - init_entry(t); + luaM_growvector(L, L->TMtable, 0, NUM_TAGS, struct TM, "", MAX_INT); + L->nblocks += NUM_TAGS*sizeof(struct TM); + L->last_tag = NUM_TAGS-1; + for (t=0; t<=L->last_tag; t++) + init_entry(L, t); } -int lua_newtag (void) { - --L->last_tag; - luaM_growvector(L->IMtable, -(L->last_tag), 1, struct IM, arrEM, MAX_INT); - init_entry(L->last_tag); +LUA_API int lua_newtag (lua_State *L) { + luaM_growvector(L, L->TMtable, L->last_tag, 1, struct TM, + "tag table overflow", MAX_INT); + L->nblocks += sizeof(struct TM); + L->last_tag++; + init_entry(L, L->last_tag); return L->last_tag; } -static void checktag (int tag) { - if (!(L->last_tag <= tag && tag <= 0)) - luaL_verror("%d is not a valid tag", tag); +static void checktag (lua_State *L, int tag) { + if (!(0 <= tag && tag <= L->last_tag)) + luaO_verror(L, "%d is not a valid tag", tag); } -void luaT_realtag (int tag) { - if (!(L->last_tag <= tag && tag < LUA_T_NIL)) - luaL_verror("tag %d was not created by `newtag'", tag); +void luaT_realtag (lua_State *L, int tag) { + if (!validtag(tag)) + luaO_verror(L, "tag %d was not created by `newtag'", tag); } -int lua_copytagmethods (int tagto, int tagfrom) { +LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom) { int e; - checktag(tagto); - checktag(tagfrom); - for (e=0; e<IM_N; e++) { + checktag(L, tagto); + checktag(L, tagfrom); + for (e=0; e<TM_N; e++) { if (luaT_validevent(tagto, e)) - *luaT_getim(tagto, e) = *luaT_getim(tagfrom, e); + luaT_gettm(L, tagto, e) = luaT_gettm(L, tagfrom, e); } return tagto; } -int luaT_effectivetag (TObject *o) { - int t; - switch (t = ttype(o)) { - case LUA_T_ARRAY: - return o->value.a->htag; - case LUA_T_USERDATA: { - int tag = o->value.ts->u.d.tag; - return (tag >= 0) ? LUA_T_USERDATA : tag; - } - case LUA_T_CLOSURE: - return o->value.cl->consts[0].ttype; -#ifdef DEBUG - case LUA_T_PMARK: case LUA_T_CMARK: - case LUA_T_CLMARK: case LUA_T_LINE: - LUA_INTERNALERROR("invalid type"); -#endif - default: - return t; +int luaT_tag (const TObject *o) { + int t = ttype(o); + switch (t) { + case LUA_TUSERDATA: return tsvalue(o)->u.d.tag; + case LUA_TTABLE: return hvalue(o)->htag; + default: return t; } } -TObject *luaT_gettagmethod (int t, char *event) { - int e = luaI_checkevent(event, luaT_eventname); - checktag(t); - if (luaT_validevent(t, e)) - return luaT_getim(t,e); - else - return &luaO_nilobject; -} - - -void luaT_settagmethod (int t, char *event, TObject *func) { - TObject temp; - int e = luaI_checkevent(event, luaT_eventname); - checktag(t); - if (!luaT_validevent(t, e)) - luaL_verror("cannot change tag method `%.20s' for type `%.20s'%.20s", - luaT_eventname[e], luaO_typenames[-t], - (t == LUA_T_ARRAY || t == LUA_T_USERDATA) ? " with default tag" - : ""); - temp = *func; - *func = *luaT_getim(t,e); - *luaT_getim(t, e) = temp; -} - - -char *luaT_travtagmethods (int (*fn)(TObject *)) { /* ORDER IM */ +LUA_API void lua_gettagmethod (lua_State *L, int t, const char *event) { int e; - for (e=IM_GETTABLE; e<=IM_FUNCTION; e++) { - int t; - for (t=0; t>=L->last_tag; t--) - if (fn(luaT_getim(t,e))) - return luaT_eventname[e]; + e = luaI_checkevent(L, event, t); + checktag(L, t); + if (luaT_validevent(t, e) && luaT_gettm(L, t, e)) { + clvalue(L->top) = luaT_gettm(L, t, e); + ttype(L->top) = LUA_TFUNCTION; } - return NULL; -} - - -/* -* =================================================================== -* compatibility with old fallback system -*/ -#ifdef LUA_COMPAT2_5 - -#include "lapi.h" -#include "lstring.h" - -static void errorFB (void) -{ - lua_Object o = lua_getparam(1); - if (lua_isstring(o)) - fprintf(stderr, "lua: %s\n", lua_getstring(o)); else - fprintf(stderr, "lua: unknown error\n"); -} - - -static void nilFB (void) { } - - -static void typeFB (void) { - lua_error("unexpected type"); + ttype(L->top) = LUA_TNIL; + incr_top; } -static void fillvalids (IMS e, TObject *func) { - int t; - for (t=LUA_T_NIL; t<=LUA_T_USERDATA; t++) - if (luaT_validevent(t, e)) - *luaT_getim(t, e) = *func; -} - - -void luaT_setfallback (void) { - static char *oldnames [] = {"error", "getglobal", "arith", "order", NULL}; - TObject oldfunc; - lua_CFunction replace; - char *name = luaL_check_string(1); - lua_Object func = lua_getparam(2); - luaL_arg_check(lua_isfunction(func), 2, "function expected"); - switch (luaL_findstring(name, oldnames)) { - case 0: { /* old error fallback */ - TObject *em = &(luaS_new("_ERRORMESSAGE")->u.s.globalval); - oldfunc = *em; - *em = *luaA_Address(func); - replace = errorFB; - break; - } - case 1: /* old getglobal fallback */ - oldfunc = *luaT_getim(LUA_T_NIL, IM_GETGLOBAL); - *luaT_getim(LUA_T_NIL, IM_GETGLOBAL) = *luaA_Address(func); - replace = nilFB; - break; - case 2: { /* old arith fallback */ - int i; - oldfunc = *luaT_getim(LUA_T_NUMBER, IM_POW); - for (i=IM_ADD; i<=IM_UNM; i++) /* ORDER IM */ - fillvalids(i, luaA_Address(func)); - replace = typeFB; +LUA_API void lua_settagmethod (lua_State *L, int t, const char *event) { + int e = luaI_checkevent(L, event, t); + checktag(L, t); + if (!luaT_validevent(t, e)) + luaO_verror(L, "cannot change `%.20s' tag method for type `%.20s'%.20s", + luaT_eventname[e], luaO_typenames[t], + (t == LUA_TTABLE || t == LUA_TUSERDATA) ? + " with default tag" : ""); + switch (ttype(L->top - 1)) { + case LUA_TNIL: + luaT_gettm(L, t, e) = NULL; break; - } - case 3: { /* old order fallback */ - int i; - oldfunc = *luaT_getim(LUA_T_NIL, IM_LT); - for (i=IM_LT; i<=IM_GE; i++) /* ORDER IM */ - fillvalids(i, luaA_Address(func)); - replace = typeFB; + case LUA_TFUNCTION: + luaT_gettm(L, t, e) = clvalue(L->top - 1); break; - } - default: { - int e; - if ((e = luaL_findstring(name, luaT_eventname)) >= 0) { - oldfunc = *luaT_getim(LUA_T_NIL, e); - fillvalids(e, luaA_Address(func)); - replace = (e == IM_GC || e == IM_INDEX) ? nilFB : typeFB; - } - else { - luaL_verror("`%.50s' is not a valid fallback name", name); - replace = NULL; /* to avoid warnings */ - } - } + default: + lua_error(L, "tag method must be a function (or nil)"); } - if (oldfunc.ttype != LUA_T_NIL) - luaA_pushobject(&oldfunc); - else - lua_pushcfunction(replace); + L->top--; } -#endif @@ -1,5 +1,5 @@ /* -** $Id: ltm.h,v 1.5 1999/01/15 13:11:57 roberto Exp $ +** $Id: ltm.h,v 1.18 2000/10/05 13:00:17 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -13,50 +13,47 @@ /* * WARNING: if you change the order of this enumeration, -* grep "ORDER IM" +* grep "ORDER TM" */ typedef enum { - IM_GETTABLE = 0, - IM_SETTABLE, - IM_INDEX, - IM_GETGLOBAL, - IM_SETGLOBAL, - IM_ADD, - IM_SUB, - IM_MUL, - IM_DIV, - IM_POW, - IM_UNM, - IM_LT, - IM_LE, - IM_GT, - IM_GE, - IM_CONCAT, - IM_GC, - IM_FUNCTION -} IMS; - -#define IM_N 18 - - -struct IM { - TObject int_method[IM_N]; + TM_GETTABLE = 0, + TM_SETTABLE, + TM_INDEX, + TM_GETGLOBAL, + TM_SETGLOBAL, + TM_ADD, + TM_SUB, + TM_MUL, + TM_DIV, + TM_POW, + TM_UNM, + TM_LT, + TM_CONCAT, + TM_GC, + TM_FUNCTION, + TM_N /* number of elements in the enum */ +} TMS; + + +struct TM { + Closure *method[TM_N]; + TString *collected; /* list of garbage-collected udata with this tag */ }; -#define luaT_getim(tag,event) (&L->IMtable[-(tag)].int_method[event]) -#define luaT_getimbyObj(o,e) (luaT_getim(luaT_effectivetag(o),(e))) +#define luaT_gettm(L,tag,event) (L->TMtable[tag].method[event]) +#define luaT_gettmbyObj(L,o,e) (luaT_gettm((L),luaT_tag(o),(e))) -extern char *luaT_eventname[]; +#define validtag(t) (NUM_TAGS <= (t) && (t) <= L->last_tag) -void luaT_init (void); -void luaT_realtag (int tag); -int luaT_effectivetag (TObject *o); -void luaT_settagmethod (int t, char *event, TObject *func); -TObject *luaT_gettagmethod (int t, char *event); -char *luaT_travtagmethods (int (*fn)(TObject *)); +extern const char *const luaT_eventname[]; + + +void luaT_init (lua_State *L); +void luaT_realtag (lua_State *L, int tag); +int luaT_tag (const TObject *o); +int luaT_validevent (int t, int e); /* used by compatibility module */ -void luaT_setfallback (void); /* only if LUA_COMPAT2_5 */ #endif diff --git a/src/lua/Makefile b/src/lua/Makefile index cf5d31b6..5b47161f 100644 --- a/src/lua/Makefile +++ b/src/lua/Makefile @@ -1,4 +1,4 @@ -# makefile for lua interpreter +# makefile for Lua interpreter LUA= ../.. @@ -14,7 +14,7 @@ T= $(BIN)/lua all: $T $T: $(OBJS) $(LIB)/liblua.a $(LIB)/liblualib.a - $(CC) -o $@ $(OBJS) -L$(LIB) -llua -llualib -lm + $(CC) -o $@ $(OBJS) -L$(LIB) -llua -llualib $(EXTRA_LIBS) $(LIB)/liblua.a: cd ..; $(MAKE) diff --git a/src/lua/README b/src/lua/README index db2eafb7..832fb5bf 100644 --- a/src/lua/README +++ b/src/lua/README @@ -1,38 +1,40 @@ -This client is a sample lua interpreter. +This is lua, a sample Lua interpreter. It can be used as a batch interpreter and also interactively. - -Here are the options it understands: - +There are man pages for it in both nroff and html in ../../doc. + +Here are the options that it understands: + - execute stdin as a file + -c close Lua when exiting + -e stat execute string `stat' + -f name execute file `name' with remaining arguments in table `arg' + -i enter interactive mode with prompt + -q enter interactive mode without prompt + -sNUM set stack size to NUM (must be the first option) -v print version information - -d turn debug on - -e stat dostring `stat' - -q interactive mode without prompt - -i interactive mode with prompt - - executes stdin as a file - a=b sets global `a' with string `b' (no need to quote b) - name dofile `name' + a=b set global `a' to string `b' + name execute file `name' If no options are given, then it reads lines from stdin and executes them -as they are read. So, each line must contain a complete statement. +as they are read -- so, each line must contain a complete statement. To span a statement across several lines, end each line with a backslash '\'. To change the prompt, set the global variable _PROMPT to whatever you want. -You can do after calling the interpreter or on the command line with - _PROMPT="lua: " -for example. +You can do this after calling the interpreter or on the command line with + lua _PROMPT="lua: " -i +for example. Note that you need "-i" in this case. You must be careful when using quotes on the command line because they are usually handled by the shell. This interpreter is good for using Lua as a standalone language. -For a minimal interpreter, see etc/min.c. +For a minimal interpreter, see ../../etc/min.c. If your application simply exports new functions to Lua (which is common), -then you can use this interpreter unmodified: just define a function - - void lua_userinit (void) - -in your code. In this function, you should do whatever initializations are -need, typically exporting your functions to Lua. -If you use this scheme, you must explicily open any standard libraries you need. -See ../lib/linit.c +then you can use this interpreter (almost) unmodified, as follows: +First, define a function + void myinit (lua_State *L) +in your own code. In this function, you should do whatever initializations +are needed by your application, typically exporting your functions to Lua. +Then, add a call "myinit(L)" in lua.c after the place marked + "add your libraries here" +Of course, you can use any name instead of "myinit". diff --git a/src/lua/lua.c b/src/lua/lua.c index 5acd6173..2da857e1 100644 --- a/src/lua/lua.c +++ b/src/lua/lua.c @@ -1,5 +1,5 @@ /* -** $Id: lua.c,v 1.21 1999/07/02 18:22:38 roberto Exp $ +** $Id: lua.c,v 1.55 2000/10/20 16:36:32 roberto Exp $ ** Lua stand-alone interpreter ** See Copyright Notice in lua.h */ @@ -11,24 +11,51 @@ #include <string.h> #include "lua.h" + #include "luadebug.h" #include "lualib.h" +static lua_State *L = NULL; + + +#ifndef PROMPT +#define PROMPT "> " +#endif + #ifdef _POSIX_SOURCE #include <unistd.h> #else -#define isatty(x) (x==0) /* assume stdin is a tty */ +static int isatty (int x) { return x==0; } /* assume stdin is a tty */ #endif +/* +** global options +*/ +struct Options { + int toclose; + int stacksize; +}; + + typedef void (*handler)(int); /* type for signal actions */ static void laction (int i); -static lua_LHFunction old_linehook = NULL; -static lua_CHFunction old_callhook = NULL; +static lua_Hook old_linehook = NULL; +static lua_Hook old_callhook = NULL; + + +static void userinit (void) { + lua_baselibopen(L); + lua_iolibopen(L); + lua_strlibopen(L); + lua_mathlibopen(L); + lua_dblibopen(L); + /* add your libraries here */ +} static handler lreset (void) { @@ -37,66 +64,125 @@ static handler lreset (void) { static void lstop (void) { - lua_setlinehook(old_linehook); - lua_setcallhook(old_callhook); + lua_setlinehook(L, old_linehook); + lua_setcallhook(L, old_callhook); lreset(); - lua_error("interrupted!"); + lua_error(L, "interrupted!"); } static void laction (int i) { + (void)i; /* to avoid warnings */ signal(SIGINT, SIG_DFL); /* if another SIGINT happens before lstop, terminate process (default action) */ - old_linehook = lua_setlinehook((lua_LHFunction)lstop); - old_callhook = lua_setcallhook((lua_CHFunction)lstop); + old_linehook = lua_setlinehook(L, (lua_Hook)lstop); + old_callhook = lua_setcallhook(L, (lua_Hook)lstop); } -static int ldo (int (*f)(char *), char *name) { +static int ldo (int (*f)(lua_State *l, const char *), const char *name) { int res; handler h = lreset(); - res = f(name); /* dostring | dofile */ + int top = lua_gettop(L); + res = f(L, name); /* dostring | dofile */ + lua_settop(L, top); /* remove eventual results */ signal(SIGINT, h); /* restore old action */ + /* Lua gives no message in such cases, so lua.c provides one */ + if (res == LUA_ERRMEM) { + fprintf(stderr, "lua: memory allocation error\n"); + } + else if (res == LUA_ERRERR) + fprintf(stderr, "lua: error in error message\n"); return res; } static void print_message (void) { fprintf(stderr, -"Lua: command line options:\n" -" -v print version information\n" -" -d turn debug on\n" -" -e stat dostring `stat'\n" -" -q interactive mode without prompt\n" -" -i interactive mode with prompt\n" -" - executes stdin as a file\n" -" a=b sets global `a' with string `b'\n" -" name dofile `name'\n\n"); + "usage: lua [options]. Available options are:\n" + " - execute stdin as a file\n" + " -c close Lua when exiting\n" + " -e stat execute string `stat'\n" + " -f name execute file `name' with remaining arguments in table `arg'\n" + " -i enter interactive mode with prompt\n" + " -q enter interactive mode without prompt\n" + " -sNUM set stack size to NUM (must be the first option)\n" + " -v print version information\n" + " a=b set global `a' to string `b'\n" + " name execute file `name'\n" +); +} + + +static void print_version (void) { + printf("%.80s %.80s\n", LUA_VERSION, LUA_COPYRIGHT); } static void assign (char *arg) { - if (strlen(arg) >= 500) - fprintf(stderr, "lua: shell argument too long"); - else { - char buffer[500]; - char *eq = strchr(arg, '='); - lua_pushstring(eq+1); - strncpy(buffer, arg, eq-arg); - buffer[eq-arg] = 0; - lua_setglobal(buffer); + char *eq = strchr(arg, '='); + *eq = '\0'; /* spilt `arg' in two strings (name & value) */ + lua_pushstring(L, eq+1); + lua_setglobal(L, arg); +} + + +static void getargs (char *argv[]) { + int i; + lua_newtable(L); + for (i=0; argv[i]; i++) { + /* arg[i] = argv[i] */ + lua_pushnumber(L, i); + lua_pushstring(L, argv[i]); + lua_settable(L, -3); + } + /* arg.n = maximum index in table `arg' */ + lua_pushstring(L, "n"); + lua_pushnumber(L, i-1); + lua_settable(L, -3); +} + + +static int l_getargs (lua_State *l) { + char **argv = (char **)lua_touserdata(l, -1); + getargs(argv); + return 1; +} + + +static int file_input (const char *argv) { + int result = ldo(lua_dofile, argv); + if (result) { + if (result == LUA_ERRFILE) { + fprintf(stderr, "lua: cannot execute file "); + perror(argv); + } + return EXIT_FAILURE; } + else + return EXIT_SUCCESS; } -static void manual_input (int prompt) { +/* maximum length of an input string */ +#ifndef MAXINPUT +#define MAXINPUT BUFSIZ +#endif + +static void manual_input (int version, int prompt) { int cont = 1; + if (version) print_version(); while (cont) { - char buffer[BUFSIZ]; + char buffer[MAXINPUT]; int i = 0; - lua_beginblock(); - if (prompt) - printf("%s", lua_getstring(lua_getglobal("_PROMPT"))); + if (prompt) { + const char *s; + lua_getglobal(L, "_PROMPT"); + s = lua_tostring(L, -1); + if (!s) s = PROMPT; + fputs(s, stdout); + lua_pop(L, 1); /* remove global */ + } for(;;) { int c = getchar(); if (c == EOF) { @@ -108,81 +194,129 @@ static void manual_input (int prompt) { buffer[i-1] = '\n'; else break; } - else if (i >= BUFSIZ-1) { - fprintf(stderr, "lua: argument line too long\n"); + else if (i >= MAXINPUT-1) { + fprintf(stderr, "lua: input line too long\n"); break; } else buffer[i++] = (char)c; } buffer[i] = '\0'; ldo(lua_dostring, buffer); - lua_endblock(); + lua_settop(L, 0); /* remove eventual results */ } printf("\n"); } -int main (int argc, char *argv[]) -{ - int i; - lua_open(); - lua_pushstring("> "); lua_setglobal("_PROMPT"); - lua_userinit(); - if (argc < 2) { /* no arguments? */ +static int handle_argv (char *argv[], struct Options *opt) { + if (opt->stacksize > 0) argv++; /* skip option `-s' (if present) */ + if (*argv == NULL) { /* no more arguments? */ if (isatty(0)) { - printf("%s %s\n", LUA_VERSION, LUA_COPYRIGHT); - manual_input(1); + manual_input(1, 1); } else ldo(lua_dofile, NULL); /* executes stdin as a file */ } - else for (i=1; i<argc; i++) { - if (argv[i][0] == '-') { /* option? */ - switch (argv[i][1]) { - case 0: - ldo(lua_dofile, NULL); /* executes stdin as a file */ - break; - case 'i': - manual_input(1); - break; - case 'q': - manual_input(0); - break; - case 'd': - lua_setdebug(1); - break; - case 'v': - printf("%s %s\n(written by %s)\n\n", - LUA_VERSION, LUA_COPYRIGHT, LUA_AUTHORS); - break; - case 'e': - i++; - if (ldo(lua_dostring, argv[i]) != 0) { - fprintf(stderr, "lua: error running argument `%s'\n", argv[i]); - return 1; + else { /* other arguments; loop over them */ + int i; + for (i = 0; argv[i] != NULL; i++) { + if (argv[i][0] != '-') { /* not an option? */ + if (strchr(argv[i], '=')) + assign(argv[i]); + else + if (file_input(argv[i]) != EXIT_SUCCESS) + return EXIT_FAILURE; /* stop if file fails */ + } + else switch (argv[i][1]) { /* option */ + case 0: { + ldo(lua_dofile, NULL); /* executes stdin as a file */ + break; + } + case 'i': { + manual_input(0, 1); + break; + } + case 'q': { + manual_input(0, 0); + break; + } + case 'c': { + opt->toclose = 1; + break; + } + case 'v': { + print_version(); + break; + } + case 'e': { + i++; + if (argv[i] == NULL) { + print_message(); + return EXIT_FAILURE; + } + if (ldo(lua_dostring, argv[i]) != 0) { + fprintf(stderr, "lua: error running argument `%.99s'\n", argv[i]); + return EXIT_FAILURE; + } + break; + } + case 'f': { + i++; + if (argv[i] == NULL) { + print_message(); + return EXIT_FAILURE; + } + getargs(argv+i); /* collect remaining arguments */ + lua_setglobal(L, "arg"); + return file_input(argv[i]); /* stop scanning arguments */ + } + case 's': { + fprintf(stderr, "lua: stack size (`-s') must be the first option\n"); + return EXIT_FAILURE; + } + default: { + print_message(); + return EXIT_FAILURE; } - break; - default: - print_message(); - exit(1); - } - } - else if (strchr(argv[i], '=')) - assign(argv[i]); - else { - int result = ldo(lua_dofile, argv[i]); - if (result) { - if (result == 2) { - fprintf(stderr, "lua: cannot execute file "); - perror(argv[i]); } - exit(1); - } } } -#ifdef DEBUG - lua_close(); -#endif - return 0; + return EXIT_SUCCESS; +} + + +static void getstacksize (int argc, char *argv[], struct Options *opt) { + if (argc >= 2 && argv[1][0] == '-' && argv[1][1] == 's') { + int stacksize = atoi(&argv[1][2]); + if (stacksize <= 0) { + fprintf(stderr, "lua: invalid stack size ('%.20s')\n", &argv[1][2]); + exit(EXIT_FAILURE); + } + opt->stacksize = stacksize; + } + else + opt->stacksize = 0; /* no stack size */ +} + + +static void register_getargs (char *argv[]) { + lua_pushuserdata(L, argv); + lua_pushcclosure(L, l_getargs, 1); + lua_setglobal(L, "getargs"); +} + + +int main (int argc, char *argv[]) { + struct Options opt; + int status; + opt.toclose = 0; + getstacksize(argc, argv, &opt); /* handle option `-s' */ + L = lua_open(opt.stacksize); /* create state */ + userinit(); /* open libraries */ + register_getargs(argv); /* create `getargs' function */ + status = handle_argv(argv+1, &opt); + if (opt.toclose) + lua_close(L); + return status; } diff --git a/src/luac/Makefile b/src/luac/Makefile index e2950e60..4517d82b 100644 --- a/src/luac/Makefile +++ b/src/luac/Makefile @@ -1,12 +1,12 @@ -# makefile for lua compiler +# makefile for Lua compiler LUA= ../.. include $(LUA)/config INCS= -I$(INC) $(EXTRA_INCS) -I.. -OBJS= dump.o luac.o opcode.o opt.o print.o stubs.o test.o -SRCS= dump.c luac.c opcode.c opt.c print.c stubs.c test.c luac.h opcode.h +OBJS= dump.o luac.o opt.o print.o stubs.o +SRCS= dump.c luac.c opt.c print.c stubs.c luac.h print.h T= $(BIN)/luac diff --git a/src/luac/README b/src/luac/README index 9fba74bb..8d8bb491 100644 --- a/src/luac/README +++ b/src/luac/README @@ -1,28 +1,22 @@ +This is luac, the Lua compiler. +There are man pages for it in both nroff and html in ../../doc. + luac translates Lua programs into binary files that can be loaded and executed with lua_dofile in C or with dofile in Lua. -The main advantages of pre-compiling chunks are: faster loading, -protecting source code from user changes, off-line syntax error detection. +The main advantages of pre-compiling chunks are: faster loading, protecting +source code from user changes, and off-line syntax error detection. luac can also be used to learn about the Lua virtual machine. Here are the options that luac understands: - -c compile (default) - -d generate debugging information - -D name predefine 'name' for conditional compilation - -l list (default for -u) - -n save numbers in native format (file may not be portable) - -o file output file for -c (default is "luac.out") - -O optimize - -p parse only - -q quiet (default for -c) - -t test code integrity - -u undump - -U name undefine 'name' for conditional compilation - -v show version information - -V verbose - - compile "stdin" + - process stdin + -l list + -o file output file (default is "luac.out") + -p parse only + -s strip debug information + -t test code integrity + -v show version information -Finally, luac is an example of how to use the internals of Lua (politely). -Also, luac does not need the runtime code and stubs.c makes sure it is not +luac is also an example of how to use the internals of Lua (politely). +Finally, luac does not need the runtime code, and stubs.c makes sure it is not linked into luac. This file also shows how to avoid linking the parser. - diff --git a/src/luac/dump.c b/src/luac/dump.c index 479ce5d4..149469ba 100644 --- a/src/luac/dump.c +++ b/src/luac/dump.c @@ -1,154 +1,121 @@ /* -** $Id: dump.c,v 1.20 1999/07/02 19:34:26 lhf Exp $ +** $Id: dump.c,v 1.30 2000/10/31 16:57:23 lhf Exp $ ** save bytecodes to file ** See Copyright Notice in lua.h */ -#include <errno.h> #include <stdio.h> #include <stdlib.h> -#include "luac.h" +#include <string.h> -#ifdef OLD_ANSI -#define strerror(e) "(no error message provided by operating system)" -#endif +#include "luac.h" +#define DumpVector(b,n,size,D) fwrite(b,size,n,D) #define DumpBlock(b,size,D) fwrite(b,size,1,D) -#define DumpInt DumpLong +#define DumpByte fputc -static void DumpWord(int i, FILE* D) +static void DumpInt(int x, FILE* D) { - int hi= 0x0000FF & (i>>8); - int lo= 0x0000FF & i; - fputc(hi,D); - fputc(lo,D); + DumpBlock(&x,sizeof(x),D); } -static void DumpLong(long i, FILE* D) +static void DumpSize(size_t x, FILE* D) { - int hi= 0x00FFFF & (i>>16); - int lo= 0x00FFFF & i; - DumpWord(hi,D); - DumpWord(lo,D); + DumpBlock(&x,sizeof(x),D); } -static void DumpNumber(real x, FILE* D, int native, TProtoFunc* tf) +static void DumpNumber(Number x, FILE* D) { - if (native) - DumpBlock(&x,sizeof(x),D); - else - { - char b[256]; - int n; - sprintf(b,NUMBER_FMT"%n",x,&n); - luaU_str2d(b,tf->source->str); /* help lundump not to fail */ - fputc(n,D); - DumpBlock(b,n,D); - } + DumpBlock(&x,sizeof(x),D); } -static void DumpCode(TProtoFunc* tf, FILE* D) +static void DumpString(const TString* s, FILE* D) { - int size=luaU_codesize(tf); - DumpLong(size,D); - DumpBlock(tf->code,size,D); -} - -static void DumpString(char* s, int size, FILE* D) -{ - if (s==NULL) - DumpLong(0,D); + if (s==NULL || s->str==NULL) + DumpSize(0,D); else { - DumpLong(size,D); - DumpBlock(s,size,D); + size_t size=s->len+1; /* include trailing '\0' */ + DumpSize(size,D); + DumpBlock(s->str,size,D); } } -static void DumpTString(TaggedString* s, FILE* D) +static void DumpCode(const Proto* tf, FILE* D) { - if (s==NULL) - DumpString(NULL,0,D); - else - DumpString(s->str,s->u.s.len+1,D); + DumpInt(tf->ncode,D); + DumpVector(tf->code,tf->ncode,sizeof(*tf->code),D); } -static void DumpLocals(TProtoFunc* tf, FILE* D) +static void DumpLocals(const Proto* tf, FILE* D) { - if (tf->locvars==NULL) - DumpInt(0,D); - else + int i,n=tf->nlocvars; + DumpInt(n,D); + for (i=0; i<n; i++) { - LocVar* v; - int n=0; - for (v=tf->locvars; v->line>=0; v++) - ++n; - DumpInt(n,D); - for (v=tf->locvars; v->line>=0; v++) - { - DumpInt(v->line,D); - DumpTString(v->varname,D); - } + DumpString(tf->locvars[i].varname,D); + DumpInt(tf->locvars[i].startpc,D); + DumpInt(tf->locvars[i].endpc,D); } } -static void DumpFunction(TProtoFunc* tf, FILE* D, int native); +static void DumpLines(const Proto* tf, FILE* D) +{ + DumpInt(tf->nlineinfo,D); + DumpVector(tf->lineinfo,tf->nlineinfo,sizeof(*tf->lineinfo),D); +} + +static void DumpFunction(const Proto* tf, FILE* D); -static void DumpConstants(TProtoFunc* tf, FILE* D, int native) +static void DumpConstants(const Proto* tf, FILE* D) { - int i,n=tf->nconsts; - DumpInt(n,D); + int i,n; + DumpInt(n=tf->nkstr,D); for (i=0; i<n; i++) - { - TObject* o=tf->consts+i; - fputc(-ttype(o),D); /* ttype(o) is negative - ORDER LUA_T */ - switch (ttype(o)) - { - case LUA_T_NUMBER: - DumpNumber(nvalue(o),D,native,tf); - break; - case LUA_T_STRING: - DumpTString(tsvalue(o),D); - break; - case LUA_T_PROTO: - DumpFunction(tfvalue(o),D,native); - break; - case LUA_T_NIL: - break; - default: /* cannot happen */ - luaU_badconstant("dump",i,o,tf); - break; - } - } + DumpString(tf->kstr[i],D); + DumpInt(tf->nknum,D); + DumpVector(tf->knum,tf->nknum,sizeof(*tf->knum),D); + DumpInt(n=tf->nkproto,D); + for (i=0; i<n; i++) + DumpFunction(tf->kproto[i],D); } -static void DumpFunction(TProtoFunc* tf, FILE* D, int native) +static void DumpFunction(const Proto* tf, FILE* D) { + DumpString(tf->source,D); DumpInt(tf->lineDefined,D); - DumpTString(tf->source,D); - DumpCode(tf,D); + DumpInt(tf->numparams,D); + DumpByte(tf->is_vararg,D); + DumpInt(tf->maxstacksize,D); DumpLocals(tf,D); - DumpConstants(tf,D,native); + DumpLines(tf,D); + DumpConstants(tf,D); + DumpCode(tf,D); if (ferror(D)) - luaL_verror("write error" IN ": %s (errno=%d)",INLOC,strerror(errno),errno); + { + perror("luac: write error"); + exit(1); + } } -static void DumpHeader(TProtoFunc* Main, FILE* D, int native) +static void DumpHeader(FILE* D) { - fputc(ID_CHUNK,D); + DumpByte(ID_CHUNK,D); fputs(SIGNATURE,D); - fputc(VERSION,D); - if (native) - { - fputc(sizeof(real),D); - DumpNumber(TEST_NUMBER,D,native,Main); - } - else - fputc(0,D); + DumpByte(VERSION,D); + DumpByte(luaU_endianess(),D); + DumpByte(sizeof(int),D); + DumpByte(sizeof(size_t),D); + DumpByte(sizeof(Instruction),D); + DumpByte(SIZE_INSTRUCTION,D); + DumpByte(SIZE_OP,D); + DumpByte(SIZE_B,D); + DumpByte(sizeof(Number),D); + DumpNumber(TEST_NUMBER,D); } -void luaU_dumpchunk(TProtoFunc* Main, FILE* D, int native) +void luaU_dumpchunk(const Proto* Main, FILE* D) { - DumpHeader(Main,D,native); - DumpFunction(Main,D,native); + DumpHeader(D); + DumpFunction(Main,D); } diff --git a/src/luac/luac.c b/src/luac/luac.c index 68af1c76..8832de62 100644 --- a/src/luac/luac.c +++ b/src/luac/luac.c @@ -1,5 +1,5 @@ /* -** $Id: luac.c,v 1.17 1999/07/02 19:34:26 lhf Exp $ +** $Id: luac.c,v 1.28 2000/11/06 20:06:27 lhf Exp $ ** lua compiler (saves bytecodes to files; also list binary files) ** See Copyright Notice in lua.h */ @@ -7,181 +7,197 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "luac.h" + #include "lparser.h" #include "lstate.h" #include "lzio.h" +#include "luac.h" #define OUTPUT "luac.out" /* default output file */ -static FILE* efopen(char* name, char* mode); -static void doit(int undump, char* filename); +static void usage(const char* message, const char* arg); +static int doargs(int argc, const char* argv[]); +static Proto* load(const char* filename); +static FILE* efopen(const char* name, const char* mode); +static void strip(Proto* tf); +static Proto* combine(Proto** P, int n); + +lua_State* lua_state=NULL; /* lazy! */ static int listing=0; /* list bytecodes? */ -static int debugging=0; /* emit debug information? */ static int dumping=1; /* dump bytecodes? */ -static int undumping=0; /* undump bytecodes? */ -static int optimizing=0; /* optimize? */ -static int parsing=0; /* parse only? */ +static int stripping=0; /* strip debug information? */ static int testing=0; /* test integrity? */ -static int verbose=0; /* tell user what is done */ -static int native=0; /* save numbers in native format? */ -static FILE* D; /* output file */ +static const char* output=OUTPUT; /* output file name */ -static void usage(char* op) +#define IS(s) (strcmp(argv[i],s)==0) + +int main(int argc, const char* argv[]) { - if (op) fprintf(stderr,"luac: unrecognized option '%s'\n",op); + Proto** P,*tf; + int i=doargs(argc,argv); + argc-=i; argv+=i; + if (argc<=0) usage("no input files given",NULL); + L=lua_open(0); + P=luaM_newvector(L,argc,Proto*); + for (i=0; i<argc; i++) + P[i]=load(IS("-")? NULL : argv[i]); + tf=combine(P,argc); + if (dumping) luaU_optchunk(tf); + if (listing) luaU_printchunk(tf); + if (testing) luaU_testchunk(tf); + if (dumping) + { + if (stripping) strip(tf); + luaU_dumpchunk(tf,efopen(output,"wb")); + } + return 0; +} + +static void usage(const char* message, const char* arg) +{ + if (message!=NULL) + { + fprintf(stderr,"luac: "); fprintf(stderr,message,arg); fprintf(stderr,"\n"); + } fprintf(stderr, "usage: luac [options] [filenames]. Available options are:\n" - " -c\t\tcompile (default)\n" - " -d\t\tgenerate debugging information\n" - " -D name\tpredefine 'name' for conditional compilation\n" - " -l\t\tlist (default for -u)\n" - " -n\t\tsave numbers in native format (file may not be portable)\n" - " -o file\toutput file for -c (default is \"" OUTPUT "\")\n" - " -O\t\toptimize\n" - " -p\t\tparse only\n" - " -q\t\tquiet (default for -c)\n" - " -t\t\ttest code integrity\n" - " -u\t\tundump\n" - " -U name\tundefine 'name' for conditional compilation\n" - " -v\t\tshow version information\n" - " -V\t\tverbose\n" - " -\t\tcompile \"stdin\"\n" + " - process stdin\n" + " -l list\n" + " -o file output file (default is \"" OUTPUT "\")\n" + " -p parse only\n" + " -s strip debug information\n" + " -t test code integrity\n" + " -v show version information\n" ); exit(1); } -#define IS(s) (strcmp(argv[i],s)==0) - -int main(int argc, char* argv[]) +static int doargs(int argc, const char* argv[]) { - char* d=OUTPUT; /* output file name */ int i; - lua_open(); for (i=1; i<argc; i++) { - if (argv[i][0]!='-') /* end of options */ + if (*argv[i]!='-') /* end of options */ break; else if (IS("-")) /* end of options; use stdin */ - break; - else if (IS("-c")) /* compile (and dump) */ - { - dumping=1; - undumping=0; - } - else if (IS("-D")) /* $define */ - { - TaggedString* s=luaS_new(argv[++i]); - s->u.s.globalval.ttype=LUA_T_NUMBER; - s->u.s.globalval.value.n=1; - } - else if (IS("-d")) /* debug */ - debugging=1; + return i; else if (IS("-l")) /* list */ listing=1; - else if (IS("-n")) /* native */ - native=1; else if (IS("-o")) /* output file */ - d=argv[++i]; - else if (IS("-O")) /* optimize */ - optimizing=1; - else if (IS("-p")) /* parse only */ { - dumping=0; - parsing=1; + output=argv[++i]; + if (output==NULL) usage(NULL,NULL); } - else if (IS("-q")) /* quiet */ - listing=0; + else if (IS("-p")) /* parse only */ + dumping=0; + else if (IS("-s")) /* strip debug information */ + stripping=1; else if (IS("-t")) /* test */ - testing=1; - else if (IS("-u")) /* undump */ { + testing=1; dumping=0; - undumping=1; - listing=1; - } - else if (IS("-U")) /* undefine */ - { - TaggedString* s=luaS_new(argv[++i]); - s->u.s.globalval.ttype=LUA_T_NIL; } else if (IS("-v")) /* show version */ - printf("%s %s\n(written by %s)\n\n",LUA_VERSION,LUA_COPYRIGHT,LUA_AUTHORS); - else if (IS("-V")) /* verbose */ - verbose=1; - else /* unknown option */ - usage(argv[i]); - } - --i; /* fake new argv[0] */ - argc-=i; - argv+=i; - if (dumping || parsing) - { - if (argc<2) usage(NULL); - if (dumping) { - for (i=1; i<argc; i++) /* play safe with output file */ - if (IS(d)) luaL_verror("will not overwrite input file \"%s\"",d); - D=efopen(d,"wb"); /* must open in binary mode */ + printf("%s %s\n",LUA_VERSION,LUA_COPYRIGHT); + if (argc==2) exit(0); } - for (i=1; i<argc; i++) doit(0,IS("-")? NULL : argv[i]); - if (dumping) fclose(D); + else /* unknown option */ + usage("unrecognized option `%s'",argv[i]); } - if (undumping) + if (i==argc && (listing || testing)) { - if (argc<2) - doit(1,OUTPUT); - else - for (i=1; i<argc; i++) doit(1,IS("-")? NULL : argv[i]); + dumping=0; + argv[--i]=OUTPUT; } - return 0; + return i; } -static void do_compile(ZIO* z) +static Proto* load(const char* filename) { - TProtoFunc* Main; - if (optimizing) L->debug=0; - if (debugging) L->debug=1; - Main=luaY_parser(z); - if (optimizing) luaU_optchunk(Main); - if (listing) luaU_printchunk(Main); - if (testing) luaU_testchunk(Main); - if (dumping) luaU_dumpchunk(Main,D,native); + Proto* tf; + ZIO z; + char source[512]; + FILE* f; + int c,undump; + if (filename==NULL) + { + f=stdin; + filename="(stdin)"; + } + else + f=efopen(filename,"r"); + c=ungetc(fgetc(f),f); + if (ferror(f)) + { + fprintf(stderr,"luac: cannot read from "); + perror(filename); + exit(1); + } + undump=(c==ID_CHUNK); + if (undump && f!=stdin) + { + fclose(f); + f=efopen(filename,"rb"); + } + sprintf(source,"@%.*s",Sizeof(source)-2,filename); + luaZ_Fopen(&z,f,source); + tf = undump ? luaU_undump(L,&z) : luaY_parser(L,&z); + if (f!=stdin) fclose(f); + return tf; } -static void do_undump(ZIO* z) +static Proto* combine(Proto** P, int n) { - for (;;) + if (n==1) + return P[0]; + else { - TProtoFunc* Main=luaU_undump1(z); - if (Main==NULL) break; - if (optimizing) luaU_optchunk(Main); - if (listing) luaU_printchunk(Main); - if (testing) luaU_testchunk(Main); + int i,pc=0; + Proto* tf=luaF_newproto(L); + tf->source=luaS_new(L,"=(luac)"); + tf->maxstacksize=1; + tf->kproto=P; + tf->nkproto=n; + tf->ncode=2*n+1; + tf->code=luaM_newvector(L,tf->ncode,Instruction); + for (i=0; i<n; i++) + { + tf->code[pc++]=CREATE_AB(OP_CLOSURE,i,0); + tf->code[pc++]=CREATE_AB(OP_CALL,0,0); + } + tf->code[pc++]=OP_END; + return tf; } } -static void doit(int undump, char* filename) +static void strip(Proto* tf) { - FILE* f= (filename==NULL) ? stdin : efopen(filename, undump ? "rb" : "r"); - ZIO z; - char source[255+2]; /* +2 for '@' and '\0' */ - luaL_filesource(source,filename,sizeof(source)); - zFopen(&z,f,source); - if (verbose) fprintf(stderr,"%s\n",source+1); - if (undump) do_undump(&z); else do_compile(&z); - if (f!=stdin) fclose(f); + int i,n=tf->nkproto; + tf->lineinfo=NULL; + tf->nlineinfo=0; + tf->source=luaS_new(L,"=(none)"); + tf->locvars=NULL; + tf->nlocvars=0; + for (i=0; i<n; i++) strip(tf->kproto[i]); } -static FILE* efopen(char* name, char* mode) +static FILE* efopen(const char* name, const char* mode) { FILE* f=fopen(name,mode); if (f==NULL) { - fprintf(stderr,"luac: cannot open %sput file ",mode[0]=='r' ? "in" : "out"); + fprintf(stderr,"luac: cannot open %sput file ",*mode=='r' ? "in" : "out"); perror(name); exit(1); } return f; } + +void luaU_testchunk(const Proto* Main) +{ + UNUSED(Main); + fprintf(stderr,"luac: -t not operational in this version\n"); + exit(1); +} diff --git a/src/luac/luac.h b/src/luac/luac.h index 1ae5267b..f8987cf2 100644 --- a/src/luac/luac.h +++ b/src/luac/luac.h @@ -1,48 +1,31 @@ /* -** $Id: luac.h,v 1.11 1999/07/02 19:34:26 lhf Exp $ +** $Id: luac.h,v 1.18 2000/10/31 16:57:23 lhf Exp $ ** definitions for luac ** See Copyright Notice in lua.h */ -#include "lauxlib.h" +#include "ldebug.h" #include "lfunc.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lstring.h" +#include "ltable.h" #include "lundump.h" -typedef struct -{ - char* name; /* name of opcode */ - int op; /* value of opcode */ - int class; /* class of opcode (byte variant) */ - int args; /* types of arguments (operands) */ - int arg; /* arg #1 */ - int arg2; /* arg #2 */ -} Opcode; +extern lua_State *lua_state; +#define L lua_state /* lazy! */ /* from dump.c */ -void luaU_dumpchunk(TProtoFunc* Main, FILE* D, int native); - -/* from opcode.c */ -int luaU_opcodeinfo(TProtoFunc* tf, Byte* p, Opcode* I, char* xFILE, int xLINE); -int luaU_codesize(TProtoFunc* tf); +void luaU_dumpchunk(const Proto* Main, FILE* D); /* from opt.c */ -void luaU_optchunk(TProtoFunc* Main); +void luaU_optchunk(Proto* Main); /* from print.c */ -void luaU_printchunk(TProtoFunc* Main); +void luaU_printchunk(const Proto* Main); /* from test.c */ -void luaU_testchunk(TProtoFunc* Main); -TObject* luaU_getconstant(TProtoFunc* tf, int i, int at); - -#define INFO(tf,p,I) luaU_opcodeinfo(tf,p,I,__FILE__,__LINE__) +void luaU_testchunk(const Proto* Main); -/* fake (but convenient) opcodes */ -#define NOP 255 -#define STACK (-1) -#define ARGS (-2) -#define VARARGS (-3) +#define Sizeof(x) ((int)sizeof(x)) diff --git a/src/luac/opcode.c b/src/luac/opcode.c deleted file mode 100644 index c2d4ae7d..00000000 --- a/src/luac/opcode.c +++ /dev/null @@ -1,102 +0,0 @@ -/* -** $Id: opcode.c,v 1.9 1999/05/25 19:58:55 lhf Exp $ -** opcode information -** See Copyright Notice in lua.h -*/ - -#include "luac.h" - -enum { /* for Opcode.args */ - ARGS_NONE, - ARGS_B, - ARGS_W, - ARGS_BB, - ARGS_WB -}; - -static Opcode Info[]= /* ORDER lopcodes.h */ -{ -#include "opcode.h" -}; - -static Opcode Fake[]= /* ORDER luac.h */ -{ -{ "NOP", NOP, NOP, ARGS_NONE, -1, -1 }, -{ "STACK", STACK, STACK, ARGS_B, -1, -1 }, -{ "ARGS", ARGS, ARGS, ARGS_B, -1, -1 }, -{ "VARARGS", VARARGS, VARARGS, ARGS_B, -1, -1 }, -}; - -#define NOPCODES (sizeof(Info)/sizeof(Info[0])) - -int luaU_opcodeinfo(TProtoFunc* tf, Byte* p, Opcode* I, char* xFILE, int xLINE) -{ - Opcode OP; - Byte* code=tf->code; - int op=*p; - int size=1; - if (p==code) /* first byte is STACK */ - { - OP=Fake[-STACK]; - OP.arg=op; - } - else if (p==code+1) /* second byte is ARGS or VARARGS */ - { - if (op<ZEROVARARG) - { - OP=Fake[-ARGS]; - OP.arg=op; - } - else - { - OP=Fake[-VARARGS]; - OP.arg=op-ZEROVARARG; - } - } - else if (op==NOP) /* NOP is fake */ - { - OP=Fake[0]; - } - else if (op>=NOPCODES) /* cannot happen */ - { - luaL_verror("[%s:%d] bad opcode %d at pc=%d" IN, - xFILE,xLINE,op,(int)(p-code),INLOC); - return 0; - } - else /* ordinary opcode */ - { - OP=Info[op]; - switch (OP.args) - { - case ARGS_NONE: size=1; - break; - case ARGS_B: size=2; OP.arg=p[1]; - break; - case ARGS_W: size=3; OP.arg=(p[1]<<8)+p[2]; - break; - case ARGS_BB: size=3; OP.arg=p[1]; OP.arg2=p[2]; - break; - case ARGS_WB: size=4; OP.arg=(p[1]<<8)+p[2]; OP.arg2=p[3]; - break; - default: /* cannot happen */ - luaL_verror("[%s:%d] bad args %d for %s at pc=%d" IN, - __FILE__,__LINE__,OP.args,OP.name,(int)(p-code),INLOC); - break; - } - } - *I=OP; - return size; -} - -int luaU_codesize(TProtoFunc* tf) -{ - Byte* code=tf->code; - Byte* p=code; - for (;;) - { - Opcode OP; - p+=INFO(tf,p,&OP); - if (OP.op==ENDCODE) break; - } - return p-code; -} diff --git a/src/luac/opcode.h b/src/luac/opcode.h deleted file mode 100644 index 4ae910f5..00000000 --- a/src/luac/opcode.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -** $Id: opcode.h,v 1.1 1999/03/25 13:43:05 lhf Exp $ -** opcode info to be #included into opcode.c -** extracted automatically from lopcodes.h by mkopcodeh -- DO NOT EDIT -** See Copyright Notice in lua.h -*/ -{ "ENDCODE", ENDCODE, ENDCODE, ARGS_NONE, -1, -1 }, -{ "RETCODE", RETCODE, RETCODE, ARGS_B, -1, -1 }, -{ "CALL", CALL, CALL, ARGS_BB, -1, -1 }, -{ "TAILCALL", TAILCALL, TAILCALL, ARGS_BB, -1, -1 }, -{ "PUSHNIL", PUSHNIL, PUSHNIL, ARGS_B, -1, -1 }, -{ "POP", POP, POP, ARGS_B, -1, -1 }, -{ "PUSHNUMBERW", PUSHNUMBERW, PUSHNUMBER, ARGS_W, -1, -1 }, -{ "PUSHNUMBER", PUSHNUMBER, PUSHNUMBER, ARGS_B, -1, -1 }, -{ "PUSHNUMBERNEGW", PUSHNUMBERNEGW, PUSHNUMBERNEG, ARGS_W, -1, -1 }, -{ "PUSHNUMBERNEG", PUSHNUMBERNEG, PUSHNUMBERNEG, ARGS_B, -1, -1 }, -{ "PUSHCONSTANTW", PUSHCONSTANTW, PUSHCONSTANT, ARGS_W, -1, -1 }, -{ "PUSHCONSTANT", PUSHCONSTANT, PUSHCONSTANT, ARGS_B, -1, -1 }, -{ "PUSHUPVALUE", PUSHUPVALUE, PUSHUPVALUE, ARGS_B, -1, -1 }, -{ "PUSHLOCAL", PUSHLOCAL, PUSHLOCAL, ARGS_B, -1, -1 }, -{ "GETGLOBALW", GETGLOBALW, GETGLOBAL, ARGS_W, -1, -1 }, -{ "GETGLOBAL", GETGLOBAL, GETGLOBAL, ARGS_B, -1, -1 }, -{ "GETTABLE", GETTABLE, GETTABLE, ARGS_NONE, -1, -1 }, -{ "GETDOTTEDW", GETDOTTEDW, GETDOTTED, ARGS_W, -1, -1 }, -{ "GETDOTTED", GETDOTTED, GETDOTTED, ARGS_B, -1, -1 }, -{ "PUSHSELFW", PUSHSELFW, PUSHSELF, ARGS_W, -1, -1 }, -{ "PUSHSELF", PUSHSELF, PUSHSELF, ARGS_B, -1, -1 }, -{ "CREATEARRAYW", CREATEARRAYW, CREATEARRAY, ARGS_W, -1, -1 }, -{ "CREATEARRAY", CREATEARRAY, CREATEARRAY, ARGS_B, -1, -1 }, -{ "SETLOCAL", SETLOCAL, SETLOCAL, ARGS_B, -1, -1 }, -{ "SETGLOBALW", SETGLOBALW, SETGLOBAL, ARGS_W, -1, -1 }, -{ "SETGLOBAL", SETGLOBAL, SETGLOBAL, ARGS_B, -1, -1 }, -{ "SETTABLEPOP", SETTABLEPOP, SETTABLEPOP, ARGS_NONE, -1, -1 }, -{ "SETTABLE", SETTABLE, SETTABLE, ARGS_B, -1, -1 }, -{ "SETLISTW", SETLISTW, SETLIST, ARGS_WB, -1, -1 }, -{ "SETLIST", SETLIST, SETLIST, ARGS_BB, -1, -1 }, -{ "SETMAP", SETMAP, SETMAP, ARGS_B, -1, -1 }, -{ "NEQOP", NEQOP, NEQOP, ARGS_NONE, -1, -1 }, -{ "EQOP", EQOP, EQOP, ARGS_NONE, -1, -1 }, -{ "LTOP", LTOP, LTOP, ARGS_NONE, -1, -1 }, -{ "LEOP", LEOP, LEOP, ARGS_NONE, -1, -1 }, -{ "GTOP", GTOP, GTOP, ARGS_NONE, -1, -1 }, -{ "GEOP", GEOP, GEOP, ARGS_NONE, -1, -1 }, -{ "ADDOP", ADDOP, ADDOP, ARGS_NONE, -1, -1 }, -{ "SUBOP", SUBOP, SUBOP, ARGS_NONE, -1, -1 }, -{ "MULTOP", MULTOP, MULTOP, ARGS_NONE, -1, -1 }, -{ "DIVOP", DIVOP, DIVOP, ARGS_NONE, -1, -1 }, -{ "POWOP", POWOP, POWOP, ARGS_NONE, -1, -1 }, -{ "CONCOP", CONCOP, CONCOP, ARGS_NONE, -1, -1 }, -{ "MINUSOP", MINUSOP, MINUSOP, ARGS_NONE, -1, -1 }, -{ "NOTOP", NOTOP, NOTOP, ARGS_NONE, -1, -1 }, -{ "ONTJMPW", ONTJMPW, ONTJMP, ARGS_W, -1, -1 }, -{ "ONTJMP", ONTJMP, ONTJMP, ARGS_B, -1, -1 }, -{ "ONFJMPW", ONFJMPW, ONFJMP, ARGS_W, -1, -1 }, -{ "ONFJMP", ONFJMP, ONFJMP, ARGS_B, -1, -1 }, -{ "JMPW", JMPW, JMP, ARGS_W, -1, -1 }, -{ "JMP", JMP, JMP, ARGS_B, -1, -1 }, -{ "IFFJMPW", IFFJMPW, IFFJMP, ARGS_W, -1, -1 }, -{ "IFFJMP", IFFJMP, IFFJMP, ARGS_B, -1, -1 }, -{ "IFTUPJMPW", IFTUPJMPW, IFTUPJMP, ARGS_W, -1, -1 }, -{ "IFTUPJMP", IFTUPJMP, IFTUPJMP, ARGS_B, -1, -1 }, -{ "IFFUPJMPW", IFFUPJMPW, IFFUPJMP, ARGS_W, -1, -1 }, -{ "IFFUPJMP", IFFUPJMP, IFFUPJMP, ARGS_B, -1, -1 }, -{ "CLOSUREW", CLOSUREW, CLOSURE, ARGS_WB, -1, -1 }, -{ "CLOSURE", CLOSURE, CLOSURE, ARGS_BB, -1, -1 }, -{ "SETLINEW", SETLINEW, SETLINE, ARGS_W, -1, -1 }, -{ "SETLINE", SETLINE, SETLINE, ARGS_B, -1, -1 }, -{ "LONGARGW", LONGARGW, LONGARG, ARGS_W, -1, -1 }, -{ "LONGARG", LONGARG, LONGARG, ARGS_B, -1, -1 }, -{ "CHECKSTACK", CHECKSTACK, CHECKSTACK, ARGS_B, -1, -1 }, diff --git a/src/luac/opt.c b/src/luac/opt.c index e2becc2a..e51a0868 100644 --- a/src/luac/opt.c +++ b/src/luac/opt.c @@ -1,5 +1,5 @@ /* -** $Id: opt.c,v 1.12 1999/07/02 19:34:26 lhf Exp $ +** $Id: opt.c,v 1.22 2000/10/31 16:57:23 lhf Exp $ ** optimize bytecodes ** See Copyright Notice in lua.h */ @@ -7,275 +7,121 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "luac.h" -static void FixArg(Byte* p, int i, int j, int isconst) -{ - if (j==i) - ; - else if (i<=MAX_BYTE) /* j<i, so j fits where i did */ - p[1]=j; - else if (i<=MAX_WORD) - { - if (isconst && j<=MAX_BYTE) /* may use byte variant instead */ - { - p[0]++; /* byte variant follows word variant */ - p[1]=j; - p[2]=NOP; - } - else /* stuck with word variant */ - { - p[1]=j>>8; - p[2]=j; - } - } - else /* previous instruction must've been LONGARG */ - { - if (isconst && j<=MAX_WORD) p[-2]=p[-1]=NOP; else p[-1]=j>>16; - p[1]=j>>8; - p[2]=j; - } -} +#include "luac.h" -static void FixConstants(TProtoFunc* tf, int* C) +static int MapConstant(Hash* t, int j, const TObject* key) { - Byte* code=tf->code; - Byte* p=code; - int longarg=0; - for (;;) + const TObject* o=luaH_get(L,t,key); + if (ttype(o)==LUA_TNUMBER) + return (int) nvalue(o); + else { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - int i=OP.arg+longarg; - longarg=0; - if (op==PUSHCONSTANT || op==GETGLOBAL || op==GETDOTTED || - op==PUSHSELF || op==SETGLOBAL || op==CLOSURE) - FixArg(p,i,C[i],1); - else if (op==LONGARG) longarg=i<<16; - else if (op==ENDCODE) break; - p+=n; + TObject val; + ttype(&val)=LUA_TNUMBER; + nvalue(&val)=j; + *luaH_set(L,t,key)=val; + LUA_ASSERT(j>=0,"MapConstant returns negative!"); + return j; } } -#define UNREF 1 /* "type" of unused constants */ -#define BIAS 128 /* mark for used constants */ - -static void NoUnrefs(TProtoFunc* tf) +static int MapConstants(Proto* tf, Hash* map) { - int i,n=tf->nconsts; - Byte* code=tf->code; - Byte* p=code; - int longarg=0; - for (;;) /* mark all used constants */ - { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - int i=OP.arg+longarg; - longarg=0; - if (op==PUSHCONSTANT || op==GETGLOBAL || op==GETDOTTED || - op==PUSHSELF || op==SETGLOBAL || op==CLOSURE) - { - TObject* o=tf->consts+i; - if (ttype(o)<=0) ttype(o)+=BIAS; /* mark as used */ - } - else if (op==LONGARG) longarg=i<<16; - else if (op==ENDCODE) break; - p+=n; - } - for (i=0; i<n; i++) /* mark all unused constants */ + int i,j,k,n,m=0; + TObject o; + j=0; n=tf->nknum; ttype(&o)=LUA_TNUMBER; + for (i=0; i<n; i++) { - TObject* o=tf->consts+i; - if (ttype(o)<=0) - ttype(o)=UNREF; /* mark as unused */ - else - ttype(o)-=BIAS; /* unmark used constant */ + nvalue(&o)=tf->knum[i]; + k=MapConstant(map,j,&o); + if (k==j) j++; } -} - -#define CMP(oa,ob,f) memcmp(&f(oa),&f(ob),sizeof(f(oa))) - -static int compare(TProtoFunc* tf, int ia, int ib) -{ - TObject* oa=tf->consts+ia; - TObject* ob=tf->consts+ib; - int t=ttype(oa)-ttype(ob); - if (t) return t; - switch (ttype(oa)) + m=j; + j=0; n=tf->nkstr; ttype(&o)=LUA_TSTRING; + for (i=0; i<n; i++) { - case LUA_T_NUMBER: return CMP(oa,ob,nvalue); - case LUA_T_STRING: return CMP(oa,ob,tsvalue); - case LUA_T_PROTO: return CMP(oa,ob,tfvalue); - case LUA_T_NIL: return 0; - case UNREF: return 0; - default: return ia-ib; /* cannot happen */ + tsvalue(&o)=tf->kstr[i]; + k=MapConstant(map,j,&o); + if (k==j) j++; } + return m+j; } -static TProtoFunc* TF; /* for sort */ - -static int compare1(const void* a, const void* b) -{ - int ia=*(int*)a; - int ib=*(int*)b; - int t=compare(TF,ia,ib); - return (t) ? t : ia-ib; -} - -static void OptConstants(TProtoFunc* tf) +static void PackConstants(Proto* tf, Hash* map) { - static int* C=NULL; - static int* D=NULL; - int i,k; - int n=tf->nconsts; - if (n==0) return; - luaM_reallocvector(C,n,int); - luaM_reallocvector(D,n,int); - NoUnrefs(tf); - for (i=0; i<n; i++) C[i]=D[i]=i; /* group duplicates */ - TF=tf; qsort(C,n,sizeof(C[0]),compare1); - k=C[0]; /* build duplicate table */ - for (i=1; i<n; i++) - { - int j=C[i]; - if (compare(tf,k,j)==0) D[j]=k; else k=j; - } - k=0; /* build rename map & pack constants */ + int i,j,k,n; + TObject o; +#ifdef DEBUG + printf("%p before pack nknum=%d nkstr=%d\n",tf,tf->nknum,tf->nkstr); +#endif + j=0; n=tf->nknum; ttype(&o)=LUA_TNUMBER; for (i=0; i<n; i++) { - if (D[i]==i) /* new value */ - { - TObject* o=tf->consts+i; - if (ttype(o)!=UNREF) - { - tf->consts[k]=tf->consts[i]; - C[i]=k++; - } - } - else C[i]=C[D[i]]; + nvalue(&o)=tf->knum[i]; + k=MapConstant(map,-1,&o); + if (k==j) tf->knum[j++]=tf->knum[i]; } - if (k<n) + tf->nknum=j; + j=0; n=tf->nkstr; ttype(&o)=LUA_TSTRING; + for (i=0; i<n; i++) { -printf("\t" SOURCE " reduced constants from %d to %d\n", - tf->source->str,tf->lineDefined,n,k); - FixConstants(tf,C); - tf->nconsts=k; + tsvalue(&o)=tf->kstr[i]; + k=MapConstant(map,-1,&o); + if (k==j) tf->kstr[j++]=tf->kstr[i]; } + tf->nkstr=j; +#ifdef DEBUG + printf("%p after pack nknum=%d nkstr=%d\n",tf,tf->nknum,tf->nkstr); +#endif } -static int NoDebug(TProtoFunc* tf) +static void OptConstants(Proto* tf) { - Byte* code=tf->code; - Byte* p=code; - int lop=NOP; /* last opcode */ - int nop=0; - for (;;) /* change SETLINE to NOP */ + Instruction* p; + int n=tf->nknum+tf->nkstr; + Hash* map=luaH_new(L,n); + int m=MapConstants(tf,map); +#ifdef DEBUG + printf("%p n=%d m=%d %s\n",tf,n,m,(m==n)?"nothing to optimize":"yes!"); +#endif + if (m==n) return; + for (p=tf->code;; p++) { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - if (op==NOP) ++nop; - else if (op==SETLINE) + Instruction i=*p; + int op=GET_OPCODE(i); + switch (op) { - int m; - if (lop==LONGARG) m=2; else if (lop==LONGARGW) m=3; else m=0; - nop+=n+m; memset(p-m,NOP,n+m); + TObject o; + int j,k; + case OP_PUSHNUM: case OP_PUSHNEGNUM: + j=GETARG_U(i); + ttype(&o)=LUA_TNUMBER; nvalue(&o)=tf->knum[j]; + k=MapConstant(map,-1,&o); + if (k!=j) *p=CREATE_U(op,k); + break; + case OP_PUSHSTRING: case OP_GETGLOBAL: case OP_GETDOTTED: + case OP_PUSHSELF: case OP_SETGLOBAL: + j=GETARG_U(i); + ttype(&o)=LUA_TSTRING; tsvalue(&o)=tf->kstr[j]; + k=MapConstant(map,-1,&o); + if (k!=j) *p=CREATE_U(op,k); + break; + case OP_END: + PackConstants(tf,map); + luaH_free(L,map); + return; + default: + break; } - else if (op==ENDCODE) break; - lop=OP.op; - p+=n; } - return nop; } -static int FixJump(TProtoFunc* tf, Byte* a, Byte* b) -{ - Byte* p; - int nop=0; - for (p=a; p<b; ) - { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - if (op==NOP) ++nop; - else if (op==ENDCODE) break; - p+=n; - } - return nop; -} +#define OptFunction luaU_optchunk -static void FixJumps(TProtoFunc* tf) -{ - Byte* code=tf->code; - Byte* p=code; - int longarg=0; - for (;;) - { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - int i=OP.arg+longarg; - int nop=0; - longarg=0; - if (op==ENDCODE) break; - else if (op==IFTUPJMP || op==IFFUPJMP) - nop=FixJump(tf,p-i+n,p); - else if (op==ONTJMP || op==ONFJMP || op==JMP || op==IFFJMP) - nop=FixJump(tf,p,p+i+n); - else if (op==LONGARG) longarg=i<<16; - if (nop>0) FixArg(p,i,i-nop,0); - p+=n; - } -} - -static void PackCode(TProtoFunc* tf) -{ - Byte* code=tf->code; - Byte* p=code; - Byte* q=code; - for (;;) - { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - if (op!=NOP) { memcpy(q,p,n); q+=n; } - p+=n; - if (op==ENDCODE) break; - } -printf("\t" SOURCE " reduced code from %d to %d\n", - tf->source->str,tf->lineDefined,(int)(p-code),(int)(q-code)); -} - -static void OptCode(TProtoFunc* tf) -{ - if (NoDebug(tf)==0) return; /* cannot improve code */ - FixJumps(tf); - PackCode(tf); -} - -static void OptFunction(TProtoFunc* tf); - -static void OptFunctions(TProtoFunc* tf) -{ - int i,n=tf->nconsts; - for (i=0; i<n; i++) - { - TObject* o=tf->consts+i; - if (ttype(o)==LUA_T_PROTO) OptFunction(tfvalue(o)); - } -} - -static void OptFunction(TProtoFunc* tf) +void OptFunction(Proto* tf) { + int i,n=tf->nkproto; OptConstants(tf); - OptCode(tf); - OptFunctions(tf); - tf->source=luaS_new(""); - tf->locvars=NULL; -} - -void luaU_optchunk(TProtoFunc* Main) -{ - OptFunction(Main); + for (i=0; i<n; i++) OptFunction(tf->kproto[i]); } diff --git a/src/luac/print.c b/src/luac/print.c index b1ee8934..4ffc8b3d 100644 --- a/src/luac/print.c +++ b/src/luac/print.c @@ -1,223 +1,99 @@ /* -** $Id: print.c,v 1.21 1999/05/25 19:58:55 lhf Exp $ +** $Id: print.c,v 1.32 2000/11/06 20:04:36 lhf Exp $ ** print bytecodes ** See Copyright Notice in lua.h */ #include <stdio.h> #include <stdlib.h> + #include "luac.h" -#ifdef DEBUG -static void PrintConstants(TProtoFunc* tf) +/* macros used in print.h, included in PrintCode */ +#define P_OP(x) printf("%-11s\t",x) +#define P_NONE +#define P_AB printf("%d %d",GETARG_A(i),GETARG_B(i)) +#define P_F printf("%d %d\t; %p",GETARG_A(i),GETARG_B(i),tf->kproto[GETARG_A(i)]) +#define P_J printf("%d\t; to %d",GETARG_S(i),GETARG_S(i)+at+1) +#define P_Q PrintString(tf,GETARG_U(i)) +#define P_K printf("%d\t; %s",GETARG_U(i),tf->kstr[GETARG_U(i)]->str) +#define P_L PrintLocal(tf,GETARG_U(i),at-1) +#define P_N printf("%d\t; " NUMBER_FMT,GETARG_U(i),tf->knum[GETARG_U(i)]) +#define P_S printf("%d",GETARG_S(i)) +#define P_U printf("%u",GETARG_U(i)) + +static void PrintString(const Proto* tf, int n) { - int i,n=tf->nconsts; - printf("constants (%d) for %p:\n",n,tf); - for (i=0; i<n; i++) + const char* s=tf->kstr[n]->str; + printf("%d\t; ",n); + putchar('"'); + for (; *s; s++) { - TObject* o=tf->consts+i; - printf("%6d ",i); - switch (ttype(o)) + switch (*s) { - case LUA_T_NUMBER: - printf("N " NUMBER_FMT "\n",(double)nvalue(o)); - break; - case LUA_T_STRING: - printf("S %p\t\"%s\"\n",tsvalue(o),svalue(o)); - break; - case LUA_T_PROTO: - printf("F %p\n",tfvalue(o)); - break; - case LUA_T_NIL: - printf("nil\n"); - break; - default: /* cannot happen */ - printf("? type=%d\n",ttype(o)); - break; + case '"': printf("\\\""); break; + case '\a': printf("\\a"); break; + case '\b': printf("\\b"); break; + case '\f': printf("\\f"); break; + case '\n': printf("\\n"); break; + case '\r': printf("\\r"); break; + case '\t': printf("\\t"); break; + case '\v': printf("\\v"); break; + default: putchar(*s); break; } } + putchar('"'); } -#endif -static void PrintConstant(TProtoFunc* tf, int i, int at) +static void PrintLocal(const Proto* tf, int n, int pc) { - TObject* o=luaU_getconstant(tf,i,at); - switch (ttype(o)) - { - case LUA_T_NUMBER: - printf(NUMBER_FMT,(double)nvalue(o)); - break; - case LUA_T_STRING: - printf("\"%s\"",svalue(o)); - break; - case LUA_T_PROTO: - printf("function at %p",(void*)tfvalue(o)); - break; - case LUA_T_NIL: - printf("(nil)"); - break; - default: /* cannot happen */ - luaU_badconstant("print",i,o,tf); - break; - } + const char* s=luaF_getlocalname(tf,n+1,pc); + printf("%u",n); + if (s!=NULL) printf("\t; %s",s); } -static void PrintCode(TProtoFunc* tf) +static void PrintCode(const Proto* tf) { - Byte* code=tf->code; - Byte* p=code; - int line=0; - int longarg=0; + const Instruction* code=tf->code; + const Instruction* p=code; for (;;) { - Opcode OP; - int n=INFO(tf,p,&OP); - int i=OP.arg+longarg; - int at=p-code; - longarg=0; - printf("%6d ",at); - { - Byte* q=p; - int j=n; - while (j--) printf("%02X",*q++); - } - printf("%*s%-14s ",2*(5-n),"",OP.name); - if (OP.arg >=0) printf("%d",i); - if (OP.arg2>=0) printf(" %d",OP.arg2); - - switch (OP.class) - { - - case ENDCODE: - printf("\n"); - return; - - case PUSHCONSTANT: - case GETGLOBAL: - case SETGLOBAL: - case GETDOTTED: - case PUSHSELF: - case CLOSURE: - printf("\t; "); - PrintConstant(tf,i,at); - break; - - case PUSHLOCAL: - case SETLOCAL: - { - char* s=luaF_getlocalname(tf,i+1,line); - if (s) printf("\t; %s",s); - break; - } - - case SETLINE: - printf("\t; " SOURCE,tf->source->str,line=i); - break; - - case LONGARG: - longarg=i<<16; - break; - -/* suggested by Norman Ramsey <nr@cs.virginia.edu> */ - case ONTJMP: - case ONFJMP: - case JMP: - case IFFJMP: - printf("\t; to %d",at+i+n); - break; - case IFTUPJMP: - case IFFUPJMP: - printf("\t; to %d",at-i+n); - break; - - } - printf("\n"); - p+=n; - } -} - -static void PrintLocals(TProtoFunc* tf) -{ - LocVar* v=tf->locvars; - int n,i; - if (v==NULL || v->line<0) return; - n=tf->code[1]; if (n>=ZEROVARARG) n-=ZEROVARARG; - printf("locals:"); - for (i=0; i<n; v++,i++) /* arguments */ - printf(" %s",v->varname->str); - for (; v->line>=0; v++) - { - if (v->varname==NULL) - { - --i; if (i<0) luaL_verror("bad locvars[%d]",v-tf->locvars); else printf(")"); - } - else - { - ++i; printf(" (%s",v->varname->str); + int at=p-code+1; + Instruction i=*p; + int line=luaG_getline(tf->lineinfo,at-1,1,NULL); + printf("%6d\t",at); + if (line>=0) printf("[%d]\t",line); else printf("[-]\t"); + switch (GET_OPCODE(i)) { +#include "print.h" } + printf("\n"); + if (i==OP_END) break; + p++; } - i-=n; - while (i--) printf(")"); - printf("\n"); } #define IsMain(tf) (tf->lineDefined==0) -static void PrintHeader(TProtoFunc* tf, TProtoFunc* Main, int at) -{ - int size=luaU_codesize(tf); - if (IsMain(tf)) - printf("\nmain " SOURCE " (%d bytes at %p)\n", - tf->source->str,tf->lineDefined,size,tf); - else - { - printf("\nfunction " SOURCE " (%d bytes at %p); used at ", - tf->source->str,tf->lineDefined,size,tf); - if (Main && IsMain(Main)) - printf("main"); - else - printf("%p",Main); - printf("+%d\n",at); - } -} - -static void PrintFunction(TProtoFunc* tf, TProtoFunc* Main, int at); +#define SS(x) (x==1)?"":"s" +#define S(x) x,SS(x) -static void PrintFunctions(TProtoFunc* Main) +static void PrintHeader(const Proto* tf) { - Byte* code=Main->code; - Byte* p=code; - int longarg=0; - for (;;) - { - Opcode OP; - int n=INFO(Main,p,&OP); - int op=OP.class; - int i=OP.arg+longarg; - longarg=0; - if (op==PUSHCONSTANT || op==CLOSURE) - { - TObject* o=Main->consts+i; - if (ttype(o)==LUA_T_PROTO) PrintFunction(tfvalue(o),Main,(int)(p-code)); - } - else if (op==LONGARG) longarg=i<<16; - else if (op==ENDCODE) break; - p+=n; - } + printf("\n%s " SOURCE_FMT " (%d instruction%s/%d bytes at %p)\n", + IsMain(tf)?"main":"function",SOURCE, + S(tf->ncode),tf->ncode*Sizeof(Instruction),tf); + printf("%d%s param%s, %d stack%s, ", + tf->numparams,tf->is_vararg?"+":"",SS(tf->numparams),S(tf->maxstacksize)); + printf("%d local%s, %d string%s, %d number%s, %d function%s, %d line%s\n", + S(tf->nlocvars),S(tf->nkstr),S(tf->nknum),S(tf->nkproto),S(tf->nlineinfo)); } -static void PrintFunction(TProtoFunc* tf, TProtoFunc* Main, int at) -{ - PrintHeader(tf,Main,at); - PrintLocals(tf); - PrintCode(tf); -#ifdef DEBUG - PrintConstants(tf); -#endif - PrintFunctions(tf); -} +#define PrintFunction luaU_printchunk -void luaU_printchunk(TProtoFunc* Main) +void PrintFunction(const Proto* tf) { - PrintFunction(Main,0,0); + int i,n=tf->nkproto; + PrintHeader(tf); + PrintCode(tf); + for (i=0; i<n; i++) PrintFunction(tf->kproto[i]); } diff --git a/src/luac/print.h b/src/luac/print.h new file mode 100644 index 00000000..5f74e149 --- /dev/null +++ b/src/luac/print.h @@ -0,0 +1,55 @@ +/* +** $Id: print.h,v 1.1 2000/11/06 20:03:12 lhf Exp $ +** extracted automatically from lopcodes.h by mkprint.lua -- DO NOT EDIT +** See Copyright Notice in lua.h +*/ + + case OP_END: P_OP("END"); P_NONE; break; + case OP_RETURN: P_OP("RETURN"); P_U; break; + case OP_CALL: P_OP("CALL"); P_AB; break; + case OP_TAILCALL: P_OP("TAILCALL"); P_AB; break; + case OP_PUSHNIL: P_OP("PUSHNIL"); P_U; break; + case OP_POP: P_OP("POP"); P_U; break; + case OP_PUSHINT: P_OP("PUSHINT"); P_S; break; + case OP_PUSHSTRING: P_OP("PUSHSTRING"); P_Q; break; + case OP_PUSHNUM: P_OP("PUSHNUM"); P_N; break; + case OP_PUSHNEGNUM: P_OP("PUSHNEGNUM"); P_N; break; + case OP_PUSHUPVALUE: P_OP("PUSHUPVALUE"); P_U; break; + case OP_GETLOCAL: P_OP("GETLOCAL"); P_L; break; + case OP_GETGLOBAL: P_OP("GETGLOBAL"); P_K; break; + case OP_GETTABLE: P_OP("GETTABLE"); P_NONE; break; + case OP_GETDOTTED: P_OP("GETDOTTED"); P_K; break; + case OP_GETINDEXED: P_OP("GETINDEXED"); P_L; break; + case OP_PUSHSELF: P_OP("PUSHSELF"); P_K; break; + case OP_CREATETABLE: P_OP("CREATETABLE"); P_U; break; + case OP_SETLOCAL: P_OP("SETLOCAL"); P_L; break; + case OP_SETGLOBAL: P_OP("SETGLOBAL"); P_K; break; + case OP_SETTABLE: P_OP("SETTABLE"); P_AB; break; + case OP_SETLIST: P_OP("SETLIST"); P_AB; break; + case OP_SETMAP: P_OP("SETMAP"); P_U; break; + case OP_ADD: P_OP("ADD"); P_NONE; break; + case OP_ADDI: P_OP("ADDI"); P_S; break; + case OP_SUB: P_OP("SUB"); P_NONE; break; + case OP_MULT: P_OP("MULT"); P_NONE; break; + case OP_DIV: P_OP("DIV"); P_NONE; break; + case OP_POW: P_OP("POW"); P_NONE; break; + case OP_CONCAT: P_OP("CONCAT"); P_U; break; + case OP_MINUS: P_OP("MINUS"); P_NONE; break; + case OP_NOT: P_OP("NOT"); P_NONE; break; + case OP_JMPNE: P_OP("JMPNE"); P_J; break; + case OP_JMPEQ: P_OP("JMPEQ"); P_J; break; + case OP_JMPLT: P_OP("JMPLT"); P_J; break; + case OP_JMPLE: P_OP("JMPLE"); P_J; break; + case OP_JMPGT: P_OP("JMPGT"); P_J; break; + case OP_JMPGE: P_OP("JMPGE"); P_J; break; + case OP_JMPT: P_OP("JMPT"); P_J; break; + case OP_JMPF: P_OP("JMPF"); P_J; break; + case OP_JMPONT: P_OP("JMPONT"); P_J; break; + case OP_JMPONF: P_OP("JMPONF"); P_J; break; + case OP_JMP: P_OP("JMP"); P_J; break; + case OP_PUSHNILJMP: P_OP("PUSHNILJMP"); P_NONE; break; + case OP_FORPREP: P_OP("FORPREP"); P_J; break; + case OP_FORLOOP: P_OP("FORLOOP"); P_J; break; + case OP_LFORPREP: P_OP("LFORPREP"); P_J; break; + case OP_LFORLOOP: P_OP("LFORLOOP"); P_J; break; + case OP_CLOSURE: P_OP("CLOSURE"); P_F; break; diff --git a/src/luac/stubs.c b/src/luac/stubs.c index 5f38940e..74f509eb 100644 --- a/src/luac/stubs.c +++ b/src/luac/stubs.c @@ -1,68 +1,109 @@ /* -** $Id: stubs.c,v 1.11 1999/03/11 17:09:10 lhf Exp $ +** $Id: stubs.c,v 1.20 2000/10/31 16:57:23 lhf Exp $ ** avoid runtime modules in luac ** See Copyright Notice in lua.h */ -#ifdef NOSTUBS - -/* according to gcc, ANSI C forbids an empty source file */ -void luaU_dummy(void); -void luaU_dummy(void){} - -#else - -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> + +#include "ldo.h" +#include "llex.h" #include "luac.h" +#undef L + +#ifndef NOSTUBS + +const char luac_ident[] = "$luac: " LUA_VERSION " " LUA_COPYRIGHT " $\n" + "$Authors: " LUA_AUTHORS " $"; /* -* avoid lapi lauxlib lbuiltin ldo lgc ltable ltm lvm -* use only lbuffer lfunc llex lmem lobject lparser lstate lstring lzio +* avoid lapi ldebug ldo lgc lstate ltm lvm +* use only lcode lfunc llex lmem lobject lparser lstring ltable lzio */ /* simplified from ldo.c */ -void lua_error(char* s) -{ - if (s) fprintf(stderr,"luac: %s\n",s); - exit(1); +void lua_error (lua_State* L, const char* s) { + UNUSED(L); + if (s) fprintf(stderr,"luac: %s\n",s); + exit(1); } -/* copied from lauxlib.c */ -void luaL_verror (char *fmt, ...) -{ - char buff[500]; - va_list argp; - va_start(argp, fmt); - vsprintf(buff, fmt, argp); - va_end(argp); - lua_error(buff); +/* simplified from ldo.c */ +void luaD_breakrun (lua_State *L, int errcode) { + UNUSED(errcode); + lua_error(L,"memory allocation error"); } -/* copied from lauxlib.c */ -void luaL_filesource (char *out, char *filename, int len) { - if (filename == NULL) filename = "(stdin)"; - sprintf(out, "@%.*s", len-2, filename); /* -2 for '@' and '\0' */ +/* simplified from lstate.c */ +lua_State *lua_open (int stacksize) { + lua_State *L = luaM_new(NULL, lua_State); + if (L == NULL) return NULL; /* memory allocation error */ + L->stack = NULL; + L->strt.size = L->udt.size = 0; + L->strt.nuse = L->udt.nuse = 0; + L->strt.hash = NULL; + L->udt.hash = NULL; + L->Mbuffer = NULL; + L->Mbuffsize = 0; + L->rootproto = NULL; + L->rootcl = NULL; + L->roottable = NULL; + L->TMtable = NULL; + L->last_tag = -1; + L->refArray = NULL; + L->refSize = 0; + L->refFree = NONEXT; + L->nblocks = sizeof(lua_State); + L->GCthreshold = MAX_INT; /* to avoid GC during pre-definitions */ + L->callhook = NULL; + L->linehook = NULL; + L->allowhooks = 1; + L->errorJmp = NULL; + if (stacksize == 0) + stacksize = DEFAULT_STACK_SIZE; + else + stacksize += LUA_MINSTACK; + L->gt = luaH_new(L, 10); /* table of globals */ + luaS_init(L); + luaX_init(L); + L->GCthreshold = 2*L->nblocks; + return L; } -/* avoid runtime modules in lstate.c */ - -#include "lbuiltin.h" -#include "ldo.h" -#include "lgc.h" -#include "ltable.h" -#include "ltm.h" - -void luaB_predefine(void){} -void luaC_hashcallIM(Hash *l){} -void luaC_strcallIM(TaggedString *l){} -void luaD_gcIM(TObject *o){} -void luaH_free(Hash *frees){} -void luaT_init(void){} +/* copied from ldebug.c */ +int luaG_getline (int *lineinfo, int pc, int refline, int *prefi) { + int refi; + if (lineinfo == NULL || pc == -1) + return -1; /* no line info or function is not active */ + refi = prefi ? *prefi : 0; + if (lineinfo[refi] < 0) + refline += -lineinfo[refi++]; + LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info"); + while (lineinfo[refi] > pc) { + refline--; + refi--; + if (lineinfo[refi] < 0) + refline -= -lineinfo[refi--]; + LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info"); + } + for (;;) { + int nextline = refline + 1; + int nextref = refi + 1; + if (lineinfo[nextref] < 0) + nextline += -lineinfo[nextref++]; + LUA_ASSERT(lineinfo[nextref] >= 0, "invalid line info"); + if (lineinfo[nextref] > pc) + break; + refline = nextline; + refi = nextref; + } + if (prefi) *prefi = refi; + return refline; +} /* -* the code below avoids the lexer and the parser (llex lparser). +* the code below avoids the lexer and the parser (llex lparser lcode). * it is useful if you only want to load binary files. * this works for interpreters like lua.c too. */ @@ -72,49 +113,14 @@ void luaT_init(void){} #include "llex.h" #include "lparser.h" -void luaX_init(void){} -void luaD_init(void){} - -TProtoFunc* luaY_parser(ZIO *z) { - lua_error("parser not loaded"); - return NULL; -} - -#else - -/* copied from lauxlib.c */ -int luaL_findstring (char *name, char *list[]) { - int i; - for (i=0; list[i]; i++) - if (strcmp(list[i], name) == 0) - return i; - return -1; /* name not found */ -} - -/* copied from lauxlib.c */ -void luaL_chunkid (char *out, char *source, int len) { - len -= 13; /* 13 = strlen("string ''...\0") */ - if (*source == '@') - sprintf(out, "file `%.*s'", len, source+1); - else if (*source == '(') - strcpy(out, "(C code)"); - else { - char *b = strchr(source , '\n'); /* stop string at first new line */ - int lim = (b && (b-source)<len) ? b-source : len; - sprintf(out, "string `%.*s'", lim, source); - strcpy(out+lim+(13-5), "...'"); /* 5 = strlen("...'\0") */ - } +void luaX_init(lua_State *L) { + UNUSED(L); } -void luaD_checkstack(int n){} - -#define STACK_UNIT 128 - -/* copied from ldo.c */ -void luaD_init (void) { - L->stack.stack = luaM_newvector(STACK_UNIT, TObject); - L->stack.top = L->stack.stack; - L->stack.last = L->stack.stack+(STACK_UNIT-1); +Proto *luaY_parser(lua_State *L, ZIO *z) { + UNUSED(z); + lua_error(L,"parser not loaded"); + return NULL; } #endif diff --git a/src/luac/test.c b/src/luac/test.c deleted file mode 100644 index 78ba4556..00000000 --- a/src/luac/test.c +++ /dev/null @@ -1,253 +0,0 @@ -/* -** $Id: test.c,v 1.10 1999/07/02 19:34:26 lhf Exp $ -** test integrity -** See Copyright Notice in lua.h -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "luac.h" - -#define AT "pc=%d" -#define ATLOC 0) -#define UNSAFE(s) \ - luaL_verror("unsafe code at " AT IN "\n " s,at,INLOC - -TObject* luaU_getconstant(TProtoFunc* tf, int i, int at) -{ - if (i>=tf->nconsts) UNSAFE("bad constant #%d (max=%d)"),i,tf->nconsts-1,ATLOC; - return tf->consts+i; -} - -static int check(int n, TProtoFunc* tf, int at, int sp, int ss) -{ - if (n==0) return sp; - sp+=n; - if (sp<00) UNSAFE("stack underflow (sp=%d)"),sp,ATLOC; - if (sp>ss) UNSAFE("stack overflow (sp=%d ss=%d)"),sp,ss,ATLOC; - return sp; -} - -#define CHECK(before,after) \ - sp=check(-(before),tf,at,sp,ss), sp=check(after,tf,at,sp,ss) - -static int jmpok(TProtoFunc* tf, int size, int at, int d) -{ - int to=at+d; - if (to<2 || to>=size) - UNSAFE("invalid jump to %d (valid range is 2..%d)"),to,size-1,ATLOC; - return to; -} - -static void TestStack(TProtoFunc* tf, int size, int* SP, int* JP) -{ - Byte* code=tf->code; - Byte* p=code; - int longarg=0; - int ss=0; - int sp=0; - for (;;) - { - Opcode OP; - int n=INFO(tf,p,&OP); - int op=OP.class; - int i=OP.arg+longarg; - int at=p-code; - longarg=0; - switch (op) /* test sanity of operands */ - { - case PUSHCONSTANT: - case GETGLOBAL: - case GETDOTTED: - case PUSHSELF: - case SETGLOBAL: - case CLOSURE: - { - TObject* o=luaU_getconstant(tf,i,at); - if ((op==CLOSURE && ttype(o)!=LUA_T_PROTO) - || (op==GETGLOBAL && ttype(o)!=LUA_T_STRING) - || (op==SETGLOBAL && ttype(o)!=LUA_T_STRING)) - UNSAFE("bad operand to %s"),OP.name,ATLOC; - break; - } - case PUSHLOCAL: - if (i>=sp) UNSAFE("bad local #%d (max=%d)"),i,sp-1,ATLOC; - break; - case SETLOCAL: - if (i>=(sp-1)) UNSAFE("bad local #%d (max=%d)"),i,sp-2,ATLOC; - break; - case ONTJMP: - case ONFJMP: /* negate to remember ON?JMP */ - JP[at]=-jmpok(tf,size,at,i+n); - break; - case JMP: /* remember JMP targets */ - case IFFJMP: - JP[at]= jmpok(tf,size,at,i+n); - break; - case IFTUPJMP: - case IFFUPJMP: - JP[at]= jmpok(tf,size,at,-i+n); - break; - } - - SP[at]=sp; /* remember depth before instruction */ - - switch (op) - { - case STACK: ss=i; break; - case ARGS: CHECK(0,i); break; - case VARARGS: break; - case ENDCODE: return; - case RETCODE: CHECK(i,0); sp=i; break; - case CALL: CHECK(OP.arg2+1,i); break; - case TAILCALL: CHECK(OP.arg2,0); sp=i; break; - case PUSHNIL: CHECK(0,i+1); break; - case POP: CHECK(0,-i); break; - case PUSHNUMBER: - case PUSHNUMBERNEG: - case PUSHCONSTANT: - case PUSHUPVALUE: - case PUSHLOCAL: - case GETGLOBAL: CHECK(0,1); break; - case GETTABLE: CHECK(2,1); break; - case GETDOTTED: CHECK(1,1); break; - case PUSHSELF: CHECK(1,2); break; - case CREATEARRAY: CHECK(0,1); break; - case SETLOCAL: CHECK(1,0); break; - case SETGLOBAL: CHECK(1,0); break; - case SETTABLEPOP: CHECK(3,0); break; - case SETTABLE: CHECK(i+3,i+2); break; - case SETLIST: CHECK(OP.arg2+1,1); break; - case SETMAP: CHECK(2*(i+1)+1,1); break; - case NEQOP: - case EQOP: - case LTOP: - case LEOP: - case GTOP: - case GEOP: - case ADDOP: - case SUBOP: - case MULTOP: - case DIVOP: - case POWOP: - case CONCOP: CHECK(2,1); break; - case MINUSOP: - case NOTOP: CHECK(1,1); break; - case ONTJMP: - case ONFJMP: - case IFFJMP: - case IFTUPJMP: - case IFFUPJMP: CHECK(1,0); break; - case JMP: break; - case CLOSURE: CHECK(OP.arg2,1); break; - case SETLINE: break; - case LONGARG: - longarg=i<<16; - if (longarg<0) UNSAFE("longarg overflow"),ATLOC; - break; - case CHECKSTACK: break; - default: /* cannot happen */ - UNSAFE("cannot test opcode %d [%s]"),OP.op,OP.name,ATLOC; - break; - } - p+=n; - } -} - -static void TestJumps(TProtoFunc* tf, int size, int* SP, int* JP) -{ - int i; - for (i=0; i<size; i++) - { - int to=JP[i]; - if (to!=0) - { - int at=i; /* for ATLOC */ - int a,b,j; - int on=(to<0); /* ON?JMP */ - if (on) to=-to; - a=SP[to]; - if (a<0) - UNSAFE("invalid jump to %d (not an instruction)"),to,ATLOC; - for (j=i; SP[++j]<0; ) /* find next instruction */ - ; - b=SP[j]+on; - if (a!=b) - UNSAFE("stack inconsistency in jump to %d (expected %d, found %d)"), - to,a,b,ATLOC; - } - } -} - -static void TestCode(TProtoFunc* tf) -{ - static int* SP=NULL; - static int* JP=NULL; - int size=luaU_codesize(tf); - luaM_reallocvector(SP,size,int); memset(SP,-1,size*sizeof(int)); - luaM_reallocvector(JP,size,int); memset(JP, 0,size*sizeof(int)); - TestStack(tf,size,SP,JP); - TestJumps(tf,size,SP,JP); -} - -#undef AT -#define AT "locvars[%d]" -static void TestLocals(TProtoFunc* tf) -{ - LocVar* v; - int l=1; - int d=0; - if (tf->locvars==NULL) return; - for (v=tf->locvars; v->line>=0; v++) - { - int at=v-tf->locvars; /* for ATLOC */ - if (l>v->line) - UNSAFE("bad line number %d; expected at least %d"),v->line,l,ATLOC; - l=v->line; - if (v->varname==NULL) - { - if (--d<0) UNSAFE("no scope to close"),ATLOC; - } - else - ++d; - } -} - -static void TestFunction(TProtoFunc* tf); - -static void TestConstants(TProtoFunc* tf) -{ - int i,n=tf->nconsts; - for (i=0; i<n; i++) - { - TObject* o=tf->consts+i; - switch (ttype(o)) - { - case LUA_T_NUMBER: - break; - case LUA_T_STRING: - break; - case LUA_T_PROTO: - TestFunction(tfvalue(o)); - break; - case LUA_T_NIL: - break; - default: /* cannot happen */ - luaU_badconstant("print",i,o,tf); - break; - } - } -} - -static void TestFunction(TProtoFunc* tf) -{ - TestCode(tf); - TestLocals(tf); - TestConstants(tf); -} - -void luaU_testchunk(TProtoFunc* Main) -{ - TestFunction(Main); -} diff --git a/src/lundump.c b/src/lundump.c index 0c3b5fd7..a8d06106 100644 --- a/src/lundump.c +++ b/src/lundump.c @@ -1,239 +1,244 @@ /* -** $Id: lundump.c,v 1.21 1999/07/02 19:34:26 lhf Exp $ +** $Id: lundump.c,v 1.33 2000/10/31 16:57:23 lhf Exp $ ** load bytecodes from files ** See Copyright Notice in lua.h */ #include <stdio.h> #include <string.h> -#include "lauxlib.h" + #include "lfunc.h" #include "lmem.h" #include "lopcodes.h" #include "lstring.h" #include "lundump.h" -#define LoadBlock(b,size,Z) ezread(Z,b,size) +#define LoadByte ezgetc -static void unexpectedEOZ (ZIO* Z) +static const char* ZNAME (ZIO* Z) { - luaL_verror("unexpected end of file in %s",zname(Z)); + const char* s=zname(Z); + return (*s=='@') ? s+1 : s; } -static int ezgetc (ZIO* Z) +static void unexpectedEOZ (lua_State* L, ZIO* Z) { - int c=zgetc(Z); - if (c==EOZ) unexpectedEOZ(Z); - return c; + luaO_verror(L,"unexpected end of file in `%.99s'",ZNAME(Z)); } -static void ezread (ZIO* Z, void* b, int n) +static int ezgetc (lua_State* L, ZIO* Z) { - int r=zread(Z,b,n); - if (r!=0) unexpectedEOZ(Z); + int c=zgetc(Z); + if (c==EOZ) unexpectedEOZ(L,Z); + return c; } -static unsigned int LoadWord (ZIO* Z) +static void ezread (lua_State* L, ZIO* Z, void* b, int n) { - unsigned int hi=ezgetc(Z); - unsigned int lo=ezgetc(Z); - return (hi<<8)|lo; + int r=zread(Z,b,n); + if (r!=0) unexpectedEOZ(L,Z); } -static unsigned long LoadLong (ZIO* Z) +static void LoadBlock (lua_State* L, void* b, size_t size, ZIO* Z, int swap) { - unsigned long hi=LoadWord(Z); - unsigned long lo=LoadWord(Z); - return (hi<<16)|lo; -} - -/* -* convert number from text -*/ -double luaU_str2d (char* b, char* where) -{ - int negative=(b[0]=='-'); - double x=luaO_str2d(b+negative); - if (x<0) luaL_verror("cannot convert number '%s' in %s",b,where); - return negative ? -x : x; + if (swap) + { + char *p=(char *) b+size-1; + int n=size; + while (n--) *p--=(char)ezgetc(L,Z); + } + else + ezread(L,Z,b,size); } -static real LoadNumber (ZIO* Z, int native) +static void LoadVector (lua_State* L, void* b, int m, size_t size, ZIO* Z, int swap) { - real x; - if (native) + if (swap) { - LoadBlock(&x,sizeof(x),Z); - return x; + char *q=(char *) b; + while (m--) + { + char *p=q+size-1; + int n=size; + while (n--) *p--=(char)ezgetc(L,Z); + q+=size; + } } else - { - char b[256]; - int size=ezgetc(Z); - LoadBlock(b,size,Z); - b[size]=0; - return luaU_str2d(b,zname(Z)); - } + ezread(L,Z,b,m*size); } -static int LoadInt (ZIO* Z, char* message) +static int LoadInt (lua_State* L, ZIO* Z, int swap) { - unsigned long l=LoadLong(Z); - unsigned int i=l; - if (i!=l) luaL_verror(message,l,zname(Z)); - return i; + int x; + LoadBlock(L,&x,sizeof(x),Z,swap); + return x; } -#define PAD 5 /* two word operands plus opcode */ +static size_t LoadSize (lua_State* L, ZIO* Z, int swap) +{ + size_t x; + LoadBlock(L,&x,sizeof(x),Z,swap); + return x; +} -static Byte* LoadCode (ZIO* Z) +static Number LoadNumber (lua_State* L, ZIO* Z, int swap) { - int size=LoadInt(Z,"code too long (%ld bytes) in %s"); - Byte* b=luaM_malloc(size+PAD); - LoadBlock(b,size,Z); - if (b[size-1]!=ENDCODE) luaL_verror("bad code in %s",zname(Z)); - memset(b+size,ENDCODE,PAD); /* pad code for safety */ - return b; + Number x; + LoadBlock(L,&x,sizeof(x),Z,swap); + return x; } -static TaggedString* LoadTString (ZIO* Z) +static TString* LoadString (lua_State* L, ZIO* Z, int swap) { - long size=LoadLong(Z); + size_t size=LoadSize(L,Z,swap); if (size==0) return NULL; else { - char* s=luaL_openspace(size); - LoadBlock(s,size,Z); - return luaS_newlstr(s,size-1); + char* s=luaO_openspace(L,size); + LoadBlock(L,s,size,Z,0); + return luaS_newlstr(L,s,size-1); /* remove trailing '\0' */ } } -static void LoadLocals (TProtoFunc* tf, ZIO* Z) +static void LoadCode (lua_State* L, Proto* tf, ZIO* Z, int swap) { - int i,n=LoadInt(Z,"too many locals (%ld) in %s"); - if (n==0) return; - tf->locvars=luaM_newvector(n+1,LocVar); + int size=LoadInt(L,Z,swap); + tf->code=luaM_newvector(L,size,Instruction); + LoadVector(L,tf->code,size,sizeof(*tf->code),Z,swap); + if (tf->code[size-1]!=OP_END) luaO_verror(L,"bad code in `%.99s'",ZNAME(Z)); + luaF_protook(L,tf,size); +} + +static void LoadLocals (lua_State* L, Proto* tf, ZIO* Z, int swap) +{ + int i,n; + tf->nlocvars=n=LoadInt(L,Z,swap); + tf->locvars=luaM_newvector(L,n,LocVar); for (i=0; i<n; i++) { - tf->locvars[i].line=LoadInt(Z,"too many lines (%ld) in %s"); - tf->locvars[i].varname=LoadTString(Z); + tf->locvars[i].varname=LoadString(L,Z,swap); + tf->locvars[i].startpc=LoadInt(L,Z,swap); + tf->locvars[i].endpc=LoadInt(L,Z,swap); } - tf->locvars[i].line=-1; /* flag end of vector */ - tf->locvars[i].varname=NULL; } -static TProtoFunc* LoadFunction (ZIO* Z, int native); +static void LoadLines (lua_State* L, Proto* tf, ZIO* Z, int swap) +{ + int n; + tf->nlineinfo=n=LoadInt(L,Z,swap); + tf->lineinfo=luaM_newvector(L,n,int); + LoadVector(L,tf->lineinfo,n,sizeof(*tf->lineinfo),Z,swap); +} + +static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap); -static void LoadConstants (TProtoFunc* tf, ZIO* Z, int native) +static void LoadConstants (lua_State* L, Proto* tf, ZIO* Z, int swap) { - int i,n=LoadInt(Z,"too many constants (%ld) in %s"); - tf->nconsts=n; - if (n==0) return; - tf->consts=luaM_newvector(n,TObject); + int i,n; + tf->nkstr=n=LoadInt(L,Z,swap); + tf->kstr=luaM_newvector(L,n,TString*); for (i=0; i<n; i++) - { - TObject* o=tf->consts+i; - ttype(o)=-ezgetc(Z); /* ttype(o) is negative - ORDER LUA_T */ - switch (ttype(o)) - { - case LUA_T_NUMBER: - nvalue(o)=LoadNumber(Z,native); - break; - case LUA_T_STRING: - tsvalue(o)=LoadTString(Z); - break; - case LUA_T_PROTO: - tfvalue(o)=LoadFunction(Z,native); - break; - case LUA_T_NIL: - break; - default: /* cannot happen */ - luaU_badconstant("load",i,o,tf); - break; - } - } + tf->kstr[i]=LoadString(L,Z,swap); + tf->nknum=n=LoadInt(L,Z,swap); + tf->knum=luaM_newvector(L,n,Number); + LoadVector(L,tf->knum,n,sizeof(*tf->knum),Z,swap); + tf->nkproto=n=LoadInt(L,Z,swap); + tf->kproto=luaM_newvector(L,n,Proto*); + for (i=0; i<n; i++) + tf->kproto[i]=LoadFunction(L,Z,swap); } -static TProtoFunc* LoadFunction (ZIO* Z, int native) +static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap) { - TProtoFunc* tf=luaF_newproto(); - tf->lineDefined=LoadInt(Z,"lineDefined too large (%ld) in %s"); - tf->source=LoadTString(Z); - if (tf->source==NULL) tf->source=luaS_new(zname(Z)); - tf->code=LoadCode(Z); - LoadLocals(tf,Z); - LoadConstants(tf,Z,native); + Proto* tf=luaF_newproto(L); + tf->source=LoadString(L,Z,swap); + tf->lineDefined=LoadInt(L,Z,swap); + tf->numparams=LoadInt(L,Z,swap); + tf->is_vararg=LoadByte(L,Z); + tf->maxstacksize=LoadInt(L,Z,swap); + LoadLocals(L,tf,Z,swap); + LoadLines(L,tf,Z,swap); + LoadConstants(L,tf,Z,swap); + LoadCode(L,tf,Z,swap); return tf; } -static void LoadSignature (ZIO* Z) +static void LoadSignature (lua_State* L, ZIO* Z) { - char* s=SIGNATURE; - while (*s!=0 && ezgetc(Z)==*s) + const char* s=SIGNATURE; + while (*s!=0 && ezgetc(L,Z)==*s) ++s; - if (*s!=0) luaL_verror("bad signature in %s",zname(Z)); + if (*s!=0) luaO_verror(L,"bad signature in `%.99s'",ZNAME(Z)); } -static int LoadHeader (ZIO* Z) +static void TestSize (lua_State* L, int s, const char* what, ZIO* Z) +{ + int r=ezgetc(L,Z); + if (r!=s) + luaO_verror(L,"virtual machine mismatch in `%.99s':\n" + " %.20s is %d but read %d",ZNAME(Z),what,s,r); +} + +#define TESTSIZE(s) TestSize(L,s,#s,Z) +#define V(v) v/16,v%16 + +static int LoadHeader (lua_State* L, ZIO* Z) { - int version,sizeofR; - int native; - LoadSignature(Z); - version=ezgetc(Z); + int version,swap; + Number f=0,tf=TEST_NUMBER; + LoadSignature(L,Z); + version=ezgetc(L,Z); if (version>VERSION) - luaL_verror( - "%s too new: version=0x%02x; expected at most 0x%02x", - zname(Z),version,VERSION); + luaO_verror(L,"`%.99s' too new:\n" + " read version %d.%d; expected at most %d.%d", + ZNAME(Z),V(version),V(VERSION)); if (version<VERSION0) /* check last major change */ - luaL_verror( - "%s too old: version=0x%02x; expected at least 0x%02x", - zname(Z),version,VERSION0); - sizeofR=ezgetc(Z); - native=(sizeofR!=0); - if (native) /* test number representation */ - { - if (sizeofR!=sizeof(real)) - luaL_verror("unknown number size in %s: read %d; expected %d", - zname(Z),sizeofR,sizeof(real)); - else - { - real tf=TEST_NUMBER; - real f=LoadNumber(Z,native); - if ((long)f!=(long)tf) - luaL_verror("unknown number format in %s: " - "read " NUMBER_FMT "; expected " NUMBER_FMT, - zname(Z),f,tf); - } - } - return native; + luaO_verror(L,"`%.99s' too old:\n" + " read version %d.%d; expected at least %d.%d", + ZNAME(Z),V(version),V(VERSION)); + swap=(luaU_endianess()!=ezgetc(L,Z)); /* need to swap bytes? */ + TESTSIZE(sizeof(int)); + TESTSIZE(sizeof(size_t)); + TESTSIZE(sizeof(Instruction)); + TESTSIZE(SIZE_INSTRUCTION); + TESTSIZE(SIZE_OP); + TESTSIZE(SIZE_B); + TESTSIZE(sizeof(Number)); + f=LoadNumber(L,Z,swap); + if ((long)f!=(long)tf) /* disregard errors in last bit of fraction */ + luaO_verror(L,"unknown number format in `%.99s':\n" + " read " NUMBER_FMT "; expected " NUMBER_FMT, ZNAME(Z),f,tf); + return swap; } -static TProtoFunc* LoadChunk (ZIO* Z) +static Proto* LoadChunk (lua_State* L, ZIO* Z) { - return LoadFunction(Z,LoadHeader(Z)); + return LoadFunction(L,Z,LoadHeader(L,Z)); } /* ** load one chunk from a file or buffer ** return main if ok and NULL at EOF */ -TProtoFunc* luaU_undump1 (ZIO* Z) +Proto* luaU_undump (lua_State* L, ZIO* Z) { + Proto* tf=NULL; int c=zgetc(Z); if (c==ID_CHUNK) - return LoadChunk(Z); - else if (c!=EOZ) - luaL_verror("%s is not a Lua binary file",zname(Z)); - return NULL; + tf=LoadChunk(L,Z); + c=zgetc(Z); + if (c!=EOZ) + luaO_verror(L,"`%.99s' apparently contains more than one chunk",ZNAME(Z)); + return tf; } /* -* handle constants that cannot happen +** find byte order */ -void luaU_badconstant (char* s, int i, TObject* o, TProtoFunc* tf) +int luaU_endianess (void) { - int t=ttype(o); - char* name= (t>0 || t<LUA_T_LINE) ? "?" : luaO_typenames[-t]; - luaL_verror("cannot %s constant #%d: type=%d [%s]" IN,s,i,t,name,INLOC); + int x=1; + return *(char*)&x; } diff --git a/src/lundump.h b/src/lundump.h index 3f905cb0..446d2de9 100644 --- a/src/lundump.h +++ b/src/lundump.h @@ -1,5 +1,5 @@ /* -** $Id: lundump.h,v 1.15 1999/07/02 19:34:26 lhf Exp $ +** $Id: lundump.h,v 1.21 2000/10/31 16:57:23 lhf Exp $ ** load pre-compiled Lua chunks ** See Copyright Notice in lua.h */ @@ -10,27 +10,23 @@ #include "lobject.h" #include "lzio.h" -TProtoFunc* luaU_undump1 (ZIO* Z); /* load one chunk */ -void luaU_badconstant (char* s, int i, TObject* o, TProtoFunc* tf); - /* handle cases that cannot happen */ -double luaU_str2d (char* b, char* where); - /* convert number from text */ +/* load one chunk */ +Proto* luaU_undump (lua_State* L, ZIO* Z); + +/* find byte order */ +int luaU_endianess (void); /* definitions for headers of binary files */ -#define VERSION 0x32 /* last format change was in 3.2 */ -#define VERSION0 0x32 /* last major change was in 3.2 */ +#define VERSION 0x40 /* last format change was in 4.0 */ +#define VERSION0 0x40 /* last major change was in 4.0 */ #define ID_CHUNK 27 /* binary files start with ESC... */ #define SIGNATURE "Lua" /* ...followed by this signature */ /* formats for error messages */ -#define SOURCE "<%s:%d>" -#define IN " in %p " SOURCE -#define INLOC tf,tf->source->str,tf->lineDefined - -/* format for numbers in listings and error messages */ -#ifndef NUMBER_FMT -#define NUMBER_FMT "%.16g" /* LUA_NUMBER */ -#endif +#define SOURCE_FMT "<%d:%.99s>" +#define SOURCE tf->lineDefined,tf->source->str +#define IN_FMT " in %p " SOURCE_FMT +#define IN tf,SOURCE /* a multiple of PI for testing native format */ /* multiplying by 1E8 gives non-trivial integer values */ @@ -1,28 +1,27 @@ /* -** $Id: lvm.c,v 1.58 1999/06/22 20:37:23 roberto Exp $ +** $Id: lvm.c,v 1.146 2000/10/26 12:47:05 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ -#include <ctype.h> -#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "lauxlib.h" +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" -#include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" -#include "luadebug.h" #include "lvm.h" @@ -31,605 +30,681 @@ #endif -#define highbyte(x) ((x)<<8) - - -/* Extra stack size to run a function: LUA_T_LINE(1), TM calls(2), ... */ -#define EXTRA_STACK 5 - +/* +** Extra stack size to run a function: +** TAG_LINE(1), NAME(1), TM calls(3) (plus some extra...) +*/ +#define EXTRA_STACK 8 -static TaggedString *strconc (TaggedString *l, TaggedString *r) { - long nl = l->u.s.len; - long nr = r->u.s.len; - char *buffer = luaL_openspace(nl+nr); - memcpy(buffer, l->str, nl); - memcpy(buffer+nl, r->str, nr); - return luaS_newlstr(buffer, nl+nr); -} -int luaV_tonumber (TObject *obj) { /* LUA_NUMBER */ - if (ttype(obj) != LUA_T_STRING) +int luaV_tonumber (TObject *obj) { + if (ttype(obj) != LUA_TSTRING) return 1; else { - double t; - char *e = svalue(obj); - int sig = 1; - while (isspace((unsigned char)*e)) e++; - if (*e == '-') { - e++; - sig = -1; - } - else if (*e == '+') e++; - /* no digit before or after decimal point? */ - if (!isdigit((unsigned char)*e) && !isdigit((unsigned char)*(e+1))) + if (!luaO_str2d(svalue(obj), &nvalue(obj))) return 2; - t = luaO_str2d(e); - if (t<0) return 2; - nvalue(obj) = (real)t*sig; - ttype(obj) = LUA_T_NUMBER; + ttype(obj) = LUA_TNUMBER; return 0; } } -int luaV_tostring (TObject *obj) { /* LUA_NUMBER */ - if (ttype(obj) != LUA_T_NUMBER) +int luaV_tostring (lua_State *L, TObject *obj) { /* LUA_NUMBER */ + if (ttype(obj) != LUA_TNUMBER) return 1; else { - char s[32]; /* 16 digits, signal, point and \0 (+ some extra...) */ - sprintf(s, "%.16g", (double)nvalue(obj)); - tsvalue(obj) = luaS_new(s); - ttype(obj) = LUA_T_STRING; + char s[32]; /* 16 digits, sign, point and \0 (+ some extra...) */ + lua_number2str(s, nvalue(obj)); /* convert `s' to number */ + tsvalue(obj) = luaS_new(L, s); + ttype(obj) = LUA_TSTRING; return 0; } } -void luaV_setn (Hash *t, int val) { - TObject index, value; - ttype(&index) = LUA_T_STRING; tsvalue(&index) = luaS_new("n"); - ttype(&value) = LUA_T_NUMBER; nvalue(&value) = val; - luaH_set(t, &index, &value); +static void traceexec (lua_State *L, StkId base, StkId top, lua_Hook linehook) { + CallInfo *ci = infovalue(base-1); + int *lineinfo = ci->func->f.l->lineinfo; + int pc = (*ci->pc - ci->func->f.l->code) - 1; + int newline; + if (pc == 0) { /* may be first time? */ + ci->line = 1; + ci->refi = 0; + ci->lastpc = pc+1; /* make sure it will call linehook */ + } + newline = luaG_getline(lineinfo, pc, ci->line, &ci->refi); + /* calls linehook when enters a new line or jumps back (loop) */ + if (newline != ci->line || pc <= ci->lastpc) { + ci->line = newline; + L->top = top; + luaD_lineHook(L, base-2, newline, linehook); + } + ci->lastpc = pc; +} + + +static Closure *luaV_closure (lua_State *L, int nelems) { + Closure *c = luaF_newclosure(L, nelems); + L->top -= nelems; + while (nelems--) + c->upvalue[nelems] = *(L->top+nelems); + clvalue(L->top) = c; + ttype(L->top) = LUA_TFUNCTION; + incr_top; + return c; } -void luaV_closure (int nelems) { - if (nelems > 0) { - struct Stack *S = &L->stack; - Closure *c = luaF_newclosure(nelems); - c->consts[0] = *(S->top-1); - memcpy(&c->consts[1], S->top-(nelems+1), nelems*sizeof(TObject)); - S->top -= nelems; - ttype(S->top-1) = LUA_T_CLOSURE; - (S->top-1)->value.cl = c; - } +void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems) { + Closure *cl = luaV_closure(L, nelems); + cl->f.c = c; + cl->isC = 1; +} + + +void luaV_Lclosure (lua_State *L, Proto *l, int nelems) { + Closure *cl = luaV_closure(L, nelems); + cl->f.l = l; + cl->isC = 0; } /* ** Function to index a table. -** Receives the table at top-2 and the index at top-1. +** Receives the table at `t' and the key at top. */ -void luaV_gettable (void) { - TObject *table = L->stack.top-2; - TObject *im; - if (ttype(table) != LUA_T_ARRAY) { /* not a table, get gettable method */ - im = luaT_getimbyObj(table, IM_GETTABLE); - if (ttype(im) == LUA_T_NIL) - lua_error("indexed expression not a table"); +const TObject *luaV_gettable (lua_State *L, StkId t) { + Closure *tm; + int tg; + if (ttype(t) == LUA_TTABLE && /* `t' is a table? */ + ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */ + luaT_gettm(L, tg, TM_GETTABLE) == NULL)) { /* or no TM? */ + /* do a primitive get */ + const TObject *h = luaH_get(L, hvalue(t), L->top-1); + /* result is no nil or there is no `index' tag method? */ + if (ttype(h) != LUA_TNIL || ((tm=luaT_gettm(L, tg, TM_INDEX)) == NULL)) + return h; /* return result */ + /* else call `index' tag method */ } - else { /* object is a table... */ - int tg = table->value.a->htag; - im = luaT_getim(tg, IM_GETTABLE); - if (ttype(im) == LUA_T_NIL) { /* and does not have a "gettable" method */ - TObject *h = luaH_get(avalue(table), table+1); - if (ttype(h) == LUA_T_NIL && - (ttype(im=luaT_getim(tg, IM_INDEX)) != LUA_T_NIL)) { - /* result is nil and there is an "index" tag method */ - luaD_callTM(im, 2, 1); /* calls it */ - } - else { - L->stack.top--; - *table = *h; /* "push" result into table position */ - } - return; - } - /* else it has a "gettable" method, go through to next command */ + else { /* try a `gettable' tag method */ + tm = luaT_gettmbyObj(L, t, TM_GETTABLE); + } + if (tm != NULL) { /* is there a tag method? */ + luaD_checkstack(L, 2); + *(L->top+1) = *(L->top-1); /* key */ + *L->top = *t; /* table */ + clvalue(L->top-1) = tm; /* tag method */ + ttype(L->top-1) = LUA_TFUNCTION; + L->top += 2; + luaD_call(L, L->top - 3, 1); + return L->top - 1; /* call result */ + } + else { /* no tag method */ + luaG_typeerror(L, t, "index"); + return NULL; /* to avoid warnings */ } - /* object is not a table, or it has a "gettable" method */ - luaD_callTM(im, 2, 1); } /* -** Receives table at *t, index at *(t+1) and value at top. +** Receives table at `t', key at `key' and value at top. */ -void luaV_settable (TObject *t) { - struct Stack *S = &L->stack; - TObject *im; - if (ttype(t) != LUA_T_ARRAY) { /* not a table, get "settable" method */ - im = luaT_getimbyObj(t, IM_SETTABLE); - if (ttype(im) == LUA_T_NIL) - lua_error("indexed expression not a table"); - } - else { /* object is a table... */ - im = luaT_getim(avalue(t)->htag, IM_SETTABLE); - if (ttype(im) == LUA_T_NIL) { /* and does not have a "settable" method */ - luaH_set(avalue(t), t+1, S->top-1); - S->top--; /* pop value */ - return; +void luaV_settable (lua_State *L, StkId t, StkId key) { + int tg; + if (ttype(t) == LUA_TTABLE && /* `t' is a table? */ + ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */ + luaT_gettm(L, tg, TM_SETTABLE) == NULL)) /* or no TM? */ + *luaH_set(L, hvalue(t), key) = *(L->top-1); /* do a primitive set */ + else { /* try a `settable' tag method */ + Closure *tm = luaT_gettmbyObj(L, t, TM_SETTABLE); + if (tm != NULL) { + luaD_checkstack(L, 3); + *(L->top+2) = *(L->top-1); + *(L->top+1) = *key; + *(L->top) = *t; + clvalue(L->top-1) = tm; + ttype(L->top-1) = LUA_TFUNCTION; + L->top += 3; + luaD_call(L, L->top - 4, 0); /* call `settable' tag method */ } - /* else it has a "settable" method, go through to next command */ + else /* no tag method... */ + luaG_typeerror(L, t, "index"); } - /* object is not a table, or it has a "settable" method */ - /* prepare arguments and call the tag method */ - *(S->top+1) = *(L->stack.top-1); - *(S->top) = *(t+1); - *(S->top-1) = *t; - S->top += 2; /* WARNING: caller must assure stack space */ - luaD_callTM(im, 3, 0); } -void luaV_rawsettable (TObject *t) { - if (ttype(t) != LUA_T_ARRAY) - lua_error("indexed expression not a table"); - else { - struct Stack *S = &L->stack; - luaH_set(avalue(t), t+1, S->top-1); - S->top -= 3; +const TObject *luaV_getglobal (lua_State *L, TString *s) { + const TObject *value = luaH_getstr(L->gt, s); + Closure *tm = luaT_gettmbyObj(L, value, TM_GETGLOBAL); + if (tm == NULL) /* is there a tag method? */ + return value; /* default behavior */ + else { /* tag method */ + luaD_checkstack(L, 3); + clvalue(L->top) = tm; + ttype(L->top) = LUA_TFUNCTION; + tsvalue(L->top+1) = s; /* global name */ + ttype(L->top+1) = LUA_TSTRING; + *(L->top+2) = *value; + L->top += 3; + luaD_call(L, L->top - 3, 1); + return L->top - 1; } } -void luaV_getglobal (TaggedString *ts) { - /* WARNING: caller must assure stack space */ - /* only userdata, tables and nil can have getglobal tag methods */ - static char valid_getglobals[] = {1, 0, 0, 1, 0, 0, 1, 0}; /* ORDER LUA_T */ - TObject *value = &ts->u.s.globalval; - if (valid_getglobals[-ttype(value)]) { - TObject *im = luaT_getimbyObj(value, IM_GETGLOBAL); - if (ttype(im) != LUA_T_NIL) { /* is there a tag method? */ - struct Stack *S = &L->stack; - ttype(S->top) = LUA_T_STRING; - tsvalue(S->top) = ts; - S->top++; - *S->top++ = *value; - luaD_callTM(im, 2, 1); - return; +void luaV_setglobal (lua_State *L, TString *s) { + const TObject *oldvalue = luaH_getstr(L->gt, s); + Closure *tm = luaT_gettmbyObj(L, oldvalue, TM_SETGLOBAL); + if (tm == NULL) { /* is there a tag method? */ + if (oldvalue != &luaO_nilobject) { + /* cast to remove `const' is OK, because `oldvalue' != luaO_nilobject */ + *(TObject *)oldvalue = *(L->top - 1); + } + else { + TObject key; + ttype(&key) = LUA_TSTRING; + tsvalue(&key) = s; + *luaH_set(L, L->gt, &key) = *(L->top - 1); } - /* else no tag method: go through to default behavior */ } - *L->stack.top++ = *value; /* default behavior */ -} - - -void luaV_setglobal (TaggedString *ts) { - TObject *oldvalue = &ts->u.s.globalval; - TObject *im = luaT_getimbyObj(oldvalue, IM_SETGLOBAL); - if (ttype(im) == LUA_T_NIL) /* is there a tag method? */ - luaS_rawsetglobal(ts, --L->stack.top); else { - /* WARNING: caller must assure stack space */ - struct Stack *S = &L->stack; - TObject newvalue; - newvalue = *(S->top-1); - ttype(S->top-1) = LUA_T_STRING; - tsvalue(S->top-1) = ts; - *S->top++ = *oldvalue; - *S->top++ = newvalue; - luaD_callTM(im, 3, 0); + luaD_checkstack(L, 3); + *(L->top+2) = *(L->top-1); /* new value */ + *(L->top+1) = *oldvalue; + ttype(L->top) = LUA_TSTRING; + tsvalue(L->top) = s; + clvalue(L->top-1) = tm; + ttype(L->top-1) = LUA_TFUNCTION; + L->top += 3; + luaD_call(L, L->top - 4, 0); } } -static void call_binTM (IMS event, char *msg) -{ - TObject *im = luaT_getimbyObj(L->stack.top-2, event);/* try first operand */ - if (ttype(im) == LUA_T_NIL) { - im = luaT_getimbyObj(L->stack.top-1, event); /* try second operand */ - if (ttype(im) == LUA_T_NIL) { - im = luaT_getim(0, event); /* try a 'global' i.m. */ - if (ttype(im) == LUA_T_NIL) - lua_error(msg); +static int call_binTM (lua_State *L, StkId top, TMS event) { + /* try first operand */ + Closure *tm = luaT_gettmbyObj(L, top-2, event); + L->top = top; + if (tm == NULL) { + tm = luaT_gettmbyObj(L, top-1, event); /* try second operand */ + if (tm == NULL) { + tm = luaT_gettm(L, 0, event); /* try a `global' method */ + if (tm == NULL) + return 0; /* error */ } } - lua_pushstring(luaT_eventname[event]); - luaD_callTM(im, 3, 1); + lua_pushstring(L, luaT_eventname[event]); + luaD_callTM(L, tm, 3, 1); + return 1; } -static void call_arith (IMS event) -{ - call_binTM(event, "unexpected type in arithmetic operation"); +static void call_arith (lua_State *L, StkId top, TMS event) { + if (!call_binTM(L, top, event)) + luaG_binerror(L, top-2, LUA_TNUMBER, "perform arithmetic on"); } -static int luaV_strcomp (char *l, long ll, char *r, long lr) -{ +static int luaV_strcomp (const TString *ls, const TString *rs) { + const char *l = ls->str; + size_t ll = ls->len; + const char *r = rs->str; + size_t lr = rs->len; for (;;) { - long temp = strcoll(l, r); + int temp = strcoll(l, r); if (temp != 0) return temp; - /* strings are equal up to a '\0' */ - temp = strlen(l); /* index of first '\0' in both strings */ - if (temp == ll) /* l is finished? */ - return (temp == lr) ? 0 : -1; /* l is equal or smaller than r */ - else if (temp == lr) /* r is finished? */ - return 1; /* l is greater than r (because l is not finished) */ - /* both strings longer than temp; go on comparing (after the '\0') */ - temp++; - l += temp; ll -= temp; r += temp; lr -= temp; + else { /* strings are equal up to a '\0' */ + size_t len = strlen(l); /* index of first '\0' in both strings */ + if (len == ll) /* l is finished? */ + return (len == lr) ? 0 : -1; /* l is equal or smaller than r */ + else if (len == lr) /* r is finished? */ + return 1; /* l is greater than r (because l is not finished) */ + /* both strings longer than `len'; go on comparing (after the '\0') */ + len++; + l += len; ll -= len; r += len; lr -= len; + } } } -void luaV_comparison (lua_Type ttype_less, lua_Type ttype_equal, - lua_Type ttype_great, IMS op) { - struct Stack *S = &L->stack; - TObject *l = S->top-2; - TObject *r = S->top-1; - real result; - if (ttype(l) == LUA_T_NUMBER && ttype(r) == LUA_T_NUMBER) - result = nvalue(l)-nvalue(r); - else if (ttype(l) == LUA_T_STRING && ttype(r) == LUA_T_STRING) - result = luaV_strcomp(svalue(l), tsvalue(l)->u.s.len, - svalue(r), tsvalue(r)->u.s.len); - else { - call_binTM(op, "unexpected type in comparison"); - return; + +int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top) { + if (ttype(l) == LUA_TNUMBER && ttype(r) == LUA_TNUMBER) + return (nvalue(l) < nvalue(r)); + else if (ttype(l) == LUA_TSTRING && ttype(r) == LUA_TSTRING) + return (luaV_strcomp(tsvalue(l), tsvalue(r)) < 0); + else { /* call TM */ + luaD_checkstack(L, 2); + *top++ = *l; + *top++ = *r; + if (!call_binTM(L, top, TM_LT)) + luaG_ordererror(L, top-2); + L->top--; + return (ttype(L->top) != LUA_TNIL); } - S->top--; - nvalue(S->top-1) = 1; - ttype(S->top-1) = (result < 0) ? ttype_less : - (result == 0) ? ttype_equal : ttype_great; } -void luaV_pack (StkId firstel, int nvararg, TObject *tab) { - TObject *firstelem = L->stack.stack+firstel; +void luaV_strconc (lua_State *L, int total, StkId top) { + do { + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (tostring(L, top-2) || tostring(L, top-1)) { + if (!call_binTM(L, top, TM_CONCAT)) + luaG_binerror(L, top-2, LUA_TSTRING, "concat"); + } + else if (tsvalue(top-1)->len > 0) { /* if len=0, do nothing */ + /* at least two string values; get as many as possible */ + lint32 tl = (lint32)tsvalue(top-1)->len + + (lint32)tsvalue(top-2)->len; + char *buffer; + int i; + while (n < total && !tostring(L, top-n-1)) { /* collect total length */ + tl += tsvalue(top-n-1)->len; + n++; + } + if (tl > MAX_SIZET) lua_error(L, "string size overflow"); + buffer = luaO_openspace(L, tl); + tl = 0; + for (i=n; i>0; i--) { /* concat all strings */ + size_t l = tsvalue(top-i)->len; + memcpy(buffer+tl, tsvalue(top-i)->str, l); + tl += l; + } + tsvalue(top-n) = luaS_newlstr(L, buffer, tl); + } + total -= n-1; /* got `n' strings to create 1 new */ + top -= n-1; + } while (total > 1); /* repeat until only 1 result left */ +} + + +static void luaV_pack (lua_State *L, StkId firstelem) { int i; - Hash *htab; - if (nvararg < 0) nvararg = 0; - htab = avalue(tab) = luaH_new(nvararg+1); /* +1 for field 'n' */ - ttype(tab) = LUA_T_ARRAY; - for (i=0; i<nvararg; i++) - luaH_setint(htab, i+1, firstelem+i); - luaV_setn(htab, nvararg); /* store counter in field "n" */ + Hash *htab = luaH_new(L, 0); + for (i=0; firstelem+i<L->top; i++) + *luaH_setint(L, htab, i+1) = *(firstelem+i); + /* store counter in field `n' */ + luaH_setstrnum(L, htab, luaS_new(L, "n"), i); + L->top = firstelem; /* remove elements from the stack */ + ttype(L->top) = LUA_TTABLE; + hvalue(L->top) = htab; + incr_top; } -static void adjust_varargs (StkId first_extra_arg) -{ - TObject arg; - luaV_pack(first_extra_arg, - (L->stack.top-L->stack.stack)-first_extra_arg, &arg); - luaD_adjusttop(first_extra_arg); - *L->stack.top++ = arg; +static void adjust_varargs (lua_State *L, StkId base, int nfixargs) { + int nvararg = (L->top-base) - nfixargs; + if (nvararg < 0) + luaD_adjusttop(L, base, nfixargs); + luaV_pack(L, base+nfixargs); } +#define dojump(pc, i) { int d = GETARG_S(i); pc += d; } + /* -** Execute the given opcode, until a RET. Parameters are between -** [stack+base,top). Returns n such that the the results are between -** [stack+n,top). +** Executes the given Lua function. Parameters are between [base,top). +** Returns n such that the the results are between [n,top). */ -StkId luaV_execute (Closure *cl, TProtoFunc *tf, StkId base) { - struct Stack *S = &L->stack; /* to optimize */ - register Byte *pc = tf->code; - TObject *consts = tf->consts; - if (L->callhook) - luaD_callHook(base, tf, 0); - luaD_checkstack((*pc++)+EXTRA_STACK); - if (*pc < ZEROVARARG) - luaD_adjusttop(base+*(pc++)); - else { /* varargs */ - luaC_checkGC(); - adjust_varargs(base+(*pc++)-ZEROVARARG); - } +StkId luaV_execute (lua_State *L, const Closure *cl, StkId base) { + const Proto *const tf = cl->f.l; + StkId top; /* keep top local, for performance */ + const Instruction *pc = tf->code; + TString **const kstr = tf->kstr; + const lua_Hook linehook = L->linehook; + infovalue(base-1)->pc = &pc; + luaD_checkstack(L, tf->maxstacksize+EXTRA_STACK); + if (tf->is_vararg) /* varargs? */ + adjust_varargs(L, base, tf->numparams); + else + luaD_adjusttop(L, base, tf->numparams); + top = L->top; + /* main loop of interpreter */ for (;;) { - register int aux = 0; - switchentry: - switch ((OpCode)*pc++) { - - case ENDCODE: - S->top = S->stack + base; - goto ret; - - case RETCODE: - base += *pc++; - goto ret; - - case CALL: aux = *pc++; - luaD_calln(*pc++, aux); + const Instruction i = *pc++; + if (linehook) + traceexec(L, base, top, linehook); + switch (GET_OPCODE(i)) { + case OP_END: { + L->top = top; + return top; + } + case OP_RETURN: { + L->top = top; + return base+GETARG_U(i); + } + case OP_CALL: { + int nres = GETARG_B(i); + if (nres == MULT_RET) nres = LUA_MULTRET; + L->top = top; + luaD_call(L, base+GETARG_A(i), nres); + top = L->top; break; - - case TAILCALL: aux = *pc++; - luaD_calln(*pc++, MULT_RET); - base += aux; - goto ret; - - case PUSHNIL: aux = *pc++; + } + case OP_TAILCALL: { + L->top = top; + luaD_call(L, base+GETARG_A(i), LUA_MULTRET); + return base+GETARG_B(i); + } + case OP_PUSHNIL: { + int n = GETARG_U(i); + LUA_ASSERT(n>0, "invalid argument"); do { - ttype(S->top++) = LUA_T_NIL; - } while (aux--); + ttype(top++) = LUA_TNIL; + } while (--n > 0); break; - - case POP: aux = *pc++; - S->top -= aux; + } + case OP_POP: { + top -= GETARG_U(i); break; - - case PUSHNUMBERW: aux += highbyte(*pc++); - case PUSHNUMBER: aux += *pc++; - ttype(S->top) = LUA_T_NUMBER; - nvalue(S->top) = aux; - S->top++; + } + case OP_PUSHINT: { + ttype(top) = LUA_TNUMBER; + nvalue(top) = (Number)GETARG_S(i); + top++; break; - - case PUSHNUMBERNEGW: aux += highbyte(*pc++); - case PUSHNUMBERNEG: aux += *pc++; - ttype(S->top) = LUA_T_NUMBER; - nvalue(S->top) = -aux; - S->top++; + } + case OP_PUSHSTRING: { + ttype(top) = LUA_TSTRING; + tsvalue(top) = kstr[GETARG_U(i)]; + top++; break; - - case PUSHCONSTANTW: aux += highbyte(*pc++); - case PUSHCONSTANT: aux += *pc++; - *S->top++ = consts[aux]; + } + case OP_PUSHNUM: { + ttype(top) = LUA_TNUMBER; + nvalue(top) = tf->knum[GETARG_U(i)]; + top++; break; - - case PUSHUPVALUE: aux = *pc++; - *S->top++ = cl->consts[aux+1]; + } + case OP_PUSHNEGNUM: { + ttype(top) = LUA_TNUMBER; + nvalue(top) = -tf->knum[GETARG_U(i)]; + top++; break; - - case PUSHLOCAL: aux = *pc++; - *S->top++ = *((S->stack+base) + aux); + } + case OP_PUSHUPVALUE: { + *top++ = cl->upvalue[GETARG_U(i)]; break; - - case GETGLOBALW: aux += highbyte(*pc++); - case GETGLOBAL: aux += *pc++; - luaV_getglobal(tsvalue(&consts[aux])); + } + case OP_GETLOCAL: { + *top++ = *(base+GETARG_U(i)); break; - - case GETTABLE: - luaV_gettable(); + } + case OP_GETGLOBAL: { + L->top = top; + *top = *luaV_getglobal(L, kstr[GETARG_U(i)]); + top++; break; - - case GETDOTTEDW: aux += highbyte(*pc++); - case GETDOTTED: aux += *pc++; - *S->top++ = consts[aux]; - luaV_gettable(); + } + case OP_GETTABLE: { + L->top = top; + top--; + *(top-1) = *luaV_gettable(L, top-1); break; - - case PUSHSELFW: aux += highbyte(*pc++); - case PUSHSELF: aux += *pc++; { - TObject receiver; - receiver = *(S->top-1); - *S->top++ = consts[aux]; - luaV_gettable(); - *S->top++ = receiver; + } + case OP_GETDOTTED: { + ttype(top) = LUA_TSTRING; + tsvalue(top) = kstr[GETARG_U(i)]; + L->top = top+1; + *(top-1) = *luaV_gettable(L, top-1); break; } - - case CREATEARRAYW: aux += highbyte(*pc++); - case CREATEARRAY: aux += *pc++; - luaC_checkGC(); - avalue(S->top) = luaH_new(aux); - ttype(S->top) = LUA_T_ARRAY; - S->top++; + case OP_GETINDEXED: { + *top = *(base+GETARG_U(i)); + L->top = top+1; + *(top-1) = *luaV_gettable(L, top-1); break; - - case SETLOCAL: aux = *pc++; - *((S->stack+base) + aux) = *(--S->top); + } + case OP_PUSHSELF: { + TObject receiver; + receiver = *(top-1); + ttype(top) = LUA_TSTRING; + tsvalue(top++) = kstr[GETARG_U(i)]; + L->top = top; + *(top-2) = *luaV_gettable(L, top-2); + *(top-1) = receiver; break; - - case SETGLOBALW: aux += highbyte(*pc++); - case SETGLOBAL: aux += *pc++; - luaV_setglobal(tsvalue(&consts[aux])); + } + case OP_CREATETABLE: { + L->top = top; + luaC_checkGC(L); + hvalue(top) = luaH_new(L, GETARG_U(i)); + ttype(top) = LUA_TTABLE; + top++; break; - - case SETTABLEPOP: - luaV_settable(S->top-3); - S->top -= 2; /* pop table and index */ + } + case OP_SETLOCAL: { + *(base+GETARG_U(i)) = *(--top); break; - - case SETTABLE: - luaV_settable(S->top-3-(*pc++)); + } + case OP_SETGLOBAL: { + L->top = top; + luaV_setglobal(L, kstr[GETARG_U(i)]); + top--; break; - - case SETLISTW: aux += highbyte(*pc++); - case SETLIST: aux += *pc++; { - int n = *(pc++); - Hash *arr = avalue(S->top-n-1); - aux *= LFIELDS_PER_FLUSH; + } + case OP_SETTABLE: { + StkId t = top-GETARG_A(i); + L->top = top; + luaV_settable(L, t, t+1); + top -= GETARG_B(i); /* pop values */ + break; + } + case OP_SETLIST: { + int aux = GETARG_A(i) * LFIELDS_PER_FLUSH; + int n = GETARG_B(i); + Hash *arr = hvalue(top-n-1); + L->top = top-n; /* final value of `top' (in case of errors) */ for (; n; n--) - luaH_setint(arr, n+aux, --S->top); + *luaH_setint(L, arr, n+aux) = *(--top); break; } - - case SETMAP: aux = *pc++; { - Hash *arr = avalue(S->top-(2*aux)-3); - do { - luaH_set(arr, S->top-2, S->top-1); - S->top-=2; - } while (aux--); + case OP_SETMAP: { + int n = GETARG_U(i); + StkId finaltop = top-2*n; + Hash *arr = hvalue(finaltop-1); + L->top = finaltop; /* final value of `top' (in case of errors) */ + for (; n; n--) { + top-=2; + *luaH_set(L, arr, top) = *(top+1); + } break; } - - case NEQOP: aux = 1; - case EQOP: { - int res = luaO_equalObj(S->top-2, S->top-1); - if (aux) res = !res; - S->top--; - ttype(S->top-1) = res ? LUA_T_NUMBER : LUA_T_NIL; - nvalue(S->top-1) = 1; + case OP_ADD: { + if (tonumber(top-2) || tonumber(top-1)) + call_arith(L, top, TM_ADD); + else + nvalue(top-2) += nvalue(top-1); + top--; break; } - - case LTOP: - luaV_comparison(LUA_T_NUMBER, LUA_T_NIL, LUA_T_NIL, IM_LT); - break; - - case LEOP: - luaV_comparison(LUA_T_NUMBER, LUA_T_NUMBER, LUA_T_NIL, IM_LE); + case OP_ADDI: { + if (tonumber(top-1)) { + ttype(top) = LUA_TNUMBER; + nvalue(top) = (Number)GETARG_S(i); + call_arith(L, top+1, TM_ADD); + } + else + nvalue(top-1) += (Number)GETARG_S(i); break; - - case GTOP: - luaV_comparison(LUA_T_NIL, LUA_T_NIL, LUA_T_NUMBER, IM_GT); + } + case OP_SUB: { + if (tonumber(top-2) || tonumber(top-1)) + call_arith(L, top, TM_SUB); + else + nvalue(top-2) -= nvalue(top-1); + top--; break; - - case GEOP: - luaV_comparison(LUA_T_NIL, LUA_T_NUMBER, LUA_T_NUMBER, IM_GE); + } + case OP_MULT: { + if (tonumber(top-2) || tonumber(top-1)) + call_arith(L, top, TM_MUL); + else + nvalue(top-2) *= nvalue(top-1); + top--; break; - - case ADDOP: { - TObject *l = S->top-2; - TObject *r = S->top-1; - if (tonumber(r) || tonumber(l)) - call_arith(IM_ADD); - else { - nvalue(l) += nvalue(r); - --S->top; - } + } + case OP_DIV: { + if (tonumber(top-2) || tonumber(top-1)) + call_arith(L, top, TM_DIV); + else + nvalue(top-2) /= nvalue(top-1); + top--; break; } - - case SUBOP: { - TObject *l = S->top-2; - TObject *r = S->top-1; - if (tonumber(r) || tonumber(l)) - call_arith(IM_SUB); - else { - nvalue(l) -= nvalue(r); - --S->top; - } + case OP_POW: { + if (!call_binTM(L, top, TM_POW)) + lua_error(L, "undefined operation"); + top--; break; } - - case MULTOP: { - TObject *l = S->top-2; - TObject *r = S->top-1; - if (tonumber(r) || tonumber(l)) - call_arith(IM_MUL); - else { - nvalue(l) *= nvalue(r); - --S->top; - } + case OP_CONCAT: { + int n = GETARG_U(i); + luaV_strconc(L, n, top); + top -= n-1; + L->top = top; + luaC_checkGC(L); break; } - - case DIVOP: { - TObject *l = S->top-2; - TObject *r = S->top-1; - if (tonumber(r) || tonumber(l)) - call_arith(IM_DIV); - else { - nvalue(l) /= nvalue(r); - --S->top; + case OP_MINUS: { + if (tonumber(top-1)) { + ttype(top) = LUA_TNIL; + call_arith(L, top+1, TM_UNM); } + else + nvalue(top-1) = -nvalue(top-1); break; } - - case POWOP: - call_binTM(IM_POW, "undefined operation"); + case OP_NOT: { + ttype(top-1) = + (ttype(top-1) == LUA_TNIL) ? LUA_TNUMBER : LUA_TNIL; + nvalue(top-1) = 1; break; - - case CONCOP: { - TObject *l = S->top-2; - TObject *r = S->top-1; - if (tostring(l) || tostring(r)) - call_binTM(IM_CONCAT, "unexpected type for concatenation"); - else { - tsvalue(l) = strconc(tsvalue(l), tsvalue(r)); - --S->top; - } - luaC_checkGC(); + } + case OP_JMPNE: { + top -= 2; + if (!luaO_equalObj(top, top+1)) dojump(pc, i); break; } - - case MINUSOP: - if (tonumber(S->top-1)) { - ttype(S->top) = LUA_T_NIL; - S->top++; - call_arith(IM_UNM); - } - else - nvalue(S->top-1) = - nvalue(S->top-1); + case OP_JMPEQ: { + top -= 2; + if (luaO_equalObj(top, top+1)) dojump(pc, i); break; - - case NOTOP: - ttype(S->top-1) = - (ttype(S->top-1) == LUA_T_NIL) ? LUA_T_NUMBER : LUA_T_NIL; - nvalue(S->top-1) = 1; + } + case OP_JMPLT: { + top -= 2; + if (luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i); break; - - case ONTJMPW: aux += highbyte(*pc++); - case ONTJMP: aux += *pc++; - if (ttype(S->top-1) != LUA_T_NIL) pc += aux; - else S->top--; + } + case OP_JMPLE: { /* a <= b === !(b<a) */ + top -= 2; + if (!luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i); break; - - case ONFJMPW: aux += highbyte(*pc++); - case ONFJMP: aux += *pc++; - if (ttype(S->top-1) == LUA_T_NIL) pc += aux; - else S->top--; + } + case OP_JMPGT: { /* a > b === (b<a) */ + top -= 2; + if (luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i); break; - - case JMPW: aux += highbyte(*pc++); - case JMP: aux += *pc++; - pc += aux; + } + case OP_JMPGE: { /* a >= b === !(a<b) */ + top -= 2; + if (!luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i); break; - - case IFFJMPW: aux += highbyte(*pc++); - case IFFJMP: aux += *pc++; - if (ttype(--S->top) == LUA_T_NIL) pc += aux; + } + case OP_JMPT: { + if (ttype(--top) != LUA_TNIL) dojump(pc, i); break; - - case IFTUPJMPW: aux += highbyte(*pc++); - case IFTUPJMP: aux += *pc++; - if (ttype(--S->top) != LUA_T_NIL) pc -= aux; + } + case OP_JMPF: { + if (ttype(--top) == LUA_TNIL) dojump(pc, i); break; - - case IFFUPJMPW: aux += highbyte(*pc++); - case IFFUPJMP: aux += *pc++; - if (ttype(--S->top) == LUA_T_NIL) pc -= aux; + } + case OP_JMPONT: { + if (ttype(top-1) == LUA_TNIL) top--; + else dojump(pc, i); break; - - case CLOSUREW: aux += highbyte(*pc++); - case CLOSURE: aux += *pc++; - *S->top++ = consts[aux]; - luaV_closure(*pc++); - luaC_checkGC(); + } + case OP_JMPONF: { + if (ttype(top-1) != LUA_TNIL) top--; + else dojump(pc, i); break; - - case SETLINEW: aux += highbyte(*pc++); - case SETLINE: aux += *pc++; - if ((S->stack+base-1)->ttype != LUA_T_LINE) { - /* open space for LINE value */ - luaD_openstack((S->top-S->stack)-base); - base++; - (S->stack+base-1)->ttype = LUA_T_LINE; + } + case OP_JMP: { + dojump(pc, i); + break; + } + case OP_PUSHNILJMP: { + ttype(top++) = LUA_TNIL; + pc++; + break; + } + case OP_FORPREP: { + if (tonumber(top-1)) + lua_error(L, "`for' step must be a number"); + if (tonumber(top-2)) + lua_error(L, "`for' limit must be a number"); + if (tonumber(top-3)) + lua_error(L, "`for' initial value must be a number"); + if (nvalue(top-1) > 0 ? + nvalue(top-3) > nvalue(top-2) : + nvalue(top-3) < nvalue(top-2)) { /* `empty' loop? */ + top -= 3; /* remove control variables */ + dojump(pc, i); /* jump to loop end */ } - (S->stack+base-1)->value.i = aux; - if (L->linehook) - luaD_lineHook(aux); break; - - case LONGARGW: aux += highbyte(*pc++); - case LONGARG: aux += *pc++; - aux = highbyte(highbyte(aux)); - goto switchentry; /* do not reset "aux" */ - - case CHECKSTACK: aux = *pc++; - LUA_ASSERT((S->top-S->stack)-base == aux && S->last >= S->top, - "wrong stack size"); + } + case OP_FORLOOP: { + LUA_ASSERT(ttype(top-1) == LUA_TNUMBER, "invalid step"); + LUA_ASSERT(ttype(top-2) == LUA_TNUMBER, "invalid limit"); + if (ttype(top-3) != LUA_TNUMBER) + lua_error(L, "`for' index must be a number"); + nvalue(top-3) += nvalue(top-1); /* increment index */ + if (nvalue(top-1) > 0 ? + nvalue(top-3) > nvalue(top-2) : + nvalue(top-3) < nvalue(top-2)) + top -= 3; /* end loop: remove control variables */ + else + dojump(pc, i); /* repeat loop */ break; - + } + case OP_LFORPREP: { + Node *node; + if (ttype(top-1) != LUA_TTABLE) + lua_error(L, "`for' table must be a table"); + node = luaH_next(L, hvalue(top-1), &luaO_nilobject); + if (node == NULL) { /* `empty' loop? */ + top--; /* remove table */ + dojump(pc, i); /* jump to loop end */ + } + else { + top += 2; /* index,value */ + *(top-2) = *key(node); + *(top-1) = *val(node); + } + break; + } + case OP_LFORLOOP: { + Node *node; + LUA_ASSERT(ttype(top-3) == LUA_TTABLE, "invalid table"); + node = luaH_next(L, hvalue(top-3), top-2); + if (node == NULL) /* end loop? */ + top -= 3; /* remove table, key, and value */ + else { + *(top-2) = *key(node); + *(top-1) = *val(node); + dojump(pc, i); /* repeat loop */ + } + break; + } + case OP_CLOSURE: { + L->top = top; + luaV_Lclosure(L, tf->kproto[GETARG_A(i)], GETARG_B(i)); + top = L->top; + luaC_checkGC(L); + break; + } } - } ret: - if (L->callhook) - luaD_callHook(0, NULL, 1); - return base; + } } - @@ -1,5 +1,5 @@ /* -** $Id: lvm.h,v 1.8 1999/02/08 17:07:59 roberto Exp $ +** $Id: lvm.h,v 1.27 2000/10/05 12:14:08 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -13,22 +13,20 @@ #include "ltm.h" -#define tonumber(o) ((ttype(o) != LUA_T_NUMBER) && (luaV_tonumber(o) != 0)) -#define tostring(o) ((ttype(o) != LUA_T_STRING) && (luaV_tostring(o) != 0)) +#define tonumber(o) ((ttype(o) != LUA_TNUMBER) && (luaV_tonumber(o) != 0)) +#define tostring(L,o) ((ttype(o) != LUA_TSTRING) && (luaV_tostring(L, o) != 0)) -void luaV_pack (StkId firstel, int nvararg, TObject *tab); int luaV_tonumber (TObject *obj); -int luaV_tostring (TObject *obj); -void luaV_setn (Hash *t, int val); -void luaV_gettable (void); -void luaV_settable (TObject *t); -void luaV_rawsettable (TObject *t); -void luaV_getglobal (TaggedString *ts); -void luaV_setglobal (TaggedString *ts); -StkId luaV_execute (Closure *cl, TProtoFunc *tf, StkId base); -void luaV_closure (int nelems); -void luaV_comparison (lua_Type ttype_less, lua_Type ttype_equal, - lua_Type ttype_great, IMS op); +int luaV_tostring (lua_State *L, TObject *obj); +const TObject *luaV_gettable (lua_State *L, StkId t); +void luaV_settable (lua_State *L, StkId t, StkId key); +const TObject *luaV_getglobal (lua_State *L, TString *s); +void luaV_setglobal (lua_State *L, TString *s); +StkId luaV_execute (lua_State *L, const Closure *cl, StkId base); +void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems); +void luaV_Lclosure (lua_State *L, Proto *l, int nelems); +int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top); +void luaV_strconc (lua_State *L, int total, StkId top); #endif @@ -1,5 +1,5 @@ /* -** $Id: lzio.c,v 1.7 1999/03/05 13:15:50 roberto Exp $ +** $Id: lzio.c,v 1.13 2000/06/12 13:52:05 roberto Exp $ ** a generic input stream interface ** See Copyright Notice in lua.h */ @@ -9,6 +9,8 @@ #include <stdio.h> #include <string.h> +#include "lua.h" + #include "lzio.h" @@ -16,62 +18,60 @@ /* ----------------------------------------------------- memory buffers --- */ static int zmfilbuf (ZIO* z) { - return EOZ; + (void)z; /* to avoid warnings */ + return EOZ; } -ZIO* zmopen (ZIO* z, char* b, int size, char *name) -{ - if (b==NULL) return NULL; - z->n=size; - z->p= (unsigned char *)b; - z->filbuf=zmfilbuf; - z->u=NULL; - z->name=name; - return z; +ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name) { + if (b==NULL) return NULL; + z->n = size; + z->p = (const unsigned char *)b; + z->filbuf = zmfilbuf; + z->u = NULL; + z->name = name; + return z; } /* ------------------------------------------------------------ strings --- */ -ZIO* zsopen (ZIO* z, char* s, char *name) -{ - if (s==NULL) return NULL; - return zmopen(z,s,strlen(s),name); +ZIO* zsopen (ZIO* z, const char* s, const char *name) { + if (s==NULL) return NULL; + return zmopen(z, s, strlen(s), name); } /* -------------------------------------------------------------- FILEs --- */ static int zffilbuf (ZIO* z) { - int n; - if (feof((FILE *)z->u)) return EOZ; - n=fread(z->buffer,1,ZBSIZE,z->u); - if (n==0) return EOZ; - z->n=n-1; - z->p=z->buffer; - return *(z->p++); + size_t n; + if (feof((FILE *)z->u)) return EOZ; + n = fread(z->buffer, 1, ZBSIZE, (FILE *)z->u); + if (n==0) return EOZ; + z->n = n-1; + z->p = z->buffer; + return *(z->p++); } -ZIO* zFopen (ZIO* z, FILE* f, char *name) -{ - if (f==NULL) return NULL; - z->n=0; - z->p=z->buffer; - z->filbuf=zffilbuf; - z->u=f; - z->name=name; - return z; +ZIO* zFopen (ZIO* z, FILE* f, const char *name) { + if (f==NULL) return NULL; + z->n = 0; + z->p = z->buffer; + z->filbuf = zffilbuf; + z->u = f; + z->name = name; + return z; } /* --------------------------------------------------------------- read --- */ -int zread (ZIO *z, void *b, int n) { +size_t zread (ZIO *z, void *b, size_t n) { while (n) { - int m; + size_t m; if (z->n == 0) { if (z->filbuf(z) == EOZ) return n; /* return number of missing bytes */ - zungetc(z); /* put result from 'filbuf' in the buffer */ + zungetc(z); /* put result from `filbuf' in the buffer */ } m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ memcpy(b, z->p, m); @@ -1,5 +1,5 @@ /* -** $Id: lzio.h,v 1.4 1998/01/09 14:57:43 roberto Exp $ +** $Id: lzio.h,v 1.7 2000/10/20 16:36:32 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ @@ -22,28 +22,31 @@ typedef struct zio ZIO; -ZIO* zFopen (ZIO* z, FILE* f, char *name); /* open FILEs */ -ZIO* zsopen (ZIO* z, char* s, char *name); /* string */ -ZIO* zmopen (ZIO* z, char* b, int size, char *name); /* memory */ +ZIO* zFopen (ZIO* z, FILE* f, const char *name); /* open FILEs */ +ZIO* zsopen (ZIO* z, const char* s, const char *name); /* string */ +ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name); /* memory */ -int zread (ZIO* z, void* b, int n); /* read next n bytes */ +size_t zread (ZIO* z, void* b, size_t n); /* read next n bytes */ -#define zgetc(z) (--(z)->n>=0 ? ((int)*(z)->p++): (z)->filbuf(z)) +#define zgetc(z) (((z)->n--)>0 ? ((int)*(z)->p++): (z)->filbuf(z)) #define zungetc(z) (++(z)->n,--(z)->p) #define zname(z) ((z)->name) + /* --------- Private Part ------------------ */ +#ifndef ZBSIZE #define ZBSIZE 256 /* buffer size */ +#endif struct zio { - int n; /* bytes still unread */ - unsigned char* p; /* current position in buffer */ - int (*filbuf)(ZIO* z); - void* u; /* additional data */ - char *name; - unsigned char buffer[ZBSIZE]; /* buffer */ + size_t n; /* bytes still unread */ + const unsigned char* p; /* current position in buffer */ + int (*filbuf)(ZIO* z); + void* u; /* additional data */ + const char *name; + unsigned char buffer[ZBSIZE]; /* buffer */ }; |