nimLua
glue code generator to bind Nim and Lua together using Nim's powerful macro
Features:
- bind free proc
- bind proc as Lua method
- bind const
- bind enum
- bind object
- generic proc binding
- closure binding
- properties getter/setter
- automatic resolve overloaded proc
- easy namespace creation
- easy debugging
- consistent simple API
- can rename exported symbol
- support automatic type conversion
- can change binding dynamically at runtime too
- generate clean and optimized glue code that you can inspect at compile time
- it's free
planned features:
- complex data types conversion, at least standard container
- access Lua code/data from Nim
Current version API: no need to remember complicated API, the API is simple but powerful
- newNimLua
- bindEnum
- bindConst
- bindFunction/bindProc
- bindObject
##DATA TYPE CONVERSION
Nim | Lua |
---|---|
char,int,uint,int8-64,uint8-64 | integer/number |
float, float32, float64 | number |
array[0..n, T], [n, T] | array[1..n] |
enum | integer/number |
string, cstring | string |
ref/object | userdata |
bool | boolean |
seq[T] | array[1..n] |
set[T] | table with unique element |
pointer | light user data |
ptr T | light user data |
range/subrange | integer |
openArray[T] | table -> seq[T] |
tuple | assoc-table or array |
varargs[T] | not supported |
##HOW TO USE
###1. bindEnum
import nimLua, os
type
FRUIT = enum
APPLE, BANANA, PEACH, PLUM
SUBATOM = enum
ELECTRON, PROTON, NEUTRON
GENE = enum
ADENINE, CYTOSINE, GUANINE, THYMINE
proc test(L: PState, fileName: string) =
if L.doFile("test" & DirSep & fileName) != 0.cint:
echo L.toString(-1)
L.pop(1)
else:
echo fileName & " .. OK"
proc main() =
var L = newNimLua()
L.bindEnum(FRUIT, SUBATOM, GENE)
L.test("test.lua")
L.close()
main()
and you can access them at Lua side like this:
assert(FRUIT.APPLE == 0)
assert(FRUIT.BANANA == 1)
assert(FRUIT.PEACH == 2)
assert(FRUIT.PLUM == 3)
assert(GENE.ADENINE == 0)
assert(GENE.CYTOSINE == 1)
assert(GENE.GUANINE == 2)
assert(GENE.THYMINE == 3)
assert(SUBATOM.ELECTRON == 0)
assert(SUBATOM.PROTON == 1)
assert(SUBATOM.NEUTRON == 2)
another style:
L.bindEnum:
FRUIT
SUBATOM
GENE
if you want to rename the namespace, you can do this:
L.bindEnum:
GENE -> "DNA"
SUBATOM -> GLOBAL
or
L.bindEnum(GENE -> "DNA", SUBATOM -> GLOBAL)
a note on GLOBAL and "GLOBAL":
- GLOBAL without quote will not create namespace on Lua side but will bind the symbol in Lua globalspace
- "GLOBAL" with quote, will create "GLOBAL" namespace on Lua side
now Lua side will become:
assert(DNA.ADENINE == 0)
assert(DNA.CYTOSINE == 1)
assert(DNA.GUANINE == 2)
assert(DNA.THYMINE == 3)
assert(ELECTRON == 0)
assert(PROTON == 1)
assert(NEUTRON == 2)
###2. bindConst
import nimLua
const
MANGOES = 10.0
PAPAYA = 11.0'f64
LEMON = 12.0'f32
GREET = "hello world"
connected = true
proc main() =
var L = newNimLua()
L.bindConst(MANGOES, PAPAYA, LEMON)
L.bindConst:
GREET
connected
L.close()
main()
by default, bindConst will not generate namespace, so how do you create namespace for const? easy:
L.bindConst("fruites", MANGOES, PAPAYA, LEMON)
L.bindConst("status"):
GREET
connected
first argument(actually second) to bindConst will become the namespace. Without namespace, symbol will be put into global namespace
if you use GLOBAL without quote as namespace, it will have no effect
operator ->
have same meaning with bindEnum, to rename exported symbol on Lua side
###3. bindFunction/bindProc
bindFunction is an alias to bindProc, they behave identically
import nimLua
proc abc(a, b: int): int =
result = a + b
var L = newNimLua()
L.bindFunction(abc)
L.bindFunction:
abc -> "cba"
L.bindFunction("alphabet", abc)
bindFunction more or less behave like bindConst, without namespace, it will bind symbol to global namespace.
overloaded procs will be automatically resolved by their params count and types
operator ->
have same meaning with bindEnum, to rename exported symbol on Lua side
###4. bindObject
import nimLua
type
Foo = ref object
name: string
proc newFoo(name: string): Foo =
new(result)
result.name = name
proc addv(f: Foo, a, b: int): int =
result = 2 * (a + b)
proc addv(f: Foo, a, b: string): string =
result = "hello: my name is $1, here is my message: $2, $3" % [f.name, a, b]
proc addk(f: Foo, a, b: int): string =
result = f.name & ": " & $a & " + " & $b & " = " & $(a+b)
proc main() =
var L = newNimLua()
L.bindObject(Foo):
newFoo -> constructor
addv
addk -> "add"
L.close()
main()
this time, Foo will become object name and also namespace name in Lua
"newFoo ->
constructor" have special meaning, it will create constructor on Lua side with special name: new
(this is an artefact)
but any other constructor like procs will be treated as constructor too:
L.bindObject(Foo):
newFoo #constructor #1 'newFoo'
newFoo -> constructor #constructor #2 'new'
newFoo -> "whatever" #constructor #3 'whatever'
makeFoo -> "constructor" #constructor #4 'constructor'
operator ->
on non constructor will behave the same as other binder.
overloaded proc will be automatically resolved by their params count and types, including overloaded constructor
destructor will be generated automatically for ref object, none for regular object. GC safety works as usual on both side of Nim and Lua, no need to worry, except when you manually allocated memory
local foo = Foo.new("fred")
local m = foo:add(3, 4)
-- "fred: 3 + 4 = 7"
print(m)
assert(foo:addv(4,5) == 2 * (4+5))
-- "hello: my name is fred, here is my message: abc, nop"
print(foo:addv("abc", "nop"))
operator ->
when applied to object, will rename exported symbol on Lua side:
L.bindObject(Foo -> "cat"):
newFoo -> constructor
on Lua side:
local c = cat.new("fred") --not 'Foo' anymore
both bindObject and bindFunction and bindConst can add member to existing namespace
if you want to turn off this functionality, call nimLuaOptions(nloAddMember, false)
L.bindObject(Foo): #namespace creation
newFoo -> constructor
L.bindObject(Foo): #add new member
addv
addk -> "add"
L.bindFunction("gem"): #namespace "gem" creation
mining
L.bindFunction("gem"): #add 'polish' member
polish
##PASSING BY REFERENCE
Lua basic data types cannot be passed by reference, but Nim does
if you have something like this in Nim:
proc abc(a, b: var int) =
a = a + 1
b = b + 5
then on Lua side:
a = 10
b = 20
a, b = abc(a, b)
assert(a == 11)
assert(b == 25)
basically, outval will become retval, FIFO ordered
##GENERIC PROC BINDING
proc mew[T, K](a: T, b: K): T =
discard
L.bindFunction:
mew[int, string]
mew[int, string] -> "mewt"
##CLOSURE BINDING
proc main() =
...
var test = 1237
proc cl() =
echo test
L.bindFunction:
[cl]
[cl] -> "clever"
##GETTER/SETTER
type
Ship = object
speed*: int
power: int
L.bindObject(Ship):
speed(set)
speeg(get) -> "getter"
speed(get, set) -> "cepat"
##HOW TO DEBUG
you can call nimLuaOptions(nloDebug, true/false)
nimLuaOptions(nloDebug, true) #turn on debug
L.bindEnum:
GENE
SUBATOM
nimLuaOptions(nloDebug, false) #turn off debug mode
L.bindFunction:
machine
engine
##HOW TO ACCESS LUA CODE FROM NIM?
still under development, contributions are welcome