├── .gitignore ├── COPYRIGHT ├── Makefile ├── README.md ├── TODO.md ├── bin └── native_objects ├── examples ├── Makefile ├── bench.nobj.lua ├── bench │ ├── method_call.nobj.lua │ ├── no_wrap_callback.nobj.lua │ └── wrapped_callback.nobj.lua ├── gd.nobj.lua ├── gdImage.nobj.lua ├── run_bench.lua └── test_gd.lua ├── native_objects.lua ├── native_objects ├── gen_dotfile.lua ├── gen_dump.lua ├── gen_lua.lua ├── gen_lua_ffi.lua ├── gen_luadoc.lua ├── gen_simple.lua ├── gen_swig.lua ├── interfaces.lua ├── lang_lua.lua ├── lang_swig.lua ├── record.lua └── stages.lua ├── project_template ├── CMakeLists.txt ├── PROJECT_README.md ├── README.regenerate.md ├── RUN_THIS_FIRST.lua ├── __mod_name__.nobj.lua ├── cmake │ └── LuaNativeObjects.cmake ├── lua-__mod_name__-scm-0.rockspec └── src │ └── object.nobj.lua └── rockspecs ├── luanativeobjects-0.5-0.rockspec ├── luanativeobjects-0.5-1.rockspec ├── luanativeobjects-luadoc-0.5-0.rockspec ├── luanativeobjects-luadoc-scm-0.rockspec └── luanativeobjects-scm-0.rockspec /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | *~ 3 | *.nobj.ffi.lua 4 | examples/*.so 5 | examples/*.o 6 | examples/*.c 7 | examples/*.png 8 | examples/*.dot 9 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 by Robert G. Jakabosky 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LUA_VERSION=5.4 2 | DESTDIR= 3 | PREFIX=/usr/local 4 | PREFIX_EXEC=$(PREFIX) 5 | BINDIR=$(PREFIX_EXEC)/bin 6 | DATADIR=$(PREFIX)/share 7 | LUA_LMOD=$(DATADIR)/lua/$(LUA_VERSION) 8 | 9 | all: 10 | @echo Nothing to do 11 | 12 | install: 13 | install -m644 -Dt $(DESTDIR)$(LUA_LMOD)/native_objects native_objects/*.lua 14 | install -m755 -Dt $(DESTDIR)$(BINDIR) native_objects.lua 15 | 16 | uninstall: 17 | rm -rf $(DESTDIR)$(LUA_LMOD)/native_objects 18 | rm -f $(DESTDIR)$(BINDIR)/native_objects.lua 19 | 20 | .PHONY: all install uninstall 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LuaNativeObjects 2 | ================ 3 | 4 | This is a bindings generator for Lua & LuaJIT2. It can be used to generator both standard Lua C API & LuaJIT2 FFI based bindings for C libraries. Both standard & FFI based bindings are packaged in a single shared library (.so or .dll) file. When the module is loaded in LuaJIT2 (please use git HEAD version of LuaJIT2 for now) it will try to load the FFI-based bindings in-place of the standard Lua API bindings. 5 | 6 | This bindings generator is design to create Object based bindings, instead of simple procedural bindings. So if you have a C structure (your object) and a set of C functions (your object's methods) that work on that structure, then you can turn them into a nice Lua object. 7 | 8 | It is still possible to generator procedural bindings for C functions that don't belong to an object (use a `package` record instead of an `object` record). 9 | 10 | 11 | Lua bindings using this generator 12 | --------------------------------- 13 | 14 | * [Lua-zmq](http://github.com/Neopallium/lua-zmq) 15 | * [Luagit2](http://github.com/Neopallium/luagit2) 16 | 17 | 18 | Template project for starting new bindings 19 | ------------------------------------------ 20 | 21 | There is a template for creating a new bindings project. This is a lua script for automatically creating a new project from the template. 22 | 23 | To start a new bindings project run: 24 | 25 | $ cd LuaNativeObjects/project_template/ 26 | $ lua RUN_THIS_FIRST.lua 27 | 28 | Then answer the questions and it will setup the new project. 29 | 30 | 31 | Example bindings 32 | ---------------- 33 | 34 | This example bindings code is take from the 'examples' folder. 35 | 36 | -- define the 'gd' module 37 | c_module "gd" { 38 | -- when set to true all objects will be registered as a global for easy access. 39 | use_globals = true, 40 | 41 | -- enable FFI bindings support. 42 | luajit_ffi = true, 43 | 44 | -- load GD shared library. 45 | ffi_load"gd", 46 | 47 | -- include library's header file 48 | include "gd.h", 49 | 50 | object "gdImage" { 51 | -- Use `ffi_cdef` records to pass extra C type info to FFI. 52 | ffi_cdef[[ 53 | typedef struct gdImageStruct gdImage; 54 | ]], 55 | -- The first constructor can be called as: gd.gdImage(x,y) or gd.gdImage.new(x,y) 56 | -- The default name for a constructor is 'new' 57 | constructor { 58 | c_call "gdImage *" "gdImageCreate" { "int", "sx", "int", "sy" } 59 | }, 60 | -- Other constructors can be called by there name: gd.gdImage.newTrueColor(x,y) 61 | constructor "newTrueColor" { 62 | c_call "gdImage *" "gdImageCreateTrueColor" { "int", "sx", "int", "sy" } 63 | }, 64 | -- A named destructor allows freeing of the object before it gets GC'ed. 65 | destructor "close" { 66 | c_method_call "void" "gdImageDestroy" {} 67 | }, 68 | 69 | method "color_allocate" { 70 | -- bindings for simple methods/functions can be generated with `c_method_call` or `c_call` 71 | -- records, which will generate both Lua API & FFI based bindings for the function. 72 | c_method_call "int" "gdImageColorAllocate" 73 | { "int", "r", "int", "g", "int", "b" } 74 | }, 75 | 76 | method "line" { 77 | c_method_call "void" "gdImageLine" 78 | { "int", "x1", "int", "y1", "int", "x2", "int", "y2", "int", "colour" } 79 | }, 80 | 81 | -- The next method need extra FFI types & function information. 82 | ffi_cdef[[ 83 | /* dummy typedef for "FILE" */ 84 | typedef struct FILE FILE; 85 | 86 | FILE *fopen(const char *path, const char *mode); 87 | int fclose(FILE *fp); 88 | 89 | void gdImagePng(gdImage *im, FILE *out); 90 | ]], 91 | -- This method is more complex and can't be generated with a simple `c_method_call` record. 92 | method "toPNG" { 93 | -- Use `var_in`/`var_out` records to define parameters & return values. 94 | var_in { "const char *", "name" }, 95 | -- Use `c_source` records to provide the C code for this method. 96 | c_source [[ 97 | FILE *pngout = fopen( ${name}, "wb"); 98 | gdImagePng(${this}, pngout); 99 | fclose(pngout); 100 | ]], 101 | -- if you want this method to have FFI-based bindings you will need to use a `ffi_source` record 102 | ffi_source [[ 103 | local pngout = ffi.C.fopen(${name}, "wb") 104 | C.gdImagePng(${this}, pngout) 105 | ffi.C.fclose(pngout) 106 | ]] 107 | }, 108 | } 109 | } 110 | 111 | Marking input & output variables 112 | -------------------------------- 113 | 114 | The `c_call` & `c_method_call` records have support for annotating the return type and function parameters to control how the generated bindings work. 115 | 116 | c_call "int>1" "func_name" 117 | { "ObjectType1", "&need_pointer_to_pointer_is_out_var_idx2>2", "ClassObject", "this<1" } 118 | 119 | `idx`, mark as an output that will be returned from the function back to Lua. The `idx` value controls the order of output values as returned to Lua. 122 | 123 | `!`, mark will cause owner-ship of an object to transfer between C & Lua. 124 | For output variables Lua will take owner-ship the object instance and free it when the object's `__gc` is called. 125 | For input variables Lua will give-up owner-ship of the object and only keep a reference to the object. 126 | 127 | `#var`, reference the length of the named variable `var`. This is used for 'string' type input parameters. 128 | 129 | `?`, mark the input parameter as optional. 130 | 131 | `&`, this will wrap the variable access with `&(var)` to pass a pointer to the value. This is needed for some C functions that have output parameters. 132 | 133 | `*`, this will wrap the variable access with `*(var)` to de-reference a pointer and pass it by-value. 134 | 135 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | * Documentation or examples! 5 | 6 | * Improve `c_call` record parameter handling: 7 | 8 | `@ref_name`, use field `ref_name` in the C object `this->ref_name` as a Lua reference, for input field it is set, for output field the reference is pushed. 9 | 10 | 11 | * Improve class type mapping & push/pop function over-rides: 12 | 13 |
14 |     object "LuaClassName" "C_struct_name" {
15 |     	-- create a wrapper struct.
16 |     	struct_def [[
17 |     	C_struct_name parent;
18 |     	lua_State     *L;
19 |     	int           ref;
20 |     ]],
21 |       custom_push_func = "name_of_push_function",
22 |     	custom_check_func = "name_of_check_function",
23 |     }
24 | 
25 | 26 | * Add better support for enumerations. Right now you can use a `package` with constants `const` records: 27 | 28 |
29 |     package "ENUM" {
30 |     	const "NONE"      { 0 },
31 |     	const "VAL_1"     { 1 },
32 |     	const "VAL_2"     { 2 },
33 |     	const "VAL_3"     { 3 },
34 |     	const "VAL_4"     { 4 },
35 |     }
36 | 
37 | 38 | # Add better support for bit flag parameters. 39 | 40 | -------------------------------------------------------------------------------- /bin/native_objects: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- Copyright (c) 2012 by Robert G. Jakabosky 3 | -- 4 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 5 | -- of this software and associated documentation files (the "Software"), to deal 6 | -- in the Software without restriction, including without limitation the rights 7 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | -- copies of the Software, and to permit persons to whom the Software is 9 | -- furnished to do so, subject to the following conditions: 10 | -- 11 | -- The above copyright notice and this permission notice shall be included in 12 | -- all copies or substantial portions of the Software. 13 | -- 14 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | -- THE SOFTWARE. 21 | 22 | local cmd_args = arg 23 | arg = nil 24 | 25 | local run = require("native_objects").run 26 | 27 | -- Run LuaNativeObjects 28 | run(cmd_args) 29 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g -O2 -march=native 2 | 3 | all: gd.so bench.so gd.png bench.png 4 | 5 | test: gd.so 6 | lua test_gd.lua 7 | 8 | gd.so: LDLIBS=-lgd 9 | gd.nobj.c gd.nobj.dot: gd*.nobj.lua 10 | bench.nobj.c bench.nobj.dot: bench/*.nobj.lua 11 | 12 | %.nobj.c %.nobj.ffi.lua: %.nobj.lua 13 | native_objects.lua -outpath ./ -gen lua $< 14 | 15 | %.so: %.nobj.c 16 | $(CC) $(CFLAGS) $(LDFLAGS) -fPIC -shared $(LDLIBS) -o $@ $< 17 | 18 | %.nobj.dot: %.nobj.lua 19 | native_objects.lua -outpath ./ -gen dotfile $< 20 | 21 | %.png: %.nobj.dot 22 | dot -Tpng $< >$@ 23 | 24 | clean: 25 | rm -f *.nobj.c *.nobj.ffi.lua *.so *.png *.dot 26 | 27 | .PHONY: all test clean 28 | -------------------------------------------------------------------------------- /examples/bench.nobj.lua: -------------------------------------------------------------------------------- 1 | 2 | -- make generated variable nicer. 3 | set_variable_format "%s%d" 4 | 5 | -- define the 'bench' module 6 | c_module "bench" { 7 | use_globals = false, 8 | hide_meta_info = false, 9 | 10 | luajit_ffi = true, 11 | --luajit_ffi = false, 12 | 13 | luajit_ffi_load_cmodule = true, 14 | 15 | -- here we include the bindings file for each object into this module. 16 | subfiles { 17 | "bench/method_call.nobj.lua", 18 | "bench/wrapped_callback.nobj.lua", 19 | "bench/no_wrap_callback.nobj.lua", 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/bench/method_call.nobj.lua: -------------------------------------------------------------------------------- 1 | object "method_call" { 2 | c_source[[ 3 | typedef struct method_call method_call; 4 | 5 | #define DEFAULT_PTR ((method_call *)0xDEADBEEF) 6 | 7 | method_call *method_call_create() { 8 | return DEFAULT_PTR; 9 | } 10 | 11 | void method_call_destroy(method_call *call) { 12 | assert(call == DEFAULT_PTR); 13 | } 14 | 15 | int method_call_null(method_call *call) { 16 | return 0; 17 | } 18 | 19 | ]], 20 | -- create object 21 | constructor { 22 | c_call "method_call *" "method_call_create" {}, 23 | }, 24 | -- destroy object 25 | destructor "close" { 26 | c_method_call "void" "method_call_destroy" {}, 27 | }, 28 | 29 | method "simple" { 30 | c_source[[ 31 | if(${this} != DEFAULT_PTR) { 32 | luaL_error(L, "INVALID PTR: %p != %p", ${this}, DEFAULT_PTR); 33 | } 34 | ]], 35 | ffi_source[[ 36 | if(${this} == nil) then 37 | error(string.format("INVALID PTR: %p == nil", ${this})); 38 | end 39 | ]], 40 | }, 41 | 42 | method "null" { 43 | c_method_call "int" "method_call_null" {}, 44 | }, 45 | 46 | } 47 | -------------------------------------------------------------------------------- /examples/bench/no_wrap_callback.nobj.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- C code for NoWrapTestObj object 3 | -- 4 | c_source "typedefs" [[ 5 | typedef struct NoWrapTestObj NoWrapTestObj; 6 | typedef int (*NoWrapTestObjFunc)(NoWrapTestObj *obj, int idx, void *data); 7 | 8 | struct NoWrapTestObj { 9 | uint32_t some_state; 10 | NoWrapTestObjFunc func; 11 | void *func_data; 12 | }; 13 | 14 | NoWrapTestObj *nowrap_testobj_new() { 15 | NoWrapTestObj *obj = calloc(1, sizeof(NoWrapTestObj)); 16 | obj->some_state = 0xDEADBEEF; 17 | return obj; 18 | } 19 | 20 | void nowrap_testobj_destroy(NoWrapTestObj *obj) { 21 | assert(obj->some_state == 0xDEADBEEF); 22 | free(obj); 23 | } 24 | 25 | int nowrap_testobj_register(NoWrapTestObj *obj, NoWrapTestObjFunc func, void *func_data) { 26 | obj->func = func; 27 | obj->func_data = func_data; 28 | return 0; 29 | } 30 | 31 | int nowrap_testobj_run(NoWrapTestObj *obj, int run) { 32 | int rc = 0; 33 | int i; 34 | for(i = 0; i < run; i++) { 35 | rc = obj->func(obj, i, obj->func_data); 36 | if(rc < 0) break; 37 | } 38 | return rc; 39 | } 40 | 41 | ]] 42 | 43 | -- define a C callback function type: 44 | callback_type "NoWrapTestObjFunc" "int" { "NoWrapTestObj *", "this", "int", "idx", "void *", "%data" } 45 | -- callback_type "" "" { 46 | -- -- call back function parameters. 47 | -- "", "%", -- the '%' marks which parameter holds the wrapped object. 48 | -- "", "", 49 | -- } 50 | 51 | object "NoWrapTestObj" { 52 | -- create object 53 | constructor { 54 | c_call "NoWrapTestObj *" "nowrap_testobj_new" {}, 55 | }, 56 | -- destroy object 57 | destructor "close" { 58 | c_method_call "void" "nowrap_testobj_destroy" {}, 59 | }, 60 | 61 | method "register" { 62 | -- Create an object wrapper for the "NoWrapTestObj" which will hold a reference to the 63 | -- lua_State & Lua callback function. 64 | callback { "NoWrapTestObjFunc", "func", "func_data", owner = "this", 65 | -- C code to run if Lua callback function throws an error. 66 | c_source[[${ret} = -1;]], 67 | ffi_source[[${ret} = -1;]], 68 | }, 69 | -- callback { "", "", "", 70 | -- -- c_source/ffi_source/c_call/etc... for error handling. 71 | -- }, 72 | c_method_call "int" "nowrap_testobj_register" { "NoWrapTestObjFunc", "func", "void *", "func_data" }, 73 | }, 74 | 75 | method "run" { 76 | c_method_call "int" "nowrap_testobj_run" { "int", "num" }, 77 | }, 78 | 79 | } 80 | -------------------------------------------------------------------------------- /examples/bench/wrapped_callback.nobj.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- C code for TestObj object 3 | -- 4 | c_source "typedefs" [[ 5 | typedef struct TestObj TestObj; 6 | typedef int (*TestObjFunc)(TestObj *obj, int idx); 7 | 8 | struct TestObj { 9 | uint32_t some_state; 10 | TestObjFunc func; 11 | }; 12 | 13 | void testobj_init(TestObj *obj, TestObjFunc func) { 14 | obj->some_state = 0xDEADBEEF; 15 | obj->func = func; 16 | } 17 | 18 | void testobj_destroy(TestObj *obj) { 19 | assert(obj->some_state == 0xDEADBEEF); 20 | } 21 | 22 | int testobj_run(TestObj *obj, int run) { 23 | int rc = 0; 24 | int i; 25 | for(i = 0; i < run; i++) { 26 | rc = obj->func(obj, i); 27 | if(rc < 0) break; 28 | } 29 | return rc; 30 | } 31 | 32 | ]] 33 | 34 | -- define a C callback function type: 35 | callback_type "TestObjFunc" "int" { "TestObj *", "%this", "int", "idx" } 36 | -- callback_type "" "" { 37 | -- -- call back function parameters. 38 | -- "", "%", -- the '%' marks which parameter holds the wrapped object. 39 | -- "", "", 40 | -- } 41 | 42 | object "TestObj" { 43 | -- create object 44 | constructor { 45 | -- Create an object wrapper for the "TestObj" which will hold a reference to the 46 | -- lua_State & Lua callback function. 47 | callback { "TestObjFunc", "func", "this", 48 | -- C code to run if Lua callback function throws an error. 49 | c_source[[${ret} = -1;]], 50 | ffi_source[[${ret} = -1;]], 51 | }, 52 | -- callback { "", "", "", 53 | -- -- c_source/ffi_source/c_call/etc... for error handling. 54 | -- }, 55 | c_call "void" "testobj_init" { "TestObj *", "this", "TestObjFunc", "func" }, 56 | }, 57 | -- destroy object 58 | destructor "close" { 59 | c_method_call "void" "testobj_destroy" {}, 60 | }, 61 | 62 | method "run" { 63 | c_method_call "int" "testobj_run" { "int", "num" }, 64 | }, 65 | 66 | } 67 | -------------------------------------------------------------------------------- /examples/gd.nobj.lua: -------------------------------------------------------------------------------- 1 | -- define the 'gd' module 2 | c_module "gd" { 3 | -- when set to true all objects will be registered as a global for easy access. 4 | use_globals = true, 5 | 6 | -- enable FFI bindings support. 7 | luajit_ffi = true, 8 | 9 | -- load GD shared library. 10 | ffi_load"gd", 11 | 12 | -- include library's header file 13 | include "gd.h", 14 | 15 | -- here we include the bindings file for each object into this module. 16 | subfiles { 17 | "gdImage.nobj.lua" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/gdImage.nobj.lua: -------------------------------------------------------------------------------- 1 | object "gdImage" { 2 | -- Use `ffi_cdef` records to pass extra C type info to FFI. 3 | ffi_cdef[[ 4 | typedef struct gdImageStruct gdImage; 5 | ]], 6 | -- The first constructor can be called as: gd.gdImage(x,y) or gd.gdImage.new(x,y) 7 | -- The default name for a constructor is 'new' 8 | constructor { 9 | c_call "gdImage *" "gdImageCreate" { "int", "sx", "int", "sy" } 10 | }, 11 | -- Other constructors can be called by there name: gd.gdImage.newTrueColor(x,y) 12 | constructor "newTrueColor" { 13 | c_call "gdImage *" "gdImageCreateTrueColor" { "int", "sx", "int", "sy" } 14 | }, 15 | -- A named destructor allows freeing of the object before it gets GC'ed. 16 | destructor "close" { 17 | c_method_call "void" "gdImageDestroy" {} 18 | }, 19 | 20 | method "color_allocate" { 21 | -- bindings for simple methods/functions can be generated with `c_method_call` or `c_call` 22 | -- records, which will generate both Lua API & FFI based bindings for the function. 23 | c_method_call "int" "gdImageColorAllocate" 24 | { "int", "r", "int", "g", "int", "b" } 25 | }, 26 | 27 | method "line" { 28 | c_method_call "void" "gdImageLine" 29 | { "int", "x1", "int", "y1", "int", "x2", "int", "y2", "int", "colour" } 30 | }, 31 | 32 | -- The next method need extra FFI types & function information. 33 | ffi_cdef[[ 34 | /* dummy typedef for "FILE" */ 35 | typedef struct FILE FILE; 36 | 37 | FILE *fopen(const char *path, const char *mode); 38 | int fclose(FILE *fp); 39 | 40 | void gdImagePng(gdImage *im, FILE *out); 41 | ]], 42 | -- This method is more complex and can't be generated with a simple `c_method_call` record. 43 | method "toPNG" { 44 | -- Use `var_in`/`var_out` records to define parameters & return values. 45 | var_in { "const char *", "name" }, 46 | -- Use `c_source` records to provide the C code for this method. 47 | c_source [[ 48 | FILE *pngout = fopen( ${name}, "wb"); 49 | gdImagePng(${this}, pngout); 50 | fclose(pngout); 51 | ]], 52 | -- if you want this method to have FFI-based bindings you will need to use a `ffi_source` record 53 | ffi_source [[ 54 | local pngout = ffi.C.fopen(${name}, "wb") 55 | C.gdImagePng(${this}, pngout) 56 | ffi.C.fclose(pngout) 57 | ]] 58 | }, 59 | } 60 | -------------------------------------------------------------------------------- /examples/run_bench.lua: -------------------------------------------------------------------------------- 1 | 2 | local bench = require"bench" 3 | local zmq = require"zmq" 4 | 5 | local N = tonumber(arg[1] or 10000000) 6 | 7 | local function run_bench(action_name, N, func) 8 | 9 | local timer = zmq.stopwatch_start() 10 | 11 | func() 12 | 13 | local elapsed = timer:stop() 14 | if elapsed == 0 then elapsed = 1 end 15 | 16 | local throughput = N / (elapsed / 1000000) 17 | 18 | print(string.format("finished in %i sec, %i millisec and %i microsec, %i '%s'/s", 19 | (elapsed / 1000000), (elapsed / 1000) % 1000, (elapsed % 1000), throughput, action_name 20 | )) 21 | end 22 | 23 | -- 24 | -- Run benchmarks of method calls. 25 | -- 26 | 27 | local test = bench.method_call() 28 | 29 | run_bench('C API method calls', N, function() 30 | for i=1,N do 31 | test:simple() 32 | end 33 | end) 34 | 35 | run_bench('null method calls', N, function() 36 | for i=1,N do 37 | test:null() 38 | end 39 | end) 40 | 41 | 42 | -- 43 | -- Run benchmarks of C callbacks. 44 | -- 45 | 46 | local function callback(idx) 47 | return 0 48 | end 49 | local test = bench.TestObj(callback) 50 | 51 | run_bench('wrapped state C callback', N, function() 52 | test:run(N) 53 | end) 54 | 55 | -- 56 | -- Run benchmarks of no wrap state C callbacks. 57 | -- 58 | 59 | local function callback(idx) 60 | return 0 61 | end 62 | local test = bench.NoWrapTestObj() 63 | test:register(callback) 64 | 65 | run_bench('no wrap state C callback', N, function() 66 | test:run(N) 67 | end) 68 | 69 | -------------------------------------------------------------------------------- /examples/test_gd.lua: -------------------------------------------------------------------------------- 1 | 2 | local gd = require"gd" 3 | 4 | local x = 200 5 | local y = 200 6 | local img = gd.gdImage(x,y) 7 | 8 | local white = img:color_allocate(0xff, 0xff, 0xff) 9 | local blue = img:color_allocate(0, 0, 0xff) 10 | local red = img:color_allocate(0xff, 0, 0) 11 | 12 | -- draw lines 13 | for i=1,100000 do 14 | img:line(0, 0, y, x, blue) 15 | img:line(0, y, y, 0, red) 16 | end 17 | 18 | -- write image 19 | img:toPNG('test.png') 20 | 21 | -------------------------------------------------------------------------------- /native_objects.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- Copyright (c) 2012 by Robert G. Jakabosky 3 | -- 4 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 5 | -- of this software and associated documentation files (the "Software"), to deal 6 | -- in the Software without restriction, including without limitation the rights 7 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | -- copies of the Software, and to permit persons to whom the Software is 9 | -- furnished to do so, subject to the following conditions: 10 | -- 11 | -- The above copyright notice and this permission notice shall be included in 12 | -- all copies or substantial portions of the Software. 13 | -- 14 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | -- THE SOFTWARE. 21 | 22 | require("native_objects.record") 23 | 24 | local tconcat=table.concat 25 | local tremove=table.remove 26 | local assert=assert 27 | local error=error 28 | local type=type 29 | local io=io 30 | local print=print 31 | local pairs=pairs 32 | local dofile=dofile 33 | local tostring=tostring 34 | local require=require 35 | 36 | -- 37 | -- Switch language we are generating bindings for. 38 | -- 39 | gen_lang="lua" 40 | 41 | -- gen_module module 42 | local gen_module="dump" 43 | 44 | -- global mapping of c_types to records. 45 | local c_types={} 46 | local function reset() 47 | clear_all_records() 48 | c_types={} 49 | end 50 | 51 | -- 52 | -- C-Type functions 53 | -- 54 | local function strip_c_type(c_type) 55 | -- strip const from c_type 56 | c_type = c_type:gsub("^%s*const%s*","") 57 | -- strip spaces from c_type 58 | c_type = c_type:gsub("%s*","") 59 | return c_type 60 | end 61 | 62 | function new_c_type(c_type, rec) 63 | c_type = strip_c_type(c_type) 64 | local old = c_types[c_type] 65 | if old and old ~= rec then 66 | print("WARNING changing c_type:", c_type, "from:", old, "to:", rec) 67 | end 68 | c_types[c_type] = rec 69 | end 70 | 71 | local function real_c_type_resolver(self) 72 | local c_type = self._c_type 73 | local _type = c_types[c_type] 74 | -- if type unknown see if it is a pointer. 75 | if _type == nil and c_type ~= "void*" and c_type:find("*",1,true) ~= nil then 76 | -- map it to a generic pointer. 77 | print("WARNING maping un-resolved pointer type '" .. c_type .."' to 'void *'") 78 | return resolve_c_type("void*") 79 | end 80 | if _type == nil then 81 | print("Unkown type: " .. c_type) 82 | return _type 83 | end 84 | rawset(self, "_type", _type) 85 | _type._in_use = true 86 | return _type 87 | end 88 | local resolve_meta = { 89 | __index = function(self, key) 90 | local _type = rawget(self, "_type") -- check for cached type. 91 | if _type == nil then 92 | -- try to resolve c_type dynamically 93 | _type = real_c_type_resolver(self) 94 | end 95 | if _type then 96 | return _type[key] 97 | else 98 | print("type not resolved yet: " .. self._c_type) 99 | end 100 | return nil 101 | end, 102 | __newindex = function(self, key, value) 103 | local _type = rawget(self, "_type") -- check for cached type. 104 | if _type == nil then 105 | -- try to resolve c_type dynamically 106 | _type = real_c_type_resolver(self) 107 | end 108 | if _type then 109 | _type[key] = value 110 | else 111 | print("type not resolved yet: " .. self._c_type) 112 | end 113 | end, 114 | __len = function(self) 115 | local _type = rawget(self, "_type") -- check for cached type. 116 | if _type == nil then 117 | -- try to resolve c_type dynamically 118 | _type = real_c_type_resolver(self) 119 | end 120 | if _type then 121 | return #_type 122 | else 123 | error("type not resolved yet: " .. self._c_type) 124 | end 125 | end, 126 | __eq = function(op1, op2) 127 | return op1._c_type == op2._c_type 128 | end, 129 | } 130 | local cache_resolvers={} 131 | function resolve_c_type(c_type) 132 | local c_type = strip_c_type(c_type) 133 | local resolver = cache_resolvers[c_type] 134 | if resolver == nil then 135 | resolver = {_c_type = c_type} 136 | setmetatable(resolver, resolve_meta) 137 | cache_resolvers[c_type] = resolver 138 | end 139 | return resolver 140 | end 141 | 142 | local function is_resolver(val) 143 | return (getmetatable(val) == resolve_meta) 144 | end 145 | 146 | function resolve_rec(rec) 147 | if rec.c_type ~= nil and rec.c_type_rec == nil then 148 | rec.c_type_rec = resolve_c_type(rec.c_type) 149 | end 150 | end 151 | 152 | reg_stage_parser("resolve_types", { 153 | unknown = function(self, rec, parent) 154 | -- find all c_type resolvers. 155 | for key,val in pairs(rec) do 156 | -- force all types to be resolved. 157 | if is_resolver(val) then 158 | rec[key] = real_c_type_resolver(val) 159 | end 160 | end 161 | end, 162 | }) 163 | 164 | -- 165 | -- Record functions -- Used to create new records. 166 | -- 167 | local function ctype(name, rec, rec_type) 168 | rec = make_record(rec, rec_type) 169 | -- record's c_type 170 | rec.name = name 171 | rec.c_type = name 172 | rec._is_c_type = true 173 | -- map the c_type to this record 174 | new_c_type(name, rec) 175 | return rec 176 | end 177 | 178 | function basetype(name) 179 | return function (lang_type) 180 | return function (default) 181 | -- make it an basetype record. 182 | local rec = ctype(name,{},"basetype") 183 | -- lang type 184 | rec.lang_type = lang_type 185 | -- default value 186 | rec.default = default 187 | return rec 188 | end 189 | end 190 | end 191 | 192 | function doc(text) 193 | return make_record({ text = text }, 'doc') 194 | end 195 | 196 | function error_code(name) 197 | return function (c_type) 198 | return function (rec) 199 | -- make error_code record 200 | ctype(name,rec,"error_code") 201 | rec.c_type = c_type 202 | -- mark this type as an error code. 203 | rec._is_error_code = true 204 | end 205 | end 206 | end 207 | 208 | function object(name) 209 | return function (rec) 210 | -- make it an object record. 211 | local userdata_type = rec.userdata_type or 'generic' 212 | rec.userdata_type = userdata_type 213 | rec.has_obj_flags = true 214 | if userdata_type == 'generic' or userdata_type == 'embed' or userdata_type == 'simple ptr' then 215 | ctype(name .. " *", rec,"object") 216 | rec.is_ptr = true 217 | rec.name = name 218 | -- map the c_type to this record 219 | new_c_type(name, rec) 220 | if userdata_type == 'embed' or userdata_type == 'simple ptr' then 221 | rec.no_weak_ref = true 222 | rec.has_obj_flags = false 223 | end 224 | else 225 | rec.no_weak_ref = true 226 | if userdata_type == 'simple' or userdata_type == 'simple ptr' then 227 | rec.has_obj_flags = false 228 | end 229 | ctype(name, rec, "object") 230 | end 231 | -- check object type flags. 232 | if rec.no_weak_ref == nil then 233 | rec.no_weak_ref = false 234 | end 235 | -- check if this type generates errors on NULLs 236 | if rec.error_on_null then 237 | -- create 'is_error_check' code 238 | rec.is_error_check = function(rec) 239 | return "(NULL == ${" .. rec.name .. "})" 240 | end 241 | rec.ffi_is_error_check = function(rec) 242 | return "(nil == ${" .. rec.name .. "})" 243 | end 244 | end 245 | return rec 246 | end 247 | end 248 | 249 | function import_object(mod) 250 | return function (name) 251 | return function (rec) 252 | rec = rec or {} 253 | local userdata_type = rec.userdata_type or 'generic' 254 | rec.userdata_type = userdata_type 255 | if userdata_type == 'generic' or userdata_type == 'embed' or userdata_type == 'simple ptr' then 256 | ctype(name .. " *", rec,"import_object") 257 | rec.is_ptr = true 258 | rec.name = name 259 | -- map the c_type to this record 260 | new_c_type(name, rec) 261 | else 262 | ctype(name, rec, "import_object") 263 | end 264 | -- external module name. 265 | rec.mod_name = mod 266 | return rec 267 | end 268 | end 269 | end 270 | 271 | function interface(name) 272 | return function (rec) 273 | local rec = ctype(name, rec,"interface") 274 | rec.name = name 275 | rec.is_interface = true 276 | return rec 277 | end 278 | end 279 | 280 | function interface_method(return_type) 281 | return function (name) 282 | return function (params) 283 | local rec = make_record({}, "interface_method") 284 | rec.is_interface_method = true 285 | -- function type name. 286 | rec.name = name 287 | -- parse return c_type. 288 | rec.ret = return_type or "void" 289 | -- parse params 290 | if params == nil then params = {} end 291 | rec.params = params 292 | return rec 293 | end 294 | end 295 | end 296 | 297 | function implements(name) 298 | return function (rec) 299 | local rec = make_record(rec, "implements") 300 | rec.is_implements = true 301 | -- interface name 302 | rec.name = name 303 | rec.interface_rec = resolve_c_type(rec.name) 304 | return rec 305 | end 306 | end 307 | 308 | function implement_method(name) 309 | return function (rec) 310 | local rec = make_record(rec, "implement_method") 311 | rec.name = name 312 | return rec 313 | end 314 | end 315 | 316 | function submodule(name) 317 | return function (rec) 318 | rec = object(name)(rec) 319 | rec.register_as_submodule = true 320 | return rec 321 | end 322 | end 323 | 324 | local function _package(name) 325 | if type(name) == 'table' then 326 | local rec = name 327 | rec = object('_MOD_GLOBAL_')(rec) 328 | rec.is_package = true 329 | rec.is_mod_global = true 330 | return rec 331 | end 332 | return function (rec) 333 | rec = object(name)(rec) 334 | rec.is_package = true 335 | return rec 336 | end 337 | end 338 | 339 | function meta_object(name) 340 | return function (rec) 341 | rec = object(name)(rec) 342 | rec.is_package = true 343 | rec.is_meta = true 344 | return rec 345 | end 346 | end 347 | 348 | function extends(name) 349 | return function (rec) 350 | rec = make_record(rec, "extends") 351 | -- base object name 352 | rec.name = name 353 | -- check for cast_type 354 | if rec.cast_type == nil then 355 | rec.cast_offset = 0 356 | rec.cast_type = 'direct' 357 | end 358 | return rec 359 | end 360 | end 361 | 362 | function dyn_caster(rec) 363 | rec = make_record(rec, "dyn_caster") 364 | return rec 365 | end 366 | 367 | function option(name) 368 | return function (rec) 369 | rec = make_record(rec, "option") 370 | -- option name. 371 | rec.name = name 372 | return rec 373 | end 374 | end 375 | 376 | function field(c_type) 377 | return function (name) 378 | return function (rec) 379 | local access = rec and rec[1] or nil 380 | rec = make_record(rec, "field") 381 | -- field's c_type 382 | rec.c_type = c_type 383 | -- field's name 384 | rec.name = name 385 | -- access permissions 386 | if type(access) == 'string' then 387 | access = access:lower() 388 | -- check for write access 389 | if access == 'rw' then 390 | rec.is_writable = true 391 | elseif access == 'ro' then 392 | rec.is_writable = false 393 | else 394 | rec.is_writable = false 395 | end 396 | elseif rec.is_writable == nil then 397 | rec.is_writable = false 398 | end 399 | return rec 400 | end 401 | end 402 | end 403 | 404 | function const(name) 405 | return function (rec) 406 | local value = rec[1] 407 | rec = make_record(rec, "const") 408 | -- field's name 409 | rec.name = name 410 | -- field's value 411 | rec.value = value 412 | return rec 413 | end 414 | end 415 | 416 | function const_def(name) 417 | return function (rec) 418 | local value = rec[1] 419 | rec = make_record(rec, "const") 420 | -- this is a constant definition. 421 | rec.is_define = true 422 | -- default to 'number' type. 423 | rec.vtype = rec.vtype or 'number' 424 | -- field's name 425 | rec.name = name 426 | -- field's value 427 | rec.value = value 428 | return rec 429 | end 430 | end 431 | 432 | function constants(values) 433 | local rec = make_record({}, "constants") 434 | rec.values = values 435 | return rec 436 | end 437 | 438 | function export_definitions(values) 439 | if type(values) == 'string' then 440 | local name = values 441 | return function(values) 442 | return _package(name)({ 443 | map_constants_bidirectional = true, 444 | export_definitions(values) 445 | }) 446 | end 447 | end 448 | local rec = make_record({}, "export_definitions") 449 | rec.values = values 450 | return rec 451 | end 452 | 453 | function include(file) 454 | local rec = {} 455 | rec = make_record(rec, "include") 456 | rec.is_system = false 457 | rec.file = file 458 | return rec 459 | end 460 | 461 | function sys_include(file) 462 | local rec = {} 463 | rec = make_record(rec, "include") 464 | rec.is_system = true 465 | rec.file = file 466 | return rec 467 | end 468 | 469 | function c_function(name) 470 | return function (rec) 471 | rec = make_record(rec, "c_function") 472 | -- function name. 473 | rec.name = name 474 | -- function type (normal function or object method) 475 | rec.f_type = "function" 476 | -- variable lookup 477 | rec.get_var = function(self, name) 478 | for i=1,#self do 479 | local var = self[i] 480 | if is_record(var) and var.name == name then 481 | return var 482 | end 483 | end 484 | return nil 485 | end 486 | return rec 487 | end 488 | end 489 | 490 | local meta_methods = { 491 | __str__ = true, 492 | __eq__ = true, 493 | -- Lua metamethods 494 | __add = true, 495 | __sub = true, 496 | __mul = true, 497 | __div = true, 498 | __mod = true, 499 | __pow = true, 500 | __unm = true, 501 | __len = true, 502 | __concat = true, 503 | __eq = true, 504 | __lt = true, 505 | __le = true, 506 | __gc = true, 507 | __tostring = true, 508 | __index = true, 509 | __newindex = true, 510 | } 511 | function method(name) 512 | return function (rec) 513 | -- handle the same way as normal functions 514 | rec = c_function(name)(rec) 515 | -- mark this function as a method. 516 | rec._is_method = true 517 | -- if the method is a destructor, then also make it a meta method 518 | -- to be used for garbagecollection 519 | if rec.is_destructor then 520 | rec._is_meta_method = true 521 | end 522 | rec.f_type = "method" 523 | -- check if method is a meta-method. 524 | rec._is_meta_method = meta_methods[rec.name] 525 | return rec 526 | end 527 | end 528 | 529 | function constructor(name) 530 | return function (rec) 531 | if type(name) == 'table' then rec = name; name = 'new' end 532 | -- handle the same way as normal method 533 | rec = method(name)(rec) 534 | -- mark this method as the constructor 535 | rec.is_constructor = true 536 | return rec 537 | end 538 | end 539 | 540 | function destructor(name) 541 | return function (rec) 542 | if type(name) == 'table' then 543 | rec = name 544 | rec._is_hidden = true 545 | name = 'delete' 546 | end 547 | -- handle the same way as normal method 548 | rec = method(name)(rec) 549 | -- mark this method as the destructor 550 | rec.is_destructor = true 551 | -- also register it as a metamethod for garbagecollection. 552 | rec._is_meta_method = true 553 | return rec 554 | end 555 | end 556 | 557 | function method_new(rec) 558 | return constructor(rec) 559 | end 560 | 561 | function method_delete(rec) 562 | return destructor(rec) 563 | end 564 | 565 | function define(name) 566 | return function(value) 567 | local rec = make_record({}, "define") 568 | rec.name = name 569 | rec.value = value 570 | return rec 571 | end 572 | end 573 | 574 | function c_source(part) 575 | return function(src) 576 | if src == nil then 577 | src = part 578 | part = nil 579 | end 580 | local rec = make_record({}, "c_source") 581 | rec.part = part or "src" 582 | rec.src = src 583 | return rec 584 | end 585 | end 586 | 587 | local function strip_variable_tokens(val, tokens) 588 | local prefix, val, postfix = val:match("^([!@&*(?#]*)([%w_ *]*)([@?)<>]*[0-9]*)") 589 | return prefix .. (tokens or '') .. postfix, val 590 | end 591 | 592 | function clean_variable_type_name(vtype,vname) 593 | local tokens 594 | tokens, vtype = strip_variable_tokens(vtype) 595 | tokens, vname = strip_variable_tokens(vname) 596 | return vtype, vname 597 | end 598 | 599 | local function parse_variable_name(var) 600 | -- no parsing needed for '' 601 | if var.c_type == '' then return end 602 | -- strip tokens from variable name & c_type 603 | local tokens, name, c_type 604 | tokens, name = strip_variable_tokens(var.name) 605 | tokens, c_type = strip_variable_tokens(var.c_type, tokens) 606 | -- set variable name to stripped name 607 | var.name = name 608 | var.c_type = c_type 609 | -- parse prefix & postfix tokens 610 | local n=1 611 | local len = #tokens 612 | while n <= len do 613 | local tok = tokens:sub(n,n) 614 | n = n + 1 615 | if tok == '*' then 616 | assert(var.wrap == nil, "Variable already has a access wrapper.") 617 | var.wrap = '*' 618 | elseif tok == '&' then 619 | assert(var.wrap == nil, "Variable already has a access wrapper.") 620 | var.wrap = '&' 621 | elseif tok == '#' then 622 | var.is_length_ref = true 623 | var.length = var.name .. '_len' -- default name for length variable. 624 | elseif tok == '?' then 625 | var.is_optional = true 626 | -- eat the rest of the tokens as the default value. 627 | if n <= len then 628 | var.default = tokens:sub(n) 629 | end 630 | break 631 | elseif tok == '!' then 632 | var.own = true 633 | elseif tok == '@' then 634 | var.is_ref_field = true 635 | error("`@ref_name` not yet supported.") 636 | elseif tok == '<' or tok == '>' then 637 | local idx = tokens:match('([0-9]*)', n) 638 | assert(idx, "Variable already has a stack order 'idx'") 639 | var.idx = tonumber(idx) 640 | if tok == '>' then 641 | -- force this variable to an output type. 642 | var._rec_type = 'var_out' 643 | else 644 | assert(var._rec_type == 'var_in', "Can't make an output variable into an input variable.") 645 | end 646 | -- skip index value. 647 | if idx then n = n + #idx end 648 | elseif tok == '(' or tok == ')' then 649 | var._rec_type = 'var_out' 650 | var.is_temp = true 651 | end 652 | end 653 | -- do some validation. 654 | if var.own then 655 | assert(var._rec_type == 'var_out', "Only output variables can be marked as 'owned'.") 656 | end 657 | end 658 | 659 | function var_out(rec) 660 | rec = make_record(rec, "var_out") 661 | -- out variable's c_type 662 | rec.c_type = tremove(rec, 1) 663 | -- out variable's name 664 | rec.name = tremove(rec, 1) 665 | -- parse tags from name. 666 | parse_variable_name(rec) 667 | -- check if variable has/needs a length variable. 668 | if rec.length or (rec.need_buffer and rec.has_length == nil) then 669 | rec.has_length = true 670 | end 671 | if rec.has_length then 672 | rec.length = rec.length or (rec.name .. '_len') 673 | end 674 | resolve_rec(rec) 675 | return rec 676 | end 677 | 678 | function var_in(rec) 679 | rec = make_record(rec, "var_in") 680 | -- in variable's c_type 681 | rec.c_type = tremove(rec, 1) 682 | -- in variable's name 683 | rec.name = tremove(rec, 1) 684 | -- parse tags from name. 685 | parse_variable_name(rec) 686 | resolve_rec(rec) 687 | return rec 688 | end 689 | 690 | function tmp_var(rec) 691 | rec = var_out(rec) 692 | rec.is_temp = true 693 | return rec 694 | end 695 | 696 | -- A reference to another var_in/var_out variable. 697 | -- This is used by `c_call` records. 698 | function var_ref(var) 699 | local rec = {} 700 | -- copy details from var_* record 701 | for k,v in pairs(var) do rec[k] = v end 702 | -- make variable reference. 703 | rec = make_record(rec, "var_ref") 704 | -- in variable's c_type 705 | rec.c_type = var.c_type 706 | -- in variable's name 707 | rec.name = var.name 708 | resolve_rec(rec) 709 | return rec 710 | end 711 | 712 | function c_call(return_type) 713 | return function (cfunc) 714 | return function (params) 715 | local rec = make_record({}, "c_call") 716 | -- parse return c_type. 717 | rec.ret = return_type or "void" 718 | -- parse c function to call. 719 | rec.cfunc = cfunc 720 | -- parse params 721 | rec.params = params 722 | if rec.params == nil then rec.params = {} end 723 | return rec 724 | end 725 | end 726 | end 727 | 728 | function c_macro_call(ret) 729 | return function (cfunc) 730 | return function (params) 731 | local rec = c_call(ret)(cfunc)(params) 732 | rec.ffi_need_wrapper = "c_wrap" 733 | rec.is_macro_call = true 734 | return rec 735 | end 736 | end 737 | end 738 | 739 | function c_inline_call(ret) 740 | return function (cfunc) 741 | return function (params) 742 | local rec = c_call(ret)(cfunc)(params) 743 | rec.ffi_need_wrapper = "c_wrap" 744 | rec.is_inline_call = true 745 | return rec 746 | end 747 | end 748 | end 749 | 750 | function c_export_call(ret) 751 | return function (cfunc) 752 | return function (params) 753 | local rec = c_call(ret)(cfunc)(params) 754 | rec.ffi_need_wrapper = "c_export" 755 | rec.is_export_call = true 756 | return rec 757 | end 758 | end 759 | end 760 | 761 | function c_method_call(ret) 762 | return function (cfunc) 763 | return function (params) 764 | local rec = c_call(ret)(cfunc)(params) 765 | rec.is_method_call = true 766 | return rec 767 | end 768 | end 769 | end 770 | 771 | function c_export_method_call(ret) 772 | return function (cfunc) 773 | return function (params) 774 | local rec = c_method_call(ret)(cfunc)(params) 775 | rec.ffi_need_wrapper = "c_export" 776 | rec.is_export_call = true 777 | return rec 778 | end 779 | end 780 | end 781 | 782 | function c_macro_method_call(ret) 783 | return function (cfunc) 784 | return function (params) 785 | local rec = c_method_call(ret)(cfunc)(params) 786 | rec.ffi_need_wrapper = "c_wrap" 787 | rec.is_macro_call = true 788 | return rec 789 | end 790 | end 791 | end 792 | 793 | function callback_type(name) 794 | return function (return_type) 795 | return function (params) 796 | local rec = make_record({}, "callback_type") 797 | rec.is_callback = true 798 | -- function type name. 799 | rec.name = name 800 | -- c_type for callback. 801 | rec.c_type = name 802 | -- parse return c_type. 803 | rec.ret = return_type or "void" 804 | -- parse params 805 | if params == nil then params = {} end 806 | rec.params = params 807 | -- add new types 808 | new_c_type(rec.c_type, rec) 809 | return rec 810 | end 811 | end 812 | end 813 | 814 | function callback(c_type) 815 | if type(c_type) == 'table' then 816 | local rec = var_in(c_type) 817 | rec.is_callback = true 818 | rec.is_ref = true 819 | rec.ref_field = rec.name 820 | -- other variable that will be wrapped to hold callback state information. 821 | rec.state_var = tremove(rec, 1) 822 | if rec.state_var == 'this' then 823 | rec.wrap_state = true 824 | rec.owner = 'this' 825 | end 826 | return rec 827 | end 828 | return function (name) 829 | return function (state_var) 830 | return callback({c_type, name, state_var}) 831 | end 832 | end 833 | end 834 | 835 | function callback_state(base_type, wrap_state) 836 | -- cleanup base_type 837 | base_type = base_type:gsub("[ *]","") 838 | -- create name for new state type 839 | local name = base_type .. "_cb_state" 840 | -- make it an callback_state record. 841 | local rec = make_record({}, "callback_state") 842 | -- the wrapper type 843 | rec.wrap_type = name 844 | -- base_type we are wrapping. 845 | rec.base_type = base_type 846 | rec.wrap_state = wrap_state 847 | -- c_type we are wrapping. (pointer to base_type) 848 | rec.c_type = name .. " *" 849 | -- resolve base_type 850 | rec.base_type_rec = resolve_c_type(rec.base_type) 851 | -- add new types 852 | new_c_type(rec.c_type, rec) 853 | return rec 854 | end 855 | 856 | function callback_func(c_type) 857 | return function (name) 858 | local rec = make_record({}, "callback_func") 859 | rec.is_ref = true 860 | rec.ref_field = name 861 | -- c_type for callback. 862 | rec.c_type = c_type 863 | -- callback variable's name 864 | rec.name = name 865 | -- callback function name. 866 | rec.c_func_name = c_type .. "_" .. name .. "_cb" 867 | resolve_rec(rec) 868 | return rec 869 | end 870 | end 871 | 872 | function cb_out(rec) 873 | rec = make_record(rec, "cb_out") 874 | -- out variable's c_type 875 | rec.c_type = tremove(rec, 1) 876 | -- out variable's name 877 | rec.name = tremove(rec, 1) 878 | resolve_rec(rec) 879 | return rec 880 | end 881 | 882 | function cb_in(rec) 883 | rec = make_record(rec, "cb_in") 884 | -- in variable's c_type 885 | rec.c_type = tremove(rec, 1) 886 | -- in variable's name 887 | local name = tremove(rec, 1) 888 | -- check if this is a wrapped object parameter. 889 | if name:sub(1,1) == '%' then 890 | rec.is_wrapped_obj = true; 891 | name = name:sub(2) 892 | end 893 | rec.name = name 894 | resolve_rec(rec) 895 | return rec 896 | end 897 | 898 | function c_module(name) 899 | return function (rec) 900 | rec = make_record(rec, "c_module") 901 | -- c_module name. 902 | rec.name = name 903 | return rec 904 | end 905 | end 906 | 907 | function lang(name) 908 | return function (rec) 909 | rec.name = name 910 | rec = make_record(rec, "lang") 911 | -- only keep records for current language. 912 | if rec.name ~= gen_lang then 913 | -- delete this record and it sub-records 914 | rec:delete_record() 915 | end 916 | return rec 917 | end 918 | end 919 | 920 | function ffi(rec) 921 | return make_record(rec, "ffi") 922 | end 923 | 924 | function ffi_files(rec) 925 | for i=1,#rec do 926 | rec[i] = subfile_path(rec[i]) 927 | end 928 | return make_record(rec, "ffi_files") 929 | end 930 | 931 | function ffi_source(part) 932 | return function(src) 933 | if src == nil then 934 | src = part 935 | part = nil 936 | end 937 | local rec = make_record({}, "ffi_source") 938 | rec.part = part or "ffi_src" 939 | rec.src = src 940 | return rec 941 | end 942 | end 943 | 944 | function ffi_typedef(cdef) 945 | return ffi_source("ffi_typedef")(cdef) 946 | end 947 | 948 | function ffi_cdef(cdef) 949 | return ffi_source("ffi_cdef")(cdef) 950 | end 951 | 952 | function ffi_load(name) 953 | if type(name) == 'table' then 954 | local default_lib = name[1] or name.default 955 | local src = { 'local os_lib_table = {\n' } 956 | local off = #src 957 | for k,v in pairs(name) do 958 | if type(k) == 'string' and type(v) == 'string' then 959 | off = off + 1; src[off] = '\t["' 960 | off = off + 1; src[off] = k 961 | off = off + 1; src[off] = '"] = "' 962 | off = off + 1; src[off] = v 963 | off = off + 1; src[off] = '",\n' 964 | end 965 | end 966 | off = off + 1; src[off] = '}\n' 967 | off = off + 1; src[off] = 'C = ffi_load(os_lib_table[ffi.os]' 968 | if type(default_lib) == 'string' then 969 | off = off + 1; src[off] = ' or "' 970 | off = off + 1; src[off] = default_lib 971 | off = off + 1; src[off] = '"' 972 | end 973 | if name.global then 974 | off = off + 1; src[off] = ', true' 975 | end 976 | off = off + 1; src[off] = ')\n' 977 | return ffi_source("ffi_src")(tconcat(src)) 978 | end 979 | return function (global) 980 | if global == nil then global = false end 981 | global = tostring(global) 982 | local src = 'C = ffi_load("' .. name .. '",' .. global .. ')\n' 983 | return ffi_source("ffi_src")(src) 984 | end 985 | end 986 | 987 | function ffi_export(c_type) 988 | return function (name) 989 | local rec = make_record({}, "ffi_export") 990 | -- parse c_type. 991 | rec.c_type = c_type 992 | -- parse name of symbol to export 993 | rec.name = name 994 | return rec 995 | end 996 | end 997 | 998 | function ffi_export_function(return_type) 999 | return function (name) 1000 | return function (params) 1001 | local rec = make_record({}, "ffi_export_function") 1002 | -- parse return c_type. 1003 | rec.ret = return_type or "void" 1004 | -- parse c function to call. 1005 | rec.name = name 1006 | -- parse params 1007 | rec.params = params 1008 | if rec.params == nil then rec.params = {} end 1009 | return rec 1010 | end 1011 | end 1012 | end 1013 | 1014 | -- 1015 | -- End records functions 1016 | -- 1017 | 1018 | local module_file = nil 1019 | local outpath = "" 1020 | local outfiles = {} 1021 | function get_outfile_name(ext) 1022 | local filename = module_file .. ext 1023 | return outpath .. filename 1024 | end 1025 | function open_outfile(filename, ext) 1026 | local filename = (filename or module_file) .. (ext or '') 1027 | local file = outfiles[filename] 1028 | if file == nil then 1029 | file = assert(io.open(outpath .. filename, "w+")) 1030 | outfiles[filename] = file 1031 | end 1032 | return file 1033 | end 1034 | function get_outpath(path) 1035 | return (outpath or './') .. (path or '') 1036 | end 1037 | function close_outfiles() 1038 | for name,file in pairs(outfiles) do 1039 | io.close(file) 1040 | outfiles[name] = nil 1041 | end 1042 | end 1043 | 1044 | require("native_objects.stages") 1045 | 1046 | local function process_module_file(file) 1047 | -- clear root_records & c_types 1048 | reset() 1049 | 1050 | -- 1051 | -- load language module 1052 | -- 1053 | require("native_objects.lang_" .. gen_lang) 1054 | 1055 | -- 1056 | -- load basic interfaces 1057 | -- 1058 | require("native_objects.interfaces") 1059 | 1060 | module_file = file:gsub("(.lua)$","") 1061 | print("module_file", module_file) 1062 | print("Parsing records from file: " .. file) 1063 | 1064 | -- swap `package` 1065 | local package = _G.package 1066 | _G.package = _package 1067 | dofile(file) 1068 | _G.package = package 1069 | 1070 | -- 1071 | -- run stage parsers 1072 | -- 1073 | run_stage_parsers() 1074 | 1075 | -- 1076 | -- load gen. module 1077 | -- 1078 | print"============ generate api bindings =================" 1079 | if gen_module ~= "null" then 1080 | require("native_objects.gen_" .. gen_module) 1081 | end 1082 | 1083 | close_outfiles() 1084 | end 1085 | 1086 | 1087 | local _M = {} 1088 | 1089 | -- 1090 | -- Run LuaNativeObjects parse command line options/files 1091 | -- 1092 | function _M.run(arg) 1093 | local len=#arg 1094 | local i=1 1095 | while i <= len do 1096 | local a=arg[i] 1097 | local eat = 0 1098 | i = i + 1 1099 | if a:sub(1,1) ~= "-" then 1100 | process_module_file(a) 1101 | else 1102 | if a == "-gen" then 1103 | gen_module = arg[i] 1104 | eat = 1 1105 | elseif a == "-outpath" then 1106 | outpath = arg[i] 1107 | if outpath:sub(-1,-1) ~= "/" then 1108 | outpath = outpath .. "/" 1109 | end 1110 | eat = 1 1111 | elseif a == "-lang" then 1112 | gen_lang = arg[i] 1113 | eat = 1 1114 | else 1115 | print("Unkown option: " .. a) 1116 | end 1117 | end 1118 | i = i + eat 1119 | end 1120 | end 1121 | 1122 | if arg then 1123 | _M.run(arg) 1124 | else 1125 | return _M 1126 | end 1127 | -------------------------------------------------------------------------------- /native_objects/gen_dotfile.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | local dottify = require"dottify" 22 | 23 | -- 24 | -- dump records 25 | -- 26 | print"============ Dump records =================" 27 | 28 | local root = {} 29 | local function parent_add_child(parent, child, name) 30 | if parent == nil then return {} end 31 | local info = parent.info 32 | if info == nil then info = {}; parent.info = info end 33 | if name == nil then name = #info + 1 end 34 | info[name] = child 35 | return child 36 | end 37 | local objects = {} 38 | 39 | process_records{ 40 | c_module_end = function(self, rec, parent) 41 | root = rec.info 42 | end, 43 | object = function(self, rec, parent) 44 | local info = parent_add_child(parent, {}) 45 | objects[rec.name] = info 46 | info[1] = rec._rec_type 47 | info[2] = rec.name 48 | info[3] = "==========================" 49 | rec.info = info 50 | end, 51 | extends = function(self, rec, parent) 52 | parent.info.extends = objects[rec.name] 53 | end, 54 | c_function = function(self, rec, parent) 55 | rec.params = '' 56 | end, 57 | c_function_end = function(self, rec, parent) 58 | local ret_type = rec.ret_type or "void" 59 | local func = ret_type .. " " .. rec.name .. "(" .. rec.params .. ")" 60 | parent_add_child(parent, func) 61 | end, 62 | var_out = function(self, rec, parent) 63 | parent.ret_type = rec.c_type 64 | end, 65 | var_in = function(self, rec, parent) 66 | if parent._first_var ~= nil then 67 | parent.params = parent.params .. ', ' 68 | else 69 | parent._first_var = true 70 | end 71 | parent.params = parent.params .. rec.c_type .. " " .. rec.name 72 | end, 73 | --[=[ 74 | unknown = function(self, rec, parent) 75 | local info = parent_add_child(parent) 76 | info[1] = rec._rec_type 77 | rec.info = info 78 | end, 79 | --[=[ 80 | c_module = function(self, rec, parent) 81 | root = rec 82 | end, 83 | unknown = function(self, rec, parent) 84 | rec._rec_idx = nil 85 | rec._parent = nil 86 | rec._symbol_map = nil 87 | rec._data_parts = nil 88 | rec._imports = nil 89 | rec._vars = nil 90 | rec.functions = nil 91 | rec.src = nil 92 | rec.subs = nil 93 | end, 94 | --]=] 95 | } 96 | 97 | dottify(get_outfile_name(".dot"), root, "nometatables", "noupvalues", "values") 98 | 99 | -------------------------------------------------------------------------------- /native_objects/gen_dump.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | 22 | -- 23 | -- dump records 24 | -- 25 | print"============ Dump records =================" 26 | local depth=0 27 | function write(...) 28 | io.write((" "):rep(depth)) 29 | io.write(...) 30 | end 31 | 32 | process_records{ 33 | unknown = function(self, rec, parent) 34 | write(rec._rec_type .. " {\n") 35 | depth = depth + 1 36 | -- dump rec info 37 | for k,v in pairs(rec) do 38 | if k == '_rec_type' then 39 | elseif k == 'c_type_rec' then 40 | write(k,' = ', tostring(v._rec_type), ' = {\n') 41 | depth = depth + 1 42 | write('name = "', tostring(v.name), '"\n') 43 | write('c_type = "', tostring(v.c_type), '"\n') 44 | write('lang_type = "', tostring(v.lang_type), '"\n') 45 | depth = depth - 1 46 | write('}\n') 47 | elseif is_record(v) then 48 | elseif type(v) == 'function' then 49 | elseif type(v) == 'table' then 50 | else 51 | write(tostring(k),' = "', tostring(v), '"\n') 52 | end 53 | end 54 | end, 55 | unknown_end = function(self, rec, parent) 56 | depth = depth - 1 57 | write("}\n") 58 | end, 59 | c_source = function(self, rec, parent) 60 | local src = rec.src 61 | if type(src) == 'table' then src = table.concat(src) end 62 | write('c_source = "', src, '"\n') 63 | end, 64 | c_source_end = function(self, rec, parent) 65 | end, 66 | } 67 | 68 | -------------------------------------------------------------------------------- /native_objects/gen_luadoc.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | local tconcat = table.concat 22 | 23 | -- re-map meta-methods. 24 | local lua_meta_methods = { 25 | __str__ = '__tostring', 26 | __eq__ = '__eq', 27 | delete = '__gc', 28 | -- Lua metamethods 29 | __add = '__add', 30 | __sub = '__sub', 31 | __mul = '__mul', 32 | __div = '__div', 33 | __mod = '__mod', 34 | __pow = '__pow', 35 | __unm = '__unm', 36 | __len = '__len', 37 | __concat = '__concat', 38 | __eq = '__eq', 39 | __lt = '__lt', 40 | __le = '__le', 41 | __gc = '__gc', 42 | __tostring = '__tostring', 43 | __index = '__index', 44 | __newindex = '__newindex', 45 | } 46 | 47 | local function ctype_to_name(ctype) 48 | if ctype.lang_type == 'userdata' then 49 | elseif ctype.lang_type == 'function' then 50 | return "Lua function" 51 | else 52 | return ctype.lang_type or ctype.name 53 | end 54 | return ctype.name 55 | end 56 | 57 | function get_type_link(rec) 58 | if rec._rec_type == 'object' then 59 | return '' .. rec.name ..'' 60 | else 61 | return '' .. ctype_to_name(rec) .. '' 62 | end 63 | end 64 | 65 | print"============ Lua Documentation =================" 66 | 67 | local parsed = process_records{ 68 | _modules_out = {}, 69 | 70 | -- record handlers 71 | c_module = function(self, rec, parent) 72 | local module_c_name = rec.name:gsub('(%.)','_') 73 | rec:add_var('module_c_name', module_c_name) 74 | rec:add_var('module_name', rec.name) 75 | rec:add_var('object_name', rec.name) 76 | rec.objects = {} 77 | self._cur_module = rec 78 | self._modules_out[rec.name] = rec 79 | 80 | rec:write_part("doc_header", { 81 | '--- Module ${object_name}.\n', 82 | '--\n', 83 | }) 84 | end, 85 | c_module_end = function(self, rec, parent) 86 | self._cur_module = nil 87 | 88 | rec:write_part("doc_footer", { 89 | 'module("${object_name}")\n\n', 90 | }) 91 | local parts = { "doc_header", "doc_src", "doc_footer", "doc_funcs" } 92 | rec:vars_parts(parts) 93 | rec:write_part("doc_out", rec:dump_parts(parts)) 94 | end, 95 | error_code = function(self, rec, parent) 96 | rec:add_var('object_name', rec.name) 97 | end, 98 | error_code_end = function(self, rec, parent) 99 | end, 100 | object = function(self, rec, parent) 101 | self._cur_module.objects[rec.name] = rec 102 | rec:add_var('object_name', rec.name) 103 | parent:write_part("doc_footer", { 104 | '--
Class ', get_type_link(rec),'\n', 105 | }) 106 | rec:write_part("doc_header", { 107 | '--- Class "${object_name}".\n', 108 | '--\n', 109 | }) 110 | rec:write_part("doc_subclasses", { 111 | '--
\n', 112 | }) 113 | end, 114 | object_end = function(self, rec, parent) 115 | rec:write_part("doc_footer", { 116 | 'module("${object_name}")\n\n', 117 | }) 118 | -- copy generated luadocs to parent 119 | local parts = { "doc_header", "doc_src", "doc_footer", "doc_funcs" } 120 | rec:vars_parts(parts) 121 | rec:write_part("doc_out", rec:dump_parts(parts)) 122 | -- copy methods to sub-classes 123 | local subs = rec.subs 124 | if subs then 125 | local methods = rec:dump_parts("doc_for_subs") 126 | for i=1,#subs do 127 | local sub = subs[i] 128 | sub.base_methods = (sub.base_methods or '') .. methods 129 | end 130 | end 131 | end, 132 | doc = function(self, rec, parent) 133 | parent:write_part("doc_src", { 134 | '-- ',rec.text:gsub("\n","\n-- "),'\n', 135 | }) 136 | end, 137 | callback_state = function(self, rec, parent) 138 | end, 139 | callback_state_end = function(self, rec, parent) 140 | end, 141 | include = function(self, rec, parent) 142 | end, 143 | define = function(self, rec, parent) 144 | end, 145 | extends = function(self, rec, parent) 146 | assert(not parent.is_package, "A Package can't extend anything: package=" .. parent.name) 147 | local base = rec.base 148 | if base == nil then return end 149 | parent:write_part("doc_footer", { 150 | '-- Extends ', get_type_link(base),'
\n', 151 | }) 152 | base:write_part("doc_subclasses", { 153 | '-- Subclass ', get_type_link(parent),'
\n', 154 | }) 155 | -- add methods/fields/constants from base object 156 | for name,val in pairs(base.name_map) do 157 | -- make sure sub-class has not override name. 158 | if parent.name_map[name] == nil or parent.name_map[name] == val then 159 | parent.name_map[name] = val 160 | if val._is_method and not val.is_constructor then 161 | parent.functions[name] = val 162 | elseif val._rec_type == 'field' then 163 | parent.fields[name] = val 164 | elseif val._rec_type == 'const' then 165 | parent.constants[name] = val 166 | end 167 | end 168 | end 169 | end, 170 | extends_end = function(self, rec, parent) 171 | end, 172 | callback_func = function(self, rec, parent) 173 | rec.wrapped_type = parent.c_type 174 | rec.wrapped_type_rec = parent.c_type_rec 175 | -- start callback function. 176 | rec:write_part("doc_src", { 177 | '--- callback: ', rec.name, '\n', 178 | '--\n', 179 | '-- @name ', rec.name, '\n', 180 | }) 181 | rec:write_part("doc_func", { 182 | 'function ', rec.name, '(' 183 | }) 184 | end, 185 | callback_func_end = function(self, rec, parent) 186 | -- end luddoc for function 187 | rec:write_part("doc_func", { 188 | ')\nend\n' 189 | }) 190 | -- map in/out variables in c source. 191 | local parts = {"doc_header", "doc_src", "doc_footer", "doc_func"} 192 | rec:vars_parts(parts) 193 | 194 | parent:write_part('doc_funcs', { rec:dump_parts(parts), "\n\n" }) 195 | end, 196 | dyn_caster = function(self, rec, parent) 197 | end, 198 | dyn_caster_end = function(self, rec, parent) 199 | end, 200 | c_function = function(self, rec, parent) 201 | if rec._is_hidden then return end 202 | rec:add_var('object_name', parent.name) 203 | 204 | local name = rec.name 205 | if rec._is_meta_method and not rec.is_destructor then 206 | name = lua_meta_methods[name] 207 | end 208 | rec:add_var('func_name', name) 209 | 210 | local desc = '' 211 | local prefix = '' 212 | if rec._is_method then 213 | if rec.is_constructor then 214 | desc = "Create a new ${object_name} object." 215 | prefix = "${object_name}." 216 | elseif rec.is_destructor then 217 | desc = "Destroy this object (will be called by Garbage Collector)." 218 | prefix = "${object_name}:" 219 | elseif rec._is_meta_method then 220 | desc = "object meta method." 221 | prefix = "${object_name}_mt:" 222 | else 223 | desc = "object method." 224 | prefix = "${object_name}:" 225 | end 226 | else 227 | desc = "module function." 228 | prefix = "${object_name}." 229 | end 230 | -- generate luadoc stub function 231 | rec:write_part("doc_src", { 232 | '--- ', desc, '\n', 233 | '--\n', 234 | }) 235 | rec:write_part("doc_func", { 236 | '-- @name ', prefix, '${func_name}\n', 237 | 'function ', prefix, name, '(' 238 | }) 239 | end, 240 | c_function_end = function(self, rec, parent) 241 | if rec._is_hidden then return end 242 | 243 | local params = {} 244 | for i=1,#rec do 245 | local var = rec[i] 246 | local rtype = var._rec_type 247 | local name = var.name 248 | if rtype == 'var_in' then 249 | if not var.is_this and name ~= 'L' then 250 | params[#params + 1] = var.name 251 | end 252 | end 253 | end 254 | params = tconcat(params, ', ') 255 | -- end luddoc for function 256 | rec:write_part("doc_func", { 257 | params, ')\nend' 258 | }) 259 | local parts = {"doc_header", "doc_src", "doc_footer", "doc_func"} 260 | rec:vars_parts(parts) 261 | if rec._is_method and not rec.is_constructor then 262 | parent:write_part("doc_for_subs", {rec:dump_parts(parts), "\n\n"}) 263 | end 264 | parent:write_part("doc_funcs", {rec:dump_parts(parts), "\n\n"}) 265 | end, 266 | c_source = function(self, rec, parent) 267 | end, 268 | var_in = function(self, rec, parent) 269 | -- no need to add code for 'lua_State *' parameters. 270 | if rec.c_type == 'lua_State *' and rec.name == 'L' then return end 271 | if rec.is_this then return end 272 | local desc = '' 273 | if rec.desc then 274 | desc = rec.desc .. '. ' 275 | end 276 | if rec.c_type == '' then 277 | desc = desc .. "Multiple types accepted." 278 | else 279 | desc = desc .."Must be of type " .. get_type_link(rec.c_type_rec) .. "." 280 | end 281 | parent:write_part("doc_footer", 282 | {'-- @param ', rec.name, ' ', desc, '\n'}) 283 | end, 284 | var_out = function(self, rec, parent) 285 | if rec.is_length_ref or rec.is_temp then 286 | return 287 | end 288 | -- push Lua value onto the stack. 289 | local error_code = parent._has_error_code 290 | local var_type = get_type_link(rec.c_type_rec) 291 | if error_code == rec then 292 | if rec._rec_idx == 1 then 293 | parent:write_part("doc_footer", { 294 | '-- @return true if no error.\n', 295 | '-- @return Error string.\n', 296 | }) 297 | else 298 | parent:write_part("doc_footer", { 299 | '-- @return Error string.\n', 300 | }) 301 | end 302 | elseif rec.no_nil_on_error ~= true and error_code then 303 | parent:write_part("doc_footer", { 304 | '-- @return ', var_type, ' or nil on error.\n', 305 | }) 306 | elseif rec.is_error_on_null then 307 | parent:write_part("doc_footer", { 308 | '-- @return ', var_type, ' or nil on error.\n', 309 | '-- @return Error string.\n', 310 | }) 311 | else 312 | parent:write_part("doc_footer", { 313 | '-- @return ', var_type, '.\n', 314 | }) 315 | end 316 | end, 317 | cb_in = function(self, rec, parent) 318 | parent:write_part("doc_footer", 319 | {'-- @param ', rec.name, '\n'}) 320 | end, 321 | cb_out = function(self, rec, parent) 322 | parent:write_part("doc_footer", 323 | {'-- @return ', rec.name, '\n'}) 324 | end, 325 | } 326 | 327 | local lfs = require"lfs" 328 | local src_file 329 | local function src_write(...) 330 | src_file:write(...) 331 | end 332 | 333 | local function dump_module(mod, path) 334 | path = path or '' 335 | lfs.mkdir(get_outpath(path)) 336 | src_file = open_outfile(path .. mod.name .. '.luadoc') 337 | -- write header 338 | src_write[[ 339 | -- 340 | -- Warning: AUTOGENERATED DOCS. 341 | -- 342 | 343 | ]] 344 | src_write( 345 | mod:dump_parts({ 346 | "doc_out", 347 | }), 348 | (mod.base_methods or '') 349 | ) 350 | end 351 | 352 | local function dump_modules(modules, path) 353 | path = path or '' 354 | for name,mod in pairs(modules) do 355 | dump_module(mod, path) 356 | local objects = mod.objects 357 | if objects then 358 | dump_modules(objects, path .. mod.name .. '/') 359 | end 360 | end 361 | end 362 | 363 | dump_modules(parsed._modules_out) 364 | 365 | print("Finished generating luadoc stubs") 366 | 367 | -------------------------------------------------------------------------------- /native_objects/gen_simple.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | 22 | -- 23 | -- dump info 24 | -- 25 | --[[ 26 | print"============ Dump types =================" 27 | for k,v in pairs(types) do 28 | local lang_type = v.lang_type 29 | if lang_type == nil then 30 | lang_type = 'userdata' 31 | end 32 | print(v.c_type .. '\t(' .. k .. ')' .. '\tlua: ' .. lang_type) 33 | end 34 | ]] 35 | print"============ Dump objects =================" 36 | local function find_ret(rec) 37 | for i=1,#rec do 38 | local v = rec[i] 39 | if is_record(v) and v._rec_type == 'var_out' then 40 | return v; 41 | end 42 | end 43 | return { c_type = "void" } 44 | end 45 | process_records{ 46 | object = function(self, rec, parent) 47 | print("object " .. rec.name .. "{") 48 | --print(dump(rec)) 49 | end, 50 | property = function(self, rec, parent) 51 | print(rec.c_type .. " " .. rec.name .. "; /* is='" .. rec.is .. "', isa='" .. rec.isa .. "' */") 52 | end, 53 | include = function(self, rec, parent) 54 | print('#include "' .. rec.file .. '"') 55 | end, 56 | option = function(self, rec, parent) 57 | print("/* option: " .. rec.name .. " */") 58 | end, 59 | object_end = function(self, rec, parent) 60 | print("}\n") 61 | end, 62 | c_function = function(self, rec, parent) 63 | local ret = find_ret(rec) 64 | io.write(ret.c_type .. " " .. rec.name .. "(") 65 | end, 66 | c_function_end = function(self, rec, parent) 67 | print(")") 68 | end, 69 | var_in = function(self, rec, parent) 70 | if parent._first_var ~= nil then 71 | io.write(', ') 72 | else 73 | parent._first_var = true 74 | end 75 | io.write(rec.c_type .. " " .. rec.name) 76 | end, 77 | } 78 | 79 | -------------------------------------------------------------------------------- /native_objects/gen_swig.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | -- 22 | -- output Swig Lua bindings 23 | -- 24 | 25 | -- use simple variable naming. 26 | set_variable_format('%s') 27 | 28 | -- 29 | -- templates 30 | -- 31 | local package_new_method = [[ 32 | ${object_name}() { return (void *)1; /* use a fake pointer. */ } 33 | ]] 34 | 35 | -- 36 | -- handle extend records. 37 | -- 38 | process_records{ 39 | extends = function(self, rec, parent) 40 | local base = rec.base 41 | if not base then return end 42 | -- copy methods from base object 43 | for name,func in pairs(base.functions) do 44 | if func._is_method and parent.functions[name] == nil then 45 | func = func:copy_record() 46 | func.cast_this_ptr = "(" .. base.c_type .. " *)" 47 | parent:add_record(func) 48 | end 49 | end 50 | end, 51 | } 52 | 53 | -- 54 | -- to/check/push/delete SWIG Object methods 55 | -- 56 | print"============ create to/check/push/delete SWIG Object methods =================" 57 | process_records{ 58 | object = function(self, rec, parent) 59 | rec.lang_type = 'userdata' 60 | local type_name = 'SWIGTYPE_p_' .. rec.name 61 | rec._obj_type_name = type_name 62 | 63 | -- create _check/_delete/_push functions 64 | rec._check = nil 65 | rec._delete = nil 66 | rec._to = rec._check 67 | rec._push = function(self, var, own) 68 | if own == nil then own = '0' end 69 | return ' SWIG_NewPointerObj(L, ${' .. var.name .. '}, ' .. 70 | type_name .. ', ' .. own .. ');\n' 71 | end 72 | end, 73 | callback_func = function(self, rec, parent) 74 | rec.lang_type = 'function' 75 | 76 | -- create _check/_delete/_push functions 77 | rec._check = function(self, var) 78 | return 'swiglua_ref_get(&(${' .. var.name .. '}));\n' .. 79 | ' luaL_checktype(${' .. var.name .. '}.L, -1, LUA_TFUNCTION);\n' .. 80 | ' lua_pop(${' .. var.name .. '}.L, 1);\n' 81 | end 82 | rec._delete = function(self, var) 83 | return 'swiglua_ref_clear(&(${' .. var.name .. '}));\n' 84 | end 85 | rec._to = rec._check 86 | rec._push = function(self, var) 87 | return 'swiglua_ref_get(&(' .. var .. '));\n' 88 | end 89 | end, 90 | } 91 | 92 | print"============ SWIG Lua bindings =================" 93 | local parsed = process_records{ 94 | _modules_out = {}, 95 | _includes = {}, 96 | 97 | -- record handlers 98 | c_module = function(self, rec, parent) 99 | self._cur_module = rec 100 | self._modules_out[rec.name] = rec 101 | rec:write_part("header", { 102 | '%module ', rec.name, '\n', 103 | [[ 104 | %include stdint.i 105 | %include lua_fnptr.i 106 | %nodefaultctor; 107 | %nodefaultdtor; 108 | ]] 109 | }) 110 | rec:write_part("includes", { 111 | '%{\n' 112 | }) 113 | rec:write_part("extra_code", { 114 | '%{\n' 115 | }) 116 | end, 117 | c_module_end = function(self, rec, parent) 118 | rec:write_part("includes", { 119 | '%}\n' 120 | }) 121 | rec:write_part("extra_code", { 122 | '%}\n' 123 | }) 124 | self._cur_module = nil 125 | end, 126 | object = function(self, rec, parent) 127 | rec:add_var('object_name', rec.name) 128 | -- make typedef for this object 129 | rec:write_part("typedefs", { 130 | 'typedef struct {\n', 131 | }) 132 | -- start extend block 133 | rec:write_part("methods", { 134 | '%extend ${object_name} {\n', 135 | }) 136 | -- create fake type for packages. 137 | if rec.is_package then 138 | rec:write_part("src", { 139 | 'typedef int ', rec.name, ';\n', 140 | }) 141 | rec:write_part('methods', package_new_method) 142 | end 143 | end, 144 | object_end = function(self, rec, parent) 145 | rec:write_part("typedefs", { 146 | '} ${object_name};\n\n', 147 | }) 148 | rec:write_part("methods", { 149 | '}\n\n' 150 | }) 151 | local parts = {"typedefs", "methods"} 152 | -- apply variables to templates. 153 | rec:vars_parts(parts) 154 | -- copy parts to parent 155 | parent:copy_parts(rec, parts) 156 | -- append extra source code. 157 | parent:write_part("extra_code", rec:dump_parts{ "src" }) 158 | end, 159 | callback_state = function(self, rec, parent) 160 | rec:add_var('wrap_type', rec.wrap_type) 161 | rec:add_var('base_type', rec.base_type) 162 | -- start callback object. 163 | rec:write_part("wrapper_obj", 164 | {'/* callback object: ', rec.name, ' */\n', 165 | 'typedef struct {\n', 166 | ' ', rec.base_type, ' base;\n', 167 | }) 168 | end, 169 | callback_state_end = function(self, rec, parent) 170 | rec:write_part("wrapper_obj", 171 | { rec:dump_parts{"wrapper_callbacks"}, 172 | '} ', rec.wrap_type,';\n', 173 | }) 174 | -- append extra source code. 175 | rec:write_part("extra_code", rec:dump_parts{ "wrapper_obj" }) 176 | -- apply variables to parts 177 | local parts = {"extra_code", "methods"} 178 | rec:vars_parts(parts) 179 | -- copy parts to parent 180 | parent:write_part("src", rec:dump_parts(parts)) 181 | end, 182 | extends = function(self, rec, parent) 183 | local base = rec.base 184 | if not base then return end 185 | parent:write_part("typedefs", { 186 | ' %immutable;\n', 187 | ' ', rec.base.c_type, ' ', rec.field, ';\n', 188 | ' %mutable;\n', 189 | }) 190 | end, 191 | include = function(self, rec, parent) 192 | if self._includes[rec.file] then return end 193 | self._includes[rec.file] = true 194 | -- append include file 195 | self._cur_module:write_part("includes", { '#include "', rec.file, '"\n' }) 196 | end, 197 | callback_func = function(self, rec, parent) 198 | rec.wrapped_type = parent.c_type 199 | rec.wrapped_type_rec = parent.c_type_rec 200 | rec.cb_ins = 0 201 | rec.cb_outs = 0 202 | -- add callback decl. 203 | rec:write_part('func_decl', {rec.c_func_decl, ';\n'}) 204 | -- start callback function. 205 | rec:write_part("cb_head", 206 | {'/* callback: ', rec.name, ' */\n', 207 | rec.c_func_decl, ' {\n', 208 | }) 209 | -- add lua reference to wrapper object. 210 | parent:write_part('wrapper_callbacks', 211 | {' SWIGLUA_REF ', rec.ref_field, ';\n'}) 212 | end, 213 | callback_func_end = function(self, rec, parent) 214 | local wrapped = rec.wrapped_var 215 | local wrap_type = parent.wrap_type .. ' *' 216 | rec:write_part("cb_head", 217 | { ' ', wrap_type,' wrap = (',wrap_type,')${', wrapped.name,'};\n', 218 | ' lua_State *L = wrap->', rec.ref_field,'.L;\n', 219 | }) 220 | rec:write_part("vars", {'\n ', rec:_push('wrap->' .. rec.ref_field),}) 221 | -- call lua callback function. 222 | rec:write_part("src", {' lua_call(L, ', rec.cb_ins, ', ', rec.cb_outs , ');\n'}) 223 | -- get return value from lua function. 224 | local ret_out = rec.ret_out 225 | if ret_out then 226 | rec:write_part("post", {' return ${', ret_out.name , '};\n'}) 227 | end 228 | -- map in/out variables in c source. 229 | local parts = {"cb_head", "vars", "params", "src", "post"} 230 | rec:vars_parts(parts) 231 | rec:vars_parts('func_decl') 232 | 233 | rec:write_part("post", {'}\n\n'}) 234 | parent:write_part('methods', rec:dump_parts(parts)) 235 | parent:write_part('funcdefs', rec:dump_parts('func_decl')) 236 | end, 237 | c_function = function(self, rec, parent) 238 | rec:add_var('object_name', parent.name) 239 | -- default no return value. 240 | rec:add_var('ret', '') 241 | rec:add_var('ret_type', 'void ') 242 | rec._ret_name = 'ret' 243 | -- is this a wrapper function 244 | if rec.wrapper_obj then 245 | local wrap_type = rec.wrapper_obj.wrap_type 246 | rec:write_part("pre", 247 | { ' ', wrap_type,' *wrap;\n', 248 | }) 249 | end 250 | -- for non-method ignore the 'self' parameter. 251 | if not rec._is_method then 252 | rec:write_part("pre", 253 | { ' (void)self;\n', 254 | }) 255 | end 256 | end, 257 | c_function_end = function(self, rec, parent) 258 | -- is this a wrapper function 259 | if rec.wrapper_obj then 260 | local wrap_obj = rec.wrapper_obj 261 | local wrap_type = wrap_obj.wrap_type 262 | local callbacks = wrap_obj.callbacks 263 | if rec.is_destructor then 264 | rec:write_part("pre", 265 | {' wrap = (',wrap_type,' *)${this};\n'}) 266 | for name,cb in pairs(callbacks) do 267 | rec:write_part("src", 268 | {' swiglua_ref_clear(&(wrap->', name,'));\n'}) 269 | end 270 | rec:write_part("post", 271 | {' n_type_free(', wrap_type, ', wrap);\n'}) 272 | elseif rec.is_constructor then 273 | rec:write_part("pre", 274 | { 275 | ' n_new(', wrap_type, ', wrap);\n', 276 | ' ${this} = &(wrap->base);\n', 277 | }) 278 | end 279 | end 280 | -- prefix non-methods with 'static' 281 | local prefix ='' 282 | -- check if this method is the object's constructor/destructor 283 | if rec.is_destructor then 284 | rec.name = '~' .. parent.name 285 | rec:add_var('ret_type', '') 286 | elseif rec.is_constructor then 287 | rec.name = parent.name 288 | rec:add_var('ret_type', '') 289 | end 290 | rec:write_part("def", { 291 | ' ', prefix, '${ret_type}', rec.name, '(' 292 | }) 293 | rec:write_part("post", { 294 | ' return ${', rec._ret_name, '};\n }\n\n' 295 | }) 296 | rec:write_part("params", ") {\n") 297 | -- map in/out variables in c source. 298 | rec:vars_parts{"def", "params", "pre", "src", "post"} 299 | 300 | parent:write_part("methods", { 301 | rec:dump_parts{"def", "params", "pre", "src", "post"} 302 | }) 303 | end, 304 | c_source = function(self, rec, parent) 305 | parent:write_part("src", " ") 306 | parent:write_part("src", rec.src) 307 | parent:write_part("src", "\n") 308 | end, 309 | var_in = function(self, rec, parent) 310 | if rec.is_this then 311 | if parent.cast_this_ptr then 312 | parent:add_var('this', parent.cast_this_ptr .. 'self') 313 | else 314 | parent:add_var('this', 'self') 315 | end 316 | return 317 | end 318 | parent:add_rec_var(rec) 319 | local c_type = rec.c_type 320 | local name = rec.name 321 | local lua = rec.c_type_rec 322 | -- is a lua reference. 323 | if lua.is_ref then 324 | c_type = 'SWIGLUA_REF' 325 | name = name .. '_ref' 326 | parent:add_var(name, rec.name) 327 | parent:add_var(rec.name, rec.cb_func.c_func_name) 328 | parent:write_part("src", 329 | {' wrap->', lua.ref_field, ' = ${', name, '};\n' }) 330 | end 331 | if parent._next_var then 332 | parent:write_part("params", ", ") 333 | else 334 | parent._next_var = true 335 | end 336 | parent:write_part("params", { 337 | c_type, ' ${', name, '}' }) 338 | end, 339 | var_out = function(self, rec, parent) 340 | assert(parent._ret_rec == nil, "Only supports one 'var_out'") 341 | parent._ret_name = rec.name 342 | parent:add_var('ret_type', rec.c_type .. ' ') 343 | parent:add_rec_var(rec) 344 | parent:write_part("pre", { 345 | ' ', rec.c_type, ' ${', rec.name, '};\n' 346 | }) 347 | end, 348 | cb_in = function(self, rec, parent) 349 | parent:add_rec_var(rec) 350 | local lua = rec.c_type_rec 351 | if not rec.is_wrapped_obj then 352 | parent:write_part("params", { lua:_push(rec) }) 353 | parent.cb_ins = parent.cb_ins + 1 354 | else 355 | -- this is the wrapped object parameter. 356 | parent.wrapped_var = rec 357 | end 358 | end, 359 | cb_out = function(self, rec, parent) 360 | parent:add_rec_var(rec) 361 | parent.cb_outs = parent.cb_outs + 1 362 | local lua = rec.c_type_rec 363 | parent:write_part("vars", 364 | {' ', rec.c_type, ' ${', rec.name, '};\n'}) 365 | parent:write_part("post", 366 | {' ', lua:_to(rec) }) 367 | end, 368 | } 369 | 370 | local src_file=open_outfile(nil, '.i') 371 | local function src_write(...) 372 | src_file:write(...) 373 | end 374 | 375 | for name,mod in pairs(parsed._modules_out) do 376 | src_write( 377 | mod:dump_parts({ 378 | "header", 379 | "includes", 380 | "extra_code", 381 | "typedefs", 382 | "methods", 383 | }, "\n\n") 384 | ) 385 | end 386 | 387 | print("Finished generating Lua bindings") 388 | 389 | -------------------------------------------------------------------------------- /native_objects/interfaces.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | -- Immutable Buffer interface 22 | interface "Buffer" { 23 | interface_method "const uint8_t *" "const_data" {}, 24 | interface_method "size_t" "get_size" {}, 25 | } 26 | -- Mutable Buffer interface 27 | interface "MutableBuffer" { 28 | interface_method "uint8_t *" "data" {}, 29 | interface_method "size_t" "get_size" {}, 30 | } 31 | 32 | -- object type for file descriptors 33 | interface "FD" { 34 | interface_method "int" "get_fd" {}, 35 | -- 0 = file, 1 = socket, -1 = other/unknown 36 | interface_method "int" "get_type" {}, 37 | } 38 | 39 | 40 | -- 41 | -- 42 | -- Stage parser to handle interface records. 43 | -- 44 | -- 45 | 46 | local tconcat = table.concat 47 | 48 | reg_stage_parser("containers",{ 49 | interface = function(self, rec, parent) 50 | rec:add_var("interface_name", rec.name) 51 | -- check if the interface was defined outside any other record. 52 | if not parent then 53 | rec.is_global = true 54 | end 55 | rec.methods = {} 56 | rec.method_idx = 0 57 | 58 | rec:write_part("interface", { 59 | "typedef struct ${interface_name}_if {\n", 60 | }) 61 | end, 62 | interface_end = function(self, rec, parent) 63 | local parts = { "interface", "defines"} 64 | rec:write_part("interface", { 65 | "} ${interface_name}IF;\n", 66 | }) 67 | rec:write_part("defines", 68 | [[ 69 | 70 | /* a per-module unique pointer for fast lookup of an interface's implementation table. */ 71 | static char obj_interface_${interface_name}IF[] = "${interface_name}IF"; 72 | 73 | #define ${interface_name}IF_VAR(var_name) \ 74 | ${interface_name}IF *var_name ## _if; \ 75 | void *var_name; 76 | 77 | #define ${interface_name}IF_LUA_OPTIONAL(L, _index, var_name) \ 78 | var_name = obj_implement_luaoptional(L, _index, (void **)&(var_name ## _if), \ 79 | obj_interface_${interface_name}IF) 80 | 81 | #define ${interface_name}IF_LUA_CHECK(L, _index, var_name) \ 82 | var_name = obj_implement_luacheck(L, _index, (void **)&(var_name ## _if), \ 83 | obj_interface_${interface_name}IF) 84 | 85 | ]]) 86 | 87 | rec:write_part("ffi_obj_type", { [[ 88 | local obj_type_${interface_name}_check = 89 | obj_get_interface_check("${interface_name}IF", "Expected object with ${interface_name} interface") 90 | ]]}) 91 | 92 | rec:vars_parts(parts) 93 | rec:add_record(c_source("typedefs")( 94 | rec:dump_parts(parts) 95 | )) 96 | -- 97 | -- FFI code 98 | -- 99 | rec:add_record(ffi_source("ffi_pre_cdef")({ 100 | 'ffi_safe_cdef("', rec.name, 'IF", [[\n', 101 | rec:dump_parts("interface"), 102 | ']])\n', 103 | })) 104 | local ffi_parts = { "ffi_obj_type" } 105 | rec:vars_parts(ffi_parts) 106 | for i=1,#ffi_parts do 107 | local part = ffi_parts[i] 108 | rec:add_record(ffi_source(part)( 109 | rec:dump_parts(part) 110 | )) 111 | end 112 | end, 113 | interface_method = function(self, rec, parent) 114 | assert(parent.is_interface, "Can't have interface_method record in a non-interface parent.") 115 | assert(not parent.methods[rec.name], "Duplicate interface method.") 116 | parent.methods[rec.name] = rec 117 | -- record order of interface methods. 118 | local idx = parent.method_idx + 1 119 | parent.method_idx = idx 120 | rec.idx = idx 121 | local psrc = { "(void *this_v" } 122 | -- method parameters 123 | local params = rec.params 124 | local names = { } 125 | for i=1,#params,2 do 126 | local c_type = params[i] 127 | local name = params[i + 1] 128 | psrc[#psrc + 1] = ", " 129 | psrc[#psrc + 1] = c_type 130 | psrc[#psrc + 1] = " " 131 | psrc[#psrc + 1] = name 132 | names[#names + 1] = name 133 | end 134 | psrc[#psrc + 1] = ")" 135 | psrc = tconcat(psrc) 136 | if #names > 0 then 137 | names = ", " .. tconcat(names, ", ") 138 | else 139 | names = "" 140 | end 141 | -- add method to interface structure. 142 | parent:write_part("interface", { 143 | " ", rec.ret, " (* const ", rec.name, ")", psrc, ";\n" 144 | }) 145 | -- create function decl for method. 146 | rec.func_name = "${object_name}_${interface_name}_" .. rec.name 147 | rec.func_decl = rec.ret .. " " .. rec.func_name .. psrc 148 | rec.param_names = names 149 | end, 150 | implements = function(self, rec, parent) 151 | local interface = rec.interface_rec 152 | rec:add_var("interface_name", rec.name) 153 | rec.c_type = parent.c_type 154 | rec.is_ptr = parent.is_ptr 155 | rec.if_methods = interface.methods 156 | rec.methods = {} 157 | 158 | rec:write_part("src", [[ 159 | /** 160 | * ${object_name} implements ${interface_name} interface 161 | */ 162 | ]]) 163 | rec:write_part("ffi_src", [[ 164 | -- ${object_name} implements ${interface_name} interface 165 | do 166 | local impl_meths = obj_register_interface("${interface_name}IF", "${object_name}") 167 | ]]) 168 | end, 169 | implements_end = function(self, rec, parent) 170 | local interface = rec.interface_rec 171 | local max_idx = interface.method_idx 172 | local define = { 173 | "\nstatic const ${interface_name}IF ${object_name}_${interface_name} = {\n", 174 | } 175 | local methods = rec.methods 176 | for idx=1,max_idx do 177 | local meth = methods[idx] 178 | if idx == 1 then 179 | define[#define + 1] = " " 180 | else 181 | define[#define + 1] = ",\n " 182 | end 183 | if meth then 184 | define[#define + 1] = "${object_name}_${interface_name}_" .. meth.name 185 | else 186 | define[#define + 1] = "NULL" 187 | end 188 | end 189 | define[#define + 1] = "\n};\n" 190 | 191 | rec:write_part("src", define) 192 | 193 | rec:write_part("ffi_src", { 194 | 'end\n', 195 | }) 196 | rec:write_part("regs", [[ 197 | { "${interface_name}IF", &(${object_name}_${interface_name}) }, 198 | ]]) 199 | 200 | local parts = { "src", "ffi_src", "regs" } 201 | rec:vars_parts(parts) 202 | parent:add_record(c_source("implements")( 203 | rec:dump_parts("src") 204 | )) 205 | parent:add_record(c_source("implement_regs")( 206 | rec:dump_parts("regs") 207 | )) 208 | parent:add_record(ffi_source("ffi_src")( 209 | rec:dump_parts("ffi_src") 210 | )) 211 | end, 212 | implement_method = function(self, rec, parent) 213 | local name = rec.name 214 | rec:add_var("this", "this_p") 215 | assert(parent.is_implements, "Can't have implement_method record in a non-implements parent.") 216 | local if_method = parent.if_methods[name] 217 | assert(if_method, "Interface doesn't contain this method.") 218 | local if_idx = if_method.idx 219 | assert(not parent.methods[if_idx], "Duplicate implement method.") 220 | parent.methods[if_idx] = rec 221 | -- generate code for method 222 | rec:write_part("src", { 223 | "/** \n", 224 | " * ${interface_name} interface method ", rec.name, "\n", 225 | " */\n", 226 | "static ", if_method.func_decl, " {\n", 227 | }) 228 | if parent.is_ptr then 229 | rec:write_part("src", { 230 | " ", parent.c_type, " ${this} = this_v;\n", 231 | }) 232 | else 233 | rec:write_part("src", { 234 | " ", parent.c_type, " ${this} = *((", parent.c_type ," *)this_v);\n", 235 | }) 236 | end 237 | if not rec.c_function then 238 | rec:write_part("ffi_src", { 239 | "-- ${interface_name} interface method ", rec.name, "\n", 240 | "function impl_meths.", rec.name, "(${this}", if_method.param_names, ")\n", 241 | }) 242 | if rec.constant then 243 | -- generate code to return ${this} 244 | rec:write_part("src", { 245 | " return ", rec.constant,";\n", 246 | }) 247 | rec:write_part("ffi_src", { 248 | " return ", rec.constant,"\n", 249 | }) 250 | elseif rec.return_this then 251 | -- generate code to return ${this} 252 | rec:write_part("src", { 253 | " return ${this};\n", 254 | }) 255 | rec:write_part("ffi_src", { 256 | " return ${this}\n", 257 | }) 258 | elseif rec.get_field then 259 | -- generate code to return a field from ${this} 260 | rec:write_part("src", { 261 | " return ${this}->", rec.get_field, ";\n", 262 | }) 263 | rec:write_part("ffi_src", { 264 | " return ${this}.", rec.get_field, "\n", 265 | }) 266 | end 267 | else 268 | -- wrap a C function that has the same parameters and return type. 269 | rec:write_part("src", { 270 | " return ", rec.c_function, "(${this}", if_method.param_names, ");\n", 271 | }) 272 | rec:write_part("ffi_src", { 273 | "-- ${interface_name} interface method ", rec.name, "\n", 274 | "impl_meths.", rec.name, " = C.", rec.c_function, "\n", 275 | }) 276 | end 277 | end, 278 | implement_method_end = function(self, rec, parent) 279 | rec:write_part("src", { 280 | "}\n", 281 | }) 282 | if not rec.c_function then 283 | rec:write_part("ffi_src", { 284 | "end\n", 285 | }) 286 | end 287 | local parts = { "src", "ffi_src" } 288 | rec:vars_parts(parts) 289 | parent:copy_parts(rec, parts) 290 | end, 291 | c_source = function(self, rec, parent) 292 | parent:write_part(rec.part, rec.src) 293 | end, 294 | ffi_source = function(self, rec, parent) 295 | parent:write_part(rec.part, rec.src) 296 | end, 297 | }) 298 | 299 | -------------------------------------------------------------------------------- /native_objects/lang_lua.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | 22 | -- 23 | -- C to Lua Base types 24 | -- 25 | basetype "bool" "boolean" "0" 26 | 27 | basetype "char" "integer" "0" 28 | basetype "unsigned char" "integer" "0" 29 | basetype "short" "integer" "0" 30 | basetype "unsigned short" "integer" "0" 31 | basetype "int" "integer" "0" 32 | basetype "unsigned" "integer" "0" 33 | basetype "unsigned int" "integer" "0" 34 | basetype "long" "integer" "0" 35 | basetype "unsigned long" "integer" "0" 36 | -- stdint types. 37 | basetype "int8_t" "integer" "0" 38 | basetype "int16_t" "integer" "0" 39 | basetype "int32_t" "integer" "0" 40 | basetype "int64_t" "integer" "0" 41 | basetype "uint8_t" "integer" "0" 42 | basetype "uint16_t" "integer" "0" 43 | basetype "uint32_t" "integer" "0" 44 | basetype "uint64_t" "integer" "0" 45 | basetype "size_t" "integer" "0" 46 | basetype "ssize_t" "integer" "0" 47 | basetype "off_t" "integer" "0" 48 | basetype "time_t" "integer" "0" 49 | 50 | basetype "float" "number" "0.0" 51 | basetype "double" "number" "0.0" 52 | 53 | basetype "char *" "string" "NULL" 54 | basetype "unsigned char *" "string" "NULL" 55 | basetype "void *" "lightuserdata" "NULL" 56 | basetype "uint8_t *" "lightuserdata" "NULL" 57 | basetype "lua_State *" "thread" "NULL" 58 | basetype "void" "nil" "NULL" 59 | 60 | basetype "" "nil" "NULL" 61 | basetype "" "table" "NULL" 62 | 63 | -- 64 | -- to/check/push/delete methods 65 | -- 66 | print"============ create Lua to/check/push/delete methods =================" 67 | local lua_base_types = { 68 | ['nil'] = { push = 'lua_pushnil' }, 69 | ['number'] = { to = 'lua_tonumber', opt = 'luaL_optnumber', check = 'luaL_checknumber', 70 | push = 'lua_pushnumber' }, 71 | ['integer'] = { to = 'lua_tointeger', opt = 'luaL_optinteger', check = 'luaL_checkinteger', 72 | push = 'lua_pushinteger' }, 73 | ['string'] = { to = 'lua_tolstring', opt = 'luaL_optlstring', check = 'luaL_checklstring', 74 | push = 'lua_pushstring', push_len = 'lua_pushlstring' }, 75 | ['boolean'] = { to = 'lua_toboolean', check = 'lua_toboolean', push = 'lua_pushboolean' }, 76 | ['thread'] = { to = 'lua_tothread', check = 'lua_tothread', push = 'lua_pushthread' }, 77 | ['lightuserdata'] = 78 | { to = 'lua_touserdata', check = 'lua_touserdata', push = 'lua_pushlightuserdata' }, 79 | } 80 | 81 | reg_stage_parser("lang_type_process", { 82 | basetype = function(self, rec, parent) 83 | local l_type = lua_base_types[rec.lang_type] 84 | if l_type == nil then return end 85 | rec._ffi_push = function(self, var, flags, unwrap) 86 | local wrap = var.ffi_wrap 87 | if wrap then 88 | return wrap .. '(${' .. var.name .. '})' .. (unwrap or '') 89 | else 90 | return '${' .. var.name .. '}' .. (unwrap or '') 91 | end 92 | end 93 | if rec.lang_type == 'string' then 94 | local cast = '' 95 | if rec.c_type ~= 'const char *' and rec.c_type ~= 'char *' then 96 | cast = '(' .. rec.c_type .. ')' 97 | end 98 | rec._to = function(self, var) 99 | return '${' .. var.name .. '} = ' .. cast .. 100 | l_type.to .. '(L,${' .. var.name .. '::idx},&(${' .. var.length .. '}));\n' 101 | end 102 | rec._define = function(self, var) 103 | return 'size_t ${' .. var.name .. '_len};\n' .. 104 | ' ' .. var.c_type .. ' ${' .. var.name .. '};\n' 105 | end 106 | rec._check = function(self, var) 107 | return '${' .. var.name .. '} = ' .. cast .. 108 | l_type.check .. '(L,${' .. var.name .. '::idx},&(${' .. var.name .. '_len}));\n' 109 | end 110 | rec._opt = function(self, var, default) 111 | if default then 112 | default = '"' .. default .. '"' 113 | else 114 | default = 'NULL' 115 | end 116 | return '${' .. var.name .. '} = ' .. cast .. 117 | l_type.opt .. '(L,${' .. var.name .. '::idx},' .. default .. 118 | ',&(${' .. var.name .. '_len}));\n' 119 | end 120 | rec._push = function(self, var) 121 | if var.has_length then 122 | return 123 | ' if(${' .. var.name .. '} == NULL) lua_pushnil(L);' .. 124 | ' else ' .. l_type.push_len .. '(L, ${' .. var.name .. '},' .. 125 | '${' .. var.length .. '});\n' 126 | end 127 | return ' ' .. l_type.push .. '(L, ${' .. var.name .. '});\n' 128 | end 129 | rec._ffi_define = function(self, var) 130 | return '' 131 | end 132 | rec._ffi_push = function(self, var) 133 | local pre = '${' .. var.name .. '} ~= nil and ffi_string(${' .. var.name .. '}' 134 | if var.has_length then 135 | return pre .. ',${' .. var.length .. '}) or nil' 136 | end 137 | return pre .. ') or nil' 138 | end 139 | rec._ffi_check = function(self, var) 140 | return 'local ${' .. var.name .. '_len} = #${' .. var.name .. '}\n' 141 | end 142 | rec._ffi_opt = function(self, var, default) 143 | if default then 144 | default = (' or %q'):format(tostring(default)) 145 | else 146 | default = '' 147 | end 148 | return 149 | '${' .. var.name .. '} = tostring(${' .. var.name .. '})' .. default .. '\n' .. 150 | ' local ${' .. var.name .. '_len} = ${' .. var.name .. '} and #${' .. var.name .. '} or 0\n' 151 | end 152 | else 153 | rec._to = function(self, var) 154 | return '${' .. var.name .. '} = ' .. l_type.to .. '(L,${' .. var.name .. '::idx});\n' 155 | end 156 | rec._define = function(self, var) 157 | return var.c_type .. ' ${' .. var.name .. '};\n' 158 | end 159 | rec._check = function(self, var) 160 | return '${' .. var.name .. '} = ' .. l_type.check .. '(L,${' .. var.name .. '::idx});\n' 161 | end 162 | rec._opt = function(self, var, default) 163 | default = default or '0' 164 | if l_type.opt then 165 | return '${' .. var.name .. '} = ' .. 166 | l_type.opt .. '(L,${' .. var.name .. '::idx},' .. default .. ');\n' 167 | end 168 | return '${' .. var.name .. '} = ' .. 169 | l_type.to .. '(L,${' .. var.name .. '::idx});\n' 170 | end 171 | rec._push = function(self, var) 172 | return ' ' .. l_type.push .. '(L, ${' .. var.name .. '});\n' 173 | end 174 | rec._ffi_define = function(self, var) 175 | return '' 176 | end 177 | rec._ffi_check = function(self, var) 178 | return '\n' 179 | end 180 | rec._ffi_opt = function(self, var, default) 181 | default = tostring(default or '0') 182 | return '${' .. var.name .. '} = ${' .. var.name .. '} or ' .. default .. '\n' 183 | end 184 | end 185 | end, 186 | error_code = function(self, rec, parent) 187 | local func_name = 'error_code__' .. rec.name .. '__push' 188 | rec.func_name = func_name 189 | 190 | -- create _push_error & _push function 191 | rec._push = function(self, var) 192 | return ' ' .. func_name ..'(L, ${' .. var.name .. '});\n' 193 | end 194 | rec._push_error = rec._push 195 | rec._ffi_push = function(self, var, flags, unwrap) 196 | return func_name ..'(${' .. var.name .. '})' .. (unwrap or '') 197 | end 198 | rec._ffi_push_error = rec._ffi_push 199 | end, 200 | import_object = function(self, rec, parent) 201 | rec.lang_type = 'userdata' 202 | local type_name = 'obj_type_' .. rec.name 203 | rec._obj_type_name = type_name 204 | 205 | -- create _check/_delete/_push functions 206 | rec._define = function(self, var) 207 | return var.c_type .. ' ${'..var.name..'};\n' 208 | end 209 | rec._check = function(self, var) 210 | return '${'..var.name..'} = '..type_name..'_check(L,${'..var.name..'::idx});\n' 211 | end 212 | rec._opt = function(self, var) 213 | return '${'..var.name..'} = '..type_name..'_optional(L,${'..var.name..'::idx});\n' 214 | end 215 | rec._delete = function(self, var, flags) 216 | error("Can't delete an imported type.") 217 | end 218 | rec._to = rec._check 219 | rec._push = function(self, var, flags) 220 | error("Can't push an imported type.") 221 | end 222 | rec._ffi_define = function(self, var) 223 | return '' 224 | end 225 | rec._ffi_check = function(self, var) 226 | local name = '${' .. var.name .. '}' 227 | return name .. ' = '..type_name..'_check('..name..')\n' 228 | end 229 | rec._ffi_opt = function(self, var) 230 | local name = '${' .. var.name .. '}' 231 | return name .. ' = '..name..' and '..type_name..'_check('..name..') or nil\n' 232 | end 233 | rec._ffi_delete = function(self, var, has_flags) 234 | error("Can't delete an imported type.") 235 | end 236 | rec._ffi_push = function(self, var, flags, unwrap) 237 | error("Can't push an imported type.") 238 | end 239 | end, 240 | object = function(self, rec, parent) 241 | rec.lang_type = 'userdata' 242 | local type_name = 'obj_type_' .. rec.name 243 | rec._obj_type_name = type_name 244 | 245 | -- create _check/_delete/_push functions 246 | rec._define = function(self, var) 247 | return var.c_type .. ' ${'..var.name..'};\n' 248 | end 249 | rec._check = function(self, var) 250 | return '${'..var.name..'} = '..type_name..'_check(L,${'..var.name..'::idx});\n' 251 | end 252 | rec._opt = function(self, var) 253 | return '${'..var.name..'} = '..type_name..'_optional(L,${'..var.name..'::idx});\n' 254 | end 255 | rec._delete = function(self, var, flags) 256 | if not flags then 257 | return '${'..var.name..'} = '..type_name..'_delete(L,${'..var.name..'::idx});\n' 258 | end 259 | return '${'..var.name..'} = '..type_name..'_delete(L,${'..var.name..'::idx},'..flags..');\n' 260 | end 261 | rec._to = rec._check 262 | rec._push = function(self, var, flags) 263 | if flags == false then 264 | return ' '..type_name..'_push(L, ${'..var.name..'});\n' 265 | end 266 | if flags == nil then flags = '0' end 267 | return ' '..type_name..'_push(L, ${'..var.name..'}, ' .. flags .. ');\n' 268 | end 269 | rec._ffi_define = function(self, var) 270 | return '' 271 | end 272 | rec._ffi_check = function(self, var) 273 | if not rec.subs then 274 | -- no sub-classes 275 | return rec._ffi_check_fast(self, var) 276 | end 277 | -- has sub-classes do extra casting if needed. 278 | if var.is_this then 279 | return 'local ${' .. var.name .. '} = '..type_name..'_check(self)\n' 280 | end 281 | local name = '${' .. var.name .. '}' 282 | return name .. ' = '..type_name..'_check('..name..')\n' 283 | end 284 | rec._ffi_opt = function(self, var) 285 | if var.is_this then 286 | return 'local ${' .. var.name .. '} = '..type_name..'_check(self)\n' 287 | end 288 | local name = '${' .. var.name .. '}' 289 | return name .. ' = '..name..' and '..type_name..'_check('..name..') or nil\n' 290 | end 291 | rec._ffi_delete = function(self, var, has_flags) 292 | if not has_flags then 293 | return 'local ${'..var.name..'} = '..type_name..'_delete(self)\n' 294 | end 295 | return 'local ${'..var.name..'},${'..var.name..'_flags} = '..type_name..'_delete(self)\n' 296 | end 297 | rec._ffi_push = function(self, var, flags, unwrap) 298 | if flags == false then 299 | return type_name..'_push(${'..var.name..'})' .. (unwrap or '') 300 | end 301 | if flags == nil then flags = '0' end 302 | return type_name..'_push(${'..var.name..'}, ' .. flags .. ')' .. (unwrap or '') 303 | end 304 | if rec.error_on_null then 305 | rec._push_error = function(self, var) 306 | return ' lua_pushstring(L, ' .. rec.error_on_null .. ');\n' 307 | end 308 | rec._ffi_push_error = function(self, var) 309 | return rec.error_on_null 310 | end 311 | end 312 | end, 313 | interface = function(self, rec, parent) 314 | rec.lang_type = 'userdata' 315 | local if_name = rec.name 316 | 317 | -- create _check/_delete/_push functions 318 | rec._define = function(self, var) 319 | return if_name..'IF_VAR(${'..var.name..'});\n' 320 | end 321 | rec._check = function(self, var) 322 | return if_name..'IF_LUA_CHECK(L,${'..var.name..'::idx}, ${'..var.name..'});\n' 323 | end 324 | rec._opt = function(self, var) 325 | return if_name..'IF_LUA_OPTIONAL(L,${'..var.name..'::idx}, ${'..var.name..'});\n' 326 | end 327 | rec._delete = function(self, var, flags) 328 | error("Can't delete an interface object.") 329 | end 330 | rec._to = rec._check 331 | rec._push = function(self, var, flags) 332 | error("Can't push an interface object.") 333 | end 334 | rec._ffi_define = function(self, var) 335 | return 'local ${' .. var.name .. '}_if' 336 | end 337 | rec._ffi_check = function(self, var) 338 | local name = '${' .. var.name .. '}' 339 | return name .. '_if = '..name..'.NOBJ_get_'..if_name..'IF' 340 | ..' or obj_type_'..if_name..'_check('..name..')\n' 341 | end 342 | rec._ffi_opt = function(self, var) 343 | local name = '${' .. var.name .. '}' 344 | return name .. '_if = '..name..' and ('..name..'.NOBJ_get_'..if_name..'IF' 345 | ..' or obj_type_'..if_name..'_check('..name..')) or nil\n' 346 | end 347 | rec._ffi_delete = function(self, var, has_flags) 348 | error("Can't delete an interface object.") 349 | end 350 | rec._ffi_push = function(self, var, flags, unwrap) 351 | error("Can't push an interface object.") 352 | end 353 | end, 354 | callback_func = function(self, rec, parent) 355 | rec.lang_type = 'function' 356 | 357 | -- create _check/_delete/_push functions 358 | rec._check = function(self, var) 359 | return 'lua_checktype_ref(L, ${' .. var.name .. '::idx}, LUA_TFUNCTION);\n' 360 | end 361 | rec._delete = nil 362 | rec._to = rec._check 363 | rec._push = function(self, var) 364 | return 'lua_rawgeti(L, LUA_REGISTRYINDEX, ' .. var .. ');\n' 365 | end 366 | end, 367 | }) 368 | 369 | -------------------------------------------------------------------------------- /native_objects/lang_swig.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | 22 | -- 23 | -- C to Lua Base types 24 | -- 25 | basetype "bool" "boolean" 26 | 27 | basetype "char" "integer" 28 | basetype "unsigned char" "integer" 29 | basetype "short" "integer" 30 | basetype "unsigned short" "integer" 31 | basetype "int" "integer" 32 | basetype "unsigned int" "integer" 33 | basetype "long" "integer" 34 | basetype "unsigned long" "integer" 35 | -- stdint types. 36 | basetype "int8_t" "integer" 37 | basetype "int16_t" "integer" 38 | basetype "int32_t" "integer" 39 | basetype "int64_t" "integer" 40 | basetype "uint8_t" "integer" 41 | basetype "uint16_t" "integer" 42 | basetype "uint32_t" "integer" 43 | basetype "uint64_t" "integer" 44 | 45 | basetype "float" "number" 46 | basetype "double" "number" 47 | 48 | basetype "char *" "string" 49 | basetype "void *" "lightuserdata" 50 | basetype "void" "nil" 51 | 52 | local lua_base_types = { 53 | ['nil'] = { push = 'lua_pushnil' }, 54 | ['number'] = { to = 'lua_tonumber', check = 'luaL_checknumber', push = 'lua_pushnumber' }, 55 | ['integer'] = { to = 'lua_tointeger', check = 'luaL_checkinteger', push = 'lua_pushinteger' }, 56 | ['string'] = { to = 'lua_tostring', check = 'luaL_checkstring', push = 'lua_pushstring' }, 57 | ['boolean'] = { to = 'lua_toboolean', check = 'lua_toboolean', push = 'lua_pushboolean' }, 58 | ['lightuserdata'] = 59 | { to = 'lua_touserdata', check = 'lua_touserdata', push = 'lua_pushlightuserdata' }, 60 | } 61 | 62 | -------------------------------------------------------------------------------- /native_objects/record.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | local tinsert,tremove=table.insert,table.remove 22 | local tappend=function(dst,src) for _,v in pairs(src) do dst[#dst+1] = v end end 23 | 24 | -- records that are not contained in other records 25 | local root_records={} 26 | local record_type_groups={} 27 | local clear_funcs={} 28 | local global_scope={} 29 | function clear_all_records() 30 | root_records={} 31 | record_type_groups={} 32 | -- run clear functions 33 | for i=1,#clear_funcs do 34 | local func = clear_funcs[i] 35 | func() 36 | end 37 | global_scope={} 38 | end 39 | function reg_clear_func(func) 40 | clear_funcs[#clear_funcs + 1] = func 41 | end 42 | 43 | -- get a named record with rec_type 44 | function get_named_record(rec_type, name) 45 | local recs = record_type_groups[rec_type] 46 | if recs ~= nil then 47 | return recs[name] 48 | end 49 | end 50 | 51 | local function group_add_record(rec, rec_type) 52 | -- add this record to the list of records with the same type. 53 | local type_group = record_type_groups[rec_type] 54 | if type_group == nil then 55 | type_group = {} 56 | record_type_groups[rec_type] = type_group 57 | end 58 | type_group[#type_group+1] = rec 59 | if name ~= nil then 60 | type_group[name] = rec 61 | end 62 | end 63 | 64 | local variable_format="%s_idx%d" 65 | function set_variable_format(format) 66 | variable_format = format 67 | end 68 | function format_variable(name, idx) 69 | return variable_format:format(name, idx) 70 | end 71 | 72 | -- Meta-table used by all records. 73 | local ignore_record={_rec_type = "ignore"} 74 | local rec_meta 75 | rec_meta={ 76 | clear = function(self) 77 | self._vars = {} 78 | self._data_parts = {} 79 | self._rec_counts = {} 80 | end, 81 | -- 82 | -- add data output functions to record. 83 | -- 84 | -- replace variables in "part" 85 | vars_part = function(self, part) 86 | local tmpl = self:dump_parts({part}) 87 | tmpl = tmpl:gsub("%${(.-)}", self._vars) 88 | self._data_parts[part] = {tmpl} 89 | return tmpl 90 | end, 91 | -- replace variables in "parts" 92 | vars_parts = function(self, parts) 93 | local out={} 94 | parts = self:parts(parts) 95 | -- apply variables to all "parts". 96 | for i=1,#parts do 97 | local part = parts[i] 98 | local d = self:vars_part(part) 99 | out[#out+1] = d 100 | end 101 | return out 102 | end, 103 | -- append data to "part" 104 | write_part = function(self, part, data) 105 | if type(data) ~= "table" then 106 | if data == nil then return end 107 | data = { tostring(data) } 108 | end 109 | local out=self._data_parts[part] 110 | if out == nil then 111 | out = {} 112 | self._data_parts[part] = out 113 | end 114 | -- append data. 115 | tappend(out, data) 116 | end, 117 | parts = function(self, parts) 118 | -- make sure "parts" is a table. 119 | if parts == nil then 120 | parts = {} 121 | for part in pairs(self._data_parts) do parts[#parts+1] = part end 122 | elseif type(parts) ~= "table" then 123 | parts = { parts } 124 | end 125 | return parts 126 | end, 127 | dump_parts = function(self, parts, sep) 128 | local out={} 129 | parts = self:parts(parts) 130 | -- return all parts listed in "parts". 131 | local data = self._data_parts 132 | for i=1,#parts do 133 | local part = parts[i] 134 | local d_part=data[part] 135 | if d_part then 136 | tappend(out, d_part) 137 | end 138 | if sep ~= nil then 139 | out[#out+1] = sep 140 | end 141 | end 142 | return table.concat(out) 143 | end, 144 | -- copy parts from "src" record. 145 | copy_parts = function(self, src, parts) 146 | parts = src:parts(parts) 147 | for i=1,#parts do 148 | local part = parts[i] 149 | self:write_part(part, src:dump_parts(part)) 150 | end 151 | end, 152 | -- 153 | -- functions for counting sub-records 154 | -- 155 | get_sub_record_count = function(self, _rec_type) 156 | local count = self._rec_counts[_rec_type] 157 | if count == nil then count = 0 end 158 | return count 159 | end, 160 | count_sub_record = function(self, rec) 161 | local count = self:get_sub_record_count(rec._rec_type) 162 | count = count + 1 163 | self._rec_counts[rec._rec_type] = count 164 | rec._rec_idx = count 165 | end, 166 | -- 167 | -- functions for adding named variables 168 | -- 169 | add_var = function(self, key, value) 170 | self._vars[key] = value 171 | end, 172 | add_rec_var = function(self, rec, name, vname, idx) 173 | local name = name or rec.name 174 | local idx = idx or rec._rec_idx 175 | self._vars[name] = vname or format_variable(name, idx) 176 | self._vars[name .. "::idx"] = idx 177 | end, 178 | -- 179 | -- sub-records management functions 180 | -- 181 | make_sub_record = function(self, parent) 182 | local root_idx 183 | -- find record in roots list 184 | for idx=1,#root_records do 185 | if root_records[idx] == self then 186 | root_idx = idx 187 | break 188 | end 189 | end 190 | -- remove it from the roots list 191 | if root_idx ~= nil and root_records[root_idx] == self then 192 | tremove(root_records, root_idx) 193 | end 194 | rawset(self, "_parent", parent) 195 | end, 196 | insert_record = function(self, rec, pos) 197 | rec:make_sub_record(self) 198 | if pos ~= nil then 199 | tinsert(self, pos, rec) 200 | else 201 | self[#self+1] = rec 202 | end 203 | end, 204 | add_record = function(self, rec) 205 | self:insert_record(rec) 206 | end, 207 | find_record = function(self, rec) 208 | for i=1,#self do 209 | local sub = self[i] 210 | if sub == rec then 211 | return i 212 | end 213 | end 214 | end, 215 | replace_record = function(self, old_rec, new_rec) 216 | for i=1,#self do 217 | local sub = self[i] 218 | if sub == old_rec then 219 | self[i] = new_rec 220 | return i 221 | end 222 | end 223 | end, 224 | remove_record = function(self, rec) 225 | for i=1,#self do 226 | local sub = self[i] 227 | if sub == rec then 228 | rawset(self, i, ignore_record) -- have to insert an empty table in it's place. 229 | rawset(sub, "_parent", nil) 230 | return 231 | end 232 | end 233 | end, 234 | -- 235 | -- delete a record and all it's sub-records 236 | -- 237 | delete_record = function(self) 238 | -- remove from parent. 239 | if self._parent ~= nil then 240 | self._parent:remove_record(self) 241 | end 242 | -- delete sub-records 243 | for i=1,#self do 244 | local sub = self[i] 245 | if is_record(sub) and sub._parent == self then 246 | self[i] = nil 247 | sub:delete_record() 248 | rawset(sub, "_parent", nil) 249 | end 250 | end 251 | -- ignore this record and it sub-records 252 | self._rec_type = "ignore" 253 | end, 254 | -- 255 | -- Copy record and all it's sub-records. 256 | -- 257 | copy_record = function(self) 258 | local copy = {} 259 | -- copy values from current record. 260 | for k,v in pairs(self) do 261 | copy[k] = v 262 | end 263 | rawset(copy, "_parent", nil) -- unlink from old parent 264 | -- copy sub-records 265 | for i=1,#copy do 266 | local sub = copy[i] 267 | if is_record(sub) then 268 | local sub_copy = sub:copy_record() 269 | rawset(copy, i, sub_copy) 270 | rawset(sub_copy, "_parent", copy) 271 | end 272 | end 273 | setmetatable(copy, rec_meta) 274 | group_add_record(copy, copy._rec_type) 275 | return copy 276 | end, 277 | -- 278 | -- Symbol resolver 279 | -- 280 | add_symbol = function(self, name, obj, scope) 281 | -- default scope 'local' 282 | if scope == nil then scope = "local" end 283 | -- if scope is global then skip local maps. 284 | if scope == 'global' then 285 | global_scope[name] = obj 286 | return 287 | end 288 | -- add symbol to local map 289 | self._symbol_map[name] = obj 290 | -- if scope is doesn't equal our scope 291 | if scope ~= self.scope and self._parent ~= nil then 292 | self._parent:add_symbol(name, obj, scope) 293 | end 294 | end, 295 | get_symbol = function(self, name) 296 | -- check our mappings 297 | local obj = self._symbol_map[name] 298 | -- check parent if we don't have a mapping for the symbol 299 | if obj == nil and self._parent ~= nil then 300 | obj = self._parent:get_symbol(name) 301 | end 302 | -- next check the imports for the symbol 303 | if obj == nil then 304 | local imports = self._imports 305 | for i=1,#imports do 306 | local import = imports[i] 307 | obj = import:get_symbol(name) 308 | if obj ~= nil then 309 | break 310 | end 311 | end 312 | end 313 | -- next check the globals for the symbol 314 | if obj == nil then 315 | obj = global_scope[name] 316 | end 317 | return obj 318 | end, 319 | -- import symbols from a "file" record 320 | add_import = function(self, import_rec) 321 | local imports = self._imports 322 | -- if already imported then skip 323 | if imports[import_rec] then return end 324 | imports[import_rec] = true 325 | -- append to head of imports list so that the last import overrides symbols 326 | -- from the previous imports 327 | table.insert(imports, 1, import_rec) 328 | end, 329 | } 330 | rec_meta.__index = rec_meta 331 | function is_record(rec) 332 | -- use a metatable to identify records 333 | return (getmetatable(rec) == rec_meta and rec._rec_type ~= nil) 334 | end 335 | setmetatable(ignore_record, rec_meta) 336 | 337 | local function remove_child_records_from_roots(rec, seen) 338 | -- make sure we don't get in a reference loop. 339 | if seen == nil then seen = {} end 340 | if seen[rec] then return end 341 | seen[rec] = true 342 | -- remove from root list. 343 | for i=1,#rec do 344 | local val = rec[i] 345 | if is_record(val) then 346 | val:make_sub_record(rec) 347 | end 348 | end 349 | end 350 | 351 | local function end_record(rec) 352 | if type(rec) ~= 'function' then return rec end 353 | 354 | local rc, result = pcall(rec, nil) 355 | if not rc then 356 | print("Error processing new record: " .. result) 357 | return rec 358 | end 359 | return end_record(result) 360 | end 361 | 362 | function make_record(rec, rec_type, name, scope) 363 | if rec == nil then rec = {} end 364 | if type(rec) ~= "table" then rec = { rec } end 365 | -- set record's name. 366 | if name == nil then name = rec_type end 367 | rec.name = name 368 | -- record's symbol scope 369 | rec.scope = scope 370 | rec._symbol_map = {} 371 | rec._imports = {} 372 | 373 | -- make "rec" into a record. 374 | rec._rec_type = rec_type 375 | setmetatable(rec, rec_meta) 376 | 377 | -- complete partial child records. 378 | for i=1,#rec do 379 | local val = rec[i] 380 | if type(val) == 'function' then 381 | val = end_record(val) 382 | rec[i] = val 383 | end 384 | end 385 | 386 | -- remove this record's child records from the root list. 387 | remove_child_records_from_roots(rec) 388 | 389 | -- add this record to the root list. 390 | root_records[#root_records + 1] = rec 391 | 392 | group_add_record(rec, rec_type) 393 | 394 | return rec 395 | end 396 | 397 | -- 398 | -- Record parser 399 | -- 400 | local function record_parser(callbacks, name) 401 | name = name or "parse" 402 | local function call_meth(self, rec_type, post, rec, parent) 403 | local func = self[rec_type .. post] 404 | if func == nil then 405 | func = self["unknown" .. post] 406 | if func == nil then return end 407 | end 408 | return func(self, rec, parent) 409 | end 410 | local seen={} 411 | callbacks = setmetatable(callbacks, { 412 | __call = function(self, rec, parent) 413 | -- make sure it is a valid record. 414 | if not is_record(rec) or seen[rec] or rec._rec_type == "ignore" then return end 415 | if parent then 416 | parent:count_sub_record(rec) -- count sub-records. 417 | end 418 | -- keep track of records we have already processed 419 | seen[rec] = true 420 | local rec_type = rec._rec_type 421 | -- clear record's data output & sub-record counts. 422 | rec:clear() 423 | -- start record. 424 | call_meth(self, rec_type, "", rec, parent) 425 | -- transverse into sub-records 426 | for _,v in ipairs(rec) do 427 | self(v, rec) 428 | end 429 | -- end record 430 | call_meth(self, rec_type, "_end", rec, parent) 431 | -- update "last_type" 432 | self.last_type = rec_type 433 | end 434 | }) 435 | return callbacks 436 | end 437 | 438 | function process_records(parser) 439 | record_parser(parser) 440 | -- process each root record 441 | for i=1,#root_records do 442 | parser(root_records[i]) 443 | end 444 | return parser 445 | end 446 | 447 | local stages = {} 448 | function reg_stage_parser(stage, parser) 449 | local parsers = stages[stage] 450 | if parsers == nil then 451 | -- new stage add it to the end of the stage list. 452 | stages[#stages + 1] = stage 453 | parsers = {} 454 | stages[stage] = parsers 455 | end 456 | parsers[#parsers + 1] = parser 457 | end 458 | 459 | -- setup default stages 460 | local default_stages = { "symbol_map", "imports" } 461 | 462 | -- run all parser stages. 463 | function run_stage_parsers() 464 | for i=1,#stages do 465 | local stage = stages[i] 466 | local parsers = stages[stage] 467 | for x=1,#parsers do 468 | process_records(parsers[x]) 469 | end 470 | end 471 | end 472 | 473 | function move_recs(dst, src, idx) 474 | -- move records from "rec" to it's parent 475 | for i=1,#src do 476 | local rec = src[i] 477 | if is_record(rec) and rec._rec_type ~= "ignore" then 478 | src:remove_record(rec) -- remove from src 479 | if idx then 480 | dst:insert_record(rec, idx) -- insert into dst 481 | idx = idx + 1 482 | else 483 | dst:add_record(rec) -- add to dst 484 | end 485 | end 486 | end 487 | -- now delete this empty container record 488 | src:delete_record() 489 | end 490 | 491 | -- 492 | -- Record functions -- Used to create new records. 493 | -- 494 | function make_generic_rec_func(rec_type, no_name) 495 | if _G[rec_type] ~= nil then error("global already exists with that name: " .. rec_type) end 496 | if not no_name then 497 | _G[rec_type] = function(name) 498 | return function(rec) 499 | rec = make_record(rec, rec_type, name) 500 | return rec 501 | end 502 | end 503 | else 504 | _G[rec_type] = function(rec) 505 | rec = make_record(rec, rec_type, rec_type) 506 | return rec 507 | end 508 | end 509 | end 510 | 511 | local path_char = package.config:sub(1,1) 512 | local path_match = '(.*)' .. path_char 513 | local path_stack = {''} 514 | function subfile_path(filename) 515 | local level = #path_stack 516 | local cur_path = path_stack[level] or '' 517 | return cur_path .. filename 518 | end 519 | function subfiles(files) 520 | local level = #path_stack 521 | local cur_path = path_stack[level] 522 | local rc 523 | level = level + 1 524 | -- use a new roots list to catch records from subfiles 525 | local prev_roots = root_records 526 | root_records={} 527 | 528 | -- process subfiles 529 | for i=1,#files do 530 | local file = files[i] 531 | -- add current path to file 532 | file = cur_path .. file 533 | -- seperate file's path from the filename. 534 | local file_path = file:match(path_match) or '' 535 | if #file_path > 0 then 536 | file_path = file_path .. path_char 537 | end 538 | -- push the file's path onto the path_stack only if it is different. 539 | if cur_path ~= file_path then 540 | path_stack[level] = file_path 541 | end 542 | -- check file path 543 | print("Parsing records from file: " .. file) 544 | rc = {dofile(file)} 545 | -- pop path 546 | if cur_path ~= file_path then 547 | path_stack[level] = nil 548 | end 549 | end 550 | -- move sub-records into new array 551 | local rec={} 552 | for i=1,#root_records do 553 | rec[i] = root_records[i] 554 | end 555 | 556 | -- switch back to previous roots list 557 | root_records = prev_roots 558 | 559 | -- make this into a record holding the sub-records from each of the sub-files 560 | rec = make_record(rec, "subfiles") 561 | if #rc > 0 then 562 | return rec, rc 563 | end 564 | return rec 565 | end 566 | -- process some container records 567 | reg_stage_parser("containers", { 568 | subfiles = function(self, rec, parent) 569 | local idx = parent:find_record(rec) 570 | move_recs(parent, rec, idx) 571 | end, 572 | }) 573 | 574 | local subfolders={} 575 | function subfolder(folder) 576 | return function(...) 577 | local files=select(1, ...) 578 | if type(files) ~= 'table' then 579 | files = {...} 580 | end 581 | -- push subfolder 582 | subfolders[#subfolders+1] = folder 583 | -- build full path 584 | folder = table.concat(subfolders, "/") .. "/" 585 | for i=1,#files do 586 | files[i] = folder .. files[i] 587 | end 588 | -- use subfile record. 589 | local rec = subfiles(files) 590 | -- pop subfolder 591 | subfolders[#subfolders] = nil 592 | return rec 593 | end 594 | end 595 | 596 | function import(name) 597 | rec = make_record({}, "import", name) 598 | return rec 599 | end 600 | -- resolve imports 601 | reg_stage_parser("imports", { 602 | import = function(self, rec, parent) 603 | end, 604 | }) 605 | 606 | -------------------------------------------------------------------------------- /native_objects/stages.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2012 by Robert G. Jakabosky 2 | -- 3 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 4 | -- of this software and associated documentation files (the "Software"), to deal 5 | -- in the Software without restriction, including without limitation the rights 6 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | -- copies of the Software, and to permit persons to whom the Software is 8 | -- furnished to do so, subject to the following conditions: 9 | -- 10 | -- The above copyright notice and this permission notice shall be included in 11 | -- all copies or substantial portions of the Software. 12 | -- 13 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | -- THE SOFTWARE. 20 | 21 | local tconcat=table.concat 22 | local tinsert=table.insert 23 | local assert=assert 24 | local error=error 25 | local type=type 26 | local io=io 27 | local pairs=pairs 28 | 29 | -- 30 | -- process some container records 31 | -- 32 | reg_stage_parser("containers",{ 33 | lang = function(self, rec, parent) 34 | -- only keep records for current language. 35 | if rec.name == gen_lang then 36 | -- keep records by moving them up to the parent 37 | move_recs(parent, rec) 38 | else 39 | -- delete this record and it sub-records 40 | rec:delete_record() 41 | end 42 | end, 43 | object = function(self, rec, parent) 44 | -- re-map c_types 45 | new_c_type(rec.name, rec) 46 | new_c_type(rec.c_type, rec) 47 | end, 48 | ffi_files = function(self, rec, parent) 49 | for i=1,#rec do 50 | local file = assert(io.open(rec[i], "r")) 51 | parent:add_record(ffi_source(rec.part)(file:read("*a"))) 52 | file:close() 53 | end 54 | end, 55 | constants = function(self, rec, parent) 56 | for key,value in pairs(rec.values) do 57 | parent:add_record(const(key)({ value })) 58 | end 59 | rec._rec_type = nil 60 | end, 61 | export_definitions = function(self, rec, parent) 62 | local values = rec.values 63 | -- export list of definitions as-is (i.e. no renaming). 64 | for i=1,#values do 65 | local name = values[i] 66 | parent:add_record(const_def(name)({ name })) 67 | values[i] = nil 68 | end 69 | -- export renamed definitions. 70 | for key, value in pairs(values) do 71 | parent:add_record(const_def(key)({ value })) 72 | end 73 | rec._rec_type = nil 74 | end, 75 | error_code = function(self, rec, parent) 76 | new_c_type(rec.name, rec) 77 | end, 78 | unknown = function(self, rec, parent) 79 | -- re-map c_types 80 | if rec._is_c_type ~= nil then 81 | new_c_type(rec.c_type, rec) 82 | end 83 | end, 84 | }) 85 | 86 | -- register place-holder 87 | reg_stage_parser("resolve_types") 88 | 89 | -- 90 | -- convert fields into get/set methods. 91 | -- 92 | reg_stage_parser("fields",{ 93 | field = function(self, rec, parent) 94 | local name = rec.name 95 | local c_type = rec.c_type 96 | parent:add_record(method(name) { 97 | var_out{c_type , "field"}, 98 | c_source 'src' {"\t${field} = ${this}->", name,";\n" }, 99 | }) 100 | if rec.is_writable then 101 | parent:add_record(method("set_" .. name) { 102 | var_in{c_type , "field"}, 103 | c_source 'src' {"\t${this}->", name," = ${field};\n" }, 104 | }) 105 | end 106 | end, 107 | }) 108 | 109 | -- 110 | -- add 'this' variable to method records. 111 | -- 112 | reg_stage_parser("this_variable",{ 113 | c_function = function(self, rec, parent) 114 | if rec._is_method and not rec.override_this then 115 | local var 116 | if parent.is_meta then 117 | var = var_in{ "", "this", is_this = true } 118 | elseif rec.is_constructor then 119 | var = var_out{ parent.c_type, "this", is_this = true } 120 | -- make the first constructor the default. 121 | if not parent.default_constructor then 122 | parent.default_constructor = rec 123 | rec.is_default_constructor = true 124 | end 125 | else 126 | var = var_in{ parent.c_type, "this", is_this = true } 127 | end 128 | rec:insert_record(var, 1) 129 | end 130 | end, 131 | }) 132 | 133 | -- 134 | -- create callback_func & callback_state records. 135 | -- 136 | reg_stage_parser("callback",{ 137 | var_in = function(self, rec, parent) 138 | -- is variable a callback type? 139 | if not rec.is_callback then return end 140 | -- get grand-parent container 141 | local container = parent._parent 142 | -- create callback_state instance. 143 | local cb_state 144 | if rec.owner == 'this' then 145 | local wrap_type = container.c_type 146 | cb_state = callback_state(wrap_type, rec.wrap_state) 147 | -- wrap 'this' object. 148 | container.callback_state = cb_state 149 | parent.callback_state = cb_state 150 | parent.state_owner = rec.owner 151 | if rec.state_var ~= 'this' then 152 | local state_var = tmp_var{ "void *", rec.state_var } 153 | parent.state_var = state_var 154 | parent:insert_record(state_var, 1) 155 | end 156 | else 157 | assert("un-supported callback owner type: " .. rec.owner) 158 | end 159 | container:insert_record(cb_state, 1) 160 | -- create callback_func instance. 161 | local cb_func = callback_func(rec.c_type)(rec.name) 162 | -- move sub-records from 'var_in' callback record into 'callback_func' 163 | local cb=rec 164 | for i=1,#cb do 165 | local rec = cb[i] 166 | if is_record(rec) and rec._rec_type ~= "ignore" then 167 | cb:remove_record(rec) -- remove from 'var_in' 168 | cb_func:add_record(rec) -- add to 'callback_func' 169 | end 170 | end 171 | cb_state:add_record(cb_func) 172 | rec.cb_func = cb_func 173 | rec.c_type_rec = cb_func 174 | end, 175 | }) 176 | 177 | -- 178 | -- process extends/dyn_caster records 179 | -- 180 | reg_stage_parser("dyn_caster",{ 181 | _obj_cnt = 0, 182 | object = function(self, rec, parent) 183 | rec._obj_id = self._obj_cnt 184 | self._obj_cnt = self._obj_cnt + 1 185 | end, 186 | import_object = function(self, rec, parent) 187 | rec._obj_id = self._obj_cnt 188 | self._obj_cnt = self._obj_cnt + 1 189 | end, 190 | extends = function(self, rec, parent) 191 | -- find base-object record. 192 | local base = resolve_c_type(rec.name) 193 | rec.base = base 194 | -- add this object to base. 195 | local subs = base.subs 196 | if subs == nil then 197 | subs = {} 198 | base.subs = subs 199 | end 200 | subs[#subs+1] = parent 201 | end, 202 | dyn_caster = function(self, rec, parent) 203 | parent.has_dyn_caster = rec 204 | if rec.caster_type == 'switch' then 205 | for k,v in pairs(rec.value_map) do 206 | rec.value_map[k] = resolve_c_type(v) 207 | end 208 | end 209 | end, 210 | unknown = function(self, rec, parent) 211 | resolve_rec(rec) 212 | end, 213 | }) 214 | 215 | -- 216 | -- Create FFI-wrappers for inline/macro calls 217 | -- 218 | local ffi_wrappers = {} 219 | reg_stage_parser("ffi_wrappers",{ 220 | c_call = function(self, rec, parent) 221 | if not rec.ffi_need_wrapper then 222 | -- normal C call don't need wrapper. 223 | return 224 | end 225 | -- find parent 'object' record. 226 | local object = parent 227 | while object._rec_type ~= 'object' and object._rec_type ~= 'c_module' do 228 | object = object._parent 229 | assert(object, "Can't find parent 'object' record of 'c_call'") 230 | end 231 | local ret_type = rec.ret 232 | local ret = ret_type 233 | -- convert return type into "var_out" if it's not a "void" type. 234 | if ret ~= "void" then 235 | if type(ret) ~= 'string' then 236 | ret_type = ret[1] 237 | end 238 | ret = " return " 239 | else 240 | ret_type = "void" 241 | ret = " " 242 | end 243 | -- build C call statement. 244 | local call = {} 245 | local cfunc_name = rec.cfunc 246 | call[#call+1] = ret 247 | call[#call+1] = cfunc_name 248 | -- process parameters. 249 | local params = {} 250 | local list = rec.params 251 | params[#params+1] = "(" 252 | call[#call+1] = "(" 253 | if rec.is_method_call then 254 | call[#call+1] = 'this' 255 | params[#params+1] = object.c_type .. ' ' 256 | params[#params+1] = 'this' 257 | if #list > 0 then 258 | params[#params+1] = ", " 259 | call[#call+1] = ", " 260 | end 261 | end 262 | for i=1,#list,2 do 263 | local c_type,name = clean_variable_type_name(list[i], list[i+1]) 264 | if i > 1 then 265 | params[#params+1] = ", " 266 | call[#call+1] = ", " 267 | end 268 | -- append parameter name 269 | call[#call+1] = name 270 | -- append parameter type & name to cdef 271 | params[#params+1] = c_type .. ' ' 272 | params[#params+1] = name 273 | end 274 | params[#params+1] = ")" 275 | call[#call+1] = ");\n" 276 | -- convert 'params' to string. 277 | params = tconcat(params) 278 | call = tconcat(call) 279 | -- get prefix 280 | local export_prefix = "" 281 | if rec.ffi_need_wrapper == 'c_wrap' then 282 | export_prefix = "ffi_wrapper_" 283 | end 284 | rec.ffi_export_prefix = export_prefix 285 | -- check for re-definitions or duplicates. 286 | local cdef = ret_type .. " " .. export_prefix .. cfunc_name .. params 287 | local old_cdef = ffi_wrappers[cfunc_name] 288 | if old_cdef == cdef then 289 | return -- duplicate, don't need to create a new wrapper. 290 | elseif old_cdef then 291 | error("Re-definition of FFI wrapper cdef: " .. cdef) 292 | end 293 | ffi_wrappers[cfunc_name] = cdef 294 | -- create wrapper function 295 | if rec.ffi_need_wrapper == 'c_wrap' then 296 | object:add_record(c_source("src")({ 297 | "\n/* FFI wrapper for inline/macro call */\n", 298 | "LUA_NOBJ_API ", cdef, " {\n", 299 | call, 300 | "}\n", 301 | })) 302 | end 303 | object:add_record(ffi_export_function(ret_type)(export_prefix .. rec.cfunc)(params)) 304 | end, 305 | }) 306 | 307 | -- 308 | -- do some pre-processing of records. 309 | -- 310 | local ffi_cdefs = {} 311 | reg_stage_parser("pre-process",{ 312 | c_module = function(self, rec, parent) 313 | rec.functions = {} 314 | rec.constants = {} 315 | rec.fields = {} 316 | rec.name_map = {} 317 | end, 318 | object = function(self, rec, parent) 319 | rec.functions = {} 320 | rec.constants = {} 321 | rec.fields = {} 322 | rec.name_map = {} 323 | rec.extends = {} 324 | end, 325 | callback_state = function(self, rec, parent) 326 | rec.callbacks = {} 327 | end, 328 | extends = function(self, rec, parent) 329 | -- add base-class to parent's base list. 330 | parent.extends[rec.name] = rec 331 | end, 332 | field = function(self, rec, parent) 333 | -- add to name map to reserve the name. 334 | assert(parent.name_map[rec.name] == nil) 335 | --parent.name_map[rec.name] = rec 336 | -- add field to parent's fields list. 337 | parent.fields[rec.name] = rec 338 | end, 339 | const = function(self, rec, parent) 340 | -- add to name map to reserve the name. 341 | assert(parent.name_map[rec.name] == nil) 342 | parent.name_map[rec.name] = rec 343 | -- add constant to parent's constants list. 344 | parent.constants[rec.name] = rec 345 | end, 346 | c_function = function(self, rec, parent) 347 | local c_name = parent.name .. '__' .. rec.name 348 | if rec._is_method then 349 | assert(not parent.is_package or parent.is_meta, 350 | "Package's can't have methods: package=" .. parent.name .. ", method=" .. rec.name) 351 | c_name = c_name .. '__meth' 352 | else 353 | c_name = c_name .. '__func' 354 | end 355 | rec.c_name = c_name 356 | -- add to name map to reserve the name. 357 | assert(parent.name_map[rec.name] == nil, 358 | "duplicate functions " .. rec.name .. " in " .. parent.name) 359 | parent.name_map[rec.name] = rec 360 | -- add function to parent's function list. 361 | parent.functions[rec.name] = rec 362 | -- prepare wrapped new/delete methods 363 | if rec._is_method and parent.callback_state then 364 | if rec.is_destructor then 365 | rec.callback_state = parent.callback_state 366 | end 367 | end 368 | -- map names to in/out variables 369 | rec.var_map = {} 370 | function rec:add_variable(var, name) 371 | name = name or var.name 372 | local old_var = self.var_map[name] 373 | if old_var and old_var ~= var then 374 | -- allow input variable to share name with an output variable. 375 | assert(var.ctype == old_var.ctype, 376 | "duplicate variable " .. name .. " in " .. self.name) 377 | -- If they are the same type. 378 | local v_in,v_out 379 | if var._rec_type == 'var_in' then 380 | assert(old_var._rec_type == 'var_out', 381 | "duplicate input variable " .. name .. " in " .. self.name) 382 | v_in = var 383 | v_out = old_var 384 | elseif var._rec_type == 'var_out' then 385 | assert(old_var._rec_type == 'var_in', 386 | "duplicate output variable " .. name .. " in " .. self.name) 387 | v_in = old_var 388 | v_out = var 389 | end 390 | -- link in & out variables. 391 | v_in.has_out = v_out 392 | v_out.has_in = v_in 393 | -- store input variable in var_map 394 | var = v_in 395 | end 396 | -- add this variable to parent 397 | self.var_map[name] = var 398 | end 399 | end, 400 | callback_func = function(self, rec, parent) 401 | local func_type = rec.c_type_rec 402 | -- add callback to parent's callback list. 403 | parent.callbacks[rec.ref_field] = rec 404 | local src={"static "} 405 | local typedef={"typedef "} 406 | -- convert return type into "cb_out" if it's not a "void" type. 407 | local ret = func_type.ret 408 | if ret ~= "void" then 409 | rec.ret_out = cb_out{ ret, "ret" } 410 | rec:insert_record(rec.ret_out, 1) 411 | end 412 | src[#src+1] = ret .. " " 413 | typedef[#typedef+1] = ret .. " " 414 | -- append c function to call. 415 | rec.c_func_name = parent.base_type .. "_".. rec.ref_field .. "_cb" 416 | src[#src+1] = rec.c_func_name .. "(" 417 | typedef[#typedef+1] = "(*" .. rec.c_type .. ")(" 418 | -- convert params to "cb_in" records. 419 | local params = func_type.params 420 | local vars = {} 421 | local idx=1 422 | for i=1,#params,2 do 423 | local c_type = params[i] 424 | local name = params[i + 1] 425 | if i > 1 then 426 | src[#src+1] = ", " 427 | typedef[#typedef+1] = ", " 428 | end 429 | -- add cb_in to this rec. 430 | local v_in = cb_in{ c_type, name} 431 | rec:insert_record(v_in, idx) 432 | idx = idx + 1 433 | src[#src+1] = c_type .. " ${" .. v_in.name .. "}" 434 | typedef[#typedef+1] = c_type .. " " .. v_in.name 435 | vars[#vars+1] = "${" .. v_in.name .. "}" 436 | end 437 | src[#src+1] = ")" 438 | typedef[#typedef+1] = ");" 439 | -- save callback func decl. 440 | rec.c_func_decl = tconcat(src) 441 | rec.c_func_typedef = tconcat(typedef) 442 | rec.param_vars = tconcat(vars, ', ') 443 | -- map names to in/out variables 444 | rec.var_map = {} 445 | function rec:add_variable(var, name) 446 | name = name or var.name 447 | local old_var = self.var_map[name] 448 | assert(old_var == nil or old_var == var, 449 | "duplicate variable " .. name .. " in " .. self.name) 450 | -- add this variable to parent 451 | self.var_map[name] = var 452 | end 453 | end, 454 | var_in = function(self, rec, parent) 455 | parent:add_variable(rec) 456 | end, 457 | var_out = function(self, rec, parent) 458 | if not rec.is_length_ref then 459 | parent:add_variable(rec) 460 | end 461 | end, 462 | cb_in = function(self, rec, parent) 463 | parent:add_variable(rec) 464 | end, 465 | cb_out = function(self, rec, parent) 466 | if not rec.is_length_ref then 467 | parent:add_variable(rec) 468 | end 469 | end, 470 | c_call = function(self, rec, parent) 471 | local src={} 472 | local ffi_cdef={} 473 | local ffi_pre={} 474 | local ffi_src={} 475 | local ffi_post={} 476 | local ret_type = rec.ret 477 | local ret = ret_type 478 | -- convert return type into "var_out" if it's not a "void" type. 479 | if ret ~= "void" then 480 | local is_this = false 481 | -- check if return value is for the "this" value in a constructor. 482 | if parent.is_constructor then 483 | local this_var = parent.var_map.this 484 | if this_var and ret == this_var.c_type then 485 | ret_type = this_var.c_type 486 | is_this = true 487 | end 488 | end 489 | if is_this then 490 | ret = " ${this} = " 491 | else 492 | local rc 493 | if type(ret) == 'string' then 494 | rc = var_out{ ret, "rc_" .. rec.cfunc, is_unnamed = true } 495 | else 496 | rc = var_out(ret) 497 | end 498 | ret_type = rc.c_type 499 | if rc.is_length_ref then 500 | -- look for related 'var_out'. 501 | local rc_val = parent.var_map[rc.name] 502 | if rc_val then 503 | rc_val.has_length = true 504 | rc_val.length = rc.length 505 | else 506 | -- related 'var_out' not processed yet. 507 | -- add place-holder 508 | parent.var_map[rc.name] = rc 509 | end 510 | ret = " ${" .. rc.length .. "} = " 511 | else 512 | ret = " ${" .. rc.name .. "} = " 513 | -- look for related length reference. 514 | local rc_len = parent.var_map[rc.name] 515 | if rc_len and rc_len.is_length_ref then 516 | -- we have a length. 517 | rc.has_length = true 518 | rc.length = rc_len.length 519 | -- remove length var place-holder 520 | parent.var_map[rc.name] = nil 521 | end 522 | -- register var_out variable. 523 | parent:add_variable(rc) 524 | end 525 | -- add var_out record to parent 526 | parent:add_record(rc) 527 | -- check for dereference. 528 | if rc.wrap == '*' then 529 | ret = ret .. '*' 530 | end 531 | end 532 | else 533 | ret = " " 534 | end 535 | src[#src+1] = ret 536 | ffi_cdef[#ffi_cdef+1] = ret_type .. " " 537 | ffi_src[#ffi_src+1] = ret 538 | -- append c function to call. 539 | local func_start = rec.cfunc .. "(" 540 | src[#src+1] = func_start 541 | ffi_cdef[#ffi_cdef+1] = func_start 542 | if rec.ffi_need_wrapper then 543 | ffi_src[#ffi_src+1] = "Cmod." .. rec.ffi_export_prefix 544 | else 545 | ffi_src[#ffi_src+1] = "C." 546 | end 547 | ffi_src[#ffi_src+1] = func_start 548 | -- convert params to "var_in" records. 549 | local params = {} 550 | local list = rec.params 551 | -- check if this `c_call` is a method call 552 | if rec.is_method_call then 553 | -- then add `this` parameter to call. 554 | local this = parent.var_map.this 555 | assert(this, "Missing `this` variable for method_call: " .. rec.cfunc) 556 | this = var_ref(this) 557 | parent:add_record(this) 558 | params[1] = this 559 | end 560 | for i=1,#list,2 do 561 | local c_type = list[i] 562 | local name = list[i+1] 563 | local param = var_in{ c_type, name} 564 | name = param.name 565 | -- check if this is a new input variable. 566 | if not parent.var_map[name] then 567 | -- add param as a variable. 568 | parent:add_variable(param) 569 | else 570 | -- variable exists, turn this input variable into a reference. 571 | local ref = var_ref(param) 572 | -- invalidate old `var_in` record 573 | param._rec_type = nil 574 | param = ref 575 | end 576 | -- add param rec to parent. 577 | parent:add_record(param) 578 | params[#params + 1] = param 579 | end 580 | -- append all input variables to "c_source" 581 | for i=1,#params do 582 | local var = params[i] 583 | if i > 1 then 584 | src[#src+1] = ", " 585 | ffi_cdef[#ffi_cdef+1] = ", " 586 | ffi_src[#ffi_src+1] = ", " 587 | end 588 | local name = var.name 589 | if var.is_length_ref then 590 | name = "${" .. var.length .. "}" 591 | else 592 | name = "${" .. name .. "}" 593 | end 594 | -- append parameter to c source call 595 | if var.wrap then 596 | src[#src+1] = var.wrap .. "(" 597 | src[#src+1] = name .. ")" 598 | else 599 | src[#src+1] = name 600 | end 601 | if var.wrap == '&' then 602 | -- need a tmp variable to dereference parameter. 603 | local var_name = var.name 604 | if var.is_length_ref then 605 | var_name = var.length 606 | end 607 | local temp_name = "${function_name}_" .. var_name .. "_tmp" 608 | parent:add_record(ffi_source("ffi_temps")( 609 | {' local ', temp_name, ' = ffi.new("',var.c_type,'[1]")\n'})) 610 | if var.has_in or var._rec_type == 'var_in' then 611 | ffi_pre[#ffi_pre+1] = ' ' .. temp_name .. '[0] = ' .. name .. '\n' 612 | end 613 | ffi_post[#ffi_post+1] = '\n ' .. name .. ' = ' .. temp_name .. '[0]' 614 | name = temp_name 615 | end 616 | -- append parameter to ffi source call 617 | ffi_src[#ffi_src+1] = name 618 | -- append parameter type & name to ffi cdef record 619 | ffi_cdef[#ffi_cdef+1] = var.c_type 620 | if var.wrap == '&' then 621 | ffi_cdef[#ffi_cdef+1] = '*' 622 | end 623 | end 624 | src[#src+1] = ");" 625 | ffi_cdef[#ffi_cdef+1] = ");\n" 626 | ffi_src[#ffi_src+1] = ")" 627 | -- replace `c_call` with `c_source` record 628 | local idx = parent:find_record(rec) 629 | idx = idx + 1 630 | parent:insert_record(c_source("src")(src), idx) 631 | -- convert to string. 632 | ffi_cdef = tconcat(ffi_cdef) 633 | -- add pre/post ffi code. 634 | if #ffi_pre > 0 then 635 | tinsert(ffi_src, 1, tconcat(ffi_pre)) 636 | end 637 | if #ffi_post > 0 then 638 | ffi_src[#ffi_src+1] = tconcat(ffi_post) 639 | end 640 | -- check for ffi cdefs re-definitions 641 | local cfunc = rec.cfunc 642 | local cdef = ffi_cdefs[cfunc] 643 | if cdef and cdef ~= ffi_cdef then 644 | local old_name = cfunc 645 | local i = 0 646 | -- search for next "free" alias name. 647 | repeat 648 | i = i + 1 649 | cfunc = old_name .. i 650 | cdef = ffi_cdefs[cfunc] 651 | -- search until "free" alias name, or same definition. 652 | until not cdef or cdef == ffi_cdef 653 | -- update ffi src with new alias name. 654 | ffi_src = tconcat(ffi_src) 655 | ffi_src = ffi_src:gsub(old_name .. '%(', cfunc .. '(') 656 | -- create a cdef "asm" alias. 657 | if not cdef then 658 | ffi_cdef = ffi_cdef:gsub(old_name, cfunc) 659 | ffi_cdef = ffi_cdef:gsub("%);\n$", [[) asm("]] .. old_name .. [[");]]) 660 | end 661 | end 662 | ffi_cdefs[cfunc] = ffi_cdef 663 | -- insert FFI source record. 664 | if not cdef then 665 | -- function not defined yet. 666 | parent:insert_record(ffi_source("ffi_cdef")(ffi_cdef), idx) 667 | end 668 | idx = idx + 1 669 | parent:insert_record(ffi_source("ffi_src")(ffi_src), idx) 670 | end, 671 | ffi_export = function(self, rec, parent) 672 | local ffi_src={} 673 | -- load exported symbol 674 | ffi_src[#ffi_src+1] = 'local ' 675 | ffi_src[#ffi_src+1] = rec.name 676 | ffi_src[#ffi_src+1] = ' = ffi.new("' 677 | ffi_src[#ffi_src+1] = rec.c_type 678 | ffi_src[#ffi_src+1] = ' *", _priv["' 679 | ffi_src[#ffi_src+1] = rec.name 680 | ffi_src[#ffi_src+1] = '"])\n' 681 | -- insert FFI source record. 682 | local idx = parent:find_record(rec) 683 | parent:insert_record(ffi_source("ffi_import")(ffi_src), idx) 684 | end, 685 | }) 686 | 687 | -- 688 | -- sort var_in/var_out records. 689 | -- 690 | local function sort_vars(var1, var2) 691 | return (var1.idx < var2.idx) 692 | end 693 | reg_stage_parser("variables",{ 694 | c_function = function(self, rec, parent) 695 | local inputs = {} 696 | local in_count = 0 697 | local outputs = {} 698 | local out_count = 0 699 | local misc = {} 700 | local max_idx = #rec 701 | -- seperate sub-records 702 | for i=1,max_idx do 703 | local var = rec[i] 704 | local var_type = var._rec_type 705 | local sort = true 706 | local list 707 | if var_type == 'var_in' then 708 | list = inputs 709 | in_count = in_count + 1 710 | elseif var_type == 'var_out' then 711 | list = outputs 712 | out_count = out_count + 1 713 | else 714 | list = misc 715 | sort = false 716 | end 717 | if sort then 718 | local idx = var.idx 719 | if idx then 720 | -- force index of this variable. 721 | local old_var = list[idx] 722 | -- variable has a fixed 723 | list[idx] = var 724 | -- move old variable to next open slot 725 | var = old_var 726 | end 727 | -- place variable in next nil slot. 728 | if var then 729 | for i=1,max_idx do 730 | if not list[i] then 731 | -- done, found empty slot 732 | list[i] = var 733 | var = nil 734 | break 735 | end 736 | end 737 | end 738 | assert(var == nil, "Failed to find empty slot for variable.") 739 | else 740 | list[#list + 1] = var 741 | end 742 | end 743 | -- make sure there are no gaps between input/output variables. 744 | assert(#inputs == in_count, 745 | "Gaps between input variables, check your usage of `idx` for function: " .. rec.name) 748 | 749 | -- put sorted sub-records back into the `c_function` record. 750 | local idx=0 751 | for i=1,in_count do 752 | idx = idx + 1 753 | rec[idx] = inputs[i] 754 | end 755 | for i=1,out_count do 756 | idx = idx + 1 757 | rec[idx] = outputs[i] 758 | end 759 | for i=1,#misc do 760 | idx = idx + 1 761 | rec[idx] = misc[i] 762 | end 763 | -- generate list of input parameter names for FFI functions. 764 | local ffi_params = {} 765 | for i=1,in_count do 766 | local name = inputs[i].name 767 | if name ~= 'this' then 768 | ffi_params[i] = '${' .. inputs[i].name .. '}' 769 | else 770 | ffi_params[i] = 'self' 771 | end 772 | end 773 | rec.ffi_params = tconcat(ffi_params, ', ') 774 | end, 775 | }) 776 | 777 | -- register place-holder 778 | reg_stage_parser("lang_type_process") 779 | 780 | -- 781 | -- mark functions which have an error_code var_out. 782 | -- 783 | reg_stage_parser("error_code",{ 784 | var_out = function(self, rec, parent) 785 | local var_type = rec.c_type_rec 786 | if var_type._is_error_code then 787 | assert(parent._has_error_code == nil, 788 | "A function/method can only have one var_out with type error_code.") 789 | -- mark the function as having an error code. 790 | parent._has_error_code = rec 791 | elseif var_type.error_on_null then 792 | -- if this variable is null then push a nil and error message. 793 | rec.is_error_on_null = true 794 | end 795 | end, 796 | }) 797 | 798 | -- register place-holder 799 | reg_stage_parser("pre_gen") 800 | 801 | -------------------------------------------------------------------------------- /project_template/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Lua bindings for lib__mod_name__ 3 | # 4 | cmake_minimum_required(VERSION 3.18) 5 | 6 | project(lua-__mod_name__ C) 7 | 8 | set(BUILD_SHARED_LIBS TRUE) 9 | 10 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) 11 | 12 | set(INSTALL_LMOD ${CMAKE_INSTALL_PREFIX}/share/lua/ CACHE PATH 13 | "Directory to install Lua source modules (configure lua via LUA_PATH)") 14 | set(INSTALL_CMOD ${CMAKE_INSTALL_PREFIX}/lib/lua/ CACHE PATH 15 | "Directory to install Lua binary modules (configure lua via LUA_CPATH)") 16 | 17 | set(COMMON_CFLAGS "${CFLAGS}") 18 | set(COMMON_LDFLAGS) 19 | set(COMMON_LIBS) 20 | 21 | ## Lua 5.x 22 | include(FindLua) 23 | if(NOT ${LUA_FOUND}) 24 | message(FATAL_ERROR "The FindLua module could not find lua :-(") 25 | endif() 26 | set(COMMON_LIBS "${COMMON_LIBS};${LUA_LIBRARIES}") 27 | 28 | ## Lib__Mod_name__ 29 | include(FindPkgConfig) 30 | pkg_search_module(__MOD_NAME__ REQUIRED lib__mod_name__) 31 | set(COMMON_CFLAGS "${COMMON_CFLAGS} ${__MOD_NAME___CFLAGS}") 32 | set(COMMON_LDFLAGS "${COMMON_LDFLAGS} ${__MOD_NAME___LDFLAGS}") 33 | set(COMMON_LIBS "${COMMON_LIBS};${__MOD_NAME___LIBRARIES}") 34 | 35 | ## LuaNativeObjects 36 | include(LuaNativeObjects) 37 | 38 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} 39 | ${CMAKE_CURRENT_BINARY_DIR} 40 | ${LUA_INCLUDE_DIR}) 41 | 42 | ## Lua__Mod_name__ 43 | set(LUA___MOD_NAME___SRC 44 | __mod_name__.nobj.lua 45 | ) 46 | 47 | if(${USE_PRE_GENERATED_BINDINGS}) 48 | set(LUA___MOD_NAME___SRC src/pre_generated-__mod_name__.nobj.c) 49 | else() 50 | # Generate Lua bindings. 51 | GenLuaNativeObjects(LUA___MOD_NAME___SRC) 52 | endif() 53 | 54 | add_library(lua-__mod_name__ MODULE ${LUA___MOD_NAME___SRC}) 55 | target_link_libraries(lua-__mod_name__ ${COMMON_LIBS}) 56 | set_target_properties(lua-__mod_name__ PROPERTIES PREFIX "") 57 | set_target_properties(lua-__mod_name__ PROPERTIES COMPILE_FLAGS "${COMMON_CFLAGS}") 58 | set_target_properties(lua-__mod_name__ PROPERTIES OUTPUT_NAME __mod_name__) 59 | 60 | install(TARGETS lua-__mod_name__ 61 | DESTINATION "${INSTALL_CMOD}") 62 | 63 | -------------------------------------------------------------------------------- /project_template/PROJECT_README.md: -------------------------------------------------------------------------------- 1 | lua-__mod_name__ 2 | ======= 3 | 4 | Lib__Mod_name__ bindings for Lua. 5 | 6 | Installing 7 | ---------- 8 | 9 | ### Install lua-__mod_name__: 10 | 11 | curl -O "__project_github_url__/raw/master/lua-__mod_name__-scm-0.rockspec" 12 | 13 | luarocks install lua-__mod_name__-scm-0.rockspec 14 | 15 | -------------------------------------------------------------------------------- /project_template/README.regenerate.md: -------------------------------------------------------------------------------- 1 | To re-generating the bindings 2 | ----------------------------- 3 | 4 | You will need to install LuaNativeObjects and set the CMake variable `USE_PRE_GENERATED_BINDINGS` to FALSE. 5 | By default CMake will use the pre-generated bindings that are include in the project. 6 | 7 | Build Dependencies 8 | ------------------ 9 | 10 | Optional dependency for re-generating Lua bindings from `*.nobj.lua` files: 11 | 12 | * [LuaNativeObjects](https://github.com/Neopallium/LuaNativeObjects), this is the bindings generator used to convert the `*.nobj.lua` files into a native Lua module. 13 | 14 | -------------------------------------------------------------------------------- /project_template/RUN_THIS_FIRST.lua: -------------------------------------------------------------------------------- 1 | print[[ 2 | This script will help setup a new bindings project for you. 3 | ]] 4 | 5 | local stat, lfs = pcall(require, "lfs") 6 | if not stat then 7 | print("Failed to load LuaFileSystem: " .. tostring(lfs)) 8 | os.exit() 9 | end 10 | 11 | -- get directory separator 12 | local dir_sep = package.config:sub(1,1) 13 | 14 | -- get current directory 15 | local template_dir = lfs.currentdir() .. dir_sep 16 | local attr = lfs.attributes(template_dir .. '__mod_name__.nobj.lua') 17 | if not attr then 18 | print("Can't find project template files, please run script in the template's folder.") 19 | os.exit() 20 | end 21 | 22 | print("Enter the module name (i.e. gd, png, event):") 23 | local mod_name = string.lower(io.read"*l") 24 | local MOD_NAME = string.upper(mod_name) 25 | local Mod_name = MOD_NAME:sub(1,1) .. mod_name:sub(2) 26 | 27 | print("Enter the Author's name (i.e. your name):") 28 | local authors_name = string.lower(io.read"*l") 29 | 30 | print("Enter the folder where you want to create the new project:") 31 | local project_path = string.lower(io.read"*l") or "." 32 | if #project_path == 0 then project_path = "." end 33 | -- expand '~' prefix to HOME 34 | if project_path:sub(1,1) == '~' then 35 | local home = os.getenv("HOME") 36 | project_path = home .. project_path:sub(2) 37 | end 38 | -- check if path exists 39 | local attr = lfs.attributes(project_path) 40 | if attr then 41 | -- path exists, install project in project_path/lua-mod_name 42 | project_path = project_path .. dir_sep .. 'lua-' .. mod_name 43 | end 44 | 45 | print("\n\nPlease verify this info:") 46 | print(string.format('Module name: "%s"', mod_name)) 47 | print(string.format('Author\'s name: "%s"', authors_name)) 48 | print(string.format('Project path: "%s"', project_path)) 49 | print("Press Enter to setup project (or Ctrl-C to cancel):") 50 | io.read"*l" 51 | 52 | -- 53 | -- helper functions 54 | -- 55 | local function copyfile(src, dst, filter) 56 | local content 57 | -- 58 | -- read source file 59 | -- 60 | local file = assert(io.open(src, "r")) 61 | content = file:read("*a") 62 | file:close() 63 | -- 64 | -- Apply content filter 65 | -- 66 | if filter then 67 | content = filter(content) 68 | end 69 | -- 70 | -- write content to destination file 71 | -- 72 | local file = assert(io.open(dst, "w")) 73 | file:write(content) 74 | file:close() 75 | end 76 | 77 | local template_replacements = { 78 | ["__mod_name__"] = mod_name, 79 | ["__MOD_NAME__"] = MOD_NAME, 80 | ["__Mod_name__"] = Mod_name, 81 | ["__authors_name__"] = authors_name, 82 | } 83 | local function template_filter(content) 84 | return content:gsub("(__[%w]*_[%w]*__)", template_replacements) 85 | end 86 | 87 | -- 88 | -- Install project 89 | -- 90 | local sub_dirs = { 91 | "cmake", 92 | "src", 93 | } 94 | local copy_files = { 95 | "cmake/LuaNativeObjects.cmake", 96 | "src/object.nobj.lua", 97 | "README.regenerate.md", 98 | } 99 | local template_files = { 100 | "CMakeLists.txt", 101 | "__mod_name__.nobj.lua", 102 | "lua-__mod_name__-scm-0.rockspec", 103 | -- rename this file 104 | ["PROJECT_README.md"] = "README.md", 105 | } 106 | 107 | -- create directories 108 | assert(lfs.mkdir(project_path)) 109 | project_path = project_path .. dir_sep 110 | for i=1,#sub_dirs do 111 | assert(lfs.mkdir(project_path .. sub_dirs[i])) 112 | end 113 | 114 | -- copy files 115 | for i=1,#copy_files do 116 | local file = copy_files[i] 117 | copyfile(template_dir .. file, project_path .. file) 118 | end 119 | 120 | -- copy template files 121 | for src,dst in pairs(template_files) do 122 | if type(src) == 'number' then 123 | -- src & dst are the same name 124 | src = template_dir .. dst 125 | else 126 | -- src name is different dst name 127 | src = template_dir .. src 128 | end 129 | -- apply template replacements to dst name 130 | dst = project_path .. template_filter(dst) 131 | -- apply template replacements to src files content and copy to destination. 132 | copyfile(src, dst, template_filter) 133 | end 134 | 135 | -------------------------------------------------------------------------------- /project_template/__mod_name__.nobj.lua: -------------------------------------------------------------------------------- 1 | 2 | c_module "__mod_name__" { 3 | 4 | -- enable FFI bindings support. 5 | luajit_ffi = true, 6 | 7 | -- load __MOD_NAME__ shared library. 8 | ffi_load"__mod_name__", 9 | 10 | include "__mod_name__.h", 11 | 12 | subfiles { 13 | "src/object.nobj.lua", 14 | }, 15 | } 16 | 17 | -------------------------------------------------------------------------------- /project_template/cmake/LuaNativeObjects.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Lua Native Objects 3 | # 4 | 5 | find_program(LUA_NATIVE_OBJECTS_EXECUTABLE native_objects.lua 6 | PATHS ${CMAKE_SOURCE_DIR}/../LuaNativeObjects 7 | DOC "LuaNativeObjects executable path") 8 | set(USE_PRE_GENERATED_BINDINGS TRUE CACHE BOOL 9 | "Set this to FALSE to re-generate bindings using LuaNativeObjects") 10 | set(GENERATE_LUADOCS TRUE CACHE BOOL 11 | "Set this to FALSE to avoid generation of docs using LuaDoc") 12 | 13 | macro(GenLuaNativeObjects _src_files_var) 14 | set(_new_src_files) 15 | foreach(_src_file ${${_src_files_var}}) 16 | if(_src_file MATCHES ".nobj.lua") 17 | string(REGEX REPLACE ".nobj.lua" ".nobj.c" _src_file_out ${_src_file}) 18 | string(REGEX REPLACE ".nobj.lua" ".nobj.ffi.lua" _ffi_file_out ${_src_file}) 19 | add_custom_command(OUTPUT ${_src_file_out} ${_ffi_file_out} 20 | COMMAND ${LUA_NATIVE_OBJECTS_EXECUTABLE} -outpath ${CMAKE_CURRENT_BINARY_DIR} -gen lua ${_src_file} 21 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 22 | DEPENDS ${_src_file} 23 | ) 24 | set_source_files_properties(${_src_file_out} PROPERTIES GENERATED TRUE) 25 | set_source_files_properties(${_ffi_file_out} PROPERTIES GENERATED TRUE) 26 | if (${GENERATE_LUADOCS}) 27 | string(REGEX REPLACE ".nobj.lua" "" _doc_base ${_src_file}) 28 | string(REGEX REPLACE ".nobj.lua" ".luadoc" _doc_file_out ${_src_file}) 29 | add_custom_target(${_doc_file_out} ALL 30 | COMMAND ${LUA_NATIVE_OBJECTS_EXECUTABLE} -outpath docs -gen luadoc ${_src_file} 31 | COMMAND luadoc -nofiles -d docs docs 32 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 33 | DEPENDS ${_src_file} 34 | ) 35 | endif() 36 | set_source_files_properties(${_doc_file_out} PROPERTIES GENERATED TRUE) 37 | set(_new_src_files ${_new_src_files} ${_src_file_out}) 38 | else(_src_file MATCHES ".nobj.lua") 39 | set(_new_src_files ${_new_src_files} ${_src_file}) 40 | endif(_src_file MATCHES ".nobj.lua") 41 | endforeach(_src_file) 42 | set(${_src_files_var} ${_new_src_files}) 43 | endmacro(GenLuaNativeObjects _src_files_var) 44 | 45 | -------------------------------------------------------------------------------- /project_template/lua-__mod_name__-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | package = 'lua-__mod_name__' 4 | version = 'scm-0' 5 | source = { 6 | url = '__project_git_url__' 7 | } 8 | description = { 9 | summary = "LuaNativeObjects project template.", 10 | detailed = '', 11 | homepage = '__project_homepage__', 12 | license = 'MIT', 13 | maintainer = "__authors_name__", 14 | } 15 | dependencies = { 16 | 'lua >= 5.1', 17 | } 18 | external_dependencies = { 19 | __MOD_NAME__ = { 20 | header = "__mod_name__.h", 21 | library = "__mod_name__", 22 | } 23 | } 24 | build = { 25 | type = "builtin", 26 | modules = { 27 | __mod_name__ = { 28 | sources = { "src/pre_generated-__mod_name__.nobj.c" }, 29 | libraries = { "__mod_name__" }, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /project_template/src/object.nobj.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This is an example object from GD example binding 3 | -- 4 | 5 | object "gdImage" { 6 | -- Use `ffi_cdef` records to pass extra C type info to FFI. 7 | ffi_cdef[[ 8 | typedef struct gdImageStruct gdImage; 9 | ]], 10 | -- The first constructor can be called as: gd.gdImage(x,y) or gd.gdImage.new(x,y) 11 | -- The default name for a constructor is 'new' 12 | constructor { 13 | c_call "gdImage *" "gdImageCreate" { "int", "sx", "int", "sy" } 14 | }, 15 | -- Other constructors can be called by there name: gd.gdImage.newTrueColor(x,y) 16 | constructor "newTrueColor" { 17 | c_call "gdImage *" "gdImageCreateTrueColor" { "int", "sx", "int", "sy" } 18 | }, 19 | -- A named destructor allows freeing of the object before it gets GC'ed. 20 | destructor "close" { 21 | c_method_call "void" "gdImageDestroy" {} 22 | }, 23 | 24 | method "color_allocate" { 25 | -- bindings for simple methods/functions can be generated with `c_method_call` or `c_call` 26 | -- records, which will generate both Lua API & FFI based bindings for the function. 27 | c_method_call "int" "gdImageColorAllocate" 28 | { "int", "r", "int", "g", "int", "b" } 29 | }, 30 | 31 | method "line" { 32 | c_method_call "void" "gdImageLine" 33 | { "int", "x1", "int", "y1", "int", "x2", "int", "y2", "int", "colour" } 34 | }, 35 | 36 | -- The next method need extra FFI types & function information. 37 | ffi_cdef[[ 38 | /* dummy typedef for "FILE" */ 39 | typedef struct FILE FILE; 40 | 41 | FILE *fopen(const char *path, const char *mode); 42 | int fclose(FILE *fp); 43 | 44 | void gdImagePng(gdImage *im, FILE *out); 45 | ]], 46 | -- This method is more complex and can't be generated with a simple `c_method_call` record. 47 | method "toPNG" { 48 | -- Use `var_in`/`var_out` records to define parameters & return values. 49 | var_in { "const char *", "name" }, 50 | -- Use `c_source` records to provide the C code for this method. 51 | c_source [[ 52 | FILE *pngout = fopen( ${name}, "wb"); 53 | gdImagePng(${this}, pngout); 54 | fclose(pngout); 55 | ]], 56 | -- if you want this method to have FFI-based bindings you will need to use a `ffi_source` record 57 | ffi_source [[ 58 | local pngout = ffi.C.fopen(${name}, "wb") 59 | C.gdImagePng(${this}, pngout) 60 | ffi.C.fclose(pngout) 61 | ]] 62 | }, 63 | } 64 | -------------------------------------------------------------------------------- /rockspecs/luanativeobjects-0.5-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "LuaNativeObjects" 2 | version = "0.5-0" 3 | source = { 4 | url = "git://github.com/Neopallium/LuaNativeObjects.git", 5 | branch = "v0.5", 6 | } 7 | description = { 8 | summary = "A Lua bindings generator.", 9 | detailed = [[ 10 | This is a bindings generator for Lua & LuaJIT2. It can be used to generator both standard Lua C API & LuaJIT2 FFI based bindings for C libraries. Both standard & FFI based bindings are packaged in a single shared library (.so or .dll) file. When the module is loaded in LuaJIT2 (please use git HEAD version of LuaJIT2 for now) it will try to load the FFI-based bindings in-place of the standard Lua API bindings. 11 | 12 | This bindings generator is design to create Object based bindings, instead of simple procedural bindings. So if you have a C structure (your object) and a set of C functions (your object's methods) that work on that structure, then you can turn them into a nice Lua object. 13 | ]], 14 | homepage = "https://github.com/Neopallium/LuaNativeObjects", 15 | license = "MIT/X11", 16 | } 17 | dependencies = { 18 | "lua >= 5.1, < 5.5", 19 | } 20 | build = { 21 | type = "builtin", 22 | modules = { 23 | ['native_objects.gen_dump'] = "native_objects/gen_dump.lua", 24 | ['native_objects.gen_lua_ffi'] = "native_objects/gen_lua_ffi.lua", 25 | ['native_objects.gen_lua'] = "native_objects/gen_lua.lua", 26 | ['native_objects.gen_simple'] = "native_objects/gen_simple.lua", 27 | ['native_objects.gen_swig'] = "native_objects/gen_swig.lua", 28 | ['native_objects.interfaces'] = "native_objects/interfaces.lua", 29 | ['native_objects.lang_lua'] = "native_objects/lang_lua.lua", 30 | ['native_objects.lang_swig'] = "native_objects/lang_swig.lua", 31 | ['native_objects.record'] = "native_objects/record.lua", 32 | ['native_objects.stages'] = "native_objects/stages.lua", 33 | ['native_objects'] = "native_objects.lua", 34 | }, 35 | install = { 36 | bin = { "bin/native_objects" } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rockspecs/luanativeobjects-0.5-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "LuaNativeObjects" 2 | version = "0.5-1" 3 | source = { 4 | url = "git://github.com/Neopallium/LuaNativeObjects.git", 5 | branch = "v0.5", 6 | } 7 | description = { 8 | summary = "A Lua bindings generator.", 9 | detailed = [[ 10 | This is a bindings generator for Lua & LuaJIT2. It can be used to generator both standard Lua C API & LuaJIT2 FFI based bindings for C libraries. Both standard & FFI based bindings are packaged in a single shared library (.so or .dll) file. When the module is loaded in LuaJIT2 (please use git HEAD version of LuaJIT2 for now) it will try to load the FFI-based bindings in-place of the standard Lua API bindings. 11 | 12 | This bindings generator is design to create Object based bindings, instead of simple procedural bindings. So if you have a C structure (your object) and a set of C functions (your object's methods) that work on that structure, then you can turn them into a nice Lua object. 13 | ]], 14 | homepage = "https://github.com/Neopallium/LuaNativeObjects", 15 | license = "MIT/X11", 16 | } 17 | dependencies = { 18 | "lua >= 5.1, < 5.5", 19 | } 20 | build = { 21 | type = "builtin", 22 | modules = { 23 | ['native_objects.gen_dump'] = "native_objects/gen_dump.lua", 24 | ['native_objects.gen_lua_ffi'] = "native_objects/gen_lua_ffi.lua", 25 | ['native_objects.gen_lua'] = "native_objects/gen_lua.lua", 26 | ['native_objects.gen_simple'] = "native_objects/gen_simple.lua", 27 | ['native_objects.gen_swig'] = "native_objects/gen_swig.lua", 28 | ['native_objects.interfaces'] = "native_objects/interfaces.lua", 29 | ['native_objects.lang_lua'] = "native_objects/lang_lua.lua", 30 | ['native_objects.lang_swig'] = "native_objects/lang_swig.lua", 31 | ['native_objects.record'] = "native_objects/record.lua", 32 | ['native_objects.stages'] = "native_objects/stages.lua", 33 | ['native_objects'] = "native_objects.lua", 34 | }, 35 | install = { 36 | bin = { "bin/native_objects" } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rockspecs/luanativeobjects-luadoc-0.5-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "LuaNativeObjects-luadoc" 2 | version = "0.5-0" 3 | source = { 4 | url = "git://github.com/Neopallium/LuaNativeObjects.git", 5 | branch = "v0.5", 6 | } 7 | description = { 8 | summary = "A Lua bindings generator. Backedn to generate luadocs.", 9 | detailed = [[ 10 | A luadocs generator backend for LuaNativeObjects. 11 | ]], 12 | homepage = "https://github.com/Neopallium/LuaNativeObjects", 13 | license = "MIT/X11", 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.5", 17 | "luanativeobjects >= 0.5", 18 | "luafilesystem", 19 | } 20 | build = { 21 | type = "builtin", 22 | modules = { 23 | ['native_objects.gen_luadoc'] = "native_objects/gen_luadoc.lua", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /rockspecs/luanativeobjects-luadoc-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "LuaNativeObjects-luadoc" 2 | version = "scm-0" 3 | source = { 4 | url = "git://github.com/Neopallium/LuaNativeObjects.git", 5 | } 6 | description = { 7 | summary = "A Lua bindings generator. Backedn to generate luadocs.", 8 | detailed = [[ 9 | A luadocs generator backend for LuaNativeObjects. 10 | ]], 11 | homepage = "https://github.com/Neopallium/LuaNativeObjects", 12 | license = "MIT/X11", 13 | } 14 | dependencies = { 15 | "lua >= 5.1, < 5.5", 16 | "luanativeobjects", 17 | "luafilesystem", 18 | } 19 | build = { 20 | type = "builtin", 21 | modules = { 22 | ['native_objects.gen_luadoc'] = "native_objects/gen_luadoc.lua", 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /rockspecs/luanativeobjects-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "LuaNativeObjects" 2 | version = "scm-0" 3 | source = { 4 | url = "git://github.com/Neopallium/LuaNativeObjects.git", 5 | } 6 | description = { 7 | summary = "A Lua bindings generator.", 8 | detailed = [[ 9 | This is a bindings generator for Lua & LuaJIT2. It can be used to generator both standard Lua C API & LuaJIT2 FFI based bindings for C libraries. Both standard & FFI based bindings are packaged in a single shared library (.so or .dll) file. When the module is loaded in LuaJIT2 (please use git HEAD version of LuaJIT2 for now) it will try to load the FFI-based bindings in-place of the standard Lua API bindings. 10 | 11 | This bindings generator is design to create Object based bindings, instead of simple procedural bindings. So if you have a C structure (your object) and a set of C functions (your object's methods) that work on that structure, then you can turn them into a nice Lua object. 12 | ]], 13 | homepage = "https://github.com/Neopallium/LuaNativeObjects", 14 | license = "MIT/X11", 15 | } 16 | dependencies = { 17 | "lua >= 5.1, < 5.5", 18 | } 19 | build = { 20 | type = "builtin", 21 | modules = { 22 | ['native_objects.gen_dump'] = "native_objects/gen_dump.lua", 23 | ['native_objects.gen_lua_ffi'] = "native_objects/gen_lua_ffi.lua", 24 | ['native_objects.gen_lua'] = "native_objects/gen_lua.lua", 25 | ['native_objects.gen_simple'] = "native_objects/gen_simple.lua", 26 | ['native_objects.gen_swig'] = "native_objects/gen_swig.lua", 27 | ['native_objects.interfaces'] = "native_objects/interfaces.lua", 28 | ['native_objects.lang_lua'] = "native_objects/lang_lua.lua", 29 | ['native_objects.lang_swig'] = "native_objects/lang_swig.lua", 30 | ['native_objects.record'] = "native_objects/record.lua", 31 | ['native_objects.stages'] = "native_objects/stages.lua", 32 | ['native_objects'] = "native_objects.lua", 33 | }, 34 | install = { 35 | bin = { "bin/native_objects" } 36 | } 37 | } 38 | --------------------------------------------------------------------------------