├── LICENSE.txt ├── config.ld ├── config.lua ├── config.txt ├── errors.c ├── file-size.c ├── lakefile ├── llib ├── obj.c ├── obj.h ├── pool.c ├── value.c └── value.h ├── llua.c ├── llua.h ├── makefile ├── read-config-err.c ├── read-config.c ├── readme.md ├── strfind.c ├── test-llua.c ├── tests-method.c └── tests.c /LICENSE.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | Copyright (c) 2014 Steve Donovan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. The name of the author may not be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /config.ld: -------------------------------------------------------------------------------- 1 | project='llua' 2 | file='llua.c' 3 | parse_extra={C=true} 4 | readme='readme.md' 5 | examples={'test-llua.c','strfind.c','file-size.c','errors.c'} 6 | format='discount' 7 | new_type("macro","Macros") 8 | 9 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | alpha = 1 2 | beta = 2 3 | gammon = 'pork' 4 | address = '8.8.8.8' 5 | ports = {2212,2213} 6 | author = { name = "Joe Blog", email = "joe@example.com" } 7 | 8 | 9 | -------------------------------------------------------------------------------- /config.txt: -------------------------------------------------------------------------------- 1 | alpha = 1 2 | beta = 2 3 | address = '8.8.8.8' 4 | ports = {2212,2213} 5 | A.B = 42 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /errors.c: -------------------------------------------------------------------------------- 1 | /* Even unsafe operations (like indexing nil) 2 | can be caught by a protected call to your function. 3 | */ 4 | #include 5 | #include 6 | 7 | typedef llua_t *llua; 8 | 9 | int l_myfun(lua_State *L) { 10 | int ival; 11 | const char *sval; 12 | // nil -> integer is tolerant 13 | lua_pushnil(L); 14 | ival = lua_tointeger(L,-1); 15 | printf("ival %d\n",ival); 16 | 17 | llua r = llua_new(L,-1); 18 | // but trying to index an arbitrary value is a problem! 19 | // this makes us fail with "failed attempt to index a nil value" 20 | // sval = llua_gets(r,"woop"); 21 | 22 | // make 'calling a nil value' a fatal error 23 | void *val = llua_call_or_die(r,L_NONE,L_VAL); 24 | 25 | // alternatively, can make the reference raise errors 26 | // (you will not get a nice file:line in the message however) 27 | //llua_set_error(r,true); 28 | //void *val = llua_callf(r,L_NONE,L_VAL); 29 | 30 | return 0; 31 | } 32 | 33 | int main (int argc, char **argv) 34 | { 35 | lua_State *L = luaL_newstate(); 36 | luaL_openlibs(L); 37 | 38 | // doing a protected call of our function! 39 | llua myfun = llua_cfunction(L,l_myfun); 40 | err_t res = llua_callf(myfun,L_NONE,L_VAL); 41 | if (res) { 42 | fprintf(stderr,"failed %s\n",res); 43 | } 44 | //~ --> failed errors.c:23: attempt to call a nil value 45 | lua_close(L); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /file-size.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef llua_t *llua; 5 | 6 | int main (int argc, char **argv) 7 | { 8 | const char *file = argv[1] ? argv[1] : argv[0]; 9 | lua_State *L = luaL_newstate(); 10 | luaL_openlibs(L); 11 | void *P = obj_pool(); 12 | 13 | llua_verbose(stderr); // tell us of unrefs... 14 | 15 | llua _G = llua_global(L); 16 | #define G(name) llua_gets(_G,name) 17 | 18 | // f = io.open(file,'rb'); text = in:read('*a'); f:close() 19 | // Lua error convention: value or nil, errstr 20 | llua in = llua_callf(G("io.open"),"ss",file,"rb",L_ERR); 21 | if (value_is_error(in)) { 22 | fprintf(stderr,"error: %s\n",in); 23 | return 1; 24 | } 25 | llua text = llua_callf(in,"ms","read","*a",L_REF); 26 | llua_callf(in,"m","close",""); 27 | 28 | // text here is a reference to a Lua string, 29 | // can use llua_tostring() to get pointer. 30 | // Note this works with an arbitrary binary file! 31 | printf("size of %s was %d\n",file,llua_len(text)); 32 | 33 | // first ten words in the text 34 | llua iter = llua_callf(G("string.gmatch"),"os",text,"%a%a%a+",L_VAL); 35 | FOR(i,10) { 36 | char* res = llua_callf(iter,L_NONE,L_VAL); 37 | printf("string %s\n",res); 38 | } 39 | 40 | 41 | unref(P); // all refs are freed... 42 | //~ size of c:\Users\steve\dev\llua\file-size.exe was 45056 43 | //~ string This 44 | //~ string program 45 | //~ string cannot 46 | //~ string run 47 | //~ string DOS 48 | //~ string mode 49 | //~ string text 50 | //~ string data 51 | //~ string rdata 52 | //~ string bss 53 | //~ free L 0000000000306B80 ref 3 type table 54 | //~ free L 0000000000306B80 ref 4 type function 55 | //~ free L 0000000000306B80 ref 5 type userdata 56 | //~ free L 0000000000306B80 ref 6 type string 57 | //~ free L 0000000000306B80 ref 7 type function 58 | //~ free L 0000000000306B80 ref 8 type function 59 | lua_close(L); 60 | } 61 | -------------------------------------------------------------------------------- /lakefile: -------------------------------------------------------------------------------- 1 | if WINDOWS then 2 | LINC='/me/dev/luabuild/lua-5.2.2' 3 | LIB='/me/dev/luabuild/bin/lua52.dll' 4 | incdirs = {LINC,"."} 5 | needs = nil 6 | else 7 | incdirs = "." 8 | needs = 'lua' 9 | LIB='-llua5.1' 10 | end 11 | 12 | llua = c99.library{'llua',src='test-llua llua llib/obj llib/value llib/pool',incdir=incdirs,needs=needs} 13 | 14 | ARGS= {incdir=incdirs,libflags=LIB,libdir='.',needs=needs} 15 | 16 | default { 17 | c99.program{'test-llua',llua,args=ARGS}, 18 | c99.program{'strfind',llua,args=ARGS}, 19 | c99.program{'tests',llua,args=ARGS}, 20 | c99.program{'tests-method',llua,args=ARGS}, 21 | c99.program{'file-size',llua,args=ARGS}, 22 | c99.program{'errors',llua,args=ARGS}, 23 | c99.program{'read-config',llua,args=ARGS}, 24 | c99.program{'read-config-err',llua,args=ARGS}, 25 | c99.program{'llib-llua',llua,args=ARGS}, 26 | } 27 | -------------------------------------------------------------------------------- /llib/obj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * llib little C library 3 | * BSD licence 4 | * Copyright Steve Donovan, 2013 5 | */ 6 | 7 | /*** 8 | Support for refcounted objects. 9 | 10 | These are (currently) allocated with `malloc` and freed with `free`, but in addition always 11 | carry a refcount (see `obj_refcount`). `obj_ref` increments this count, and `obj_unref` 12 | decrements this count; when the count becomes zero, the object is freed. If the object 13 | has an associated dispose function specified in `obj_new`, then this will be called first. 14 | 15 | Unless `OBJ_REF_ABBREV` is defined, these are also defined as `ref` and `unref`; there is 16 | a bulk `dispose` for multiple objects. 17 | 18 | _Arrays_ are refcounted _containers_; if created with `array_new_ref`, they will unref 19 | their elements when disposed. They carry their own size, accessible with `array_len`. 20 | 21 | _Sequences_ are a wrapper around arrays, and are resizeable. `seq_add` appends new values 22 | to a sequence. A sequence `s` has the type `T**`; it is a pointer to an array of T. The underlying 23 | array can always be accessed with `*s`. 24 | 25 | @module obj 26 | */ 27 | 28 | #include "obj.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define MAX_PTRS 10000 36 | 37 | // number of created 'live' objects -- access with obj_kount() 38 | static int kount = 0; 39 | 40 | #ifdef LLIB_PTR_LIST 41 | // Generally one can't depend on malloc or other allocators returning pointers 42 | // within a given range. So we keep an array of 'our' pointers, which we know 43 | // for a fact were allocated using `new_obj` 44 | static int max_p = 0; 45 | static void *our_ptrs[MAX_PTRS]; 46 | 47 | static int our_ptr_idx(void *p) { 48 | FOR(i,max_p) { 49 | if (our_ptrs[i] == p) 50 | return i; 51 | } 52 | return -1; 53 | } 54 | 55 | static void add_our_ptr(void *p) { 56 | // look for first empty slot, or a new one if none found. 57 | int idx = our_ptr_idx(NULL); 58 | if (idx == -1) { 59 | our_ptrs[max_p] = p; 60 | ++max_p; 61 | assert(max_p < MAX_PTRS); 62 | } else { 63 | our_ptrs[idx] = p; 64 | } 65 | ++kount; 66 | } 67 | 68 | static void remove_our_ptr(void *p) { 69 | int ptr_idx = our_ptr_idx(p); 70 | assert(ptr_idx != -1); // might not be one of ours! 71 | our_ptrs[ptr_idx] = NULL; 72 | --kount; 73 | } 74 | #define our_ptr(p) (our_ptr_idx(p) != -1) 75 | #else 76 | // if the libc supports, here is a simpler scheme which assumes that 'our' pointers are always 77 | // between the lowest and highest pointer value allocated so far. This scheme of course gives a false 78 | // positive if a pointer was just allocated with plain malloc, so be careful about using obj_unref 79 | static void *low_ptr, *high_ptr; 80 | 81 | static void add_our_ptr(void *p) { 82 | if (! low_ptr) { 83 | low_ptr = p; 84 | high_ptr = p; 85 | } else { 86 | if (p < low_ptr) 87 | low_ptr = p; 88 | else if (p > high_ptr) 89 | high_ptr = p; 90 | } 91 | ++kount; 92 | } 93 | 94 | static int our_ptr (void *p) { 95 | return p >= low_ptr && p <= high_ptr; 96 | } 97 | 98 | static void remove_our_ptr(void *p) { 99 | --kount; 100 | } 101 | #endif 102 | 103 | int obj_kount() { return kount; } 104 | 105 | static void s_free(void *p, void *obj) { 106 | free(obj); 107 | } 108 | 109 | static void *s_alloc(void *p, int size) { 110 | return malloc(size); 111 | }; 112 | 113 | ObjAllocator obj_default_allocator = { 114 | s_alloc, s_free, NULL 115 | }; 116 | 117 | static ObjHeader *new_obj(int size, ObjType *t) { 118 | size += sizeof(ObjHeader); 119 | void *obj; 120 | 121 | if (! t->alloc) { 122 | obj = malloc(size); 123 | } else { 124 | obj = t->alloc->alloc(t->alloc,size); 125 | } 126 | add_our_ptr(obj); 127 | #ifdef DEBUG 128 | ++t->instances; 129 | #endif 130 | return (ObjHeader *)obj; 131 | } 132 | 133 | /// refcount of an object. 134 | // Will return -1 if it isn't one of ours! 135 | int obj_refcount (const void *p) 136 | { 137 | if (p == NULL) return -1; 138 | ObjHeader *pr = obj_header_(p); 139 | if (our_ptr(pr)) { 140 | return pr->_ref; 141 | } else { 142 | return -1; 143 | } 144 | } 145 | 146 | #define PTR_FROM_HEADER(h) ((void*)(h+1)) 147 | #define HEADER_FROM_PTR(P) ((ObjHeader*)P-1) 148 | 149 | // Type descripters are kept in an array 150 | #define LLIB_TYPE_MAX 4096 151 | 152 | ObjType obj_types[LLIB_TYPE_MAX]; 153 | const ObjType *obj_types_ptr = obj_types; 154 | 155 | int obj_types_size = 8; 156 | 157 | typedef ObjType *OTP; 158 | 159 | OTP obj_type_(ObjHeader *h) { 160 | return &obj_types[h->type]; 161 | } 162 | 163 | OTP type_from_dtor(const char *name, DisposeFn dtor) { 164 | if (dtor) { 165 | for(OTP pt = obj_types; pt->name; ++pt) { 166 | if (pt->dtor == dtor) 167 | return pt; 168 | } 169 | } else { // no dispose fun, so let's match by name 170 | for(OTP pt = obj_types; pt->name; ++pt) { 171 | if (strcmp(pt->name,name) == 0) 172 | return pt; 173 | } 174 | } 175 | return NULL; 176 | } 177 | 178 | OTP obj_new_type(int size, const char *type, DisposeFn dtor) { 179 | OTP t = &obj_types[obj_types_size]; 180 | t->name = type; 181 | t->dtor = dtor; 182 | t->mlem = size; 183 | t->idx = obj_types_size++; 184 | // just in case... 185 | obj_types[obj_types_size].name = NULL; 186 | return t; 187 | } 188 | 189 | DisposeFn _pool_filter, _pool_cleaner; 190 | 191 | static void *pin_ (ObjHeader *h) { 192 | void *obj = PTR_FROM_HEADER(h); 193 | if (_pool_filter) 194 | _pool_filter(obj); 195 | return obj; 196 | } 197 | 198 | // the type system needs to associate certain common types with fixed slots 199 | static bool initialized = false; 200 | 201 | static ObjType obj_types_initialized[] = { 202 | {"char",NULL,NULL,1,0}, 203 | {"echar_",NULL,NULL,1,1}, 204 | {"int",NULL,NULL,sizeof(int),2}, 205 | {"long long",NULL,NULL,sizeof(long long),3}, 206 | {"double",NULL,NULL,sizeof(double),4}, 207 | {"float",NULL,NULL,sizeof(float),5}, 208 | {"bool",NULL,NULL,sizeof(bool),6}, 209 | {"MapKeyValue",NULL,NULL,sizeof(MapKeyValue),7} 210 | }; 211 | 212 | static void initialize_types() { 213 | initialized = true; 214 | memcpy(obj_types,obj_types_initialized,sizeof(obj_types_initialized)); 215 | } 216 | 217 | /// allocate a new refcounted object. 218 | // @tparam type T 219 | // @tparam DisposeFn optional destructior 220 | // @treturn T* 221 | // @function obj_new 222 | 223 | void *obj_new_(int size, const char *type, DisposeFn dtor) { 224 | if (! initialized) { 225 | initialize_types(); 226 | } 227 | OTP t = type_from_dtor(type,dtor); 228 | if (! t) 229 | t = obj_new_type(size,type,dtor); 230 | 231 | ObjHeader *h = new_obj(size,t); 232 | h->_len = 0; 233 | h->_ref = 1; 234 | h->is_array = 0; 235 | h->is_ref_container = 0; 236 | h->type = t->idx; 237 | return pin_(h); 238 | } 239 | 240 | // getting element size is now a little more indirect... 241 | 242 | /// size of this object. 243 | // For arrays, this is size of base type. 244 | int obj_elem_size(void *P) { 245 | ObjHeader *h = obj_header_(P); 246 | return obj_type_(h)->mlem; 247 | } 248 | 249 | /// whether an object matches a type by name 250 | bool obj_is_instance(const void *P, const char *name) { 251 | if (obj_refcount(P) == -1) 252 | return false; 253 | return strcmp(obj_type(P)->name,name) == 0; 254 | } 255 | 256 | // Ref counted objects are either arrays or structs. 257 | // If they are arrays of refcounted objects, then 258 | // this has to unref those objects. For structs, 259 | // there may be an explicit destructor. 260 | static void obj_free_(ObjHeader *h, const void *P) { 261 | OTP t = obj_type_(h); 262 | if (h->is_array) { // arrays may be reference containers 263 | if (h->is_ref_container) { 264 | void **arr = (void**)P; 265 | for (int i = 0, n = h->_len; i < n; i++) { 266 | obj_unref(arr[i]); 267 | } 268 | } 269 | } else { // otherwise there may be a custom dispose operation for the type 270 | if (t->dtor) 271 | t->dtor((void*)P); 272 | } 273 | 274 | remove_our_ptr(h); 275 | 276 | #ifdef DEBUG 277 | --t->instances; 278 | #endif 279 | 280 | // if the object pool is active, then remove our pointer from it! 281 | if (_pool_cleaner) 282 | _pool_cleaner((void *)P); 283 | 284 | // the object's type might have a custom allocator 285 | if (t->alloc) { 286 | t->alloc->free(t->alloc,h); 287 | } else { 288 | free(h); 289 | } 290 | } 291 | 292 | // Reference counting 293 | 294 | /// increase reference count (`ref`). 295 | // @tparam T object 296 | // @treturn T 297 | // @function obj_ref 298 | 299 | void obj_incr_(const void *P) { 300 | ObjHeader *h = obj_header_(P); 301 | ++(h->_ref); 302 | } 303 | 304 | /// decrease reference count (`unref`). 305 | // When this goes to zero, free the object and call the 306 | // dispose function, if any. 307 | void obj_unref(const void *P) { 308 | if (P == NULL) return; 309 | ObjHeader *h = obj_header_(P); 310 | #ifdef DEBUG 311 | assert(our_ptr(h)); 312 | #endif 313 | --(h->_ref); 314 | if (h->_ref == 0) 315 | obj_free_(h,P); 316 | } 317 | 318 | void obj_apply_v_varargs(void *o, PFun fn,va_list ap) { 319 | void *P; 320 | while ((P = va_arg(ap,void*)) != NULL) { 321 | if (o == NULL) 322 | fn(P); 323 | else 324 | fn(o,P); 325 | } 326 | } 327 | 328 | /// decrease ref count for multiple values (`dispose`). 329 | // @param ... objects 330 | // @function obj_unref_v 331 | 332 | /// apply a function to all arguments. 333 | // If `o` is not `NULL`, then call `fn(o,arg)`, otherwise 334 | // call `fn(arg)`. 335 | // @param o optional object 336 | // @param fn 337 | // @param ... extra arguments, ending with `NULL`. 338 | void obj_apply_varargs(void *o, PFun fn,...) { 339 | va_list ap; 340 | va_start(ap,fn); 341 | obj_apply_v_varargs(o,fn,ap); 342 | va_end(ap); 343 | } 344 | 345 | #ifdef DEBUG 346 | void obj_dump_types(bool all) { 347 | printf("+++ llib types\n"); 348 | FOR(i,obj_types_size) { 349 | ObjType *t = &obj_types[i]; 350 | if (all || t->instances > 0) { 351 | printf("%3d (%s) size %d",t->idx, t->name,t->mlem); 352 | if (t->instances > 0) 353 | printf(" --> %d alive\n",t->instances); 354 | else 355 | printf("\n"); 356 | } 357 | } 358 | printf("+++\n"); 359 | } 360 | 361 | // these non-macro versions are useful when in a debugger such as GDB 362 | int a_len(void *a) { return array_len(a); } 363 | ObjHeader* o_hdr(void *a) { return obj_header_(a); } 364 | ObjType *o_type(void *a) { return obj_type(a); } 365 | 366 | const char *obj_type_name(void *P) { 367 | static char buff[256]; 368 | char *b = buff; 369 | b += sprintf(buff,"(%s*)",obj_type(P)->name); 370 | if (obj_is_array(P)) 371 | sprintf(b,"[%d]",array_len(P)); 372 | return buff; 373 | } 374 | 375 | void obj_dump_ptr(void *P) { 376 | int c = obj_refcount(P); 377 | if (c != -1) 378 | printf("%p ref %d type %s\n",P,c,obj_type_name(P)); 379 | else 380 | printf("not one of ours\n"); 381 | } 382 | 383 | #ifdef LLIB_PTR_LIST 384 | void obj_dump_pointers() { 385 | printf("+++ llib objects\n"); 386 | FOR(i,max_p) { 387 | if (our_ptrs[i] != NULL) { 388 | void *P = (void*)((ObjHeader*)our_ptrs[i] + 1); 389 | obj_dump_ptr(P); 390 | } 391 | } 392 | printf("+++\n"); 393 | } 394 | #endif 395 | 396 | void obj_dump_all() { 397 | obj_dump_types(false); 398 | #ifdef LLIB_PTR_LIST 399 | obj_dump_pointers(); 400 | #endif 401 | } 402 | #endif 403 | 404 | typedef unsigned char byte; 405 | 406 | //? allocates len+1 - ok? 407 | 408 | void *array_new_(int mlen, const char *name, int len, int isref) { 409 | if (! initialized) { 410 | initialize_types(); 411 | } 412 | 413 | OTP t = type_from_dtor(name,NULL); 414 | if (! t) 415 | t = obj_new_type(mlen,name,NULL); 416 | 417 | ObjHeader *h = new_obj(mlen*(len+1),t); 418 | byte *P; 419 | h->type = t->idx; 420 | h->_len = len; 421 | h->_ref = 1; 422 | h->is_array = 1; 423 | h->is_ref_container = isref; 424 | P = (byte*)pin_(h); 425 | if (isref) { // ref arrays are fully zeroed out 426 | memset(P,0,mlen*(len+1)); 427 | } else { // otherwise just the last element 428 | memset(P+mlen*len,0,mlen); 429 | } 430 | return P; 431 | } 432 | 433 | /// new array from type. 434 | // @tparam T type name of type 435 | // @int sz size of array 436 | // @treturn T* 437 | // @function array_new 438 | 439 | /// new array of refcounted objects. 440 | // @tparam T type name of type 441 | // @int sz size of array 442 | // @treturn T* 443 | // @function array_new_ref 444 | 445 | /// new array from buffer. 446 | // @tparam T type name of type 447 | // @tparam T* buff 448 | // @int sz size of array 449 | // @function array_new_copy 450 | 451 | /// length of an array. 452 | // tparam type* array 453 | // @function array_len 454 | 455 | void *array_new_copy_ (int mlen, const char *name, int len, int isref, void *P) { 456 | void *arr = array_new_(mlen,name,len,isref); 457 | memcpy(arr,P,mlen*len); 458 | return arr; 459 | } 460 | 461 | /// get a slice of an array. 462 | // @param P the array 463 | // @param i1 lower bound 464 | // @param i2 upper bound (can be -1 for the rest) 465 | void * array_copy(void *P, int i1, int i2) { 466 | ObjHeader *pr = obj_header_(P); 467 | OTP t = obj_type_(pr); 468 | int mlem = t->mlem; 469 | int offs = i1*mlem; 470 | int len = i2 == -1 ? pr->_len : i2-i1; 471 | void *newp = array_new_(mlem,t->name,len,pr->is_ref_container); 472 | memcpy(newp,(char*)P+offs,mlem*len); 473 | return newp; 474 | } 475 | 476 | /// resize an array. 477 | // @param P the array 478 | // @param newsz the new size 479 | void * array_resize(void *P, int newsz) { 480 | ObjHeader *pr = obj_header_(P); 481 | OTP t = obj_type_(pr); 482 | int mlen = t->mlem; 483 | void *newp = array_new_(mlen,t->name,newsz,pr->is_ref_container); 484 | memcpy(newp,P,mlen*pr->_len); 485 | // if old ref array is going to die, make sure it doesn't dispose our elements 486 | pr->is_ref_container = 0; 487 | obj_unref(P); 488 | return newp; 489 | } 490 | 491 | // Arrays of char can be directly used as C strings, 492 | // since they always have a trailing zero byte. 493 | 494 | /// create a refcounted string copy. 495 | char *str_new(const char *s) { 496 | int sz = strlen(s); 497 | char *ns = str_new_size(sz); 498 | memcpy(ns,s,sz); 499 | return ns; 500 | } 501 | 502 | /// create a refcounted string of given size 503 | char *str_new_size(int sz) { 504 | return array_new(char,sz); 505 | } 506 | 507 | /// increase the refcount of a string. 508 | const char *str_ref(const char *s) { 509 | int rc = obj_refcount(s); 510 | if (rc == -1) 511 | return str_new(s); 512 | else 513 | return (const char*)obj_ref(s); 514 | } 515 | 516 | /// make a refcounted string from an arbitrary string. 517 | char *str_cpy(const char *s) { 518 | int rc = obj_refcount(s); 519 | if (rc == -1) { 520 | return str_new(s); 521 | } else { 522 | return (char*)s; 523 | } 524 | } 525 | 526 | /// sort an array. 527 | // @tparam T* P the array 528 | // @int kind either `ARRAY_INT` or `ARRAY_STR` 529 | // @int ofs offset into each item 530 | // @function array_sort 531 | 532 | /// sort an array of structs by integer/pointer field 533 | // @tparam T* A the array 534 | // @tparam T type the struct 535 | // @param field the name of the struct field 536 | // @function array_sort_struct_ptr 537 | 538 | /// sort an array of structs by string field 539 | // @tparam T* A the array 540 | // @tparam T type the struct 541 | // @param field the name of the struct field 542 | // @function array_sort_struct_str 543 | 544 | /// get from a source and put to a destination. 545 | // @param dest destination object 546 | // @param setter function of dest and the gotten object 547 | // @param src source object 548 | // @param getter function of `src` to call repeatedly until it returns `NULL` 549 | // @function obj_apply 550 | void obj_apply_ (void *dest, PFun setter, void *src, PFun getter) 551 | { 552 | void *p; 553 | while ((p = getter(src)) != NULL) { 554 | setter(dest,p); 555 | } 556 | } 557 | 558 | // sequences 559 | 560 | const int INITIAL_CAP = 4, GROW_CAP = 2; 561 | 562 | void seq_dispose(Seq *s) 563 | { 564 | obj_unref(s->arr); 565 | } 566 | 567 | /// create a plain sequence of a type 568 | // @param T type 569 | // @function seq_new 570 | 571 | /// create a sequence of a refcounted type 572 | // @param T type 573 | // @function seq_new_ref 574 | 575 | void *seq_new_(int nlem, const char *name, int isref) { 576 | Seq *s = obj_new(Seq,seq_dispose); 577 | s->arr = array_new_(nlem,name,INITIAL_CAP,isref); 578 | s->cap = INITIAL_CAP;// our capacity 579 | array_len(s->arr) = 0;// our size 580 | return (void*)s; 581 | } 582 | 583 | void seq_resize(Seq *s, int nsz) { 584 | // this function does not shrink! 585 | if (nsz <= s->cap) 586 | return; 587 | // best size is next power of two 588 | int result = 1; 589 | while (result < nsz) result <<= 1; 590 | nsz = result; 591 | s->arr = array_resize(s->arr, nsz); 592 | s->cap = nsz; 593 | } 594 | 595 | /// add an arbitrary value to a sequence 596 | // @param s the sequence 597 | // @param v the value 598 | // @function seq_add 599 | 600 | int seq_next_(void *sp) { 601 | Seq *s = (Seq *)sp; 602 | void *a = s->arr; 603 | int len = array_len(a); 604 | if (len == s->cap) { 605 | s->cap *= GROW_CAP; 606 | s->arr = array_resize(s->arr, s->cap); 607 | } 608 | // the idea is that the seq's array has _always_ got the correct length 609 | array_len(s->arr) = len + 1; 610 | return len; 611 | } 612 | 613 | /// add a pointer value to a sequence. 614 | void seq_add_ptr(void *sp, void *p) { 615 | int idx = seq_next_(sp); 616 | void **a = (void **)((Seq*)sp)->arr; 617 | a[idx] = p; 618 | } 619 | 620 | void seq_add_str(void *sp, const char *p) { 621 | int idx = seq_next_(sp); 622 | char **a = (char **)((Seq*)sp)->arr; 623 | a[idx] = (char*)str_cpy((char*)p); 624 | } 625 | 626 | /// get the array from a sequence. 627 | // (This will transfer ownership, shrink to fit, 628 | // and unref the sequence.) 629 | // @param sp the sequence 630 | void *seq_array_ref(void *sp) { 631 | Seq *s = (Seq *)sp; 632 | int len = array_len(s->arr); 633 | if (len < s->cap) { 634 | s->arr = array_resize(s->arr, len); 635 | } 636 | void *arr = s->arr; 637 | obj_incr_(arr); 638 | obj_unref(sp); 639 | return arr; 640 | } 641 | 642 | /// standard for-loop. 643 | // @param i loop variable. 644 | // @param n loop count. 645 | // @macro FOR 646 | 647 | /// for-loop over array. 648 | // @tparam type T base type 649 | // @tparam type* loop pointer 650 | // @tparam type* array 651 | // @macro FOR_ARR 652 | // @usage FOR_ARR(int,pi,arr) printf("%d ",*pi); 653 | -------------------------------------------------------------------------------- /llib/obj.h: -------------------------------------------------------------------------------- 1 | /* 2 | * llib little C library 3 | * BSD licence 4 | * Copyright Steve Donovan, 2013 5 | */ 6 | 7 | #ifndef _LLIB_OBJ_H 8 | #define _LLIB_OBJ_H 9 | typedef unsigned int uint32; 10 | typedef unsigned short uint16; 11 | typedef long long int64; 12 | typedef void (*DisposeFn)(void*); 13 | typedef void (*PtrFun)(void*); 14 | typedef void *(*PFun)(void *,...); 15 | typedef void (*FreeFn)(void *,void *); 16 | typedef void *(*AllocFn)(void*,int); 17 | 18 | #ifndef __cplusplus 19 | #include 20 | #endif 21 | 22 | typedef struct ObjAllocator_ { 23 | AllocFn alloc; 24 | FreeFn free; 25 | void *data; 26 | } ObjAllocator; 27 | 28 | typedef struct ObjHeader_ { 29 | unsigned int type:14; 30 | unsigned int is_array:1; 31 | unsigned int is_ref_container:1; 32 | unsigned int _ref:16; 33 | unsigned int _len:32; 34 | } ObjHeader; 35 | 36 | typedef struct ObjType_ { 37 | const char *name; 38 | DisposeFn dtor; 39 | ObjAllocator *alloc; 40 | uint16 mlem; 41 | uint16 idx; 42 | #ifdef DEBUG 43 | int instances; 44 | #endif 45 | } ObjType; 46 | 47 | enum { 48 | OBJ_CHAR_T = 0, 49 | OBJ_ECHAR_T = 1, 50 | OBJ_INT_T = 2, 51 | OBJ_LLONG_T = 3, 52 | OBJ_DOUBLE_T = 4, 53 | OBJ_FLOAT_T = 5, 54 | OBJ_BOOL_T = 6, 55 | OBJ_KEYVALUE_T = 7 56 | }; 57 | 58 | typedef struct { 59 | void *key; 60 | void *value; 61 | } MapKeyValue; 62 | 63 | #if defined(_M_X64) || defined(__amd64__) 64 | #define LLIB_64_BITS 65 | typedef long long intptr; 66 | #else 67 | typedef int intptr; 68 | #endif 69 | 70 | #define obj_header_(P) ((ObjHeader*)(P)-1) 71 | #define array_len(P) (obj_header_(P)->_len) 72 | #define obj_is_array(P) (obj_header_(P)->is_array) 73 | #define obj_ref_array(P) (obj_header_(P)->is_ref_container) 74 | #define obj_type_index(P) (obj_header_(P)->type) 75 | #define obj_type(P) obj_type_(obj_header_(P)) 76 | 77 | #define obj_new(T,dtor) (T*)obj_new_(sizeof(T),#T,(DisposeFn)dtor) 78 | #define obj_ref(P) (obj_incr_(P), P) 79 | #define obj_unref_v(...) obj_apply_varargs(NULL,(PFun)obj_unref,__VA_ARGS__,NULL) 80 | #define array_new(T,sz) (T*)array_new_(sizeof(T),#T,sz,0) 81 | #define array_new_ref(T,sz) (T*)array_new_(sizeof(T),#T,sz,1) 82 | #define array_new_copy(T,buff,sz) (T*)array_new_copy_(sizeof(T),#T,sz,0,buff) 83 | 84 | #define FOR(i,n) for (int i = 0, n_ = (n); i < n_; i++) 85 | #define FOR_PTRZ(T,var,expr) for(T *var = expr; *var; var++) 86 | #define FOR_ARR(T,p,arr) for(T *p=(arr),*e_=p+array_len(p); p!=e_; p++) 87 | 88 | #define str_sub (char*)array_copy 89 | #define str_len array_len 90 | 91 | #define obj_scoped __attribute((cleanup(__auto_unref))) 92 | 93 | #ifndef OBJ_REF_ABBREV 94 | #define ref obj_ref 95 | #define unref obj_unref 96 | #define dispose obj_unref_v 97 | #define scoped obj_scoped 98 | #endif 99 | 100 | #define obj_pool_count(P) (array_len(**((void****)P) )) 101 | 102 | #ifdef DEBUG 103 | void obj_dump_types(bool all); 104 | const char *obj_type_name(void *P); 105 | #ifdef LLIB_PTR_LIST 106 | void obj_dump_pointers(); 107 | #endif 108 | void obj_dump_all(); 109 | #endif 110 | int obj_kount(); 111 | void *obj_pool(); 112 | ObjType *obj_type_(ObjHeader *h); 113 | ObjType *obj_new_type(int size, const char *type, DisposeFn dtor); 114 | int obj_elem_size(void *P); 115 | void *obj_new_(int size, const char *type,DisposeFn dtor); 116 | bool obj_is_instance(const void *P, const char *name); 117 | void obj_incr_(const void *P); 118 | void obj_unref(const void *P); 119 | void obj_apply_varargs(void *o, PFun fn,...); 120 | void __auto_unref(void *p) ; 121 | 122 | #define obj_apply(dest,s,src,g) obj_apply_(dest,(PFun)s,src,(PFun)g) 123 | void obj_apply_ (void *dest, PFun setter, void *src, PFun getter); 124 | int obj_refcount (const void *P); 125 | 126 | void *array_new_(int mlen, const char *name, int len, int ref); 127 | void *array_new_copy_ (int mlen, const char *name, int len, int ref, void *P); 128 | void *array_copy(void *P, int i1, int i2); 129 | void *array_resize(void *P, int newsz); 130 | char *str_new(const char *s); 131 | char *str_new_size(int sz); 132 | const char *str_ref(const char *s); 133 | char *str_cpy(const char *s); 134 | 135 | typedef enum { 136 | ARRAY_INT = 0, 137 | ARRAY_STRING = 1 138 | } ElemKind; 139 | 140 | void array_sort(void *P, ElemKind kind, bool desc, int offs) ; 141 | 142 | #define OBJ_STRUCT_OFFS(T,f) ( (intptr)(&((T*)0)->f) ) 143 | 144 | #define array_sort_struct_ptr(P,desc,T,fname) array_sort(P,ARRAY_INT,desc,OBJ_STRUCT_OFFS(T,fname)) 145 | #define array_sort_struct_str(P,desc,T,fname) array_sort(P,ARRAY_STRING,desc,OBJ_STRUCT_OFFS(T,fname)) 146 | 147 | // this may be safely cast to a pointer to the array 148 | typedef struct Seq_ { 149 | void *arr; 150 | int cap; 151 | } Seq; 152 | 153 | #define seq_new(T) (T**)seq_new_(sizeof(T),#T,0) 154 | #define seq_new_ref(T) (T**)seq_new_(sizeof(T),#T,1) 155 | #define seq_new_str() (char***)seq_new_(sizeof(char*),"char",1) 156 | #define seq_add(s,v) {int idx_=seq_next_(s); (*(s)) [idx_] = (v);} 157 | #define seq_add_items(s,...) obj_apply_varargs(s,(PFun)seq_add_ptr,__VA_ARGS__,NULL) 158 | #define seq_add2(s,x1,x2) {seq_add(s,x1); seq_add(s,x2);} 159 | 160 | void *seq_new_(int nlem, const char *name, int ref); 161 | int seq_next_(void *s); 162 | void seq_add_ptr(void *sp, void *p); 163 | void seq_add_str(void *sp, const char*p); 164 | void seq_resize(Seq *s, int nsz); 165 | void seq_remove(void *sp, int pos, int len); 166 | void seq_insert(void *sp, int pos, void *src, int sz); 167 | void seq_adda(void *sp, void *buff, int sz); 168 | void *seq_array_ref(void *sp); 169 | 170 | #endif 171 | 172 | -------------------------------------------------------------------------------- /llib/pool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * llib little C library 3 | * BSD licence 4 | * Copyright Steve Donovan, 2013 5 | */ 6 | #include 7 | #include "obj.h" 8 | 9 | ////// Object Pool Support ////// 10 | // An object pool is a ref seq containing all objects generated since the pool 11 | // was created. The actual pool object is a ref to that seq, so that disposing 12 | // of it will drain the pool. 13 | // 14 | // As objects are explicitly unref'd, they're taken out of the pool by setting 15 | // their entry to NULL. So when we finally drain the pool, it only contains 16 | // genuine alive orphan objects. 17 | // 18 | // 19 | 20 | extern DisposeFn _pool_filter, _pool_cleaner; 21 | 22 | typedef void* ObjPool; 23 | 24 | typedef void*** VoidSeq; 25 | 26 | static VoidSeq _obj_pool; 27 | static VoidSeq _pool_stack; 28 | static ObjPool *_pool_marker; 29 | 30 | static void seq_stack_push(VoidSeq stack, void *value) { 31 | seq_add(_pool_stack,_obj_pool); 32 | } 33 | 34 | static void *seq_stack_pop(VoidSeq stack) { 35 | void **arr = *stack; 36 | int top = array_len(arr)-1; 37 | if (top == -1) // empty? 38 | return NULL; 39 | void *val = arr[top]; 40 | array_len(arr) = top; 41 | return val; 42 | } 43 | 44 | static void pool_add(void *P) { 45 | seq_add(_obj_pool, P); 46 | } 47 | 48 | static void pool_clean(void *P) { 49 | void **objs = *_obj_pool; 50 | FOR(i,array_len(objs)) { 51 | if (objs[i] == P) { 52 | objs[i] = NULL; 53 | break; 54 | } 55 | } 56 | } 57 | 58 | static void pool_dispose(ObjPool * p) { 59 | // NB not to try any cleanup at this point! 60 | _pool_cleaner = NULL; 61 | obj_unref(*p); // kill the actual pool (ref seq containing objects) 62 | _pool_cleaner = pool_clean; 63 | 64 | _obj_pool = (VoidSeq)seq_stack_pop(_pool_stack); 65 | 66 | if (_obj_pool == NULL) { // stop using the pool; it's dead! 67 | _pool_marker = NULL; 68 | _pool_filter = NULL; 69 | _pool_cleaner = NULL; 70 | obj_unref(_pool_stack); 71 | _pool_stack = NULL; 72 | } 73 | } 74 | 75 | /// create an object pool which will collect all references generated by llib 76 | void *obj_pool() { 77 | if (! _pool_stack) { 78 | _pool_stack = seq_new(void*); 79 | } 80 | // push the current pool on the stack (will always be NULL initially) 81 | seq_stack_push(_pool_stack,_obj_pool); 82 | _obj_pool = seq_new_ref(void*); 83 | // the new pool is referenced by this object which controls 84 | // the pool's lifetime 85 | _pool_marker = obj_new(ObjPool,pool_dispose); 86 | *_pool_marker = _obj_pool; 87 | // the core will access the pool through these function pointers 88 | _pool_filter = pool_add; 89 | _pool_cleaner = pool_clean; 90 | return (void*)_pool_marker; 91 | } 92 | 93 | // this is a helper for the magic 'scoped' macro 94 | void __auto_unref(void *p) { 95 | obj_unref(*(void**)p); 96 | } 97 | -------------------------------------------------------------------------------- /llib/value.c: -------------------------------------------------------------------------------- 1 | /* 2 | * llib little C library 3 | * BSD licence 4 | * Copyright Steve Donovan, 2013 5 | */ 6 | 7 | #include "value.h" 8 | #include 9 | #include 10 | #include 11 | #ifdef _MSC_VER 12 | #define strtoll _strtoi64 13 | #define snprintf _snprintf 14 | #endif 15 | 16 | bool value_is_box(PValue v) { 17 | return array_len(v)==1 && ! obj_is_array(v); 18 | } 19 | 20 | static bool check_type(PValue v, int ttype) { 21 | return value_is_box(v) && obj_type_index(v) == ttype; 22 | } 23 | 24 | bool value_is_string(PValue v) { 25 | return obj_is_array(v) && obj_type_index(v) == OBJ_CHAR_T; 26 | } 27 | 28 | bool value_is_error(PValue v) { 29 | return obj_is_array(v) && obj_type_index(v) == OBJ_ECHAR_T; 30 | } 31 | 32 | bool value_is_float(PValue v) { 33 | return check_type(v,OBJ_DOUBLE_T); 34 | } 35 | 36 | bool value_is_int(PValue v) { 37 | return check_type(v,OBJ_LLONG_T); 38 | } 39 | 40 | bool value_is_bool(PValue v) { 41 | return check_type(v,OBJ_BOOL_T); 42 | } 43 | 44 | bool value_is_simple_map(PValue v) { 45 | return obj_type_index(v) == OBJ_KEYVALUE_T; 46 | } 47 | 48 | void obj_set_type(void *P,int t) { 49 | ObjHeader* h = obj_header_(P); 50 | h->type = t; 51 | } 52 | 53 | PValue value_error (const char *msg) { 54 | PValue v = str_new(msg); 55 | obj_set_type(v,OBJ_ECHAR_T); 56 | return v; 57 | } 58 | 59 | PValue value_float (double x) { 60 | double *px = array_new(double,1); 61 | *px = x; 62 | obj_is_array(px) = 0; 63 | return (PValue)px; 64 | } 65 | 66 | PValue value_int (long long i) { 67 | long long *px = array_new(long long,1); 68 | *px = i; 69 | obj_is_array(px) = 0; 70 | return (PValue)px; 71 | } 72 | 73 | PValue value_bool (bool i) { 74 | bool *px = array_new(bool,1); 75 | *px = i; 76 | obj_is_array(px) = 0; 77 | return (PValue)px; 78 | } 79 | 80 | #define str_eq(s1,s2) (strcmp((s1),(s2))==0) 81 | 82 | static PValue conversion_error(const char *s, const char *t) { 83 | char buff[50]; 84 | snprintf(buff,sizeof(buff),"'%s' is not a valid %s",s,t); 85 | return value_error(buff); 86 | } 87 | 88 | //. convert a string into a value of the desired type. 89 | PValue value_parse(const char *str, ValueType type) { 90 | long long ival; 91 | double fval; 92 | char *endptr; 93 | switch(type) { 94 | case ValueString: 95 | return (void*)str_new(str); 96 | case ValueInt: 97 | ival = strtoll(str, &endptr,10); 98 | if (*endptr) 99 | return conversion_error(endptr,"int"); 100 | return value_int(ival); 101 | case ValueFloat: 102 | fval = strtod(str, &endptr); 103 | if (*endptr) 104 | return conversion_error(endptr,"float"); 105 | return value_float(fval); 106 | case ValueBool: 107 | return value_bool(str_eq(str,"true")); 108 | case ValueNull: 109 | if (! str_eq(str,"null")) 110 | return value_error("only 'null' allowed"); 111 | return NULL;//* 112 | default: 113 | return value_error("cannot parse this type"); 114 | } 115 | } 116 | 117 | #define S str_new 118 | 119 | #ifdef _WIN32 120 | #define LONG_LONG_FMT "%I64d" 121 | #else 122 | #define LONG_LONG_FMT "%lld" 123 | #endif 124 | 125 | /// Default representation of a value as a string. 126 | // Only applies to scalar values (if you want to show arrays & maps 127 | // use json module). 128 | const char *value_tostring(PValue v) { 129 | char buff[65]; 130 | if (v == NULL) 131 | return S("null"); 132 | if (obj_refcount(v) == -1) 133 | return S(""); // Not An Object 134 | int typeslot = obj_type_index(v); 135 | if (! value_is_array(v)) { 136 | switch(typeslot) { 137 | #define outf(fmt,T) snprintf(buff,sizeof(buff),fmt,*((T*)v)) 138 | case OBJ_LLONG_T: 139 | outf(LONG_LONG_FMT,int64); 140 | break; 141 | case OBJ_DOUBLE_T: 142 | outf("%0.16g",double); 143 | break; 144 | case OBJ_BOOL_T: 145 | return S((*(bool*)v) ? "true" : "false"); 146 | } 147 | } else if (typeslot == OBJ_CHAR_T || typeslot == OBJ_ECHAR_T) { 148 | return str_ref((char*)v); // already a string object 149 | } else { 150 | snprintf(buff,sizeof(buff),"%s(%p)",obj_type(v)->name,v); 151 | } 152 | return S(buff); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /llib/value.h: -------------------------------------------------------------------------------- 1 | /* 2 | * llib little C library 3 | * BSD licence 4 | * Copyright Steve Donovan, 2013 5 | */ 6 | 7 | #ifndef _LLIB_VALUE_H 8 | #define _LLIB_VALUE_H 9 | 10 | #include "obj.h" 11 | 12 | typedef enum ValueType_ { 13 | ValueNull = 0, 14 | ValueRef = 0x100, 15 | ValueString = ValueRef + 1, 16 | ValueError= ValueRef + 2, 17 | ValueFloat = 1, 18 | ValueInt = 2, 19 | ValueValue = ValueRef + 3, 20 | ValuePointer = 3, 21 | ValueBool = 4 22 | } ValueType; 23 | 24 | typedef void *PValue; 25 | 26 | #define value_is_list(v) list_object(v) 27 | #define value_is_array(v) obj_is_array(v) 28 | #define value_is_map(v) map_object(v) 29 | 30 | #define value_errorf(fmt,...) value_error(str_fmt(fmt,__VA_ARGS__)) 31 | 32 | #define value_as_int(P) (int)(*(long long*)(P)) 33 | #define value_as_float(P) (*(double*)(P)) 34 | #define value_as_bool(P) (*(bool*)(P)) 35 | #define value_as_string(P) ((char*)P) 36 | 37 | bool value_is_string(PValue v); 38 | bool value_is_error(PValue v); 39 | PValue value_error (const char *msg); 40 | bool value_is_float(PValue v); 41 | PValue value_float (double x); 42 | bool value_is_int(PValue v); 43 | PValue value_int (long long i); 44 | bool value_is_bool(PValue v); 45 | PValue value_bool (bool i); 46 | bool value_is_box(PValue v); 47 | bool value_is_simple_map(PValue v); 48 | 49 | PValue value_parse(const char *str, ValueType type); 50 | const char *value_tostring(PValue v); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /llua.c: -------------------------------------------------------------------------------- 1 | /*** 2 | llua provides a higher-level way to integrate Lua into C projects, 3 | by defining reference objects and providing operations like calling, 4 | accessing, etc on them. Most explicit manipulation of the Lua 5 | stack becomes unnecessary. 6 | 7 | @license BSD 8 | @copyright Steve Donovan,2014 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "llua.h" 15 | 16 | // Lua 5.1 compatibility 17 | #if LUA_VERSION_NUM == 501 18 | #define LUA_OK 0 19 | #define lua_rawlen lua_objlen 20 | #endif 21 | 22 | static FILE *s_verbose = false; 23 | 24 | /// raise an error when the argument is an error. 25 | // @function llua_assert 26 | 27 | void *_llua_assert(lua_State *L, const char *file, int line, void *val) { 28 | if (! value_is_error(val)) 29 | return val; 30 | else 31 | return luaL_error(L,"%s:%d: %s",file,line,value_as_string(val)); 32 | } 33 | 34 | /// report whenever a Lua reference is freed 35 | void llua_verbose(FILE *f) { 36 | s_verbose = f; 37 | } 38 | 39 | void llua_set_error(llua_t *o, bool yesno) { 40 | o->error = yesno; 41 | } 42 | 43 | const char *llua_error(llua_t *o, const char *msg) { 44 | if (! (o && o->error && msg && value_is_error(msg))) 45 | return msg; 46 | return luaL_error(o->L,msg); 47 | } 48 | 49 | /// is this a Lua reference? 50 | // @within Properties 51 | bool llua_is_lua_object(llua_t *o) { 52 | return obj_is_instance(o,"llua_t"); 53 | } 54 | 55 | static void llua_Dispose(llua_t *o) { 56 | if (s_verbose) { 57 | fprintf(s_verbose,"free L %p ref %d type %s\n",o->L,o->ref,llua_typename(o)); 58 | } 59 | luaL_unref(o->L,LUA_REGISTRYINDEX,o->ref); 60 | } 61 | 62 | /// new Lua reference to value on stack. 63 | // @within Creating 64 | llua_t *llua_new(lua_State *L, int idx) { 65 | llua_t *res = obj_new(llua_t,llua_Dispose); 66 | res->L = L; 67 | lua_pushvalue(L,idx); 68 | res->ref = luaL_ref(L,LUA_REGISTRYINDEX); 69 | res->type = lua_type(L,idx); 70 | res->error = false; 71 | return res; 72 | } 73 | 74 | /// get the global state as a reference. 75 | // @within Creating 76 | llua_t* llua_global(lua_State *L) { 77 | llua_t *res; 78 | lua_getglobal(L,"_G"); 79 | res = llua_new(L,-1); 80 | lua_pop(L,1); 81 | return res; 82 | } 83 | 84 | /// get the metatable of `o` 85 | // @within Properties 86 | llua_t *llua_getmetatable(llua_t *o) { 87 | lua_State *L = llua_push(o); 88 | if (! lua_getmetatable(L,-1)) { 89 | return NULL; 90 | } else { 91 | llua_t *res = llua_new(L,-1); 92 | lua_pop(L,1); 93 | return res; 94 | } 95 | } 96 | 97 | /// set the metatable of `o` 98 | void llua_setmetatable(llua_t *o, llua_t *mt) { 99 | lua_State *L = llua_push(o); 100 | if (mt) 101 | llua_push(mt); 102 | else 103 | lua_pushnil(L); 104 | lua_setmetatable(L,-2); 105 | lua_pop(L,1); 106 | } 107 | 108 | // Lua strings may have embedded nuls, so don't 109 | // depend on lua_tostring! 110 | static char *string_copy(lua_State *L, int idx) { 111 | size_t sz; 112 | const char *s = lua_tolstring(L,idx,&sz); 113 | char *res = str_new_size(sz); 114 | memcpy(res,s,sz); 115 | return res; 116 | } 117 | 118 | /// value on stack as a llib object, or Lua reference. 119 | // Result can be NULL, a string, a llib boxed value, 120 | // or a `llua_t` reference. 121 | // @within Converting 122 | void *llua_to_obj(lua_State *L, int idx) { 123 | switch(lua_type(L,idx)) { 124 | case LUA_TNIL: return NULL; 125 | case LUA_TNUMBER: return value_float(lua_tonumber(L,idx)); 126 | case LUA_TBOOLEAN: return value_bool(lua_toboolean(L,idx)); 127 | case LUA_TSTRING: return string_copy(L,idx); 128 | case LUA_TLIGHTUSERDATA: return lua_topointer(L,idx); 129 | default: 130 | return llua_new(L,idx); 131 | //LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA. 132 | } 133 | } 134 | 135 | /// convenient way to call `llua_to_obj` which pops the stack. 136 | // @within Converting 137 | void *llua_to_obj_pop(lua_State *L, int idx) { 138 | void *res = llua_to_obj(L,idx); 139 | lua_pop(L,1); 140 | return res; 141 | } 142 | 143 | /// type name of the reference. 144 | // @within Properties 145 | const char *llua_typename(llua_t *o) { 146 | return lua_typename(o->L,o->type); 147 | } 148 | 149 | /// a reference to a new Lua table. 150 | // @within Creating 151 | llua_t *llua_newtable(lua_State *L) { 152 | llua_t *ref; 153 | lua_newtable(L); 154 | ref = llua_new(L,-1); 155 | lua_pop(L,1); 156 | return ref; 157 | } 158 | 159 | /// a reference to a C function. 160 | // @within Creating 161 | llua_t *llua_cfunction(lua_State *L, lua_CFunction f) { 162 | llua_t *ref; 163 | lua_pushcfunction(L,f); 164 | ref = llua_new(L,-1); 165 | lua_pop(L,1); 166 | return ref; 167 | } 168 | 169 | static err_t l_error(lua_State *L) { 170 | const char *errstr = value_error(lua_tostring(L,-1)); 171 | lua_pop(L,1); 172 | return errstr; 173 | } 174 | 175 | /// load a code string and return the compiled chunk as a reference. 176 | // @within LoadingAndEvaluating 177 | llua_t *llua_load(lua_State *L, const char *code, const char *name) { 178 | int res = luaL_loadbuffer(L,code,strlen(code),name); 179 | if (res != LUA_OK) { 180 | return (llua_t*)l_error(L); 181 | } 182 | return llua_to_obj_pop(L,-1); 183 | } 184 | 185 | /// load a file and return the compiled chunk as a reference. 186 | // @within LoadingAndEvaluating 187 | llua_t *llua_loadfile(lua_State *L, const char *filename) { 188 | int res = luaL_loadfile(L,filename); 189 | if (res != LUA_OK) { 190 | return (llua_t*)l_error(L); 191 | } 192 | return llua_to_obj_pop(L,-1); 193 | } 194 | 195 | /// push the reference on the stack. 196 | lua_State *llua_push(llua_t *o) { 197 | lua_rawgeti(o->L,LUA_REGISTRYINDEX,o->ref); 198 | return o->L; 199 | } 200 | 201 | lua_State *_llua_push_nil(llua_t *o) { 202 | lua_State *L = llua_push(o); 203 | lua_pushnil(L); 204 | return L; 205 | } 206 | 207 | /// length of Lua reference, if a table or userdata. 208 | // Note that we are specifically not using `lua_rawlen` here! 209 | // @within Properties 210 | int llua_len(llua_t *o) { 211 | llua_push(o); 212 | #if LUA_VERSION_NUM == 501 213 | int n = lua_objlen(o->L,-1); 214 | #else 215 | int n = luaL_len(o->L,-1); 216 | #endif 217 | lua_pop(o->L,1); 218 | return n; 219 | } 220 | 221 | /// Lua table as an array of doubles. 222 | // @within Converting 223 | double *llua_tonumarray(lua_State* L, int idx) { 224 | int i,n = lua_rawlen(L,idx); 225 | double *res = array_new(double,n); 226 | for (i = 0; i < n; i++) { 227 | lua_rawgeti(L,idx,i+1); 228 | res[i] = lua_tonumber(L,-1); 229 | lua_pop(L,1); 230 | } 231 | return res; 232 | } 233 | 234 | /// Lua table as an array of ints. 235 | // @within Converting 236 | int *llua_tointarray(lua_State* L, int idx) { 237 | int i,n = lua_rawlen(L,idx); 238 | int *res = array_new(int,n); 239 | for (i = 0; i < n; i++) { 240 | lua_rawgeti(L,idx,i+1); 241 | res[i] = lua_tointeger(L,-1); 242 | lua_pop(L,1); 243 | } 244 | return res; 245 | } 246 | 247 | /// Lua table as an array of strings. 248 | // @within Converting 249 | char** llua_tostrarray(lua_State* L, int idx) { 250 | int i,n = lua_rawlen(L,idx); 251 | char** res = array_new_ref(char*,n); 252 | for (i = 0; i < n; i++) { 253 | lua_rawgeti(L,idx,i+1); 254 | res[i] = string_copy(L,-1); 255 | lua_pop(L,1); 256 | } 257 | return res; 258 | } 259 | 260 | static int is_indexable(lua_State *L, int idx) { 261 | return lua_istable(L,-1) || lua_isuserdata(L,-1); 262 | } 263 | 264 | /// Read a value on the stack into a variable. 265 | // `kind` is a _type specifier_ 266 | // 267 | // * 'i' integer 268 | // * 'b' boolean 269 | // * 'f' double 270 | // * 's' string 271 | // * 'o' object (as in `llua_to_obj`) 272 | // * 'L' llua reference 273 | // * 'I' array of integers 274 | // * 'F' array of doubles 275 | // * 'S' array of strings 276 | // 277 | // @within Converting 278 | err_t llua_convert(lua_State *L, char kind, void *P, int idx) { 279 | err_t err = NULL; 280 | switch(kind) { 281 | case 'i': // this is a tolerant operation; returns 0 if wrong type 282 | *((int*)P) = lua_tointeger(L,idx); 283 | break; 284 | case 'f': 285 | if (! lua_isnumber(L,idx)) 286 | err = "not a number!"; 287 | else 288 | *((double*)P) = lua_tonumber(L,idx); 289 | break; 290 | case 's': 291 | if (! lua_isstring(L,idx)) 292 | err = "not a string!"; 293 | else 294 | *((char**)P) = string_copy(L,idx); 295 | break; 296 | case 'o': 297 | *((llua_t**)P) = llua_to_obj(L,idx); 298 | break; 299 | case 'L': 300 | *((llua_t**)P) = llua_new(L,idx); 301 | break; 302 | case 'F': 303 | if (! is_indexable(L,idx)) 304 | err = "not indexable!*"; 305 | else 306 | *((double**)P) = llua_tonumarray(L,idx); 307 | break; 308 | case 'I': 309 | if (! is_indexable(L,idx)) 310 | err = "not indexable!"; 311 | else 312 | *((int**)P) = llua_tointarray(L,idx); 313 | break; 314 | case 'S': 315 | if (! is_indexable(L,idx)) 316 | err = "not indexable!"; 317 | else 318 | *((char***)P) = llua_tostrarray(L,idx); 319 | break; 320 | default: 321 | break; 322 | } 323 | if (err) { 324 | if (lua_isnil(L,idx)) 325 | err = "was nil"; 326 | return value_error(err); 327 | } else { 328 | return NULL; 329 | } 330 | 331 | } 332 | 333 | static err_t push_value(lua_State *L, char kind, void *data) { 334 | switch(kind) { 335 | case 's': 336 | lua_pushstring(L, (const char*)data); 337 | break; 338 | case 'o': 339 | llua_push((llua_t*)data); 340 | break; 341 | case 'x': // usually possible to do this cast 342 | lua_pushcfunction(L,(lua_CFunction)data); 343 | break; 344 | case 'p': 345 | lua_pushlightuserdata(L,data); 346 | break; 347 | default: 348 | return value_error("unknown type"); 349 | } 350 | return NULL; 351 | } 352 | 353 | /// call the reference, passing a number of arguments. 354 | // These are specified by a set of _type specifiers_ `fmt`. Apart 355 | // from the usual ones, we have 'm' (which must be first) which 356 | // means "call the method by name" where `o` must be an object, 357 | // 'v' which means "push the value at the index", and 'x' which 358 | // means "C function". 359 | // 360 | // May optionally capture multiple return values with `llua_convert`. 361 | // There are some common cases that have symbolic names: 362 | // 363 | // * `L_NONE` call doesn't return anything 364 | // * `L_VAL` we return a single value 365 | // * `L_REF` we always return result as a llua reference 366 | // * `L_ERR` Lua error convention, either or 367 | // 368 | // @within Calling 369 | // @usage llua_callf(strfind,"ss",str,";$","i",&i); 370 | // @usage llua_callf(open,"s","test.txt",L_ERR) 371 | // @usage llua_callf(file,"ms","write","hello there\n",L_NONE); 372 | void *llua_callf(llua_t *o, const char *fmt,...) { 373 | lua_State *L = o->L; 374 | int nargs = 0, nres = LUA_MULTRET, nerr; 375 | err_t res = NULL; 376 | char rtype; 377 | va_list ap; 378 | va_start(ap,fmt); 379 | llua_push(o); // push the function or object 380 | if (*fmt == 'm') { // method call! 381 | const char *name = va_arg(ap,char*); 382 | lua_getfield(L,-1,name); 383 | // method at top, then self 384 | lua_insert(L,-2); 385 | ++fmt; 386 | ++nargs; 387 | } 388 | // and push the arguments... 389 | while (*fmt) { 390 | switch(*fmt) { 391 | case 'i': 392 | lua_pushinteger(L, va_arg(ap,int)); 393 | break; 394 | case 'v': 395 | lua_pushvalue(L,va_arg(ap,int) - nargs - 1); 396 | break; 397 | case 'b': 398 | lua_pushboolean(L, va_arg(ap,int)); 399 | break; 400 | case 'f': 401 | lua_pushnumber(L, va_arg(ap,double)); 402 | break; 403 | default: 404 | res = push_value(L,*fmt, va_arg(ap,void*)); 405 | if (res) 406 | return (void*)llua_error(o,res); 407 | break; 408 | } 409 | ++fmt; 410 | ++nargs; 411 | } 412 | fmt = va_arg(ap,char*); 413 | if (fmt) { 414 | nres = strlen(fmt); 415 | rtype = *(fmt+1); 416 | if (*fmt == 'r' && rtype && rtype != 'E') // single return with explicit type 417 | nres = 1; 418 | } 419 | nerr = lua_pcall(L,nargs,nres,0); 420 | if (nerr != LUA_OK) { 421 | res = l_error(L); 422 | } 423 | if (nres == LUA_MULTRET || res != NULL) { // leave results on stack, or error! 424 | return (void*)llua_error(o,res); 425 | } else 426 | if (*fmt == 'r') { // return one value as object... 427 | if (rtype == 'E') { 428 | // Lua error return convention is object 429 | // or nil,error-string 430 | void *val = llua_to_obj(L,-2); 431 | void *err = llua_to_obj(L,-1); 432 | if (! val) 433 | val = value_error(err); 434 | lua_pop(L,2); 435 | return val; 436 | } else 437 | if (rtype) { // force the type! 438 | void *value; 439 | res = llua_convert(L,rtype,&value,-1); 440 | lua_pop(L,1); 441 | if (res) // failed... 442 | return (void*)llua_error(o,res); 443 | else 444 | return value; 445 | } else { 446 | return llua_to_obj_pop(L,-1); 447 | } 448 | } else 449 | if (nres != LUA_MULTRET) { 450 | int idx = -nres; 451 | while (*fmt) { 452 | res = llua_convert(L,*fmt,va_arg(ap,void*),idx); 453 | if (res) // conversion error! 454 | break; 455 | ++fmt; 456 | ++idx; 457 | } 458 | lua_pop(L,nres); 459 | } 460 | va_end(ap); 461 | return (void*)res; 462 | } 463 | 464 | /// call a function, raising an error. 465 | // A useful combination of `llua_callf` and `llua_assert` 466 | // @function llua_call_or_die 467 | // @within Calling 468 | 469 | /// pop some values off the Lua stack. 470 | // Uses `llua_convert` 471 | // @within Converting 472 | err_t llua_pop_vars(lua_State *L, const char *fmt,...) { 473 | err_t res = NULL; 474 | va_list ap; 475 | va_start(ap,fmt); 476 | while (*fmt) { 477 | res = llua_convert(L,*fmt,va_arg(ap,void*),-1); 478 | if (res) // conversion error! 479 | break; 480 | ++fmt; 481 | lua_pop(L,1); 482 | } 483 | va_end(ap); 484 | return res; 485 | } 486 | 487 | /// this returns the _original_ raw C string. 488 | // @within Properties 489 | const char *llua_tostring(llua_t *o) { 490 | const char *res; 491 | llua_push(o); 492 | res = lua_tostring(o->L,-1); 493 | lua_pop(o->L,1); 494 | return res; 495 | } 496 | 497 | /// the Lua reference as a number. 498 | // @within Properties 499 | lua_Number llua_tonumber(llua_t *o) { 500 | lua_Number res; 501 | llua_push(o); 502 | res = lua_tonumber(o->L,-1); 503 | lua_pop(o->L,1); 504 | return res; 505 | } 506 | 507 | // can we index this object? 508 | static bool accessible(llua_t *o, int ltype, const char *metamethod) { 509 | if (o->type == ltype) { // always cool 510 | return true; 511 | } else { 512 | lua_State *L = llua_push(o); 513 | if (! luaL_getmetafield(L,-1,metamethod)) { // no metatable! 514 | lua_pop(L,1); 515 | return false; 516 | } 517 | lua_pop(L,2); // metamethod and table 518 | return true; 519 | } 520 | } 521 | 522 | /// can we get a field of this object? 523 | // @within Properties 524 | bool llua_gettable(llua_t *o) { 525 | return accessible(o,LUA_TTABLE,"__index"); 526 | } 527 | 528 | /// can we set a field of this object? 529 | // @within Properties 530 | bool llua_settable(llua_t *o) { 531 | return accessible(o,LUA_TTABLE,"__newindex"); 532 | } 533 | 534 | /// can we call this object? 535 | // @within Properties 536 | bool llua_callable(llua_t *o) { 537 | return accessible(o,LUA_TFUNCTION,"__call"); 538 | } 539 | 540 | static char *splitdot(char *key) { 541 | char *p = strchr(key,'.'); 542 | if (p) { // sub.key 543 | *p = '\0'; 544 | return p+1; 545 | } else 546 | return NULL; 547 | } 548 | 549 | #define MAX_KEY 256 550 | 551 | // assume the table is initially on top of the stack. 552 | // leaves final value on top 553 | static void safe_gets(lua_State *L, const char *key) { 554 | char ckey[MAX_KEY], *subkey; 555 | strcpy(ckey,key); 556 | subkey = splitdot(ckey); 557 | lua_getfield(L,-1,ckey); 558 | if (subkey) { 559 | if (! lua_isnil(L,-1)) { 560 | lua_getfield(L,-1,subkey); 561 | lua_remove(L,-2); 562 | } 563 | } 564 | } 565 | 566 | /// index the reference with a string key, returning an object. 567 | // 'object' defined as with `llua_to_obj` 568 | // @within GettingAndSetting 569 | void *llua_gets(llua_t *o, const char *key) { 570 | lua_State *L = llua_push(o); 571 | safe_gets(L,key); 572 | lua_remove(L,-2); 573 | return llua_to_obj_pop(L,-1); 574 | } 575 | 576 | /// index the reference with multiple string keys and type-specifiers. 577 | // Type specifiers are as with `llua_convert` 578 | // @within GettingAndSetting 579 | // @usage llua_gets_v(T,"key1","s",&str,NULL); 580 | err_t llua_gets_v(llua_t *o, const char *key,...) { 581 | lua_State *L = llua_push(o); 582 | const char *fmt; 583 | void *P; 584 | bool convert; 585 | err_t err = NULL; 586 | va_list ap; 587 | va_start(ap,key); 588 | while (key) { // key followed by type specifier and pointer-to-data 589 | fmt = va_arg(ap,const char*); 590 | P = va_arg(ap,void*); 591 | convert = true; 592 | safe_gets(L,key); 593 | if (*fmt == '?') { 594 | ++fmt; 595 | if (lua_isnil(L,-1)) // fine! leave value alone! 596 | convert = false; 597 | } 598 | if (convert) 599 | err = llua_convert(L,*fmt,P,-1); 600 | lua_pop(L,1); 601 | if (err) { 602 | char buff[256]; 603 | snprintf(buff,sizeof(buff),"field '%s': %s",key,err); 604 | unref(err); 605 | err = value_error(buff); 606 | break; 607 | } 608 | key = va_arg(ap,const char*); 609 | } 610 | va_end(ap); 611 | lua_pop(L,1); // the reference 612 | return llua_error(o,err); 613 | } 614 | 615 | /// index the reference with an integer key. 616 | // @within GettingAndSetting 617 | void *llua_geti(llua_t *o, int key) { 618 | lua_State *L = llua_push(o); 619 | lua_pushinteger(L,key); 620 | lua_gettable(L,-2); 621 | lua_remove(L,-2); // the reference 622 | return llua_to_obj_pop(L,-1); 623 | } 624 | 625 | /// raw indexing with an integer key. 626 | // @within GettingAndSetting 627 | void *llua_rawgeti(llua_t* o, int key) { 628 | lua_State *L = llua_push(o); 629 | lua_rawgeti(L,-1,key); 630 | lua_remove(L,-2); // the reference 631 | return llua_to_obj_pop(L,-1); 632 | } 633 | 634 | /// push an llib object. 635 | // equivalent to `llua_push` if it's a llua ref, otherwise 636 | // uses llib type. If there's no type it assumes a plain 637 | // string literal. 638 | void llua_push_object(lua_State *L, void *value) { 639 | if (llua_is_lua_object((llua_t*)value)) { 640 | llua_push((llua_t*)value); 641 | } else 642 | if (value_is_float(value)) { 643 | lua_pushnumber(L,value_as_float(value)); 644 | } else 645 | if (value_is_string(value)) { 646 | lua_pushstring(L,(const char*)value); 647 | } else 648 | if (value_is_int(value)) { 649 | lua_pushnumber(L,value_as_int(value)); 650 | } else 651 | if (value_is_bool(value)) { 652 | lua_pushboolean(L,value_as_bool(value)); 653 | } else { // _probably_ a string. You have been warned... 654 | lua_pushstring(L,(const char*)value); 655 | } 656 | } 657 | 658 | /// set value using integer key. 659 | // uses `llua_push_object` 660 | // @within GettingAndSetting 661 | void llua_seti(llua_t *o, int key, void *value) { 662 | lua_State *L = llua_push(o); 663 | lua_pushinteger(L,key); 664 | llua_push_object(L,value); 665 | lua_settable(L,-3); 666 | } 667 | /// set value using integer key. 668 | // uses `llua_push_object` 669 | // @within GettingAndSetting 670 | void llua_sets(llua_t *o, const char *key, void *value) { 671 | lua_State *L = llua_push(o); 672 | llua_push_object(L,value); 673 | lua_setfield(o->L,-2,key); 674 | } 675 | 676 | // there's some code duplication here with llua_callf, but I'm not 677 | // 100% sure how far to push ! 678 | 679 | /// set multiple keys and values on the reference. 680 | // uses type specifiers like `llua_callf` 681 | // @within GettingAndSetting 682 | err_t llua_sets_v(llua_t *o, const char *key,...) { 683 | lua_State *L = llua_push(o); 684 | const char *fmt; 685 | err_t err = NULL; 686 | va_list ap; 687 | va_start(ap,key); 688 | while (key) { // key followed by type specifier and pointer-to-data 689 | fmt = va_arg(ap,const char*); 690 | switch(*fmt) { 691 | case 'i': 692 | lua_pushinteger(L, va_arg(ap,int)); 693 | break; 694 | case 'b': 695 | lua_pushboolean(L, va_arg(ap,int)); 696 | break; 697 | case 'f': 698 | lua_pushnumber(L, va_arg(ap,double)); 699 | break; 700 | default: 701 | err = push_value(L,*fmt,va_arg(ap,void*)); 702 | break; 703 | } 704 | lua_setfield(o->L,-2,key); 705 | key = va_arg(ap,const char*); 706 | } 707 | va_end(ap); 708 | lua_pop(L,1); // the reference 709 | return llua_error(o,err); 710 | } 711 | 712 | /// load and evaluate an expression. 713 | // `fret` is a type specifier for the result, like `llua_callf`. 714 | // @within LoadingAndEvaluating 715 | void *llua_eval(lua_State *L, const char *expr, const char *fret) { 716 | llua_t *chunk = llua_load(L,expr,"tmp"); 717 | if (value_is_error(chunk)) // compile failed... 718 | return chunk; 719 | void *res = llua_callf(chunk,"",fret); 720 | obj_unref(chunk); // free the chunk reference... 721 | return res; 722 | } 723 | 724 | /// load and evaluate a file in an environment 725 | // `env` may be NULL. 726 | // `fret` is a type specifier for the result, like `llua_callf`. 727 | // @within LoadingAndEvaluating 728 | void *llua_evalfile(lua_State *L, const char *file, const char *fret, llua_t *env) { 729 | llua_t *chunk = llua_loadfile(L,file); 730 | if (value_is_error(chunk)) // compile failed... 731 | return llua_error(env,(err_t)chunk); 732 | if (env) { 733 | llua_push(chunk); 734 | llua_push(env); 735 | #if LUA_VERSION_NUM == 501 736 | lua_setfenv(L,-2); 737 | #else 738 | // _ENV is first upvalue of main chunks 739 | lua_setupvalue(L,-2,1); 740 | #endif 741 | lua_pop(L,1); 742 | } 743 | void *res = llua_callf(chunk,"",fret); 744 | obj_unref(chunk); 745 | return (void*) llua_error(env,res); 746 | } 747 | 748 | 749 | -------------------------------------------------------------------------------- /llua.h: -------------------------------------------------------------------------------- 1 | #ifdef LLUA_H 2 | #define LLUA_H 3 | #else 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef const char *err_t; 12 | 13 | typedef struct LLua_ { 14 | lua_State *L; 15 | int ref; 16 | int type; 17 | bool error; 18 | } llua_t; 19 | 20 | // useful names for common function returns 21 | #define L_VAL "r" 22 | #define L_REF "rL" 23 | #define L_ERR "rE" 24 | #define L_NONE "" 25 | 26 | // the FOR_TABLE construct 27 | #define L_TKEY (-2) 28 | #define L_TVAL (-1) 29 | #define FOR_TABLE(t) \ 30 | for (lua_State *_L=_llua_push_nil(t);lua_next(_L,-2) != 0;lua_pop(_L,1)) 31 | #define llua_table_break(t) {lua_pop(_L,1);break;} 32 | 33 | #define llua_assert(L,val) _llua_assert(L,__FILE__,__LINE__,val) 34 | 35 | void *_llua_assert(lua_State *L, const char *file, int line, void *val); 36 | 37 | #define llua_call_or_die(r,...) llua_assert((r)->L,llua_callf(r,__VA_ARGS__)) 38 | #define llua_eval_or_die(L,txt,ret) llua_assert(L,llua_eval(L,txt,ret)) 39 | 40 | llua_t *llua_new(lua_State *L, int idx); 41 | void llua_verbose(FILE *f); 42 | void llua_set_error(llua_t *o, bool yesno); 43 | const char *llua_error(llua_t *o, const char *msg); 44 | bool llua_is_lua_object(llua_t *o); 45 | llua_t* llua_global(lua_State *L); 46 | void *llua_to_obj(lua_State *L, int idx); 47 | void *llua_to_obj_pop(lua_State *L, int idx); 48 | llua_t *llua_getmetatable(llua_t *o); 49 | void llua_setmetatable(llua_t *o, llua_t *mt); 50 | const char *llua_typename(llua_t *o); 51 | llua_t *llua_newtable(lua_State *L); 52 | llua_t *llua_cfunction(lua_State *L, lua_CFunction f); 53 | llua_t *llua_load(lua_State *L, const char *code, const char *name); 54 | llua_t *llua_loadfile(lua_State *L, const char *filename); 55 | lua_State *llua_push(llua_t *o); 56 | lua_State *_llua_push_nil(llua_t *o); 57 | int llua_len(llua_t *o); 58 | err_t llua_call(llua_t *o, int nargs, int nresults); 59 | double *llua_tonumarray(lua_State* L, int idx); 60 | int *llua_tointarray(lua_State* L, int idx); 61 | char** llua_tostrarray(lua_State* L, int idx); 62 | err_t llua_convert(lua_State *L, char kind, void *P, int idx); 63 | void *llua_callf(llua_t *o, const char *fmt,...); 64 | err_t llua_pop_vars(lua_State *L, const char *fmt,...); 65 | const char *llua_tostring(llua_t *o); 66 | lua_Number llua_tonumber(llua_t *o); 67 | bool llua_gettable(llua_t *o); 68 | bool llua_settable(llua_t *o); 69 | bool llua_callable(llua_t *o); 70 | void *llua_gets(llua_t *o, const char *key); 71 | err_t llua_gets_v(llua_t *o, const char *key,...); 72 | void *llua_geti(llua_t *o, int key); 73 | void *llua_rawgeti(llua_t* o, int key); 74 | void llua_push_object(lua_State *L, void *value); 75 | void llua_seti(llua_t *o, int key, void *value); 76 | void llua_sets(llua_t *o, const char *key, void *value); 77 | err_t llua_sets_v(llua_t *o, const char *key,...); 78 | void *llua_eval(lua_State *L, const char *expr, const char *fret); 79 | void *llua_evalfile(lua_State *L, const char *file, const char *fret, llua_t *env); 80 | #endif 81 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | # Debian/Ubuntu etc after installing liblua5.2-dev 3 | VS=5.1 4 | LINC=/usr/include/lua$(VS) 5 | LUALIB=-llua$(VS) 6 | # release 7 | #CFLAGS=-std=c99 -O2 -I$(LINC) -I. 8 | #LINK=$(LUALIB) -L. -lllua -Wl,-s 9 | # debug 10 | CFLAGS=-std=c99 -g -I$(LINC) -I. 11 | LINK=$(LUALIB) -L. -lllua 12 | 13 | OBJS=llua.o llib/obj.o llib/value.o llib/pool.o 14 | LLUA=libllua.a 15 | 16 | all: $(LLUA) test-llua strfind tests tests-method file-size errors read-config read-config-err 17 | 18 | clean: 19 | rm *.o *.a 20 | 21 | $(LLUA): $(OBJS) 22 | ar rcu $(LLUA) $(OBJS) && ranlib $(LLUA) 23 | 24 | test-llua: test-llua.o $(LLUA) 25 | $(CC) test-llua.o -o test-llua $(LINK) 26 | 27 | strfind: strfind.o $(LLUA) 28 | $(CC) strfind.o -o strfind $(LINK) 29 | 30 | tests: tests.o $(LLUA) 31 | $(CC) tests.o -o tests $(LINK) 32 | 33 | tests-method: tests-method.o $(LLUA) 34 | $(CC) tests-method.o -o tests-method $(LINK) 35 | 36 | file-size: file-size.o $(LLUA) 37 | $(CC) file-size.o -o file-size $(LINK) 38 | 39 | errors: errors.o $(LLUA) 40 | $(CC) errors.o -o errors $(LINK) 41 | 42 | read-config: read-config.o $(LLUA) 43 | $(CC) read-config.o -o read-config $(LINK) 44 | 45 | read-config-err: read-config-err.o $(LLUA) 46 | $(CC) read-config-err.o -o read-config-err $(LINK) 47 | -------------------------------------------------------------------------------- /read-config-err.c: -------------------------------------------------------------------------------- 1 | /* This is like read-config, except we do all the 2 | initialization in a protected call, 3 | and error handling can happen in one place. 4 | 5 | Just to make things more interesting, the environment has a metatable 6 | which turns unknown symbols into tables, so that 'A.B = 42' is 7 | equivalent to 'A={B=42}'. llua_gets* can do two-level lookup, 8 | so the key 'A.B' works as expected. 9 | 10 | There are two reference leaks in this program; we throw away 11 | the reference to the C function, and won't clean up the environment 12 | if we blow up. Not a major problem for this once-off task. 13 | */ 14 | #include 15 | #include 16 | #include "llua.h" 17 | 18 | typedef struct { 19 | int alpha; 20 | double beta; 21 | char *address; 22 | int *ports; 23 | int a_b; 24 | } Config; 25 | 26 | const char *meta = 27 | "return setmetatable({},{ " 28 | " __index = function(t,k) local v = {}; rawset(t,k,v); return v end" 29 | "})"; 30 | 31 | int parse_config (lua_State *L) { 32 | llua_t *env; 33 | // you can of course use the llib macro obj_new(Config,NULL) here 34 | Config *c = malloc(sizeof(Config)); 35 | c->address = "127.0.0.1"; 36 | 37 | env = llua_eval_or_die(L,meta,L_VAL); 38 | llua_set_error(env,true); // <--- any op on 'env' will raise an error, not just return it 39 | llua_evalfile(L,"config.txt","",env); 40 | 41 | llua_gets_v(env, 42 | "alpha","i",&c->alpha, 43 | "beta","f",&c->beta, 44 | "address","?s",&c->address, // ? means use existing value as default! 45 | "ports","I",&c->ports, // big 'I' means 'array of int' 46 | "A.B","i", &c->a_b, 47 | NULL); 48 | 49 | unref(env); // don't need it any more... 50 | 51 | // alternatively, can create the Config pointer in main and pass as lightuserdata ('p') 52 | lua_pushlightuserdata(L,c); 53 | return 1; 54 | } 55 | 56 | bool handle_config(lua_State *L) { 57 | 58 | // doing a protected call of our function! 59 | Config* c = llua_callf(llua_cfunction(L,parse_config),L_NONE,L_VAL); 60 | if (value_is_error(c)) { // compile, run or bad field error? 61 | fprintf(stderr,"could not load config: %s\n",value_as_string(c)); 62 | return false; 63 | } 64 | 65 | printf("got alpha=%d beta=%f a_b=%d, address='%s'\n", 66 | c->alpha, c->beta,c->a_b,c->address 67 | ); 68 | 69 | // note how you get the size of the returned array 70 | int *ports = c->ports; 71 | printf("ports "); 72 | for (int i = 0; i < array_len(ports); i++) 73 | printf("%d ",ports[i]); 74 | printf("\n"); 75 | return true; 76 | } 77 | 78 | int main (int argc, char **argv) 79 | { 80 | lua_State *L = luaL_newstate(); 81 | luaL_openlibs(L); 82 | handle_config(L); 83 | lua_close(L); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /read-config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "llua.h" 3 | 4 | int main (int argc, char **argv) 5 | { 6 | lua_State *L = luaL_newstate(); 7 | 8 | // load the config file using a table as an environment 9 | // (it doesn't need '.lua' extension of course) 10 | llua_t *env = llua_newtable(L); 11 | const char *err = llua_evalfile(L,"config.lua","",env); 12 | if (err) { // a compile error? 13 | fprintf(stderr,"could not load config.lua: %s\n",err); 14 | return 1; 15 | } 16 | 17 | // we can now read values from the config 18 | int alpha; 19 | double beta; 20 | char *address = "127.0.0.1"; 21 | int *ports; 22 | char *author_name, *author_email; 23 | 24 | err = llua_gets_v(env, 25 | "alpha","i",&alpha, 26 | "beta","f",&beta, 27 | "address","?s",&address, // ? means use existing value as default! 28 | "ports","I",&ports, // big 'I' means 'array of int' 29 | "author.name","s",&author_name, 30 | "author.email","s",&author_email, 31 | NULL); 32 | if (err) { // required field not specified? 33 | fprintf(stderr,"config field: %s\n",err); 34 | return 1; 35 | } 36 | 37 | printf("got alpha=%d beta=%f address='%s' name='%s' email='%s'\n", 38 | alpha, beta, address, author_name, author_email 39 | ); 40 | 41 | // note how you get the size of the returned array 42 | printf("ports "); 43 | for (int i = 0; i < array_len(ports); i++) 44 | printf("%d ",ports[i]); 45 | printf("\n"); 46 | 47 | lua_close(L); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Rationale 2 | 3 | llua is a higher-level C API for Lua, organized around reference objects. 4 | It was inspired by [luar](http://github.com/stevedonovan/luar) and 5 | [LuaJava](https://github.com/jasonsantos/luajava) which 6 | provide similar operations when using Lua with Go and Java. The idea 7 | is to encapsulate a Lua reference as an object which supports general 8 | operations such as indexing (setting and getting), size, pushing on 9 | the Lua stack and calling. 10 | 11 | For instance, a common use of Lua is to load configuration files: 12 | 13 | ```lua 14 | -- config.lua 15 | alpha = 1 16 | beta = 2 17 | gammon = 'pork' 18 | 19 | ``` 20 | 21 | Here is how this file can be loaded into a custom environment: 22 | 23 | ```C 24 | llua_t *env = llua_newtable(L); 25 | err_t err = llua_evalfile(L,"config.lua","",env); 26 | if (err) { 27 | fprintf(stderr,"could not load config.lua: %s\n",err); 28 | } else { 29 | av = llua_gets(env,"gammon"); 30 | printf("gammon was %s\n",av); 31 | } 32 | //--> gammon was pork 33 | 34 | ``` 35 | 36 | This example also works with both Lua 5.1 and 5.2, by hiding the 37 | difference in how 'environments' work with the API. 38 | 39 | ## References, Objects and Strings 40 | 41 | llua references are llib objects; to free the reference use `unref`. If given 42 | an arbitrary object, `llua_is_lua_object(obj)` will be true if a Lua reference. 43 | 44 | `llua_t` has the Lua state `L`, the reference 45 | as `ref` (which is index into registry) and `type`. 46 | 47 | 48 | `llua_new(L,idx)` will wrap a value on the stack as a reference; `llua_newtable(L)` 49 | will make a reference to a new table, and `llua_global(L)` is the global state. 50 | These operations don't effect the stack; `llua_push(o)` will make the reference 51 | available on the stack. 52 | 53 | In general, all 54 | objects returned by llua will be allocated by llib, with the exception of strings 55 | returned by `llua_tostring` where you get the underlying 56 | char pointer managed by Lua. This should be safe, since we're guaranteed 57 | to have a reference to the string. Lua strings can contain nuls, so to be 58 | safe use `array_len(s)` rather than `strlen(s)` to determine length. 59 | 60 | For accessing a large Lua string without wanting a copy, the special type 61 | specifier 'L' will force all Lua values (including strings) to be reference 62 | objects. Once you have the string reference, `llua_tostring` gives you 63 | the managed pointer and `llua_len` gives you its actual length. 64 | 65 | ## Calling Lua Functions 66 | 67 | llua conceals tedious and error-prone Lua stack operations when calling 68 | Lua functions from C: 69 | 70 | ```C 71 | llua_t *G = llua_global(L); 72 | llua_t *strfind = llua_gets(G,"string.find"); 73 | int i1,i2; 74 | 75 | llua_callf(strfind,"ssi","hello dolly","doll",1,"ii",&i1,&i2); 76 | 77 | printf("i1 %d i2 %d\n",i1,i2); 78 | //--> i1 7 i2 10 79 | ``` 80 | 81 | `llua_callf` takes a callable reference (a function or something which 82 | has a `__call` metamethod), passes arguments specified by a type string, 83 | and can return a number of values. The 'type string' is akin to `printf` 84 | style formats: 'i' -> `int`, `f` -> `double`, `s` -> `string`, `b` -> 85 | `boolean` (integer value either 0 or 1), 'o' -> `object`, 'v' -> "value on stack", 86 | and 'x' -> `C function`. 87 | 88 | In the above example, there 89 | are three arguments, two strings and a integer, and the result is two integers, 90 | which are returned by reference. 91 | 92 | 93 | `llua_callf` can return a single value, by using the special type "r". 94 | Because llib objects have run-time info, the return value can always be distinguished 95 | from an error, which is llib's solution to C's single-value-return problem. 96 | 97 | ```C 98 | const char *res = llua_callf(my_tostring,"o",obj,"r"); 99 | if (value_is_error(res)) { 100 | fprintf(stderr,"my_tostring failed: %s\n",res); 101 | } else { 102 | // ok! 103 | } 104 | 105 | ``` 106 | 107 | Since this little "r" can be hard to see in code, it's given a name L_VAL. 108 | As a special case, `llua_callf` can call methods. The pseudo-type 'm' means 109 | "call the named method of the given object": 110 | 111 | 112 | ```C 113 | llua_t *out = llua_gets(G,"io.stdout"); 114 | llua_callf(out,"ms","write","hello dolly!\n",L_NONE); 115 | 116 | ``` 117 | 118 | ## Accessing Lua Tables 119 | 120 | We've already seen `llua_gets` for indexing tables and userdata; it will return 121 | an object as above; numbers will be returned as llib boxed values, which is not so 122 | very convenient. `llua_gets_v` allows multiple key lookups with type specified as 123 | with `llua_callf`. 124 | 125 | ```C 126 | char *av, *bv; 127 | int cv; 128 | double dv = 23.5; 129 | err_t *err; 130 | // load a table... 131 | llua_t *res = llua_eval(L,"return {a = 'one', b = 'two', c=66}",L_VAL); 132 | err = llua_gets_v(res, 133 | "a","s",&av, 134 | "b","s",&bv, 135 | "c","i",&cv, 136 | "d","?f",&dv, // no error if not defined, use default! 137 | NULL); 138 | printf("got a='%s' b='%s' c=%d d=%f\n",av,bv,cv,dv); 139 | //--> got a='one' b='two' c=66 d=23.500000 140 | ``` 141 | 142 | Note the special '?' which allows the existing value to pass through unchanged 143 | if the key does not exist. If there was no default, then the returned error will 144 | be non-NULL. 145 | 146 | There is also `llua_geti` and `llua_rawgeti`, which also return objects. 147 | 148 | ```C 149 | llua_t *res = llua_eval(L,"return {10,20,30}",L_VAL); 150 | void *obj = llua_geti(res,1); // always returns an object 151 | if (value_is_float(obj)) // paranoid! 152 | printf("value was %f\n",value_as_float(obj)); // unbox 153 | 154 | // or we can convert the table to an array of ints 155 | llua_push(res); 156 | int *arr = llua_tointarray(L,-1); 157 | int i; 158 | for (i = 0; i < array_len(arr); i++) 159 | printf("%d\n",arr[i]); 160 | ``` 161 | 162 | There are three table-to-array functions, `llua_tointarray`, `llua_tonumarray` and 163 | `llua_tostrarray` which return llib arrays. Again, using llib means that these 164 | arrays know how long they are! 165 | 166 | A particularly intense one-liner implicitly uses this table-to-int-array conversion: 167 | you may force the return type with a type specifier after 'r'. 168 | 169 | ``` 170 | int* arr = llua_eval(L,"return {10,20,30}","rI"); 171 | 172 | ``` 173 | 174 | Trying to index a non-indexable object will cause a Lua panic, so `llua_gettable` and 175 | `llua_settable` are defined so you can program defensively. 176 | 177 | Iterating over a table is fairly straightforward using the usual API but I usually 178 | have to look it up each time. So llua provides a `FOR_TABLE` macro: 179 | 180 | 181 | ```C 182 | FOR_TABLE(G) { // all table keys matching "^s" 183 | void *obj = llua_callf(strfind,"vs",L_TKEY,"^s",L_VAL); 184 | if (obj) { 185 | printf("match %s\n",lua_tostring(L,L_TKEY)); 186 | unref(obj); 187 | } 188 | } 189 | ``` 190 | 191 | Like the explicit version, this requires stack discipline! L_TKEY is (-2) and 192 | L_TVAL is (-1); the 'v' type specifier expects a stack index. 193 | 194 | If you do need to break out of this loop, use the `llua_table_break` 195 | macro which does the necessary key-popping. 196 | 197 | ## Error Handling 198 | 199 | Generally, all llua functions which can return an object, can also return an error; 200 | llib's `value_is_error` can distinguish between error strings and any other object, 201 | including plain strings. We can do this because all results are llib objects and 202 | have dynamic type information, which provides an elegant solution to the old 203 | "return value or error" problem with C. (These runtime checks are pretty fast 204 | since the type infomation is part of the hidden object header.) 205 | 206 | Scalar values like ints and floats will also be returned this way, as llib 'boxed' 207 | values (check with `value_is_int`, unbox with `value_as_int`, etc.) This is 208 | not so convenient, hence llua's use of scanf-like type specifiers with variables. 209 | 210 | Lua has a common idiom, where normally a function will return one value, 211 | or `nil` plus an error string. The `llua_callf` return type `L_ERR` makes this into 212 | a single value, which can be distinguished from an error as above. 213 | 214 | However, passing a Lua API or library function a wrong parameter will result 215 | in an error being raised. For instance, trying to index a number, or call a `nil` 216 | value. So `llua_gets*` will not return an error in this case; you should query 217 | `llua_gettable(o)` first. This is true if the object was a table or something with an 218 | `__index` metamethod. (It would be too expensive to do this check each time.) 219 | So then you need the C equivalent of Lua's protected function calls: 220 | 221 | ```C 222 | int l_myfun(lua_State *L) { 223 | const char *sval; 224 | llua_t *r; 225 | lua_pushnil(L); 226 | r = llua_new(L,-1); 227 | sval = llua_gets(r,"woop"); 228 | printf("got '%s'\n",sval); 229 | return 0; 230 | } 231 | 232 | ..... 233 | llua_t *myfun = llua_cfunction(L,l_myfun); 234 | err_t res = llua_callf(myfun,L_NONE,L_VAL); 235 | if (res) { 236 | fprintf(stderr,"failed: %s\n",res); 237 | } 238 | --> failed: attempt to index a nil value 239 | ``` 240 | 241 | Note that `l_myfun` doesn't return any value, so `llua_callf` only returns non-nil 242 | when there's an error. 243 | 244 | However, if we tried to call the `nil` reference `r` above with `llua_callf`, no 245 | error will be raised, since it does a protected call. You can use the following form 246 | to raise the error: 247 | 248 | ```C 249 | // make 'calling a nil value' a fatal error 250 | void *val = llua_call_or_die(r,L_NONE,L_VAL); 251 | ... 252 | // ---> failed errors.c:23: attempt to call a nil value 253 | ``` 254 | 255 | This is a macro built on `llua_assert(L,val)` which you can generally use for 256 | converting error values into raised errors. (Like the C macro `assert`, it adds 257 | file:line information) 258 | 259 | There is yet another mechanism that forces returned errors into raised errors. 260 | If a llua reference has its `error` field set, then any operation involving it will 261 | raise an error. So an alternative way of doing the last operation would be: 262 | 263 | ```C 264 | llua_set_error(r,true); 265 | void *val = llua_callf(r,L_NONE,L_VAL); 266 | 267 | ``` 268 | The error message won't be so pretty, however. (I apologize for providing 269 | several mechanisms for achieving the same result; in the early days 270 | of experimenting with a library interface it's useful to present alternatives.) 271 | 272 | This is useful if you're found of exception handling as a strategy to separate 273 | 'normal' program flow from error handling. Consider the case of reading a 274 | configuration file. All the straightforward business of loading and querying 275 | happens in a protected call: 276 | 277 | ```C 278 | typedef struct { 279 | int alpha; 280 | double beta; 281 | char *address; 282 | int *ports; 283 | } Config; 284 | 285 | int parse_config (lua_State *L) { 286 | llua_t *env; 287 | Config *c = malloc(sizeof(Config)); 288 | c->address = "127.0.0.1"; 289 | 290 | env = llua_newtable(L); 291 | llua_set_error(env,true); // <--- any op on 'env' will raise an error, not just return it 292 | llua_evalfile(L,"config.lua","",env); 293 | 294 | llua_gets_v(env, 295 | "alpha","i",&c->alpha, 296 | "beta","f",&c->beta, 297 | "address","?s",&c->address, // ? means use existing value as default! 298 | "ports","I",&c->ports, // big 'I' means 'array of int' 299 | NULL); 300 | 301 | unref(env); // don't need it any more... 302 | 303 | lua_pushlightuserdata(L,c); 304 | return 1; 305 | } 306 | 307 | ``` 308 | 309 | And the error handling then becomes centralized: 310 | 311 | ```C 312 | Config* c = llua_callf(llua_cfunction(L,parse_config),L_NONE,L_VAL); 313 | if (value_is_error(c)) { // compile, run or bad field error? 314 | fprintf(stderr,"could not load config: %s\n",value_as_string(c)); 315 | return 1; 316 | } 317 | 318 | printf("got alpha=%d beta=%f address='%s'\n", 319 | c->alpha, c->beta,c->address 320 | ); 321 | 322 | // note how you get the size of the returned array 323 | int *ports = c->ports; 324 | printf("ports "); 325 | for (int i = 0; i < array_len(ports); i++) 326 | printf("%d ",ports[i]); 327 | printf("\n"); 328 | 329 | ``` 330 | 331 | The point is that both `llua_evalfile` and `llua_gets_v` may throw errors; the first 332 | if there's an error parsing and executing the configuration, the second 333 | if a required field is missing or has the wrong type. For complicated sequences 334 | of operations, this will give you cleaner code and leave your 'happy path' 335 | uncluttered. 336 | 337 | Please note how easy it is for the protected function to return a pointer as 338 | 'light userdata'; alternatively we could have created the config object and 339 | passed it as light userdata using the 'p' type specifier, and picked it up as 340 | `lua_topointer(L,1)` in the protected code. 341 | 342 | ## Managing References 343 | 344 | In the above example, there are two 'reference leaks'; the first comes from 345 | throwing away the reference to `parse_config` and the second happens when 346 | an error is raised and `env` is not dereferenced with `unref`. This is not C++, 347 | and we don't have any guaranteed cleanup on scope exit! 348 | 349 | In this case (once-off reading of configuration file) it's probably no big deal, 350 | but you do have to manage the lifetime of references in general. 351 | 352 | One approach to scope cleanup is to use 'object pools', which is similar to how 353 | Objective-C manages reference-counting. 354 | 355 | ```C 356 | void my_task() { 357 | void *P = obj_pool(); 358 | llua_t *result; 359 | 360 | //.... many references created 361 | result = ref(obj); 362 | 363 | unref(P); // all references freed, except 'result' 364 | return result; 365 | } 366 | ``` 367 | 368 | Object pools can be nested (they are implemented as a stack of resizeable 369 | reference-owning arrays) and they will do the Right Thing. To make sure 370 | they don't clean up _everything_, use `ref` to increase the reference count 371 | of objects you wish to keep - in this case, the result of the function. 372 | 373 | If you're using GCC (and compilers which remain GCC-compatible, like Clang 374 | and later versions of ICC) there is a 'cleanup' attribute which allows us to 375 | leave out that explicit 'unref(P)`: 376 | 377 | ``` 378 | { 379 | scoped void *P = obj_pool(); 380 | 381 | // no unref needed! 382 | } 383 | ``` 384 | 385 | The cool thing is that we then have that favourite feature of C++ programmers: 386 | the ability to do auto cleanup on a scope level. This is particularly convenient if 387 | you like the freedom to do multiple returns from a function and dislike the 388 | bookkeeping needed to do manual cleanup. It isn't a C standard, but its wide 389 | implementation makes it an option for those who can choose their compilers. 390 | 391 | (scoped_pool) 392 | 393 | A potential problem with relying too heavily on object pools is that you may 394 | create and destroy many temporary references, which could slow you 395 | down in critical places. 396 | 397 | -------------------------------------------------------------------------------- /strfind.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main (int argc, char **argv) 5 | { 6 | lua_State *L = luaL_newstate(); 7 | luaL_openlibs(L); 8 | 9 | llua_t *G = llua_global(L); 10 | llua_t *strfind = llua_gets(G,"string.find"); 11 | printf("start %d\n",lua_gettop(L)); 12 | 13 | int i1,i2; 14 | llua_callf(strfind,"ssi","hello dolly","doll",1,"ii",&i1,&i2); 15 | 16 | printf("i1 %d i2 %d\n",i1,i2); 17 | 18 | // global keys beginning with 's'. 19 | // note 'v' for 'push value at index' 20 | // -> conversions are tolerant, so you get 21 | // zero if not an integer, e.g. when nil. This fits 22 | // nicely with what C regards as true/false. 23 | FOR_TABLE(G) { 24 | int i; 25 | llua_callf(strfind,"vs",L_TKEY,"^s","i",&i); 26 | if (i) 27 | printf("match %s\n",lua_tostring(L,L_TKEY)); 28 | } 29 | 30 | lua_close(L); 31 | } 32 | -------------------------------------------------------------------------------- /test-llua.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "llua.h" 3 | 4 | int main (int argc, char **argv) 5 | { 6 | lua_State *L = luaL_newstate(); 7 | char *expr = "print(10+20)"; 8 | 9 | luaL_openlibs(L); 10 | if (argc > 1) 11 | expr = argv[1]; 12 | 13 | // lower-level llua functions are similar to corresponding Lua API calls, 14 | // except that llua_t objects carry the references, and we can 15 | // always distinguish valid results from errors by llib runtime type info 16 | llua_t *chunk = llua_load(L,expr, "line"); 17 | if (value_is_error(chunk)) { 18 | fprintf(stderr,"compile %s\n",value_as_string(chunk)); 19 | return 1; 20 | } 21 | err_t err = llua_callf(chunk,"",""); 22 | if (err) { 23 | fprintf(stderr, "run %s\n", err); 24 | return 1; 25 | } 26 | 27 | const char *code = "return {10,20,30}"; 28 | 29 | // compile the cunk 30 | chunk = llua_load(L,code,"tmp"); 31 | 32 | // callf allows values to be returned directly...("" means 'no arguments' here) 33 | llua_t *res = llua_callf(chunk,"","r"); 34 | 35 | // we get back a Lua table 36 | printf("type '%s', length %d\n",llua_typename(res),llua_len(res)); 37 | 38 | // llua_geti, gets always returns objects. 39 | void *obj = llua_geti(res,1); 40 | printf("value was %f\n",value_as_float(obj)); 41 | 42 | // or we can convert the table to an array of ints 43 | llua_push(res); 44 | int *arr = llua_tointarray(L,-1); 45 | int i; 46 | for (i = 0; i < array_len(arr); i++) 47 | printf("%d\n",arr[i]); 48 | lua_pop(L,1); 49 | 50 | // Take 2. Compile & run, and return explicitly as int array 51 | arr = llua_eval(L,code,"rI"); 52 | printf("size %d type %s\n",array_len(arr),obj_type(arr)->name); 53 | 54 | llua_t *G = llua_global(L); 55 | // straightforward to call standard Lua functions ("" means 'no returns') 56 | res = llua_gets(G,"print"); 57 | llua_callf(res,"ssi","one","two",42,""); 58 | 59 | // llua_gets will work with one extra level of lookup... 60 | const char *ppath = llua_gets(G,"package.path"); 61 | llua_t* sub = llua_gets(G,"string.sub"); 62 | res = llua_callf(sub,"sii",ppath,1,10,"r"); 63 | printf("first part of package.path '%s'\n",res); 64 | 65 | // load a table... 66 | res = llua_eval(L,"return {a = 'one', b = 'two', c=66}","r"); 67 | char *av, *bv; 68 | int cv; 69 | double dv = 23.5; 70 | llua_gets_v(res, 71 | "a","s",&av, 72 | "b","s",&bv, 73 | "c","i",&cv, 74 | "d","?f",&dv, // no error if not defined, use default! 75 | NULL); 76 | printf("got a='%s' b='%s' c=%d d=%f\n",av,bv,cv,dv); 77 | 78 | res = llua_newtable(L); 79 | 80 | // two ways to set values: first uses _dynamic_ type of 81 | // value 82 | //llua_sets(res,"one","hello"); 83 | //llua_sets(res,"two",value_float(1.4)); 84 | // and second works like gets_v, using explicit format. 85 | llua_sets_v(res, 86 | "one","s","hello", 87 | "two","f",1.4, 88 | NULL); 89 | 90 | av = llua_gets(res,"one"); 91 | printf("got %s\n",av); 92 | 93 | llua_t *env = llua_newtable(L); 94 | err = llua_evalfile(L,"config.lua","",env); 95 | if (err) { 96 | fprintf(stderr,"could not load config.lua: %s\n",err); 97 | } else { 98 | av = llua_gets(env,"gammon"); 99 | printf("gammon was %s\n",av); 100 | } 101 | 102 | 103 | lua_close(L); 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /tests-method.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "llua.h" 3 | 4 | int main (int argc, char **argv) 5 | { 6 | lua_State *L = luaL_newstate(); 7 | luaL_openlibs(L); 8 | 9 | typedef llua_t *llua; 10 | 11 | llua G = llua_global(L); 12 | printf("end %d\n",lua_gettop(L)); 13 | llua out = llua_gets(G,"io.stdout"); 14 | 15 | err_t res = llua_callf(out,"ms","write","hello dolly!\n",""); 16 | if (res) { 17 | fprintf(stderr,"error %s\n",res); 18 | } 19 | 20 | #ifdef _WIN32 21 | const char *file = "tests-method.exe"; 22 | #else 23 | const char *file = "tests-method"; 24 | #endif 25 | if (argv[1]) 26 | file = argv[1]; 27 | llua open = llua_gets(G,"io.open"); 28 | llua in = llua_callf(open,"ss",file,"rb","r"); 29 | 30 | char* text = llua_callf(in,"ms","read","*a","r"); 31 | llua_callf(in,"m","close",""); 32 | printf("size was %d\n",array_len(text)); 33 | unref(text); 34 | 35 | lua_close(L); 36 | } 37 | -------------------------------------------------------------------------------- /tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int l_test(lua_State *L) { 8 | const char *s = lua_tostring(L,1); 9 | char *cs = str_new(s); 10 | *cs = toupper(*cs); 11 | lua_pushstring(L,cs); 12 | unref(cs); 13 | return 1; 14 | } 15 | 16 | int main (int argc, char **argv) 17 | { 18 | lua_State *L = luaL_newstate(); 19 | luaL_openlibs(L); 20 | 21 | llua_t *G = llua_global(L); 22 | llua_t *strfind = llua_gets(G,"string.find"); 23 | 24 | ///////// Gettable and Settable //////// 25 | // functions are _usually_ a no-no 26 | assert(! llua_gettable(strfind)); 27 | 28 | lua_pushstring(L,"hello dolly"); 29 | llua_t *str = llua_new(L,-1); 30 | 31 | // strings are fine, although not very useful 32 | assert(llua_gettable(str)); 33 | 34 | // but they aren't settable... 35 | assert(! llua_settable(str)); 36 | 37 | // this userdata is ok because it has a metatable with __index defined 38 | llua_t *out = llua_gets(G,"io.stdout"); 39 | assert(llua_gettable(out)); 40 | 41 | //////// passing C function to a Lua function //////////// 42 | llua_t *gsub = llua_gets(G,"string.gsub"); 43 | 44 | const char *s = llua_callf(gsub,"ssx", 45 | "$home is where the $heart is","%$(%a+)",l_test,"r"); 46 | 47 | assert(strcmp(s,"Home is where the Heart is")==0); 48 | 49 | //////// strings with embedded nuls 50 | const char *B = "one\0two\0three"; 51 | const char *C; 52 | int n = 13; 53 | lua_pushlstring(L,B,n); 54 | llua_pop_vars(L,"s",&C,NULL); 55 | assert(array_len(C) == n); 56 | unref(C); // we own the string - clean it up 57 | 58 | lua_close(L); 59 | } 60 | --------------------------------------------------------------------------------