Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
llua/llua.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
748 lines (689 sloc)
19.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*** | |
| llua provides a higher-level way to integrate Lua into C projects, | |
| by defining reference objects and providing operations like calling, | |
| accessing, etc on them. Most explicit manipulation of the Lua | |
| stack becomes unnecessary. | |
| @license BSD | |
| @copyright Steve Donovan,2014 | |
| */ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include "llua.h" | |
| // Lua 5.1 compatibility | |
| #if LUA_VERSION_NUM == 501 | |
| #define LUA_OK 0 | |
| #define lua_rawlen lua_objlen | |
| #endif | |
| static FILE *s_verbose = false; | |
| /// raise an error when the argument is an error. | |
| // @function llua_assert | |
| void *_llua_assert(lua_State *L, const char *file, int line, void *val) { | |
| if (! value_is_error(val)) | |
| return val; | |
| else | |
| return luaL_error(L,"%s:%d: %s",file,line,value_as_string(val)); | |
| } | |
| /// report whenever a Lua reference is freed | |
| void llua_verbose(FILE *f) { | |
| s_verbose = f; | |
| } | |
| void llua_set_error(llua_t *o, bool yesno) { | |
| o->error = yesno; | |
| } | |
| const char *llua_error(llua_t *o, const char *msg) { | |
| if (! (o && o->error && msg && value_is_error(msg))) | |
| return msg; | |
| return luaL_error(o->L,msg); | |
| } | |
| /// is this a Lua reference? | |
| // @within Properties | |
| bool llua_is_lua_object(llua_t *o) { | |
| return obj_is_instance(o,"llua_t"); | |
| } | |
| static void llua_Dispose(llua_t *o) { | |
| if (s_verbose) { | |
| fprintf(s_verbose,"free L %p ref %d type %s\n",o->L,o->ref,llua_typename(o)); | |
| } | |
| luaL_unref(o->L,LUA_REGISTRYINDEX,o->ref); | |
| } | |
| /// new Lua reference to value on stack. | |
| // @within Creating | |
| llua_t *llua_new(lua_State *L, int idx) { | |
| llua_t *res = obj_new(llua_t,llua_Dispose); | |
| res->L = L; | |
| lua_pushvalue(L,idx); | |
| res->ref = luaL_ref(L,LUA_REGISTRYINDEX); | |
| res->type = lua_type(L,idx); | |
| res->error = false; | |
| return res; | |
| } | |
| /// get the global state as a reference. | |
| // @within Creating | |
| llua_t* llua_global(lua_State *L) { | |
| llua_t *res; | |
| lua_getglobal(L,"_G"); | |
| res = llua_new(L,-1); | |
| lua_pop(L,1); | |
| return res; | |
| } | |
| /// get the metatable of `o` | |
| // @within Properties | |
| llua_t *llua_getmetatable(llua_t *o) { | |
| lua_State *L = llua_push(o); | |
| if (! lua_getmetatable(L,-1)) { | |
| return NULL; | |
| } else { | |
| llua_t *res = llua_new(L,-1); | |
| lua_pop(L,1); | |
| return res; | |
| } | |
| } | |
| /// set the metatable of `o` | |
| void llua_setmetatable(llua_t *o, llua_t *mt) { | |
| lua_State *L = llua_push(o); | |
| if (mt) | |
| llua_push(mt); | |
| else | |
| lua_pushnil(L); | |
| lua_setmetatable(L,-2); | |
| lua_pop(L,1); | |
| } | |
| // Lua strings may have embedded nuls, so don't | |
| // depend on lua_tostring! | |
| static char *string_copy(lua_State *L, int idx) { | |
| size_t sz; | |
| const char *s = lua_tolstring(L,idx,&sz); | |
| char *res = str_new_size(sz); | |
| memcpy(res,s,sz); | |
| return res; | |
| } | |
| /// value on stack as a llib object, or Lua reference. | |
| // Result can be NULL, a string, a llib boxed value, | |
| // or a `llua_t` reference. | |
| // @within Converting | |
| void *llua_to_obj(lua_State *L, int idx) { | |
| switch(lua_type(L,idx)) { | |
| case LUA_TNIL: return NULL; | |
| case LUA_TNUMBER: return value_float(lua_tonumber(L,idx)); | |
| case LUA_TBOOLEAN: return value_bool(lua_toboolean(L,idx)); | |
| case LUA_TSTRING: return string_copy(L,idx); | |
| case LUA_TLIGHTUSERDATA: return lua_topointer(L,idx); | |
| default: | |
| return llua_new(L,idx); | |
| //LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA. | |
| } | |
| } | |
| /// convenient way to call `llua_to_obj` which pops the stack. | |
| // @within Converting | |
| void *llua_to_obj_pop(lua_State *L, int idx) { | |
| void *res = llua_to_obj(L,idx); | |
| lua_pop(L,1); | |
| return res; | |
| } | |
| /// type name of the reference. | |
| // @within Properties | |
| const char *llua_typename(llua_t *o) { | |
| return lua_typename(o->L,o->type); | |
| } | |
| /// a reference to a new Lua table. | |
| // @within Creating | |
| llua_t *llua_newtable(lua_State *L) { | |
| llua_t *ref; | |
| lua_newtable(L); | |
| ref = llua_new(L,-1); | |
| lua_pop(L,1); | |
| return ref; | |
| } | |
| /// a reference to a C function. | |
| // @within Creating | |
| llua_t *llua_cfunction(lua_State *L, lua_CFunction f) { | |
| llua_t *ref; | |
| lua_pushcfunction(L,f); | |
| ref = llua_new(L,-1); | |
| lua_pop(L,1); | |
| return ref; | |
| } | |
| static err_t l_error(lua_State *L) { | |
| const char *errstr = value_error(lua_tostring(L,-1)); | |
| lua_pop(L,1); | |
| return errstr; | |
| } | |
| /// load a code string and return the compiled chunk as a reference. | |
| // @within LoadingAndEvaluating | |
| llua_t *llua_load(lua_State *L, const char *code, const char *name) { | |
| int res = luaL_loadbuffer(L,code,strlen(code),name); | |
| if (res != LUA_OK) { | |
| return (llua_t*)l_error(L); | |
| } | |
| return llua_to_obj_pop(L,-1); | |
| } | |
| /// load a file and return the compiled chunk as a reference. | |
| // @within LoadingAndEvaluating | |
| llua_t *llua_loadfile(lua_State *L, const char *filename) { | |
| int res = luaL_loadfile(L,filename); | |
| if (res != LUA_OK) { | |
| return (llua_t*)l_error(L); | |
| } | |
| return llua_to_obj_pop(L,-1); | |
| } | |
| /// push the reference on the stack. | |
| lua_State *llua_push(llua_t *o) { | |
| lua_rawgeti(o->L,LUA_REGISTRYINDEX,o->ref); | |
| return o->L; | |
| } | |
| lua_State *_llua_push_nil(llua_t *o) { | |
| lua_State *L = llua_push(o); | |
| lua_pushnil(L); | |
| return L; | |
| } | |
| /// length of Lua reference, if a table or userdata. | |
| // Note that we are specifically not using `lua_rawlen` here! | |
| // @within Properties | |
| int llua_len(llua_t *o) { | |
| llua_push(o); | |
| #if LUA_VERSION_NUM == 501 | |
| int n = lua_objlen(o->L,-1); | |
| #else | |
| int n = luaL_len(o->L,-1); | |
| #endif | |
| lua_pop(o->L,1); | |
| return n; | |
| } | |
| /// Lua table as an array of doubles. | |
| // @within Converting | |
| double *llua_tonumarray(lua_State* L, int idx) { | |
| int i,n = lua_rawlen(L,idx); | |
| double *res = array_new(double,n); | |
| for (i = 0; i < n; i++) { | |
| lua_rawgeti(L,idx,i+1); | |
| res[i] = lua_tonumber(L,-1); | |
| lua_pop(L,1); | |
| } | |
| return res; | |
| } | |
| /// Lua table as an array of ints. | |
| // @within Converting | |
| int *llua_tointarray(lua_State* L, int idx) { | |
| int i,n = lua_rawlen(L,idx); | |
| int *res = array_new(int,n); | |
| for (i = 0; i < n; i++) { | |
| lua_rawgeti(L,idx,i+1); | |
| res[i] = lua_tointeger(L,-1); | |
| lua_pop(L,1); | |
| } | |
| return res; | |
| } | |
| /// Lua table as an array of strings. | |
| // @within Converting | |
| char** llua_tostrarray(lua_State* L, int idx) { | |
| int i,n = lua_rawlen(L,idx); | |
| char** res = array_new_ref(char*,n); | |
| for (i = 0; i < n; i++) { | |
| lua_rawgeti(L,idx,i+1); | |
| res[i] = string_copy(L,-1); | |
| lua_pop(L,1); | |
| } | |
| return res; | |
| } | |
| static int is_indexable(lua_State *L, int idx) { | |
| return lua_istable(L,-1) || lua_isuserdata(L,-1); | |
| } | |
| /// Read a value on the stack into a variable. | |
| // `kind` is a _type specifier_ | |
| // | |
| // * 'i' integer | |
| // * 'b' boolean | |
| // * 'f' double | |
| // * 's' string | |
| // * 'o' object (as in `llua_to_obj`) | |
| // * 'L' llua reference | |
| // * 'I' array of integers | |
| // * 'F' array of doubles | |
| // * 'S' array of strings | |
| // | |
| // @within Converting | |
| err_t llua_convert(lua_State *L, char kind, void *P, int idx) { | |
| err_t err = NULL; | |
| switch(kind) { | |
| case 'i': // this is a tolerant operation; returns 0 if wrong type | |
| *((int*)P) = lua_tointeger(L,idx); | |
| break; | |
| case 'f': | |
| if (! lua_isnumber(L,idx)) | |
| err = "not a number!"; | |
| else | |
| *((double*)P) = lua_tonumber(L,idx); | |
| break; | |
| case 's': | |
| if (! lua_isstring(L,idx)) | |
| err = "not a string!"; | |
| else | |
| *((char**)P) = string_copy(L,idx); | |
| break; | |
| case 'o': | |
| *((llua_t**)P) = llua_to_obj(L,idx); | |
| break; | |
| case 'L': | |
| *((llua_t**)P) = llua_new(L,idx); | |
| break; | |
| case 'F': | |
| if (! is_indexable(L,idx)) | |
| err = "not indexable!*"; | |
| else | |
| *((double**)P) = llua_tonumarray(L,idx); | |
| break; | |
| case 'I': | |
| if (! is_indexable(L,idx)) | |
| err = "not indexable!"; | |
| else | |
| *((int**)P) = llua_tointarray(L,idx); | |
| break; | |
| case 'S': | |
| if (! is_indexable(L,idx)) | |
| err = "not indexable!"; | |
| else | |
| *((char***)P) = llua_tostrarray(L,idx); | |
| break; | |
| default: | |
| break; | |
| } | |
| if (err) { | |
| if (lua_isnil(L,idx)) | |
| err = "was nil"; | |
| return value_error(err); | |
| } else { | |
| return NULL; | |
| } | |
| } | |
| static err_t push_value(lua_State *L, char kind, void *data) { | |
| switch(kind) { | |
| case 's': | |
| lua_pushstring(L, (const char*)data); | |
| break; | |
| case 'o': | |
| llua_push((llua_t*)data); | |
| break; | |
| case 'x': // usually possible to do this cast | |
| lua_pushcfunction(L,(lua_CFunction)data); | |
| break; | |
| case 'p': | |
| lua_pushlightuserdata(L,data); | |
| break; | |
| default: | |
| return value_error("unknown type"); | |
| } | |
| return NULL; | |
| } | |
| /// call the reference, passing a number of arguments. | |
| // These are specified by a set of _type specifiers_ `fmt`. Apart | |
| // from the usual ones, we have 'm' (which must be first) which | |
| // means "call the method by name" where `o` must be an object, | |
| // 'v' which means "push the value at the index", and 'x' which | |
| // means "C function". | |
| // | |
| // May optionally capture multiple return values with `llua_convert`. | |
| // There are some common cases that have symbolic names: | |
| // | |
| // * `L_NONE` call doesn't return anything | |
| // * `L_VAL` we return a single value | |
| // * `L_REF` we always return result as a llua reference | |
| // * `L_ERR` Lua error convention, either <value> or <nil> <error-string> | |
| // | |
| // @within Calling | |
| // @usage llua_callf(strfind,"ss",str,";$","i",&i); | |
| // @usage llua_callf(open,"s","test.txt",L_ERR) | |
| // @usage llua_callf(file,"ms","write","hello there\n",L_NONE); | |
| void *llua_callf(llua_t *o, const char *fmt,...) { | |
| lua_State *L = o->L; | |
| int nargs = 0, nres = LUA_MULTRET, nerr; | |
| err_t res = NULL; | |
| char rtype; | |
| va_list ap; | |
| va_start(ap,fmt); | |
| llua_push(o); // push the function or object | |
| if (*fmt == 'm') { // method call! | |
| const char *name = va_arg(ap,char*); | |
| lua_getfield(L,-1,name); | |
| // method at top, then self | |
| lua_insert(L,-2); | |
| ++fmt; | |
| ++nargs; | |
| } | |
| // and push the arguments... | |
| while (*fmt) { | |
| switch(*fmt) { | |
| case 'i': | |
| lua_pushinteger(L, va_arg(ap,int)); | |
| break; | |
| case 'v': | |
| lua_pushvalue(L,va_arg(ap,int) - nargs - 1); | |
| break; | |
| case 'b': | |
| lua_pushboolean(L, va_arg(ap,int)); | |
| break; | |
| case 'f': | |
| lua_pushnumber(L, va_arg(ap,double)); | |
| break; | |
| default: | |
| res = push_value(L,*fmt, va_arg(ap,void*)); | |
| if (res) | |
| return (void*)llua_error(o,res); | |
| break; | |
| } | |
| ++fmt; | |
| ++nargs; | |
| } | |
| fmt = va_arg(ap,char*); | |
| if (fmt) { | |
| nres = strlen(fmt); | |
| rtype = *(fmt+1); | |
| if (*fmt == 'r' && rtype && rtype != 'E') // single return with explicit type | |
| nres = 1; | |
| } | |
| nerr = lua_pcall(L,nargs,nres,0); | |
| if (nerr != LUA_OK) { | |
| res = l_error(L); | |
| } | |
| if (nres == LUA_MULTRET || res != NULL) { // leave results on stack, or error! | |
| return (void*)llua_error(o,res); | |
| } else | |
| if (*fmt == 'r') { // return one value as object... | |
| if (rtype == 'E') { | |
| // Lua error return convention is object | |
| // or nil,error-string | |
| void *val = llua_to_obj(L,-2); | |
| void *err = llua_to_obj(L,-1); | |
| if (! val) | |
| val = value_error(err); | |
| lua_pop(L,2); | |
| return val; | |
| } else | |
| if (rtype) { // force the type! | |
| void *value; | |
| res = llua_convert(L,rtype,&value,-1); | |
| lua_pop(L,1); | |
| if (res) // failed... | |
| return (void*)llua_error(o,res); | |
| else | |
| return value; | |
| } else { | |
| return llua_to_obj_pop(L,-1); | |
| } | |
| } else | |
| if (nres != LUA_MULTRET) { | |
| int idx = -nres; | |
| while (*fmt) { | |
| res = llua_convert(L,*fmt,va_arg(ap,void*),idx); | |
| if (res) // conversion error! | |
| break; | |
| ++fmt; | |
| ++idx; | |
| } | |
| lua_pop(L,nres); | |
| } | |
| va_end(ap); | |
| return (void*)res; | |
| } | |
| /// call a function, raising an error. | |
| // A useful combination of `llua_callf` and `llua_assert` | |
| // @function llua_call_or_die | |
| // @within Calling | |
| /// pop some values off the Lua stack. | |
| // Uses `llua_convert` | |
| // @within Converting | |
| err_t llua_pop_vars(lua_State *L, const char *fmt,...) { | |
| err_t res = NULL; | |
| va_list ap; | |
| va_start(ap,fmt); | |
| while (*fmt) { | |
| res = llua_convert(L,*fmt,va_arg(ap,void*),-1); | |
| if (res) // conversion error! | |
| break; | |
| ++fmt; | |
| lua_pop(L,1); | |
| } | |
| va_end(ap); | |
| return res; | |
| } | |
| /// this returns the _original_ raw C string. | |
| // @within Properties | |
| const char *llua_tostring(llua_t *o) { | |
| const char *res; | |
| llua_push(o); | |
| res = lua_tostring(o->L,-1); | |
| lua_pop(o->L,1); | |
| return res; | |
| } | |
| /// the Lua reference as a number. | |
| // @within Properties | |
| lua_Number llua_tonumber(llua_t *o) { | |
| lua_Number res; | |
| llua_push(o); | |
| res = lua_tonumber(o->L,-1); | |
| lua_pop(o->L,1); | |
| return res; | |
| } | |
| // can we index this object? | |
| static bool accessible(llua_t *o, int ltype, const char *metamethod) { | |
| if (o->type == ltype) { // always cool | |
| return true; | |
| } else { | |
| lua_State *L = llua_push(o); | |
| if (! luaL_getmetafield(L,-1,metamethod)) { // no metatable! | |
| lua_pop(L,1); | |
| return false; | |
| } | |
| lua_pop(L,2); // metamethod and table | |
| return true; | |
| } | |
| } | |
| /// can we get a field of this object? | |
| // @within Properties | |
| bool llua_gettable(llua_t *o) { | |
| return accessible(o,LUA_TTABLE,"__index"); | |
| } | |
| /// can we set a field of this object? | |
| // @within Properties | |
| bool llua_settable(llua_t *o) { | |
| return accessible(o,LUA_TTABLE,"__newindex"); | |
| } | |
| /// can we call this object? | |
| // @within Properties | |
| bool llua_callable(llua_t *o) { | |
| return accessible(o,LUA_TFUNCTION,"__call"); | |
| } | |
| static char *splitdot(char *key) { | |
| char *p = strchr(key,'.'); | |
| if (p) { // sub.key | |
| *p = '\0'; | |
| return p+1; | |
| } else | |
| return NULL; | |
| } | |
| #define MAX_KEY 256 | |
| // assume the table is initially on top of the stack. | |
| // leaves final value on top | |
| static void safe_gets(lua_State *L, const char *key) { | |
| char ckey[MAX_KEY], *subkey; | |
| strcpy(ckey,key); | |
| subkey = splitdot(ckey); | |
| lua_getfield(L,-1,ckey); | |
| if (subkey) { | |
| if (! lua_isnil(L,-1)) { | |
| lua_getfield(L,-1,subkey); | |
| lua_remove(L,-2); | |
| } | |
| } | |
| } | |
| /// index the reference with a string key, returning an object. | |
| // 'object' defined as with `llua_to_obj` | |
| // @within GettingAndSetting | |
| void *llua_gets(llua_t *o, const char *key) { | |
| lua_State *L = llua_push(o); | |
| safe_gets(L,key); | |
| lua_remove(L,-2); | |
| return llua_to_obj_pop(L,-1); | |
| } | |
| /// index the reference with multiple string keys and type-specifiers. | |
| // Type specifiers are as with `llua_convert` | |
| // @within GettingAndSetting | |
| // @usage llua_gets_v(T,"key1","s",&str,NULL); | |
| err_t llua_gets_v(llua_t *o, const char *key,...) { | |
| lua_State *L = llua_push(o); | |
| const char *fmt; | |
| void *P; | |
| bool convert; | |
| err_t err = NULL; | |
| va_list ap; | |
| va_start(ap,key); | |
| while (key) { // key followed by type specifier and pointer-to-data | |
| fmt = va_arg(ap,const char*); | |
| P = va_arg(ap,void*); | |
| convert = true; | |
| safe_gets(L,key); | |
| if (*fmt == '?') { | |
| ++fmt; | |
| if (lua_isnil(L,-1)) // fine! leave value alone! | |
| convert = false; | |
| } | |
| if (convert) | |
| err = llua_convert(L,*fmt,P,-1); | |
| lua_pop(L,1); | |
| if (err) { | |
| char buff[256]; | |
| snprintf(buff,sizeof(buff),"field '%s': %s",key,err); | |
| unref(err); | |
| err = value_error(buff); | |
| break; | |
| } | |
| key = va_arg(ap,const char*); | |
| } | |
| va_end(ap); | |
| lua_pop(L,1); // the reference | |
| return llua_error(o,err); | |
| } | |
| /// index the reference with an integer key. | |
| // @within GettingAndSetting | |
| void *llua_geti(llua_t *o, int key) { | |
| lua_State *L = llua_push(o); | |
| lua_pushinteger(L,key); | |
| lua_gettable(L,-2); | |
| lua_remove(L,-2); // the reference | |
| return llua_to_obj_pop(L,-1); | |
| } | |
| /// raw indexing with an integer key. | |
| // @within GettingAndSetting | |
| void *llua_rawgeti(llua_t* o, int key) { | |
| lua_State *L = llua_push(o); | |
| lua_rawgeti(L,-1,key); | |
| lua_remove(L,-2); // the reference | |
| return llua_to_obj_pop(L,-1); | |
| } | |
| /// push an llib object. | |
| // equivalent to `llua_push` if it's a llua ref, otherwise | |
| // uses llib type. If there's no type it assumes a plain | |
| // string literal. | |
| void llua_push_object(lua_State *L, void *value) { | |
| if (llua_is_lua_object((llua_t*)value)) { | |
| llua_push((llua_t*)value); | |
| } else | |
| if (value_is_float(value)) { | |
| lua_pushnumber(L,value_as_float(value)); | |
| } else | |
| if (value_is_string(value)) { | |
| lua_pushstring(L,(const char*)value); | |
| } else | |
| if (value_is_int(value)) { | |
| lua_pushnumber(L,value_as_int(value)); | |
| } else | |
| if (value_is_bool(value)) { | |
| lua_pushboolean(L,value_as_bool(value)); | |
| } else { // _probably_ a string. You have been warned... | |
| lua_pushstring(L,(const char*)value); | |
| } | |
| } | |
| /// set value using integer key. | |
| // uses `llua_push_object` | |
| // @within GettingAndSetting | |
| void llua_seti(llua_t *o, int key, void *value) { | |
| lua_State *L = llua_push(o); | |
| lua_pushinteger(L,key); | |
| llua_push_object(L,value); | |
| lua_settable(L,-3); | |
| } | |
| /// set value using integer key. | |
| // uses `llua_push_object` | |
| // @within GettingAndSetting | |
| void llua_sets(llua_t *o, const char *key, void *value) { | |
| lua_State *L = llua_push(o); | |
| llua_push_object(L,value); | |
| lua_setfield(o->L,-2,key); | |
| } | |
| // there's some code duplication here with llua_callf, but I'm not | |
| // 100% sure how far to push <stdarg.h>! | |
| /// set multiple keys and values on the reference. | |
| // uses type specifiers like `llua_callf` | |
| // @within GettingAndSetting | |
| err_t llua_sets_v(llua_t *o, const char *key,...) { | |
| lua_State *L = llua_push(o); | |
| const char *fmt; | |
| err_t err = NULL; | |
| va_list ap; | |
| va_start(ap,key); | |
| while (key) { // key followed by type specifier and pointer-to-data | |
| fmt = va_arg(ap,const char*); | |
| switch(*fmt) { | |
| case 'i': | |
| lua_pushinteger(L, va_arg(ap,int)); | |
| break; | |
| case 'b': | |
| lua_pushboolean(L, va_arg(ap,int)); | |
| break; | |
| case 'f': | |
| lua_pushnumber(L, va_arg(ap,double)); | |
| break; | |
| default: | |
| err = push_value(L,*fmt,va_arg(ap,void*)); | |
| break; | |
| } | |
| lua_setfield(o->L,-2,key); | |
| key = va_arg(ap,const char*); | |
| } | |
| va_end(ap); | |
| lua_pop(L,1); // the reference | |
| return llua_error(o,err); | |
| } | |
| /// load and evaluate an expression. | |
| // `fret` is a type specifier for the result, like `llua_callf`. | |
| // @within LoadingAndEvaluating | |
| void *llua_eval(lua_State *L, const char *expr, const char *fret) { | |
| llua_t *chunk = llua_load(L,expr,"tmp"); | |
| if (value_is_error(chunk)) // compile failed... | |
| return chunk; | |
| void *res = llua_callf(chunk,"",fret); | |
| obj_unref(chunk); // free the chunk reference... | |
| return res; | |
| } | |
| /// load and evaluate a file in an environment | |
| // `env` may be NULL. | |
| // `fret` is a type specifier for the result, like `llua_callf`. | |
| // @within LoadingAndEvaluating | |
| void *llua_evalfile(lua_State *L, const char *file, const char *fret, llua_t *env) { | |
| llua_t *chunk = llua_loadfile(L,file); | |
| if (value_is_error(chunk)) // compile failed... | |
| return llua_error(env,(err_t)chunk); | |
| if (env) { | |
| llua_push(chunk); | |
| llua_push(env); | |
| #if LUA_VERSION_NUM == 501 | |
| lua_setfenv(L,-2); | |
| #else | |
| // _ENV is first upvalue of main chunks | |
| lua_setupvalue(L,-2,1); | |
| #endif | |
| lua_pop(L,1); | |
| } | |
| void *res = llua_callf(chunk,"",fret); | |
| obj_unref(chunk); | |
| return (void*) llua_error(env,res); | |
| } | |