├── tests ├── luas │ ├── closure.lua │ ├── generic.lua │ ├── openarray.lua │ ├── range_param_ret.lua │ ├── enum_param_ret.lua │ ├── free_function.lua │ ├── single_scoped_enum.lua │ ├── ov_func.lua │ ├── scoped_function.lua │ ├── getter_setter.lua │ ├── ptr_pointer.lua │ ├── tuple.lua │ ├── scoped_and_global_enum.lua │ ├── namespace.lua │ ├── regular_object.lua │ ├── array_param_ret.lua │ ├── set_param_ret.lua │ ├── inheritance.lua │ ├── sequence_param_ret.lua │ ├── scoped_enum.lua │ ├── constants.lua │ ├── fun.lua │ └── bind.nim ├── test_bugfixes.nim └── test_features.nim ├── .gitignore ├── LICENSE ├── scripts ├── build_static_lib.nims └── build.nims ├── nimLUA.nimble ├── .travis.yml ├── .appveyor.yml ├── .github └── workflows │ └── ci.yml ├── README.md ├── nimLUA └── lua.nim └── nimLUA.nim /tests/luas/closure.lua: -------------------------------------------------------------------------------- 1 | assert(wow.cl() == "1237") 2 | assert(wow.clever() == "1237") 3 | -------------------------------------------------------------------------------- /tests/luas/generic.lua: -------------------------------------------------------------------------------- 1 | assert(wow.mew(3,5) == 3) 2 | assert(wow.mewt(7, "hello") == 7) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimPDF/ 3 | bug/ 4 | *.exe 5 | *.dll 6 | lua/ 7 | master/ 8 | external/ 9 | -------------------------------------------------------------------------------- /tests/luas/openarray.lua: -------------------------------------------------------------------------------- 1 | local c = {1,3,5,7} 2 | 3 | x = 0 4 | for i=1, #c do 5 | x = x + c[i] 6 | end 7 | 8 | assert(wow.opa(c) == x) -------------------------------------------------------------------------------- /tests/luas/range_param_ret.lua: -------------------------------------------------------------------------------- 1 | assert(range.trangA(1) == 1 + 5) 2 | assert(range.trangB(1,1) == 2) 3 | assert(range.trangC(3,3) == 3 + 3 + 5) -------------------------------------------------------------------------------- /tests/luas/enum_param_ret.lua: -------------------------------------------------------------------------------- 1 | assert(proto_banana.radiate(FRUIT.BANANA) == ATOM.PROTON) 2 | assert(proto_banana.radiate(FRUIT.APPLE) == ATOM.NEUTRON) -------------------------------------------------------------------------------- /tests/luas/free_function.lua: -------------------------------------------------------------------------------- 1 | assert(mulv(2, 3) == 2*3) 2 | assert(tpc("hello") == "hello") 3 | assert(goodMan("gust", "boost") == "gust boost") 4 | 5 | -------------------------------------------------------------------------------- /tests/luas/single_scoped_enum.lua: -------------------------------------------------------------------------------- 1 | assert(GENE.ADENINE == 0) 2 | assert(GENE.CYTOSINE == 1) 3 | assert(GENE.GUANINE == 2) 4 | assert(GENE.THYMINE == 3) -------------------------------------------------------------------------------- /tests/luas/ov_func.lua: -------------------------------------------------------------------------------- 1 | assert(mac.machine(3,4) == 3 + 4) 2 | assert(mac.machine(5) == 5 * 3) 3 | assert(mac.machine(1, "hello") == "hello1") 4 | assert(mac.machine("a","C","b") == "aCb") -------------------------------------------------------------------------------- /tests/luas/scoped_function.lua: -------------------------------------------------------------------------------- 1 | assert(gum.mulv(2, 3) == 2*3) 2 | assert(gum.tpc("hello") == "hello") 3 | assert(gum.goodMan("gust", "boost") == "gust boost") 4 | assert(gum["`++`"](1, 2) == 3) -------------------------------------------------------------------------------- /tests/luas/getter_setter.lua: -------------------------------------------------------------------------------- 1 | local a = Foos.newFoos("kodok") 2 | assert(a.name == "kodok") 3 | assert(a:getName() == "kodok") 4 | 5 | local b = Ship.newShip() 6 | assert(b.cepat == 11) 7 | b.cepat = 17 8 | assert(b.cepat == 17) 9 | 10 | b.speed = 19 11 | assert(b.speed == nil) -------------------------------------------------------------------------------- /tests/luas/ptr_pointer.lua: -------------------------------------------------------------------------------- 1 | local a = ptr.seedP() 2 | assert(ptr.geneP(a) == a) 3 | assert(ptr.intP(a) == a) 4 | assert(ptr.genePA(a) == a) 5 | assert(ptr.intPA(a) == a) 6 | assert(ptr.genePPA(a) == a) 7 | assert(ptr.intPPA(a) == a) 8 | assert(ptr.genePPB(a) == a) 9 | assert(ptr.intPPB(a) == a) -------------------------------------------------------------------------------- /tests/luas/tuple.lua: -------------------------------------------------------------------------------- 1 | assert(tup.dino({a = "hello", b = 10}) == "hello") 2 | 3 | local x = tup.saurus("world") 4 | assert(x.a == "world") 5 | assert(x.b == 10) 6 | 7 | assert(tup.croco({a = 10, b = 11}) == 11) 8 | 9 | local y = tup.dile(13) 10 | assert(y.a == "13") 11 | assert(y.b == "13") -------------------------------------------------------------------------------- /tests/luas/scoped_and_global_enum.lua: -------------------------------------------------------------------------------- 1 | assert(DNA.ADENINE == 0) 2 | assert(DNA.CYTOSINE == 1) 3 | assert(DNA.GUANINE == 2) 4 | assert(DNA.THYMINE == 3) 5 | 6 | assert(ELECTRON == 0) 7 | assert(PROTON == 1) 8 | assert(NEUTRON == 2) 9 | 10 | assert(FRUIT.APPLE == 0) 11 | assert(FRUIT.BANANA == 1) 12 | assert(FRUIT.PEACH == 2) 13 | assert(FRUIT.PLUM == 3) 14 | -------------------------------------------------------------------------------- /tests/luas/namespace.lua: -------------------------------------------------------------------------------- 1 | assert(mulv(2,5) == 10) 2 | assert(GLOBAL.mulv(3, 7) == 21) 3 | assert(GLOBAL.subb(7, 5) == 2) 4 | 5 | assert(LEMON == 12.0) 6 | assert(GLOBAL.LEMON == 12.0) 7 | 8 | assert(ADENINE == 0) 9 | assert(CYTOSINE == 1) 10 | assert(GUANINE == 2) 11 | assert(THYMINE == 3) 12 | 13 | assert(GLOBAL.APPLE == 0) 14 | assert(GLOBAL.BANANA == 1) 15 | assert(GLOBAL.PEACH == 2) 16 | assert(GLOBAL.PLUM == 3) -------------------------------------------------------------------------------- /tests/luas/regular_object.lua: -------------------------------------------------------------------------------- 1 | local a = Acid.new(10) 2 | 3 | assert(a:getLen() == 10) 4 | a:setLen(11) 5 | assert(a:getLen() == 11) 6 | 7 | 8 | local b = makeAcid(12) 9 | assert(b:getLen() == 12) 10 | 11 | local c = acd.makeAcid(13) 12 | assert(c:getLen() == 13) 13 | 14 | local f = Fish.new(15) 15 | assert(f:grill() == "grill 15") 16 | assert(f:fry() == "fry 15") 17 | 18 | assert(gem.mining() == "mining gem") 19 | assert(gem.polish() == "polishing gem") 20 | assert(gem.ANODE == 11) 21 | 22 | local k = kakap.new(16) 23 | assert(k:grill() == "grill 16") 24 | assert(k:fry() == "fry 16") 25 | -------------------------------------------------------------------------------- /tests/luas/array_param_ret.lua: -------------------------------------------------------------------------------- 1 | local a = {3,7,9,11,12,123,345} 2 | local b = arr.chemA(a) 3 | assert(#b == 6) 4 | for i=1, #b do 5 | assert(b[i]==a[i]) 6 | end 7 | 8 | 9 | local c = {DNA.GUANINE, DNA.THYMINE, DNA.ADENINE, DNA.CYTOSINE} 10 | local d = arr.geneA(c) 11 | assert(#d == 3) 12 | for i=1, #d do 13 | assert(d[i] == c[i]) 14 | end 15 | 16 | local e = {FRUIT.PEACH, FRUIT.PLUM, FRUIT.BANANA} 17 | local f = arr.fruitA(e) 18 | assert(#f == 3) 19 | for i=1, #f do 20 | assert(f[i] == e[i]) 21 | end 22 | 23 | local g = {DNA.GUANINE, DNA.THYMINE, DNA.ADENINE, DNA.CYTOSINE, DNA.THYMINE, DNA.ADENINE, DNA.CYTOSINE} 24 | local h = arr.geneB(g) 25 | assert(#h == 7) 26 | for i=1, #h do 27 | assert(h[i] == g[i]) 28 | end -------------------------------------------------------------------------------- /tests/luas/set_param_ret.lua: -------------------------------------------------------------------------------- 1 | function contains(t, e) 2 | for i = 1,#t do 3 | if t[i] == e then return true end 4 | end 5 | return false 6 | end 7 | 8 | local a = {FRUIT.BANANA, FRUIT.APPLE} 9 | local b = set.fruitS(a) 10 | assert(#a == #b) 11 | for i=1, #b do 12 | assert(contains(a, b[i])) 13 | end 14 | 15 | local a = {1, 3, 5} 16 | local b = set.alphaS(a) 17 | assert(#a == #b) 18 | for i=1, #b do 19 | assert(contains(a, b[i])) 20 | end 21 | 22 | local a = {1, 3, 5} 23 | local b = set.alphaSA(a) 24 | assert(#a == #b) 25 | for i=1, #b do 26 | assert(contains(a, b[i])) 27 | end 28 | 29 | local a = {FRUIT.BANANA, FRUIT.APPLE} 30 | local b = set.fruitSA(a) 31 | assert(#a == #b) 32 | for i=1, #b do 33 | assert(contains(a, b[i])) 34 | end 35 | -------------------------------------------------------------------------------- /tests/luas/inheritance.lua: -------------------------------------------------------------------------------- 1 | local av = Avocado.new("alpukat", 123) 2 | local pn = Pineapple.init("nanas", 134) 3 | 4 | assert(av.name == "alpukat") 5 | assert(av.id == 123) 6 | assert(av:getId() == 123) 7 | assert(pn.name == "nanas") 8 | assert(pn.id == 134) 9 | assert(pn:getId() == 134) 10 | 11 | local av2 = Avocado.new() -- missing argument or wrong argument type result in nil 12 | assert(av2 == nil) 13 | 14 | local av3 = Avocado.new("gull") 15 | assert(av3 == nil) 16 | 17 | local foo = Foos.newFoos("seamonkey") 18 | local av4 = Avocado.new(foo) 19 | assert(av4.name == "seamonkey") -- good, it works 20 | 21 | local av5 = pn:getAvocado(0) 22 | assert(av5 ~= nil) 23 | assert(av5.name == "nanas") 24 | assert(av5.id == 123) 25 | 26 | local av6 = pn:getAvocado(1) 27 | assert(av6 == nil) 28 | 29 | local av7 = Avocado.new("garbage") 30 | assert(av7 == nil) 31 | -------------------------------------------------------------------------------- /tests/luas/sequence_param_ret.lua: -------------------------------------------------------------------------------- 1 | local a = {FRUIT.BANANA, FRUIT.APPLE, FRUIT.PLUM, FRUIT.PEACH} 2 | local b = seq.fruitQ(a) 3 | local c = seq.fruitQA(a) 4 | assert(#b == #a) 5 | assert(#c == #a) 6 | for i=1, #b do 7 | assert(b[i] == a[i]) 8 | assert(c[i] == a[i]) 9 | end 10 | 11 | local a = {DNA.GUANINE, DNA.THYMINE, DNA.ADENINE, DNA.CYTOSINE} 12 | local b = seq.geneQ(a) 13 | local c = seq.geneQA(a) 14 | assert(#b == #a) 15 | assert(#c == #a) 16 | for i=1, #b do 17 | assert(b[i] == a[i]) 18 | assert(c[i] == a[i]) 19 | end 20 | 21 | local a = {"mama", "mia", "lezatos", "hmm"} 22 | local b = seq.stringQ(a) 23 | local c = seq.stringQA(a) 24 | assert(#b == #a) 25 | assert(#c == #a) 26 | for i=1, #b do 27 | assert(b[i] == a[i]) 28 | assert(c[i] == a[i]) 29 | end 30 | 31 | local b = seq.rootv(11.5) 32 | for i=1, 10 do 33 | local c = (i-1) * 11.5 34 | assert(c == b[i]) 35 | end 36 | -------------------------------------------------------------------------------- /tests/luas/scoped_enum.lua: -------------------------------------------------------------------------------- 1 | assert(GENE.ADENINE == 0) 2 | assert(GENE.CYTOSINE == 1) 3 | assert(GENE.GUANINE == 2) 4 | assert(GENE.THYMINE == 3) 5 | 6 | assert(ATOM.ELECTRON == 0) 7 | assert(ATOM.PROTON == 1) 8 | assert(ATOM.NEUTRON == 2) 9 | 10 | assert(FRUIT.APPLE == 0) 11 | assert(FRUIT.BANANA == 1) 12 | assert(FRUIT.PEACH == 2) 13 | assert(FRUIT.PLUM == 3) 14 | 15 | -- since nim 1.2.6 accquoted enum 16 | -- become ident 17 | 18 | x = { major = 1, minor = 2, patch = 6 } 19 | 20 | function verValue(x) 21 | return x.major * 100 + x.minor * 10 + x.patch 22 | end 23 | 24 | if verValue(Nim) <= verValue(x) then 25 | assert(_G["`poncho`"]["`glucho`"] == 0) 26 | assert(_G["`poncho`"]["`becho`"] == 1) 27 | assert(_G["`poncho`"]["`type`"] == 2) 28 | else 29 | assert(_G["`poncho`"]["glucho"] == 0) 30 | assert(_G["`poncho`"]["becho"] == 1) 31 | assert(_G["`poncho`"]["type"] == 2) 32 | end 33 | -------------------------------------------------------------------------------- /tests/luas/constants.lua: -------------------------------------------------------------------------------- 1 | assert(MANGOES == 10.0) 2 | assert(PAPAYA == 11.0) 3 | assert(LEMON == 12.0) 4 | assert(MAX_DASH_PATTERN == 8) 5 | assert(CATHODE == 10) 6 | assert(ANODE == 11) 7 | 8 | assert(mmm.ELECTRON16 == 12) 9 | assert(mmm.PROTON16 == 13) 10 | assert(mmm.ELECTRON32 == 14) 11 | assert(mmm.PROTON32 == 15) 12 | assert(mmm.ELECTRON64 == 16) 13 | assert(mmm.PROTON64 == 17) 14 | 15 | assert(ccc.LABEL_STYLE_CH[0] == "D") --["D", "R", "r", "A", "a"] 16 | assert(ccc.INFO_FIELD[0] == "Creator")--["Creator", "Producer", "Title", "Subject", "Author", "Keywords"] 17 | assert(ccc.STAIR[0] == 123) --[123, 456] 18 | assert(ccc.HELIX[0] == 123.4) --[123.4, 567.8] 19 | assert(ccc.GREET == "hello world") 20 | assert(mmm.connected == true) 21 | assert(ccc.mime.apple == "fruit") 22 | assert(ccc.mime.men == "woman") 23 | assert(ccc.programme[1] == "state") 24 | assert(ccc.programme[2] == "power") 25 | assert(ccc.programme[3] == "result") 26 | 27 | assert(MANGGA == 10.0) 28 | assert(PEPAYA == 11.0) 29 | assert(JERUK == 12.0) 30 | 31 | assert(buah.MANGGA == 10.0) 32 | assert(buah.PEPAYA == 11.0) 33 | assert(buah.JERUK == 12.0) 34 | -------------------------------------------------------------------------------- /tests/luas/fun.lua: -------------------------------------------------------------------------------- 1 | -- fun.lua 2 | 3 | -- Because the metatable has been exposed 4 | -- to us, we can actually add new functions 5 | -- to Foo 6 | function Foo:speak() 7 | return "Hello, I am a Foo" 8 | end 9 | 10 | local foo = Foo.new("fred") 11 | local m = foo:add(3, 4) 12 | 13 | -- "fred: 3 + 4 = 7" 14 | assert(m == "fred: 3 + 4 = 7") 15 | 16 | -- "Hello, I am a Foo" 17 | assert(foo:speak() == "Hello, I am a Foo") 18 | 19 | -- Let's rig the original metatable 20 | Foo.add_ = Foo.add 21 | function Foo:add(a, b) 22 | return "here comes the magic: " .. self:add_(a, b) 23 | end 24 | 25 | m = foo:add(9, 8) 26 | assert(m == "here comes the magic: fred: 9 + 8 = 17") 27 | 28 | assert(foo:addv(4,5) == 2 * (4+5)) 29 | assert(foo:addv("abc", "nop") == "hello: my name is fred, here is my message: abc, nop") 30 | 31 | local mee = Foo.new(3, 8) 32 | assert(mee:add(1,2) == "here comes the magic: 38: 1 + 2 = 3") 33 | 34 | local x = Foo.newFoo("cat") 35 | assert(x:add(1,10) == "here comes the magic: cat: 1 + 10 = 11") 36 | 37 | local y = Foo.whatever("king") 38 | assert(y:add(10,10) == "here comes the magic: king: 10 + 10 = 20") 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 andri lim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /tests/test_bugfixes.nim: -------------------------------------------------------------------------------- 1 | when defined(importLogging): 2 | import ../nimLUA, os, sequtils, logging, unittest 3 | else: 4 | import ../nimLUA, os, sequtils, json, random, unittest 5 | 6 | type 7 | Foo = ref object 8 | name: string 9 | 10 | proc newFoo(name: string): Foo = 11 | new(result) 12 | result.name = name 13 | 14 | proc addv(f: Foo, a, b: int): int = 15 | result = 2 * (a + b) 16 | 17 | proc addv(f: Foo, a, b: string): string = 18 | result = "hello: my name is , here is my message: " & a & b & f.name 19 | 20 | proc testBugFixes() = 21 | suite "bugfixes": 22 | var L = newNimLua() 23 | 24 | L.bindObject(Foo): 25 | newFoo -> constructor 26 | addv 27 | 28 | test "bug19": 29 | let luaScript = """ 30 | local foo = Foo.new("jacky") 31 | assert(foo:addv(1,2) == 6) 32 | assert(foo:addv("apple","banana") == "hello: my name is , here is my message: applebananajacky") 33 | """ 34 | 35 | check L.doString(luaScript) == 0.cint 36 | 37 | test "bug23": 38 | let r = L.doString("a = 7 + 11aa") 39 | check r != LUA_OK 40 | check L.toString(-1) == """[string "a = 7 + 11aa"]:1: malformed number near '11aa'""" 41 | 42 | L.close() 43 | 44 | testBugFixes() 45 | -------------------------------------------------------------------------------- /scripts/build_static_lib.nims: -------------------------------------------------------------------------------- 1 | import os, strutils 2 | 3 | const 4 | compatFlag = "-DLUA_COMPAT_ALL" # or "-DLUA_COMPAT_5_2" 5 | 6 | type 7 | FileName = tuple[dir, name, ext: string] 8 | 9 | proc getCFiles(dir: string): seq[FileName] = 10 | var files = listFiles(dir) 11 | result = @[] 12 | for c in files: 13 | let x = c.splitFile 14 | if cmpIgnoreCase(x.name, "lua") == 0: continue 15 | if cmpIgnoreCase(x.name, "luac") == 0: continue 16 | if cmpIgnoreCase(x.ext, ".c") == 0: 17 | result.add x 18 | 19 | proc toString(names: seq[string]): string = 20 | result = "" 21 | for c in names: 22 | result.add c 23 | result.add " " 24 | 25 | proc objList(): string = 26 | let src = getCFiles("external" / "lua" / "src") 27 | var objs: seq[string] = @[] 28 | 29 | for x in src: 30 | let fileName = x.dir / x.name 31 | let buildCmd = "gcc -O2 -Wall $1 -c -o $2.o $2.c" % [compatFlag, fileName] 32 | try: 33 | exec(buildCmd) 34 | echo buildCmd 35 | objs.add(fileName & ".o") 36 | except: 37 | echo "failed to build ", fileName 38 | 39 | result = toString(objs) 40 | 41 | proc makeLib() = 42 | let linkCmd = "ar rcs external/liblua.a " & objList() 43 | echo linkCmd 44 | exec(linkCmd) 45 | 46 | makeLib() 47 | -------------------------------------------------------------------------------- /nimLUA.nimble: -------------------------------------------------------------------------------- 1 | packageName = "nimLUA" 2 | version = "0.3.8" 3 | author = "Andri Lim" 4 | description = "glue code generator to bind Nim and Lua together using Nim's powerful macro" 5 | license = "MIT" 6 | skipDirs = @["test", "scripts"] 7 | 8 | requires: "nim >= 1.2.2" 9 | 10 | ### Helper functions 11 | proc test(env, path: string) = 12 | # Compilation language is controlled by TEST_LANG 13 | var lang = "c" 14 | if existsEnv"TEST_LANG": 15 | lang = getEnv"TEST_LANG" 16 | debugEcho "LANG: ", lang 17 | 18 | when defined(unix): 19 | const libm = "-lm" 20 | else: 21 | const libm = "" 22 | 23 | when defined(macosx): 24 | # nim bug, incompatible pointer assignment 25 | # see nim-lang/Nim#16123 26 | if lang == "cpp": 27 | lang = "c" 28 | 29 | if not dirExists "build": 30 | mkDir "build" 31 | exec "nim " & lang & " " & env & 32 | " --outdir:build -r --hints:off --warnings:off " & 33 | " -d:lua_static_lib --passL:\"-Lexternal -llua " & libm & " \" " & path 34 | 35 | task test, "Run all tests": 36 | test "-d:nimDebugDlOpen", "tests/test_features" 37 | test "-d:nimDebugDlOpen -d:release", "tests/test_features" 38 | test "-d:importLogging", "tests/test_bugfixes" 39 | test "-d:importLogging -d:release", "tests/test_bugfixes" 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c # or other C/C++ variants 2 | 3 | sudo: false 4 | 5 | # https://docs.travis-ci.com/user/caching/ 6 | # 7 | # Caching the whole nim folder is better than relying on ccache - this way, we 8 | # skip the expensive bootstrap process and linking 9 | cache: 10 | directories: 11 | - nim 12 | 13 | os: 14 | - linux 15 | - osx 16 | 17 | install: 18 | - mkdir external 19 | - wget -q https://www.lua.org/ftp/lua-5.3.5.tar.gz 20 | - tar xfz lua-5.3.5.tar.gz -C external 21 | - mv external/lua-5.3.5 external/lua 22 | 23 | # build nim from our own branch - this to avoid the day-to-day churn and 24 | # regressions of the fast-paced Nim development while maintaining the 25 | # flexibility to apply patches 26 | # 27 | # check version of remote branch 28 | - "export NIMVER=$(git ls-remote https://github.com/nim-lang/nim.git HEAD | cut -f 1)" 29 | 30 | # after building nim, wipe csources to save on cache space 31 | - "{ [ -f nim/$NIMVER/bin/nim ] && [ -f nim/$NIMVER/bin/nimble ] ; } || 32 | { rm -rf nim ; 33 | mkdir -p nim ; 34 | git clone --depth=1 https://github.com/nim-lang/nim.git nim/$NIMVER ; 35 | cd nim/$NIMVER ; 36 | sh build_all.sh > /dev/null; 37 | rm -rf csources ; 38 | cd ../.. ; 39 | }" 40 | - "export PATH=$PWD/nim/$NIMVER/bin:$PATH" 41 | 42 | script: 43 | - nim e scripts/build_static_lib.nims 44 | - nimble test 45 | -------------------------------------------------------------------------------- /scripts/build.nims: -------------------------------------------------------------------------------- 1 | import os, strutils 2 | 3 | when defined(unix): 4 | const 5 | extraFlag = "-fPIC" #"-DLUA_BUILD_AS_DLL" 6 | compatFlag = "-DLUA_COMPAT_ALL" # or "-DLUA_COMPAT_5_2" 7 | linkFlags = "" 8 | else: 9 | when defined(build32): 10 | const extraFlag = "-m32" 11 | else: 12 | const extraFlag = "" 13 | 14 | const 15 | compatFlag = "-DLUA_COMPAT_ALL" # or "-DLUA_COMPAT_5_2" 16 | linkFlags = "" 17 | 18 | when defined(MACOSX): 19 | const LIB_NAME* = "liblua5.3.dylib" 20 | elif defined(FREEBSD): 21 | const LIB_NAME* = "liblua-5.3.so" 22 | elif defined(UNIX): 23 | const LIB_NAME* = "liblua5.3.so" 24 | else: 25 | const LIB_NAME* = "lua53.dll" 26 | 27 | type 28 | FileName = tuple[dir, name, ext: string] 29 | 30 | proc getCFiles(dir: string): seq[FileName] = 31 | debugEcho dir 32 | var files = listFiles(dir) 33 | result = @[] 34 | for c in files: 35 | let x = c.splitFile 36 | if cmpIgnoreCase(x.name, "lua") == 0: continue 37 | if cmpIgnoreCase(x.name, "luac") == 0: continue 38 | if cmpIgnoreCase(x.ext, ".c") == 0: 39 | result.add x 40 | 41 | proc toString(names: seq[string]): string = 42 | result = "" 43 | for c in names: 44 | result.add c 45 | result.add " " 46 | 47 | let src = getCFiles("external" / "lua" / "src") 48 | var objs: seq[string] = @[] 49 | 50 | for x in src: 51 | let fileName = x.dir / x.name 52 | let buildCmd = "gcc -O2 -Wall $1 $2 -c -o $3.o $3.c $4" % [extraFlag, compatFlag, fileName, linkFlags] 53 | try: 54 | exec(buildCmd) 55 | echo buildCmd 56 | objs.add(fileName & ".o") 57 | except: 58 | echo "failed to build ", fileName 59 | 60 | let objList = toString(objs) 61 | let linkCmd = "gcc -shared $4 -o $1$2$3 $5" % [".", $DirSep, LIB_NAME, extraFlag, objList] 62 | echo linkCmd 63 | exec(linkCmd) 64 | -------------------------------------------------------------------------------- /tests/luas/bind.nim: -------------------------------------------------------------------------------- 1 | import nimLUA, "nimPDF/nimBMP", "nimPDF/nimAES", "nimPDF/nimSHA2", streams, "nimPDF/nimPDF" 2 | import basic2d 3 | 4 | proc main() = 5 | var L = newNimLua() 6 | 7 | L.bindObject(AESContext -> "AES"): 8 | initAES 9 | setEncodeKey 10 | setDecodeKey 11 | encryptECB 12 | decryptECB 13 | cryptOFB 14 | encryptCBC 15 | decryptCBC 16 | encryptCFB128 17 | decryptCFB128 18 | encryptCFB8 19 | decryptCFB8 20 | cryptCTR 21 | 22 | L.bindObject(SHA256): 23 | initSHA 24 | update 25 | final 26 | 27 | L.bindObject(BMP): 28 | decodeBMP 29 | loadBMP 30 | convertTo32Bit 31 | convertTo24Bit 32 | convert 33 | 34 | L.bindFunction("BMP"): 35 | loadBMP32 36 | loadBMP24 37 | 38 | L.bindEnum: 39 | LabelStyle 40 | PageOrientationType 41 | CoordinateMode 42 | DestStyle 43 | 44 | L.bindFunction(): 45 | getSizeFromName 46 | makePageSize 47 | getVersion 48 | 49 | L.bindObject(DocOpt): 50 | makeDocOpt 51 | addResourcesPath 52 | addImagesPath 53 | addFontsPath 54 | clearFontsPath 55 | clearImagesPath 56 | clearResourcesPath 57 | clearAllPath 58 | 59 | L.bindObject(Document): 60 | setInfo 61 | initPDF 62 | getOpt 63 | setLabel 64 | loadImage 65 | getVersion 66 | setUnit 67 | getUnit 68 | setCoordinateMode 69 | getCoordinateMode 70 | getSize 71 | setFont 72 | writePDF 73 | drawText 74 | drawVText 75 | beginText 76 | moveTextPos 77 | setTextRenderingMode 78 | setTextMatrix 79 | showText 80 | setTextLeading 81 | moveToNextLine 82 | endText 83 | setCharSpace 84 | setTextHScale 85 | setWordSpace 86 | setTransform 87 | rotate 88 | move 89 | scale 90 | stretch 91 | skew 92 | toUser 93 | fromUser 94 | drawImage 95 | drawRect 96 | moveTo 97 | lineTo 98 | bezierCurveTo 99 | curveTo1 100 | curveTo2 101 | closePath 102 | roundRect 103 | drawEllipse 104 | drawCircle 105 | setLineWidth 106 | setLineCap 107 | setLineJoin 108 | setMiterLimit 109 | setGrayFill 110 | setGrayStroke 111 | setRGBFill 112 | setRGBStroke 113 | setCMYKFill 114 | setCMYKStroke 115 | setAlpha 116 | setBlendMode 117 | saveState 118 | restoreState 119 | getTextWidth 120 | getTextHeight 121 | clip 122 | executePath 123 | drawBounds 124 | fill 125 | stroke 126 | fillAndStroke 127 | setGradientFill 128 | makeXYZDest 129 | makeFitDest 130 | makeFitHDest 131 | makeFitVDest 132 | makeFitRDest 133 | makeFitBDest 134 | makeFitBHDest 135 | makeFitBVDest 136 | makeOutline 137 | linkAnnot 138 | textAnnot 139 | setPassword 140 | setEncryptionMode 141 | initAcroForm 142 | textField 143 | drawArc 144 | arcTo 145 | setDash 146 | 147 | #addPage 148 | 149 | L.bindObject(AcroForm): 150 | setFontColor 151 | setFontSize 152 | setFontFamily 153 | setFontStyle 154 | setEncoding 155 | 156 | 157 | main() -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | cache: 4 | - x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z -> .appveyor.yml 5 | - i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z -> .appveyor.yml 6 | - Nim -> .appveyor.yml 7 | 8 | matrix: 9 | # We always want 32 and 64-bit compilation 10 | fast_finish: false 11 | 12 | platform: 13 | - x86 14 | - x64 15 | 16 | install: 17 | - setlocal EnableExtensions EnableDelayedExpansion 18 | 19 | - IF "%PLATFORM%" == "x86" ( 20 | SET "MINGW_ARCHIVE=i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z" & 21 | SET "MINGW_URL=https://sourceforge.net/projects/mingw-w64/files/Toolchains%%20targetting%%20Win32/Personal%%20Builds/mingw-builds/8.1.0/threads-posix/dwarf/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z" & 22 | SET "MINGW_DIR=mingw32" 23 | ) ELSE ( 24 | IF "%PLATFORM%" == "x64" ( 25 | SET "MINGW_ARCHIVE=x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z" & 26 | SET "MINGW_URL=https://sourceforge.net/projects/mingw-w64/files/Toolchains%%20targetting%%20Win64/Personal%%20Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z" & 27 | SET "MINGW_DIR=mingw64" 28 | ) else ( 29 | echo "Unknown platform" 30 | ) 31 | ) 32 | 33 | - SET PATH=%CD%\%MINGW_DIR%\bin;%CD%\Nim\bin;%PATH% 34 | 35 | # Unpack mingw 36 | - IF NOT EXIST "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" 37 | - 7z x -y "%MINGW_ARCHIVE%" > nul 38 | 39 | # build nim from our own branch - this to avoid the day-to-day churn and 40 | # regressions of the fast-paced Nim development while maintaining the 41 | # flexibility to apply patches 42 | - SET "NEED_REBUILD=" 43 | 44 | - IF NOT EXIST "Nim\\.git\\" ( 45 | git clone --depth 1 https://github.com/nim-lang/Nim.git 46 | ) ELSE ( 47 | ( cd Nim ) & 48 | ( git pull ) & 49 | ( cd .. ) 50 | ) 51 | 52 | # Rebuild Nim if HEAD has moved or if we don't yet have a cached version 53 | - IF NOT EXIST "Nim\\ver.txt" ( 54 | SET NEED_REBUILD=1 55 | ) ELSE ( 56 | ( CD Nim ) & 57 | ( git rev-parse HEAD > ..\\cur_ver.txt ) & 58 | ( fc ver.txt ..\\cur_ver.txt > nul ) & 59 | ( IF NOT ERRORLEVEL == 0 SET NEED_REBUILD=1 ) & 60 | ( cd .. ) 61 | ) 62 | 63 | - IF NOT EXIST "Nim\\bin\\nim.exe" SET NEED_REBUILD=1 64 | - IF NOT EXIST "Nim\\bin\\nimble.exe" SET NEED_REBUILD=1 65 | 66 | # after building nim, wipe csources to save on cache space 67 | - IF DEFINED NEED_REBUILD ( 68 | cd Nim & 69 | ( IF EXIST "csources" rmdir /s /q csources ) & 70 | git clone --depth 1 https://github.com/nim-lang/csources & 71 | cd csources & 72 | ( IF "%PLATFORM%" == "x64" ( build64.bat > nul ) else ( build.bat > nul ) ) & 73 | cd .. & 74 | bin\nim c --verbosity:0 --hints:off koch & 75 | koch boot -d:release --verbosity:0 --hints:off & 76 | koch nimble > nul & 77 | git rev-parse HEAD > ver.txt & 78 | rmdir /s /q csources 79 | ) 80 | 81 | - appveyor DownloadFile "https://www.lua.org/ftp/lua-5.3.5.tar.gz" 82 | - 7z e lua-5.3.5.tar.gz 83 | - 7z x lua-5.3.5.tar 84 | - mkdir external 85 | - move lua-5.3.5 external\lua 86 | 87 | build_script: 88 | - cd C:\projects\%APPVEYOR_PROJECT_SLUG% 89 | 90 | test_script: 91 | - nim e scripts/build_static_lib.nims 92 | - nimble test 93 | 94 | deploy: off 95 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: nimLUA CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | strategy: 7 | fail-fast: false 8 | max-parallel: 20 9 | matrix: 10 | branch: [master] 11 | target: 12 | - os: linux 13 | cpu: amd64 14 | TEST_LANG: c 15 | - os: linux 16 | cpu: amd64 17 | TEST_LANG: cpp 18 | - os: linux 19 | cpu: i386 20 | TEST_LANG: c 21 | - os: linux 22 | cpu: i386 23 | TEST_LANG: cpp 24 | - os: macos 25 | cpu: amd64 26 | TEST_LANG: c 27 | - os: macos 28 | cpu: amd64 29 | TEST_LANG: cpp 30 | - os: windows 31 | cpu: amd64 32 | TEST_LANG: c 33 | - os: windows 34 | cpu: amd64 35 | TEST_LANG: cpp 36 | - os: windows 37 | cpu: i386 38 | TEST_LANG: c 39 | - os: windows 40 | cpu: i386 41 | TEST_LANG: cpp 42 | include: 43 | - target: 44 | os: linux 45 | builder: ubuntu-18.04 46 | - target: 47 | os: macos 48 | builder: macos-10.15 49 | - target: 50 | os: windows 51 | builder: windows-2019 52 | 53 | name: '${{ matrix.target.os }}-${{ matrix.target.cpu }}-${{ matrix.target.TEST_LANG }} (${{ matrix.branch }})' 54 | runs-on: ${{ matrix.builder }} 55 | steps: 56 | - name: Checkout nimLUA 57 | uses: actions/checkout@v2 58 | with: 59 | path: nimLUA 60 | submodules: false 61 | 62 | - name: Install build dependencies (Linux i386) 63 | if: runner.os == 'Linux' && matrix.target.cpu == 'i386' 64 | run: | 65 | sudo dpkg --add-architecture i386 66 | sudo apt-fast update -qq 67 | sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \ 68 | --no-install-recommends -yq gcc-multilib g++-multilib \ 69 | libssl-dev:i386 70 | mkdir -p external/bin 71 | cat << EOF > external/bin/gcc 72 | #!/bin/bash 73 | exec $(which gcc) -m32 "\$@" 74 | EOF 75 | cat << EOF > external/bin/g++ 76 | #!/bin/bash 77 | exec $(which g++) -m32 "\$@" 78 | EOF 79 | chmod 755 external/bin/gcc external/bin/g++ 80 | echo '${{ github.workspace }}/external/bin' >> $GITHUB_PATH 81 | 82 | - name: Restore MinGW-W64 (Windows) from cache 83 | if: runner.os == 'Windows' 84 | id: windows-mingw-cache 85 | uses: actions/cache@v2 86 | with: 87 | path: external/mingw-${{ matrix.target.cpu }} 88 | key: 'mingw-${{ matrix.target.cpu }}' 89 | 90 | - name: Restore Nim DLLs dependencies (Windows) from cache 91 | if: runner.os == 'Windows' 92 | id: windows-dlls-cache 93 | uses: actions/cache@v2 94 | with: 95 | path: external/dlls-${{ matrix.target.cpu }} 96 | key: 'dlls-${{ matrix.target.cpu }}' 97 | 98 | - name: Install MinGW64 dependency (Windows) 99 | if: > 100 | steps.windows-mingw-cache.outputs.cache-hit != 'true' && 101 | runner.os == 'Windows' 102 | shell: bash 103 | run: | 104 | mkdir -p external 105 | if [[ '${{ matrix.target.cpu }}' == 'amd64' ]]; then 106 | MINGW_URL="https://sourceforge.net/projects/mingw-w64/files/Toolchains targetting Win64/Personal Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z" 107 | ARCH=64 108 | else 109 | MINGW_URL="https://sourceforge.net/projects/mingw-w64/files/Toolchains targetting Win32/Personal Builds/mingw-builds/8.1.0/threads-posix/dwarf/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z" 110 | ARCH=32 111 | fi 112 | curl -L "$MINGW_URL" -o "external/mingw-${{ matrix.target.cpu }}.7z" 113 | 7z x -y "external/mingw-${{ matrix.target.cpu }}.7z" -oexternal/ 114 | mv external/mingw$ARCH external/mingw-${{ matrix.target.cpu }} 115 | 116 | - name: Install DLLs dependencies (Windows) 117 | if: > 118 | steps.windows-dlls-cache.outputs.cache-hit != 'true' && 119 | runner.os == 'Windows' 120 | shell: bash 121 | run: | 122 | mkdir -p external 123 | curl -L "https://nim-lang.org/download/windeps.zip" -o external/windeps.zip 124 | 7z x -y external/windeps.zip -oexternal/dlls-${{ matrix.target.cpu }} 125 | 126 | - name: Path to cached dependencies (Windows) 127 | if: > 128 | runner.os == 'Windows' 129 | shell: bash 130 | run: | 131 | echo '${{ github.workspace }}'"/external/mingw-${{ matrix.target.cpu }}/bin" >> $GITHUB_PATH 132 | echo '${{ github.workspace }}'"/external/dlls-${{ matrix.target.cpu }}" >> $GITHUB_PATH 133 | 134 | - name: Setup environment 135 | shell: bash 136 | run: echo '${{ github.workspace }}/nim/bin' >> $GITHUB_PATH 137 | 138 | - name: Get latest Nim commit hash 139 | id: versions 140 | shell: bash 141 | run: | 142 | getHash() { 143 | git ls-remote "https://github.com/$1" "${2:-HEAD}" | cut -f 1 144 | } 145 | nimHash=$(getHash nim-lang/Nim devel) 146 | csourcesHash=$(getHash nim-lang/csources) 147 | echo "::set-output name=nim::$nimHash" 148 | echo "::set-output name=csources::$csourcesHash" 149 | 150 | - name: Restore prebuilt Nim from cache 151 | id: nim-cache 152 | uses: actions/cache@v1 153 | with: 154 | path: nim 155 | key: 'nim-${{ matrix.target.os }}-${{ matrix.target.cpu }}-${{ steps.versions.outputs.nim }}' 156 | 157 | - name: Restore prebuilt csources from cache 158 | if: steps.nim-cache.outputs.cache-hit != 'true' 159 | id: csources-cache 160 | uses: actions/cache@v1 161 | with: 162 | path: csources/bin 163 | key: 'csources-${{ matrix.target.os }}-${{ matrix.target.cpu }}-${{ steps.versions.outputs.csources }}' 164 | 165 | - name: Checkout Nim csources 166 | if: > 167 | steps.csources-cache.outputs.cache-hit != 'true' && 168 | steps.nim-cache.outputs.cache-hit != 'true' 169 | uses: actions/checkout@v2 170 | with: 171 | repository: nim-lang/csources 172 | path: csources 173 | ref: ${{ steps.versions.outputs.csources }} 174 | 175 | - name: Checkout Nim 176 | if: steps.nim-cache.outputs.cache-hit != 'true' 177 | uses: actions/checkout@v2 178 | with: 179 | repository: nim-lang/Nim 180 | path: nim 181 | ref: ${{ steps.versions.outputs.nim }} 182 | 183 | - name: Build Nim and associated tools 184 | if: steps.nim-cache.outputs.cache-hit != 'true' 185 | shell: bash 186 | run: | 187 | ncpu= 188 | ext= 189 | case '${{ runner.os }}' in 190 | 'Linux') 191 | ncpu=$(nproc) 192 | ;; 193 | 'macOS') 194 | ncpu=$(sysctl -n hw.ncpu) 195 | ;; 196 | 'Windows') 197 | ncpu=$NUMBER_OF_PROCESSORS 198 | ext=.exe 199 | ;; 200 | esac 201 | [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 202 | if [[ ! -e csources/bin/nim$ext ]]; then 203 | make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.target.cpu }}' 204 | else 205 | echo 'Using prebuilt csources' 206 | fi 207 | cp -v csources/bin/nim$ext nim/bin 208 | cd nim 209 | nim c koch 210 | ./koch boot -d:release 211 | ./koch nimble -d:release 212 | # clean up to save cache space 213 | rm koch 214 | rm -rf nimcache 215 | rm -rf dist 216 | rm -rf .git 217 | 218 | - name: Restore prebuilt Lua from cache 219 | id: lua-cache 220 | uses: actions/cache@v1 221 | with: 222 | path: nimLUA/external 223 | key: 'lua-${{ matrix.target.os }}-${{ matrix.target.cpu }}' 224 | 225 | - name : Download and build lualib 226 | if: steps.lua-cache.outputs.cache-hit != 'true' 227 | shell: bash 228 | run: | 229 | EXTPATH=nimLUA/external 230 | mkdir -p "$EXTPATH" 231 | curl -L https://www.lua.org/ftp/lua-5.3.5.tar.gz -o "$EXTPATH/lua_source.tar.gz" 232 | tar xfz "$EXTPATH/lua_source.tar.gz" -C "$EXTPATH" 233 | mv "$EXTPATH/lua-5.3.5" "$EXTPATH/lua" 234 | cd nimLUA 235 | nim e scripts/build_static_lib.nims 236 | cd .. 237 | 238 | - name: Run nimLUA tests 239 | shell: bash 240 | run: | 241 | cd nimLUA 242 | env TEST_LANG="${{ matrix.target.TEST_LANG }}" nimble test 243 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nimLua 2 | glue code generator to bind Nim and Lua together using Nim's powerful macro 3 | 4 | [![Build Status (Travis)](https://img.shields.io/travis/jangko/nimLUA/master.svg?label=Linux%20/%20macOS "Linux/macOS build status (Travis)")](https://travis-ci.org/jangko/nimLUA) 5 | [![Windows build status (Appveyor)](https://img.shields.io/appveyor/ci/jangko/nimLUA/master.svg?label=Windows "Windows build status (Appveyor)")](https://ci.appveyor.com/project/jangko/nimLUA) 6 | ![nimble](https://img.shields.io/badge/available%20on-nimble-yellow.svg?style=flat-square) 7 | ![license](https://img.shields.io/github/license/citycide/cascade.svg?style=flat-square) 8 | ![Github action](https://github.com/jangko/nimLUA/workflows/nimLUA%20CI/badge.svg) 9 | - - - 10 | 11 | **Features**: 12 | 13 | * bind free proc 14 | * bind proc as Lua method 15 | * bind const 16 | * bind enum 17 | * bind object 18 | * generic proc binding 19 | * closure binding 20 | * properties getter/setter 21 | * automatic resolve overloaded proc 22 | * easy namespace creation 23 | * easy debugging 24 | * consistent simple API 25 | * can rename exported symbol 26 | * support automatic type conversion 27 | * can change binding dynamically at runtime too 28 | * generate clean and optimized glue code that you can inspect at compile time 29 | * it's free 30 | 31 | **planned features**: 32 | 33 | * complex data types conversion, at least standard container 34 | * access Lua code/data from Nim 35 | 36 | - - - 37 | **Current version API**: 38 | no need to remember complicated API, the API is simple but powerful 39 | 40 | * newNimLua 41 | * bindEnum 42 | * bindConst 43 | * bindFunction/bindProc 44 | * bindObject 45 | 46 | - - - 47 | 48 | ## **DATA TYPE CONVERSION** 49 | 50 | | Nim | Lua | 51 | |--------------------------------|----------------------------------| 52 | | char,int,uint,int8-64,uint8-64 | integer/number | 53 | | float, float32, float64 | number | 54 | | array[0..n, T], [n, T] | array[1..n] | 55 | | enum | integer/number | 56 | | string, cstring | string | 57 | | ref/object | userdata | 58 | | bool | boolean | 59 | | seq[T] | array[1..n] | 60 | | set[T] | table with unique element | 61 | | pointer | light user data | 62 | | ptr T | light user data | 63 | | range/subrange | integer | 64 | | openArray[T] | table -> seq[T] | 65 | | tuple | assoc-table or array | 66 | | varargs[T] | not supported | 67 | --- 68 | ## **HOW TO USE** 69 | 70 | ### **1. bindEnum** 71 | 72 | ```nimrod 73 | import nimLUA, os 74 | 75 | type 76 | FRUIT = enum 77 | APPLE, BANANA, PEACH, PLUM 78 | SUBATOM = enum 79 | ELECTRON, PROTON, NEUTRON 80 | GENE = enum 81 | ADENINE, CYTOSINE, GUANINE, THYMINE 82 | 83 | proc test(L: PState, fileName: string) = 84 | if L.doFile("test" & DirSep & fileName) != 0.cint: 85 | echo L.toString(-1) 86 | L.pop(1) 87 | else: 88 | echo fileName & " .. OK" 89 | 90 | proc main() = 91 | var L = newNimLua() 92 | L.bindEnum(FRUIT, SUBATOM, GENE) 93 | L.test("test.lua") 94 | L.close() 95 | 96 | main() 97 | ``` 98 | and you can access them at Lua side like this: 99 | 100 | ```Lua 101 | assert(FRUIT.APPLE == 0) 102 | assert(FRUIT.BANANA == 1) 103 | assert(FRUIT.PEACH == 2) 104 | assert(FRUIT.PLUM == 3) 105 | 106 | assert(GENE.ADENINE == 0) 107 | assert(GENE.CYTOSINE == 1) 108 | assert(GENE.GUANINE == 2) 109 | assert(GENE.THYMINE == 3) 110 | 111 | assert(SUBATOM.ELECTRON == 0) 112 | assert(SUBATOM.PROTON == 1) 113 | assert(SUBATOM.NEUTRON == 2) 114 | ``` 115 | 116 | another style: 117 | 118 | ```nimrod 119 | L.bindEnum: 120 | FRUIT 121 | SUBATOM 122 | GENE 123 | ``` 124 | if you want to rename the namespace, you can do this: 125 | ```nimrod 126 | L.bindEnum: 127 | GENE -> "DNA" 128 | SUBATOM -> GLOBAL 129 | ``` 130 | or 131 | ```nimrod 132 | L.bindEnum(GENE -> "DNA", SUBATOM -> GLOBAL) 133 | ``` 134 | 135 | a note on **GLOBAL** and "GLOBAL": 136 | 137 | * **GLOBAL** without quote will not create namespace on Lua side but will bind the symbol in Lua globalspace 138 | * "GLOBAL" with quote, will create "GLOBAL" namespace on Lua side 139 | 140 | now Lua side will become: 141 | 142 | ```Lua 143 | assert(DNA.ADENINE == 0) 144 | assert(DNA.CYTOSINE == 1) 145 | assert(DNA.GUANINE == 2) 146 | assert(DNA.THYMINE == 3) 147 | 148 | assert(ELECTRON == 0) 149 | assert(PROTON == 1) 150 | assert(NEUTRON == 2) 151 | ``` 152 | 153 | ### **2. bindConst** 154 | 155 | ```nimrod 156 | import nimLUA 157 | 158 | const 159 | MANGOES = 10.0 160 | PAPAYA = 11.0'f64 161 | LEMON = 12.0'f32 162 | GREET = "hello world" 163 | connected = true 164 | 165 | proc main() = 166 | var L = newNimLua() 167 | L.bindConst(MANGOES, PAPAYA, LEMON) 168 | L.bindConst: 169 | GREET 170 | connected 171 | L.close() 172 | 173 | main() 174 | ``` 175 | 176 | by default, bindConst will not generate namespace, so how do you create namespace for const? easy: 177 | 178 | ```nimrod 179 | L.bindConst("fruites", MANGOES, PAPAYA, LEMON) 180 | L.bindConst("status"): 181 | GREET 182 | connected 183 | ``` 184 | first argument(actually second) to bindConst will become the namespace. Without namespace, symbol will be put into global namespace 185 | 186 | if you use **GLOBAL** without quote as namespace, it will have no effect 187 | 188 | operator `->` have same meaning with bindEnum, to rename exported symbol on Lua side 189 | 190 | ### **3. bindFunction/bindProc** 191 | 192 | bindFunction is an alias to bindProc, they behave identically 193 | 194 | ```nimrod 195 | import nimLUA 196 | 197 | proc abc(a, b: int): int = 198 | result = a + b 199 | 200 | var L = newNimLua() 201 | L.bindFunction(abc) 202 | L.bindFunction: 203 | abc -> "cba" 204 | L.bindFunction("alphabet", abc) 205 | ``` 206 | 207 | bindFunction more or less behave like bindConst, without namespace, it will bind symbol to global namespace. 208 | 209 | overloaded procs will be automatically resolved by their params count and types 210 | 211 | operator `->` have same meaning with bindEnum, to rename exported symbol on Lua side 212 | 213 | ### **4. bindObject** 214 | 215 | ```nimrod 216 | import nimLUA 217 | 218 | type 219 | Foo = ref object 220 | name: string 221 | 222 | proc newFoo(name: string): Foo = 223 | new(result) 224 | result.name = name 225 | 226 | proc addv(f: Foo, a, b: int): int = 227 | result = 2 * (a + b) 228 | 229 | proc addv(f: Foo, a, b: string): string = 230 | result = "hello: my name is $1, here is my message: $2, $3" % [f.name, a, b] 231 | 232 | proc addk(f: Foo, a, b: int): string = 233 | result = f.name & ": " & $a & " + " & $b & " = " & $(a+b) 234 | 235 | proc main() = 236 | var L = newNimLua() 237 | L.bindObject(Foo): 238 | newFoo -> constructor 239 | addv 240 | addk -> "add" 241 | L.close() 242 | 243 | main() 244 | ``` 245 | this time, Foo will become object name and also namespace name in Lua 246 | 247 | "newFoo `->` constructor" have special meaning, it will create constructor on Lua side with special name: `new`(this is an artefact) 248 | but any other constructor like procs will be treated as constructor too: 249 | 250 | ```nimrod 251 | L.bindObject(Foo): 252 | newFoo #constructor #1 'newFoo' 253 | newFoo -> constructor #constructor #2 'new' 254 | newFoo -> "whatever" #constructor #3 'whatever' 255 | makeFoo -> "constructor" #constructor #4 'constructor' 256 | ``` 257 | 258 | operator `->` on non constructor will behave the same as other binder. 259 | 260 | overloaded proc will be automatically resolved by their params count and types, including overloaded constructor 261 | 262 | destructor will be generated automatically for ref object, none for regular object. 263 | GC safety works as usual on both side of Nim and Lua, no need to worry, except when you manually allocated memory 264 | 265 | ```Lua 266 | local foo = Foo.new("fred") 267 | local m = foo:add(3, 4) 268 | 269 | -- "fred: 3 + 4 = 7" 270 | print(m) 271 | 272 | assert(foo:addv(4,5) == 2 * (4+5)) 273 | 274 | -- "hello: my name is fred, here is my message: abc, nop" 275 | print(foo:addv("abc", "nop")) 276 | ``` 277 | 278 | operator `->` when applied to object, will rename exported symbol on Lua side: 279 | 280 | ```nimrod 281 | L.bindObject(Foo -> "cat"): 282 | newFoo -> constructor 283 | ``` 284 | 285 | on Lua side: 286 | 287 | ```lua 288 | local c = cat.new("fred") --not 'Foo' anymore 289 | ``` 290 | 291 | both **bindObject** and **bindFunction** and **bindConst** can add member to existing namespace 292 | 293 | if you want to turn off this functionality, call **nimLuaOptions**(nloAddMember, false) 294 | 295 | ```nimrod 296 | L.bindObject(Foo): #namespace creation 297 | newFoo -> constructor 298 | 299 | L.bindObject(Foo): #add new member 300 | addv 301 | addk -> "add" 302 | 303 | L.bindFunction("gem"): #namespace "gem" creation 304 | mining 305 | 306 | L.bindFunction("gem"): #add 'polish' member 307 | polish 308 | ``` 309 | 310 | #### **4.1. bindObject without member** 311 | 312 | It's ok to call bindObject without any additional member/method if you want to 313 | register object type and use it later. For example if you want to create your own 314 | object constructor 315 | 316 | #### **4.2. bindObject for opaque C pointer** 317 | 318 | Usually a C library have constructor(s) and destructor function. 319 | The constructor will return an opaque pointer. 320 | 321 | On Nim side, we usually use something like: 322 | 323 | ```Nim 324 | type 325 | CContext* = distinct pointer 326 | 327 | proc createCContext*(): CContext {.cdecl, importc.} 328 | proc deleteCContext*(ctx: CContext) {.cdecl, importc.} 329 | ``` 330 | 331 | Of course this is not an object or ref object, but we treat it as an object in this case. 332 | Therefore bindObject will work like usual. 333 | Only this time, we also need to specify the destructor function using `~` operator. 334 | 335 | ```Nim 336 | L.bindObject(CContext): 337 | createCContext -> "create" 338 | ~deleteCContext 339 | ``` 340 | 341 | ## **PASSING BY REFERENCE** 342 | 343 | Lua basic data types cannot be passed by reference, but Nim does 344 | 345 | if you have something like this in Nim: 346 | 347 | ```nimrod 348 | proc abc(a, b: var int) = 349 | a = a + 1 350 | b = b + 5 351 | ``` 352 | 353 | then on Lua side: 354 | 355 | ```lua 356 | a = 10 357 | b = 20 358 | a, b = abc(a, b) 359 | assert(a == 11) 360 | assert(b == 25) 361 | ``` 362 | 363 | basically, outval will become retval, FIFO ordered 364 | 365 | ## **GENERIC PROC BINDING** 366 | ```nimrod 367 | proc mew[T, K](a: T, b: K): T = 368 | discard 369 | 370 | L.bindFunction: 371 | mew[int, string] 372 | mew[int, string] -> "mewt" 373 | ``` 374 | 375 | ## **CLOSURE BINDING** 376 | ```nimrod 377 | proc main() = 378 | ... 379 | 380 | var test = 1237 381 | proc cl() = 382 | echo test 383 | 384 | L.bindFunction: 385 | [cl] 386 | [cl] -> "clever" 387 | ``` 388 | 389 | ## **GETTER/SETTER** 390 | ```nimrod 391 | type 392 | Ship = object 393 | speed*: int 394 | power: int 395 | 396 | L.bindObject(Ship): 397 | speed(set) 398 | speed(get) -> "currentSpeed" 399 | speed(get, set) -> "velocity" 400 | ``` 401 | 402 | then you can access the object's properties on lua side using '.' (dot) and not ':' (colon) 403 | ```lua 404 | local b = Ship.newShip() 405 | b.speed = 19 406 | assert(b.speed == nil) -- setter only 407 | assert(b.currentSpeed == 19) -- getter only 408 | b.velocity = 20 409 | assert(b.velocity == 20) -- getter & setter 410 | ``` 411 | 412 | ## **HOW TO DEBUG** 413 | 414 | you can call **nimLuaOptions**(nloDebug, true/false) 415 | 416 | ```nimrod 417 | nimLuaOptions(nloDebug, true) #turn on debug 418 | L.bindEnum: 419 | GENE 420 | SUBATOM 421 | 422 | nimLuaOptions(nloDebug, false) #turn off debug mode 423 | L.bindFunction: 424 | machine 425 | engine 426 | ``` 427 | 428 | ## **DANGEROUS ZONE** 429 | lua_error, lua_checkstring, lua_checkint, lua_checkudata and other lua C API that can throw error 430 | are dangerous functions when called from Nim context. lua_error use `longjmp` when compiled to C 431 | or `throw` when compiled to C++. 432 | 433 | Although Nim compiled to C, Nim have it's own stack frame. Calling lua_error and other functions 434 | that can throw error will disrupt Nim stack frame, and application will crash. 435 | 436 | nimLUA avoid using those dangerous functions and and use it's own set of functions that is considerably 437 | safe. those functions are: 438 | 439 | | lua | nimLUA | 440 | |-----|--------| 441 | | lua_error | N/A | 442 | | lua_checkstring | nimCheckString | 443 | | lua_checkinteger | nimCheckInteger | 444 | | lua_checkbool | nimCheckBool | 445 | | lua_checknumber | nimCheckNumber | 446 | | N/A | nimCheckCstring | 447 | | N/A | nimCheckChar | 448 | | lua_newmetatable | nimNewMetaTable | 449 | | lua_getmetatable | nimGetMetaTable | 450 | | lua_checkudata | nimCheckUData | 451 | 452 | ## **Error Handling** 453 | ```Nim 454 | NLError* = object 455 | source: string 456 | currentLine: int 457 | msg: string 458 | 459 | NLErrorFunc* = proc(ctx: pointer, err: NLError) {.nimcall.} 460 | 461 | proc NLSetErrorHandler*(L: PState, errFunc: NLErrorFunc) 462 | proc NLSetErrorContext*(L: PState, errCtx: pointer) 463 | ``` 464 | 465 | This is actually not a real error handler, because you cannot use raise exception. 466 | The purpose of this function is to provide information to user about wrong argument type 467 | passed from Lua to Nim. 468 | 469 | nimLUA already provide a default error handler in case you forget to provide one. 470 | 471 | ## **HOW TO ACCESS LUA CODE FROM NIM?** 472 | 473 | still under development, contributions are welcome 474 | 475 | ## Installation via nimble 476 | > nimble install nimLUA 477 | 478 | ## Override shared library name 479 | 480 | You can use compiler switch `-d:SHARED_LIB_NAME="yourlibname"` 481 | 482 | ```bash 483 | $> nim c -r -d:SHARED_LIB_NAME="lua534.dll" test/test 484 | ``` 485 | -------------------------------------------------------------------------------- /tests/test_features.nim: -------------------------------------------------------------------------------- 1 | import ../nimLUA, os, strutils, unittest 2 | 3 | type 4 | GENE {.pure.} = enum 5 | ADENINE, CYTOSINE, GUANINE, THYMINE 6 | 7 | ATOM = enum 8 | ELECTRON, PROTON, NEUTRON 9 | 10 | FRUIT = enum 11 | APPLE, BANANA, PEACH, PLUM 12 | 13 | `poncho` = enum 14 | `glucho`, `becho`, `type` 15 | 16 | const 17 | MANGOES = 10.0 18 | PAPAYA = 11.0'f64 19 | LEMON = 12.0'f32 20 | MAX_DASH_PATTERN = 8 21 | CATHODE = 10'u8 22 | ANODE = 11'i8 23 | ELECTRON16 = 12'u16 24 | PROTON16 = 13'i16 25 | ELECTRON32 = 14'u32 26 | PROTON32 = 15'i32 27 | ELECTRON64 = 16'u64 28 | PROTON64 = 17'i64 29 | LABEL_STYLE_CH = ["D", "R", "r", "A", "a"] 30 | INFO_FIELD = ["Creator", "Producer", "Title", "Subject", "Author", "Keywords"] 31 | STAIR = [123, 456] 32 | HELIX = [123.4, 567.8] 33 | GREET = "hello world" 34 | connected = true 35 | mime = { 36 | "apple": "fruit", 37 | "men": "woman" 38 | } 39 | 40 | programme = { 41 | 1: "state", 42 | 2: "power", 43 | 3: "result" 44 | } 45 | 46 | proc addv(a: seq[int]): int = 47 | result = 0 48 | for k in a: 49 | result += k 50 | 51 | proc mulv(a,b:int): int = a * b 52 | 53 | proc tpc(s: string): string = 54 | result = s 55 | 56 | proc tpm(s: string, v: string): string = 57 | result = s & " " & v 58 | 59 | proc rootv(u: float): seq[float] = 60 | result = newSeq[float](10) 61 | for i in 0..9: result[i] = u * i.float 62 | 63 | proc testIMPL(L: PState, fileName: string): bool = 64 | if L.doFile("tests" / "luas" / fileName) != 0.cint: 65 | echo L.toString(-1) 66 | L.pop(1) 67 | return 68 | result = true 69 | 70 | template test(L: PState, fileName: string) = 71 | test fileName: 72 | check testIMPL(L, fileName) == true 73 | 74 | proc `++`(a, b: int): int = a + b 75 | 76 | type 77 | Foo = ref object 78 | name: string 79 | 80 | proc newFoo(name: string): Foo = 81 | new(result) 82 | result.name = name 83 | 84 | proc newFoo(a, b: int): Foo = 85 | new(result) 86 | result.name = $a & $b 87 | 88 | proc addv(f: Foo, a, b: int): int = 89 | result = 2 * (a + b) 90 | 91 | proc addv(f: Foo, a, b: string): string = 92 | result = "hello: my name is $1, here is my message: $2, $3" % [f.name, a, b] 93 | 94 | proc addk(f: Foo, a, b: int): string = 95 | result = f.name & ": " & $a & " + " & $b & " = " & $(a+b) 96 | 97 | proc machine(a, b: int): int = 98 | result = a + b 99 | 100 | proc machine(a: int): int = 101 | result = a * 3 102 | 103 | proc machine(a: int, b:string): string = 104 | result = b & $a 105 | 106 | proc machine(a,b,c:string): string = 107 | result = a & b & c 108 | 109 | proc subb(a,b: int): int = a - b 110 | 111 | type 112 | Acid = object 113 | len: int 114 | 115 | Fish = object 116 | len: int 117 | 118 | proc makeAcid(a: int): Acid = 119 | result.len = a 120 | 121 | proc setLen(a: var Acid, len: int) = 122 | a.len = len 123 | 124 | proc getLen(a: Acid): int = 125 | result = a.len 126 | 127 | proc setAcid(a: Foo, b: Acid) = 128 | echo "setAcid" 129 | 130 | proc setAcid2(a: Foo, b: var Acid) = 131 | echo "setAcid" 132 | 133 | proc fishing(len: int): Fish = 134 | result.len = len 135 | 136 | proc grill(a: Fish): string = "grill " & $a.len 137 | proc fry(a: Fish): string = "fry " & $a.len 138 | 139 | proc mining(): string = "mining gem" 140 | proc polish(): string = "polishing gem" 141 | 142 | const numFruits = 3 143 | type 144 | chemArray = array[0..11, int] 145 | geneArray = array[7, GENE] 146 | fruitArray = array[numFruits, FRUIT] 147 | fruitSet = set[FRUIT] 148 | cSet = set[char] 149 | fruitSeq = seq[FRUIT] 150 | geneSeq = seq[GENE] 151 | stringSeq = seq[string] 152 | PGene = ptr GENE 153 | Pint = pointer 154 | PPGene = ptr ptr GENE 155 | 156 | proc chemA(a: chemArray): array[0..5, int] = 157 | for i in 0..result.high: 158 | result[i] = a[i] 159 | 160 | proc geneA(a: geneArray): array[3, GENE] = 161 | for i in 0..result.high: 162 | result[i] = a[i] 163 | 164 | proc fruitA(a: fruitArray): array[numFruits, FRUIT] = 165 | for i in 0..result.high: 166 | result[i] = a[i] 167 | 168 | proc geneB(a: geneArray): geneArray = 169 | for i in 0..result.high: 170 | result[i] = a[i] 171 | 172 | proc geneC(a: array[7, GENE]): geneArray = 173 | for i in 0..result.high: 174 | result[i] = a[i] 175 | 176 | proc fruitC(a: array[numFruits, FRUIT]): fruitArray = 177 | for i in 0..result.high: 178 | result[i] = a[i] 179 | 180 | proc chemC(a: array[0..11, int]): chemArray = 181 | for i in 0..result.high: 182 | result[i] = a[i] 183 | 184 | proc fruitE(a: FRUIT): ATOM = 185 | if a == BANANA: result = PROTON 186 | else: result = NEUTRON 187 | 188 | proc fruitS(a: fruitSet): set[FRUIT] = 189 | for k in a: result.incl k 190 | 191 | proc alphaS(a: cSet): set[char] = 192 | for k in a: result.incl k 193 | 194 | proc fruitSA(a: set[FRUIT]): fruitSet = 195 | for k in a: result.incl k 196 | 197 | proc alphaSA(a: set[char]): cSet = 198 | for k in a: result.incl k 199 | 200 | proc fruitQ(a: fruitSeq): seq[FRUIT] = result = a 201 | proc fruitQA(a: seq[FRUIT]): fruitSeq = result = a 202 | proc geneQ(a: geneSeq): seq[GENE] = result = a 203 | proc geneQA(a: seq[GENE]): geneSeq = result = a 204 | proc stringQ(a: stringSeq): seq[string] = result = a 205 | proc stringQA(a: seq[string]): stringSeq = result = a 206 | 207 | proc seedP(): Pint = cast[Pint](123) 208 | proc geneP(a: PGene): Pint = 209 | result = cast[Pint](a) 210 | 211 | proc intP(a: Pint): PGene = 212 | result = cast[PGene](a) 213 | 214 | proc genePA(a: ptr GENE): pointer = 215 | result = cast[pointer](a) 216 | 217 | proc intPA(a: pointer): ptr GENE = 218 | result = cast[ptr GENE](a) 219 | 220 | proc genePPA(a: PPGene): pointer = 221 | result = cast[pointer](a) 222 | 223 | proc intPPA(a: ptr ptr GENE): pointer = 224 | result = cast[pointer](a) 225 | 226 | proc genePPB(a: pointer): PPGene = 227 | result = cast[PPGEne](a) 228 | 229 | proc intPPB(a: pointer): ptr ptr GENE = 230 | result = cast[ptr ptr GENE](a) 231 | 232 | type 233 | arango = range[0..10] 234 | brango = range[5..100] 235 | 236 | proc trangA(a: arango): brango = 237 | result = a + 5 238 | 239 | proc trangB(a: range[1..7], b: range[0..8]): range[0..100] = 240 | result = a + b 241 | 242 | proc trangC(a, b: int): range[5..50] = 243 | result = a + b + 5 244 | 245 | proc mew[T, K](a: T, b: K): T = 246 | # echo a, " ", b 247 | result = a 248 | 249 | proc opa(arg: openArray[int]): int = 250 | result = 0 251 | for i in arg: 252 | inc(result, i) 253 | 254 | type 255 | myTup = tuple[a: string, b: int] 256 | 257 | proc dino(a: myTup): string = 258 | result = a.a 259 | 260 | proc saurus(a: string): myTup = 261 | result = (a, 10) 262 | 263 | proc croco(a: tuple[a,b:int]): int = 264 | result = a.b 265 | 266 | proc dile(a: int): tuple[a,b: string] = 267 | result = ($a, $a) 268 | 269 | type 270 | Foos = ref object 271 | name: string 272 | 273 | Ship = object 274 | speed*: int 275 | power, engine: int 276 | 277 | proc newFoos(name: string): Foos = 278 | new(result) 279 | result.name = name 280 | 281 | proc getName(a: Foos): string = 282 | result = a.name 283 | 284 | proc newShip(): Ship = 285 | result.speed = 11 286 | result.power = 12 287 | result.engine = 3 288 | 289 | #type 290 | # Car = ref object 291 | # speed: int 292 | # 293 | # Bike = object 294 | # wheel: int 295 | # 296 | # OIL = enum 297 | # GREASE, LUBRICANT, PETROLEUM, GASOLINE, KEROSENE 298 | # 299 | # oilArray = array[0..11, OIL] 300 | # oilSet = set[OIL] 301 | # Poil = ptr OIL 302 | # roil = range[5..10] 303 | 304 | type 305 | BaseFruit = object of RootObj 306 | id: int 307 | Pineapple = object of BaseFruit 308 | name: string 309 | Avocado = ref object of BaseFruit 310 | name: string 311 | 312 | proc newAvocado(name: string, id: int): Avocado = 313 | new(result) 314 | result.name = name 315 | result.id = id 316 | 317 | proc newAvocado(foo: Foos): Avocado = 318 | new(result) 319 | result.name = foo.name 320 | result.id = 154 321 | 322 | proc initPineapple(name: string, id: int): Pineapple = 323 | result.name = name 324 | result.id = id 325 | 326 | proc getId(self: BaseFruit): int = 327 | self.id 328 | 329 | proc getId(self: ref BaseFruit): int = 330 | self.id 331 | 332 | proc getAvocado(self: PineApple, idx: int): Avocado = 333 | result = nil 334 | if idx == 0: result = newAvocado("nanas", 123) 335 | 336 | proc testFromLua(L: PState) = 337 | type 338 | Layout = ref object 339 | name: string 340 | 341 | proc newLayout(): Layout = 342 | new(result) 343 | result.name = "The Layout" 344 | 345 | L.bindObject(Layout): 346 | name(get) 347 | 348 | var lay = newLayout() 349 | 350 | # store Layout reference 351 | L.pushLightUserData(cast[pointer](NLMaxID)) # push key 352 | L.pushLightUserData(cast[pointer](lay)) # push value 353 | L.setTable(LUA_REGISTRYINDEX) # registry[lay.addr] = lay 354 | 355 | # register the only entry point of layout hierarchy to lua 356 | proc layoutProxy(L: PState): cint {.cdecl.} = 357 | getRegisteredType(Layout, mtName, pxName) 358 | var ret = cast[ptr pxName](L.newUserData(sizeof(pxName))) 359 | 360 | # retrieve Layout 361 | L.pushLightUserData(cast[pointer](NLMaxID)) # push key 362 | L.getTable(LUA_REGISTRYINDEX) # retrieve value 363 | ret.ud = cast[Layout](L.toUserData(-1)) # convert to layout 364 | L.pop(1) # remove userdata 365 | GC_ref(ret.ud) 366 | L.nimGetMetaTable(mtName) 367 | discard L.setMetatable(-2) 368 | return 1 369 | 370 | L.pushCfunction(layoutProxy) 371 | L.setGlobal("getLayout") 372 | 373 | #[L.getGlobal("View") # get View table 374 | discard L.pushString("onClick") # push the key "onClick" 375 | L.rawGet(-2) # get the function 376 | if L.isNil(-1): 377 | echo "onClick not found" 378 | else: 379 | var proxy = L.getUD(lay.root) # push first argument 380 | assert(proxy == lay.root) 381 | if L.pcall(1, 0, 0) != 0: 382 | let errorMsg = L.toString(-1) 383 | L.pop(1) 384 | lay.context.otherError(errLua, errorMsg) 385 | L.pop(1) # pop View Table]# 386 | 387 | proc main() = 388 | suite "nimLUA test suite": 389 | var L = newNimLua() 390 | 391 | L.bindConst: 392 | MANGOES 393 | PAPAYA 394 | LEMON 395 | MAX_DASH_PATTERN 396 | CATHODE 397 | ANODE 398 | 399 | L.bindConst("mmm"): 400 | ELECTRON16 401 | PROTON16 402 | ELECTRON32 403 | PROTON32 404 | ELECTRON64 405 | PROTON64 406 | connected 407 | 408 | L.bindConst("ccc"): 409 | LABEL_STYLE_CH 410 | INFO_FIELD 411 | STAIR 412 | HELIX 413 | GREET 414 | mime 415 | programme 416 | 417 | L.bindConst: 418 | MANGOES -> "MANGGA" 419 | PAPAYA -> "PEPAYA" 420 | LEMON -> "JERUK" 421 | 422 | L.bindConst("buah"): 423 | MANGOES -> "MANGGA" 424 | PAPAYA -> "PEPAYA" 425 | LEMON -> "JERUK" 426 | L.test("constants.lua") 427 | 428 | L.bindEnum(GENE) 429 | L.test("single_scoped_enum.lua") 430 | 431 | L.bindEnum(GENE -> "DNA", ATOM -> GLOBAL, FRUIT) 432 | L.test("scoped_and_global_enum.lua") 433 | 434 | L.bindEnum: 435 | ATOM 436 | GENE 437 | FRUIT 438 | `poncho` 439 | L.test("scoped_enum.lua") 440 | 441 | L.bindFunction(mulv, tpc, tpm -> "goodMan") 442 | L.test("free_function.lua") 443 | 444 | L.bindFunction("gum"): 445 | mulv 446 | tpc 447 | tpm -> "goodMan" 448 | `++` 449 | L.test("scoped_function.lua") 450 | 451 | L.bindObject(Foo): 452 | newFoo -> constructor 453 | addv 454 | addk -> "add" 455 | setAcid2 456 | setAcid 457 | newFoo 458 | newFoo -> "whatever" 459 | L.test("fun.lua") 460 | 461 | L.bindFunction("mac"): 462 | machine 463 | L.test("ov_func.lua") 464 | 465 | L.bindObject(Acid): 466 | makeAcid -> constructor 467 | setLen 468 | getLen 469 | 470 | L.bindFunction("acd"): 471 | makeAcid 472 | 473 | L.bindFunction(makeAcid) 474 | L.bindObject(Fish): 475 | fishing -> constructor 476 | 477 | L.bindObject(Fish): 478 | grill 479 | fry 480 | 481 | L.bindFunction("gem", mining) 482 | L.bindFunction("gem", polish) 483 | L.bindConst("gem", ANODE) 484 | 485 | L.bindObject(Fish -> "kakap"): 486 | grill 487 | fry 488 | L.test("regular_object.lua") 489 | 490 | L.bindFunction(GLOBAL): 491 | mulv 492 | 493 | L.bindFunction("GLOBAL", mulv, subb) 494 | 495 | L.bindConst(GLOBAL): 496 | LEMON 497 | 498 | L.bindConst("GLOBAL"): 499 | LEMON 500 | 501 | L.bindEnum: 502 | GENE -> GLOBAL 503 | FRUIT -> "GLOBAL" 504 | L.test("namespace.lua") 505 | 506 | L.bindFunction("arr"): 507 | chemA 508 | geneA 509 | fruitA 510 | geneB 511 | geneC 512 | fruitC 513 | chemC 514 | L.test("array_param_ret.lua") 515 | 516 | L.bindFunction("proto_banana"): 517 | fruitE -> "radiate" 518 | L.test("enum_param_ret.lua") 519 | 520 | L.bindFunction("set"): 521 | fruitS 522 | alphaS 523 | fruitSA 524 | alphaSA 525 | L.test("set_param_ret.lua") 526 | 527 | L.bindFunction("seq"): 528 | fruitQ 529 | fruitQA 530 | geneQ 531 | geneQA 532 | stringQ 533 | stringQA 534 | rootv 535 | L.test("sequence_param_ret.lua") 536 | 537 | L.bindFunction("ptr"): 538 | seedP 539 | geneP 540 | genePA 541 | intP 542 | intPA 543 | genePPA 544 | intPPA 545 | genePPB 546 | intPPB 547 | L.test("ptr_pointer.lua") 548 | 549 | L.bindFunction("range"): 550 | trangA 551 | trangB 552 | trangC -> "haha" 553 | trangC 554 | L.test("range_param_ret.lua") 555 | 556 | var test = 1237 557 | proc cl(): string = 558 | # echo test 559 | result = $test 560 | 561 | L.bindFunction("wow"): 562 | [cl] 563 | [cl] -> "clever" 564 | mew[int, int] 565 | mew[int, string] -> "mewt" 566 | opa 567 | 568 | L.test("generic.lua") 569 | L.test("closure.lua") 570 | L.test("openarray.lua") 571 | 572 | L.bindObject(Foos): 573 | newFoos 574 | getName 575 | name(get,set) 576 | 577 | L.bindObject(Ship): 578 | newShip 579 | speed(set) 580 | speed(get, set) -> "cepat" 581 | engine(set) 582 | power(get) 583 | L.test("getter_setter.lua") 584 | 585 | L.bindFunction("tup"): 586 | dino 587 | saurus 588 | croco 589 | dile 590 | L.test("tuple.lua") 591 | 592 | L.bindObject(Avocado): 593 | newAvocado -> "new" 594 | name(get) 595 | id(get) 596 | getId 597 | 598 | L.bindObject(Pineapple): 599 | initPineapple -> "init" 600 | name(get) 601 | id(get) 602 | getId 603 | getAvocado 604 | L.test("inheritance.lua") 605 | 606 | L.testFromLua() 607 | L.close() 608 | 609 | main() 610 | -------------------------------------------------------------------------------- /nimLUA/lua.nim: -------------------------------------------------------------------------------- 1 | # 2 | #* $Id: lua.h,v 1.283 2012/04/20 13:18:26 roberto Exp $ 3 | #* Lua - A Scripting Language 4 | #* Lua.org, PUC-Rio, Brazil (http://www.lua.org) 5 | #* See Copyright Notice at the end of this file 6 | # 7 | const 8 | LUA_VERSION_MAJOR* = "5" 9 | LUA_VERSION_MINOR* = "3" 10 | LUA_VERSION_NUM* = 531 11 | LUA_VERSION_RELEASE* = "1" 12 | LUA_VERSION* = "Lua " & LUA_VERSION_MAJOR & "." & LUA_VERSION_MINOR 13 | #LUA_RELEASE = LUA_VERSION & "." & LUA_VERSION_RELEASE 14 | #LUA_COPYRIGHT = LUA_RELEASE & " Copyright (C) 1994-2012 Lua.org, PUC-Rio" 15 | #LUA_AUTHORS = "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" 16 | 17 | #{.deadCodeElim: on.} 18 | #when defined(useLuaJIT): 19 | # {.warning: "Lua JIT does not support Lua 5.3 at this time."} 20 | const SHARED_LIB_NAME* {.strdefine.} = "none" 21 | 22 | when SHARED_LIB_NAME != "none": 23 | const LIB_NAME* = SHARED_LIB_NAME 24 | elif not defined(useLuaJIT): 25 | when defined(MACOSX): 26 | const 27 | LIB_NAME* = "liblua53.dylib" 28 | elif defined(FREEBSD): 29 | const 30 | LIB_NAME* = "liblua-5.3.so" 31 | elif defined(UNIX): 32 | const 33 | LIB_NAME* = "liblua53.so" 34 | else: 35 | const 36 | LIB_NAME* = "lua53.dll" 37 | else: 38 | when defined(MACOSX): 39 | const 40 | LIB_NAME* = "libluajit.dylib" 41 | elif defined(FREEBSD): 42 | const 43 | LIB_NAME* = "libluajit-5.1.so" 44 | elif defined(UNIX): 45 | const 46 | LIB_NAME* = "libluajit.so" 47 | else: 48 | const 49 | LIB_NAME* = "luajit.dll" 50 | 51 | const 52 | # mark for precompiled code ('Lua') 53 | LUA_SIGNATURE* = "\x1BLua" 54 | 55 | # option for multiple returns in 'lua_pcall' and 'lua_call' 56 | LUA_MULTRET* = (-1) 57 | 58 | # 59 | #* pseudo-indices 60 | # 61 | #@@ LUAI_MAXSTACK limits the size of the Lua stack. 62 | #* CHANGE it if you need a different limit. This limit is arbitrary; 63 | #* its only purpose is to stop Lua to consume unlimited stack 64 | #* space (and to reserve some numbers for pseudo-indices). 65 | # 66 | when sizeof(int) >= 4: #LUAI_BITSINT >= 32: 67 | const 68 | LUAI_MAXSTACK* = 1000000 69 | else: 70 | const 71 | LUAI_MAXSTACK* = 15000 72 | 73 | # reserve some space for error handling 74 | const 75 | LUAI_FIRSTPSEUDOIDX* = (-LUAI_MAXSTACK - 1000) 76 | LUA_REGISTRYINDEX* = LUAI_FIRSTPSEUDOIDX 77 | 78 | proc upvalueindex*(i: int): int {.inline.} = LUA_REGISTRYINDEX - i 79 | 80 | # thread status 81 | type TThreadStatus* {.size:sizeof(cint).}= enum 82 | Thread_OK = 0, Thread_Yield, Thread_ErrRun, Thread_ErrSyntax, 83 | Thread_ErrMem, Thread_ErrGCMM, Thread_ErrErr 84 | 85 | const 86 | LUA_OK* = 0 87 | LUA_YIELD* = 1 88 | LUA_ERRRUN* = 2 89 | LUA_ERRSYNTAX* = 3 90 | LUA_ERRMEM* = 4 91 | LUA_ERRGCMM* = 5 92 | LUA_ERRERR* = 6 93 | 94 | type 95 | PState* = distinct pointer 96 | lua_State* = PState 97 | TCFunction* = proc (L: PState): cint{.cdecl.} 98 | 99 | #* functions that read/write blocks when loading/dumping Lua chunks 100 | TReader* = proc (L: PState; ud: pointer; sz: var csize_t): cstring {.cdecl.} 101 | TWriter* = proc (L: PState; p: pointer; sz: csize_t; ud: pointer): cint {.cdecl.} 102 | 103 | #* prototype for memory-allocation functions 104 | TAlloc* = proc (ud, p: pointer; osize, nsize: csize_t): pointer 105 | 106 | #* basic types 107 | const 108 | LUA_TNONE* = (-1) 109 | LUA_TNIL* = 0 110 | LUA_TBOOLEAN* = 1 111 | LUA_TLIGHTUSERDATA* = 2 112 | LUA_TNUMBER* = 3 113 | LUA_TSTRING* = 4 114 | LUA_TTABLE* = 5 115 | LUA_TFUNCTION* = 6 116 | LUA_TUSERDATA* = 7 117 | LUA_TTHREAD* = 8 118 | LUA_NUMTAGS* = 9 119 | 120 | type 121 | LUA_TYPE* = enum 122 | LNONE = -1, LNIL, LBOOLEAN, LLIGHTUSERDATA, LNUMBER, 123 | LSTRING, LTABLE, LFUNCTION, LUSERDATA, LTHREAD, LNUMTAGS 124 | 125 | # minimum Lua stack available to a C function 126 | const 127 | LUA_MINSTACK* = 20 128 | 129 | # predefined values in the registry 130 | const 131 | LUA_RIDX_MAINTHREAD* = 1 132 | LUA_RIDX_GLOBALS* = 2 133 | LUA_RIDX_LAST* = LUA_RIDX_GLOBALS 134 | 135 | type 136 | lua_Number* = float64 # type of numbers in Lua 137 | lua_Integer* = int64 # ptrdiff_t \ type for integer functions 138 | 139 | when defined(lua_static_lib): 140 | {.pragma: ilua, cdecl, importc: "lua_$1".} # lua.h 141 | {.pragma: iluaLIB, cdecl, importc: "lua$1".} # lualib.h 142 | {.pragma: iluaL, cdecl, importc: "luaL_$1".} # lauxlib.h 143 | else: 144 | {.push callconv: cdecl, dynlib: LIB_NAME .} # importc: "lua_$1" was not allowed? 145 | {.pragma: ilua, importc: "lua_$1".} # lua.h 146 | {.pragma: iluaLIB, importc: "lua$1".} # lualib.h 147 | {.pragma: iluaL, importc: "luaL_$1".} # lauxlib.h 148 | 149 | proc newstate*(f: TAlloc; ud: pointer): PState {.ilua.} 150 | proc close*(L: PState) {.ilua.} 151 | proc newthread*(L: PState): PState {.ilua.} 152 | proc atpanic*(L: PState; panicf: TCFunction): TCFunction {.ilua.} 153 | proc version*(L: PState): ptr lua_Number {.ilua.} 154 | 155 | # 156 | #* basic stack manipulation 157 | # 158 | proc absindex*(L: PState; idx: cint): cint {.ilua.} 159 | proc gettop*(L: PState): cint {.ilua.} 160 | proc settop*(L: PState; idx: cint) {.ilua.} 161 | proc pushvalue*(L: PState; idx: cint) {.ilua.} 162 | proc rotate*(L: PState; idx, n: cint) {.ilua.} 163 | 164 | proc copy*(L: PState; fromidx: cint; toidx: cint) {.ilua.} 165 | proc checkstack*(L: PState; sz: cint): cint {.ilua.} 166 | proc xmove*(src: PState; dst: PState; n: cint) {.ilua.} 167 | 168 | proc pop*(L: PState; n: cint) {.inline.} = L.settop(-n - 1) 169 | proc insert*(L: PState, idx: cint) {.inline.} = L.rotate(idx, 1) 170 | proc remove*(L: PState, idx: cint) {.inline.} = L.rotate(idx, -1); L.pop(1) 171 | proc replace*(L: PState, idx: cint) {.inline.} = L.copy(-1, idx); L.pop(1) 172 | 173 | # 174 | #* access functions (stack -> C) 175 | # 176 | proc isnumber*(L: PState; idx: cint): cint {.ilua.} 177 | proc isstring*(L: PState; idx: cint): cint {.ilua.} 178 | proc iscfunction*(L: PState; idx: cint): cint {.ilua.} 179 | proc isuserdata*(L: PState; idx: cint): cint {.ilua.} 180 | proc isinteger*(L: PState; idx: cint): cint {.ilua.} 181 | when defined(lua_static_lib): 182 | proc luatype*(L: PState; idx: cint): cint {.cdecl, importc: "lua_type".} 183 | else: 184 | proc luatype*(L: PState; idx: cint): cint {.importc: "lua_type".} 185 | proc typename*(L: PState; tp: cint): cstring {.ilua.} 186 | proc tonumberx*(L: PState; idx: cint; isnum: ptr cint): lua_Number {.ilua.} 187 | proc tointegerx*(L: PState; idx: cint; isnum: ptr cint): lua_Integer {.ilua.} 188 | proc toboolean*(L: PState; idx: cint): cint {.ilua.} 189 | proc tolstring*(L: PState; idx: cint; len: ptr csize_t): cstring {.ilua.} 190 | proc rawlen*(L: PState; idx: cint): csize_t {.ilua.} 191 | proc tocfunction*(L: PState; idx: cint): TCFunction {.ilua.} 192 | proc touserdata*(L: PState; idx: cint): pointer {.ilua.} 193 | proc tothread*(L: PState; idx: cint): PState {.ilua.} 194 | proc topointer*(L: PState; idx: cint): pointer {.ilua.} 195 | 196 | # 197 | #* Comparison and arithmetic functions 198 | # 199 | const 200 | LUA_OPADD* = 0 # ORDER TM 201 | LUA_OPSUB* = 1 202 | LUA_OPMUL* = 2 203 | LUA_OPDIV* = 3 204 | LUA_OPMOD* = 4 205 | LUA_OPPOW* = 5 206 | LUA_OPUNM* = 6 207 | proc arith*(L: PState; op: cint) {.ilua.} 208 | 209 | const 210 | LUA_OPEQ* = 0 211 | LUA_OPLT* = 1 212 | LUA_OPLE* = 2 213 | proc rawequal*(L: PState; idx1: cint; idx2: cint): cint {.ilua.} 214 | proc compare*(L: PState; idx1: cint; idx2: cint; op: cint): cint {.ilua.} 215 | 216 | # 217 | #* push functions (C -> stack) 218 | # 219 | proc pushnil*(L: PState) {.ilua.} 220 | proc pushnumber*(L: PState; n: lua_Number) {.ilua.} 221 | proc pushinteger*(L: PState; n: lua_Integer) {.ilua.} 222 | proc pushlstring*(L: PState; s: cstring; len: csize_t): cstring {.ilua.} 223 | proc pushstring*(L: PState; s: cstring): cstring {.ilua.} 224 | proc pushvfstring*(L: PState; fmt: cstring): cstring {.varargs,ilua.} 225 | proc pushfstring*(L: PState; fmt: cstring): cstring {.varargs,ilua.} 226 | proc pushcclosure*(L: PState; fn: TCFunction; n: cint) {.ilua.} 227 | proc pushboolean*(L: PState; b: cint) {.ilua.} 228 | proc pushlightuserdata*(L: PState; p: pointer) {.ilua.} 229 | proc pushthread*(L: PState): cint {.ilua.} 230 | 231 | # 232 | #* get functions (Lua -> stack) 233 | # 234 | proc getglobal*(L: PState; variable: cstring) {.ilua.} 235 | proc gettable*(L: PState; idx: cint) {.ilua.} 236 | proc getfield*(L: PState; idx: cint; k: cstring) {.ilua.} 237 | proc rawget*(L: PState; idx: cint) {.ilua.} 238 | proc rawgeti*(L: PState; idx: cint; n: cint) {.ilua.} 239 | proc rawgetp*(L: PState; idx: cint; p: pointer) {.ilua.} 240 | proc createtable*(L: PState; narr: cint; nrec: cint) {.ilua.} 241 | proc newuserdata*(L: PState; sz: csize_t): pointer {.ilua.} 242 | proc getmetatable*(L: PState; idx: cint): cint {.ilua.} 243 | proc getuservalue*(L: PState; idx: cint) {.ilua.} 244 | 245 | # 246 | #* set functions (stack -> Lua) 247 | # 248 | proc setglobal*(L: PState; variable: cstring) {.ilua.} 249 | proc settable*(L: PState; idx: cint) {.ilua.} 250 | proc setfield*(L: PState; idx: cint; k: cstring) {.ilua.} 251 | proc rawset*(L: PState; idx: cint) {.ilua.} 252 | proc rawseti*(L: PState; idx: cint; n: lua_Integer) {.ilua.} 253 | proc rawsetp*(L: PState; idx: cint; p: pointer) {.ilua.} 254 | proc setmetatable*(L: PState; objindex: cint): cint {.ilua.} 255 | proc setuservalue*(L: PState; idx: cint) {.ilua.} 256 | 257 | # 258 | #* 'load' and 'call' functions (load and run Lua code) 259 | # 260 | proc callk*(L: PState; nargs, nresults, ctx: cint; k: TCFunction) {.ilua.} 261 | proc call*(L: PState; n, r: cint) {.inline.} = L.callk(n, r, 0, nil) 262 | 263 | #proc getctx*(L: PState; ctx: ptr cint): cint {.ilua.} 264 | proc pcallk*(L: PState; nargs, nresults, errfunc, ctx: cint; k: TCFunction): cint {.ilua.} 265 | proc pcall*(L: PState; nargs, nresults, errFunc: cint): cint {.inline.} = 266 | L.pcallK(nargs, nresults, errFunc, 0, nil) 267 | 268 | proc load*(L: PState; reader: TReader; dt: pointer; chunkname, mode: cstring): cint {.ilua.} 269 | proc dump*(L: PState; writer: TWriter; data: pointer): cint {.ilua.} 270 | 271 | # 272 | #* coroutine functions 273 | # 274 | proc yieldk*(L: PState; nresults: cint; ctx: cint; k: TCFunction): cint {.ilua.} 275 | proc luayield*(L: PState, n: cint): cint {.inline.} = L.yieldk(n, 0, nil) 276 | proc resume*(L: PState; fromL: PState; narg: cint): cint {.ilua.} 277 | proc status*(L: PState): cint {.ilua.} 278 | 279 | # 280 | #* garbage-collection function and options 281 | # 282 | const 283 | LUA_GCSTOP* = 0 284 | LUA_GCRESTART* = 1 285 | LUA_GCCOLLECT* = 2 286 | LUA_GCCOUNT* = 3 287 | LUA_GCCOUNTB* = 4 288 | LUA_GCSTEP* = 5 289 | LUA_GCSETPAUSE* = 6 290 | LUA_GCSETSTEPMUL* = 7 291 | LUA_GCSETMAJORINC* = 8 292 | LUA_GCISRUNNING* = 9 293 | LUA_GCGEN* = 10 294 | LUA_GCINC* = 11 295 | proc gc*(L: PState; what: cint; data: cint): cint {.ilua.} 296 | 297 | # 298 | #* miscellaneous functions 299 | # 300 | proc error*(L: PState): cint {.ilua.} 301 | proc next*(L: PState; idx: cint): cint {.ilua.} 302 | proc concat*(L: PState; n: cint) {.ilua.} 303 | proc len*(L: PState; idx: cint) {.ilua.} 304 | proc getallocf*(L: PState; ud: var pointer): TAlloc {.ilua.} 305 | proc setallocf*(L: PState; f: TAlloc; ud: pointer) {.ilua.} 306 | 307 | # 308 | #* =============================================================== 309 | #* some useful macros 310 | #* =============================================================== 311 | # 312 | proc tonumber*(L: PState; i: cint): lua_Number {.inline.} = L.tonumberx(i, nil) 313 | proc tointeger*(L: PState; i: cint): lua_Integer {.inline.} = L.tointegerx(i, nil) 314 | proc newtable*(L: PState) {.inline.} = L.createtable(0,0) 315 | proc pushcfunction*(L: PState; fn: TCfunction) {.inline.} = L.pushCclosure(fn, 0) 316 | proc register*(L: PState, n: string, f :TCFunction) {.inline.} = 317 | L.pushcfunction(f); L.setglobal(n) 318 | 319 | proc isfunction* (L: PState; n: cint): bool {.inline.} = 320 | L.luatype(n) == LUA_TFUNCTION 321 | 322 | proc istable* (L: PState; n: cint): bool {.inline.} = 323 | L.luatype(n) == LUA_TTABLE 324 | 325 | proc islightuserdata*(L: PState; n: cint): bool {.inline.} = 326 | L.luatype(n) == LUA_TLIGHTUSERDATA 327 | 328 | proc isnil*(L: PState; n: cint): bool {.inline.} = 329 | L.luatype(n) == LUA_TNIL 330 | 331 | proc isboolean*(L: PState; n: cint): bool {.inline.} = 332 | L.luatype(n) == LUA_TBOOLEAN 333 | 334 | proc isthread* (L: PState; n: cint): bool {.inline.} = 335 | L.luatype(n) == LUA_TTHREAD 336 | 337 | proc isnone* (L: PState; n: cint): bool {.inline.} = 338 | L.luatype(n) == LUA_TNONE 339 | 340 | proc isnoneornil*(L: PState; n: cint): bool {.inline.} = 341 | L.luatype(n) <= 0 342 | 343 | proc pushliteral*(L: PState, s: string): cstring {.inline, discardable.} = 344 | L.pushlstring(s, s.len.csize_t) 345 | 346 | proc pushglobaltable*(L: PState) {.inline.} = 347 | L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) 348 | 349 | proc tostring*(L: PState; index: cint): string = 350 | var len: csize_t = 0 351 | var s = L.tolstring(index, addr(len)) 352 | result = newString(len) 353 | copyMem(result.cstring, s, len.int) 354 | 355 | proc tobool*(L: PState; index: cint): bool = 356 | result = if L.toboolean(index) == 1: true else: false 357 | 358 | proc gettype*(L: PState, index: int): LUA_TYPE = 359 | result = LUA_TYPE(L.luatype(index.cint)) 360 | 361 | # 362 | #* {====================================================================== 363 | #* Debug API 364 | #* ======================================================================= 365 | # 366 | # 367 | #* Event codes 368 | # 369 | const 370 | LUA_HOOKCALL* = 0 371 | LUA_HOOKRET* = 1 372 | LUA_HOOKLINE* = 2 373 | LUA_HOOKCOUNT* = 3 374 | LUA_HOOKTAILCALL* = 4 375 | # 376 | #* Event masks 377 | # 378 | const 379 | LUA_MASKCALL* = (1 shl LUA_HOOKCALL) 380 | LUA_MASKRET* = (1 shl LUA_HOOKRET) 381 | LUA_MASKLINE* = (1 shl LUA_HOOKLINE) 382 | LUA_MASKCOUNT* = (1 shl LUA_HOOKCOUNT) 383 | # activation record 384 | 385 | 386 | #@@ LUA_IDSIZE gives the maximum size for the description of the source 387 | #@* of a function in debug information. 388 | #* CHANGE it if you want a different size. 389 | # 390 | const 391 | LUA_IDSIZE* = 60 392 | 393 | # Functions to be called by the debugger in specific events 394 | type 395 | PDebug* = ptr lua.TDebug 396 | TDebug* {.pure, final.} = object 397 | event*: cint 398 | name*: cstring # (n) 399 | namewhat*: cstring # (n) 'global', 'local', 'field', 'method' 400 | what*: cstring # (S) 'Lua', 'C', 'main', 'tail' 401 | source*: cstring # (S) 402 | currentline*: cint # (l) 403 | linedefined*: cint # (S) 404 | lastlinedefined*: cint # (S) 405 | nups*: cuchar # (u) number of upvalues 406 | nparams*: cuchar # (u) number of parameters 407 | isvararg*: char # (u) 408 | istailcall*: char # (t) 409 | short_src*: array[LUA_IDSIZE, char] # (S) \ # private part 410 | i_ci: pointer#ptr CallInfo # active function 411 | 412 | 413 | type 414 | lua_Hook* = proc (L: PState; ar: PDebug) {.cdecl.} 415 | proc getstack*(L: PState; level: cint; ar: PDebug): cint {.ilua.} 416 | proc getinfo*(L: PState; what: cstring; ar: PDebug): cint {.ilua.} 417 | proc getlocal*(L: PState; ar: PDebug; n: cint): cstring {.ilua.} 418 | proc setlocal*(L: PState; ar: PDebug; n: cint): cstring {.ilua.} 419 | proc getupvalue*(L: PState; funcindex: cint; n: cint): cstring {.ilua.} 420 | proc setupvalue*(L: PState; funcindex: cint; n: cint): cstring {.ilua.} 421 | proc upvalueid*(L: PState; fidx: cint; n: cint): pointer {.ilua.} 422 | proc upvaluejoin*(L: PState; fidx1: cint; n1: cint; fidx2: cint; n2: cint) {.ilua.} 423 | proc sethook*(L: PState; fn: lua_Hook; mask: cint; count: cint): cint {.ilua.} 424 | proc gethook*(L: PState): lua_Hook {.ilua.} 425 | proc gethookmask*(L: PState): cint {.ilua.} 426 | proc gethookcount*(L: PState): cint {.ilua.} 427 | 428 | # }====================================================================== 429 | #***************************************************************************** 430 | # Copyright (C) 1994-2012 Lua.org, PUC-Rio. 431 | # 432 | # Permission is hereby granted, free of charge, to any person obtaining 433 | # a copy of this software and associated documentation files (the 434 | # "Software"), to deal in the Software without restriction, including 435 | # without limitation the rights to use, copy, modify, merge, publish, 436 | # distribute, sublicense, and/or sell copies of the Software, and to 437 | # permit persons to whom the Software is furnished to do so, subject to 438 | # the following conditions: 439 | # 440 | # The above copyright notice and this permission notice shall be 441 | # included in all copies or substantial portions of the Software. 442 | # 443 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 444 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 445 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 446 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 447 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 448 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 449 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 450 | #**************************************************************************** 451 | 452 | 453 | 454 | 455 | # 456 | #* $Id: lualib.h,v 1.43 2011/12/08 12:11:37 roberto Exp $ 457 | #* Lua standard libraries 458 | #* See Copyright Notice in lua.h 459 | # 460 | 461 | proc open_base*(L: PState): cint {.iluaLIB.} 462 | const 463 | LUA_COLIBNAME* = "coroutine" 464 | proc open_coroutine*(L: PState): cint {.iluaLIB.} 465 | const 466 | LUA_TABLIBNAME* = "table" 467 | proc open_table*(L: PState): cint {.iluaLIB.} 468 | const 469 | LUA_IOLIBNAME* = "io" 470 | proc open_io*(L: PState): cint {.iluaLIB.} 471 | const 472 | LUA_OSLIBNAME* = "os" 473 | proc open_os*(L: PState): cint {.iluaLIB.} 474 | const 475 | LUA_STRLIBNAME* = "string" 476 | proc open_string*(L: PState): cint {.iluaLIB.} 477 | const 478 | LUA_UTF8LIBNAME* = "utf8" 479 | proc open_utf8*(L: PState): cint {.iluaLIB.} 480 | const 481 | LUA_BITLIBNAME* = "bit32" 482 | proc open_bit32*(L: PState): cint {.iluaLIB.} 483 | const 484 | LUA_MATHLIBNAME* = "math" 485 | proc open_math*(L: PState): cint {.iluaLIB.} 486 | const 487 | LUA_DBLIBNAME* = "debug" 488 | proc open_debug*(L: PState): cint {.iluaLIB.} 489 | const 490 | LUA_LOADLIBNAME* = "package" 491 | proc open_package*(L: PState): cint {.iluaLIB.} 492 | # open all previous libraries 493 | proc openlibs*(L: PState) {.iluaL.} 494 | 495 | when not defined(lua_assert): 496 | template lua_assert*(x: typed) = 497 | (cast[nil](0)) 498 | 499 | 500 | # 501 | #* $Id: lauxlib.h,v 1.120 2011/11/29 15:55:08 roberto Exp $ 502 | #* Auxiliary functions for building Lua libraries 503 | #* See Copyright Notice in lua.h 504 | # 505 | 506 | # extra error code for `luaL_load' 507 | const 508 | LUA_ERRFILE* = Thread_ErrErr.cint + 1'i32 #(LUA_ERRERR + 1) 509 | 510 | type 511 | luaL_Reg* {.pure, final.} = object 512 | name*: cstring 513 | fn*: TCFunction 514 | 515 | const 516 | LUAL_NUMSIZES = (sizeof(lua_Integer)*16 + sizeof(lua_Number)) 517 | 518 | ### IMPORT FROM "luaL_$1" 519 | proc checkversion*(L: PState; ver: lua_Number; sz: csize_t) {.importc: "luaL_checkversion_".} 520 | proc checkversion*(L: PState) {.inline.} = L.checkversion(LUA_VERSION_NUM, LUAL_NUMSIZES) 521 | 522 | proc getmetafield*(L: PState; obj: cint; e: cstring): cint {.iluaL.} 523 | proc callmeta*(L: PState; obj: cint; e: cstring): cint {.iluaL.} 524 | #proc tolstring*(L: PState; idx: cint; len: ptr csize_t): cstring {.importc: "luaL_tolstring".} 525 | # ^ duplicate? 526 | proc argerror*(L: PState; numarg: cint; extramsg: cstring): cint {.iluaL.} 527 | proc checklstring*(L: PState; arg: cint; len: ptr csize_t): cstring {.iluaL.} 528 | proc optlstring*(L: PState; arg: cint; def: cstring; len: ptr csize_t): cstring {.iluaL.} 529 | proc checknumber*(L: PState; arg: cint): lua_Number {.iluaL.} 530 | proc optnumber*(L: PState; arg: cint; def: lua_Number): lua_Number {.iluaL.} 531 | proc checkinteger*(L: PState; arg: cint): lua_Integer {.iluaL.} 532 | proc optinteger*(L: PState; arg: cint; def: lua_Integer): lua_Integer {.iluaL.} 533 | proc checkstack*(L: PState; sz: cint; msg: cstring) {.iluaL.} 534 | proc checktype*(L: PState; arg: cint; t: cint) {.iluaL.} 535 | proc checkany*(L: PState; arg: cint) {.iluaL.} 536 | proc newmetatable*(L: PState; tname: cstring): cint {.iluaL.} 537 | proc setmetatable*(L: PState; tname: cstring) {.iluaL.} 538 | proc testudata*(L: PState; ud: cint; tname: cstring): pointer {.iluaL.} 539 | proc checkudata*(L: PState; ud: cint; tname: cstring): pointer {.iluaL.} 540 | proc where*(L: PState; lvl: cint) {.iluaL.} 541 | proc error*(L: PState; fmt: cstring): cint {.varargs, iluaL.} 542 | proc checkoption*(L: PState; arg: cint; def: cstring; lst: var cstring): cint {.iluaL.} 543 | proc fileresult*(L: PState; stat: cint; fname: cstring): cint {.iluaL.} 544 | proc execresult*(L: PState; stat: cint): cint {.iluaL.} 545 | 546 | # pre-defined references 547 | const 548 | LUA_NOREF* = (- 2) 549 | LUA_REFNIL* = (- 1) 550 | proc luaref*(L: PState; t: cint): cint {.iluaL, importc:"luaL_ref".} 551 | proc unref*(L: PState; t: cint; iref: cint) {.iluaL.} 552 | proc loadfilex*(L: PState; filename: cstring; mode: cstring): cint {.iluaL.} 553 | proc loadfile*(L: PState; filename: cstring): cint = L.loadfilex(filename, nil) 554 | 555 | proc loadbufferx*(L: PState; buff: cstring; sz: csize_t; name, mode: cstring): cint {.iluaL.} 556 | proc loadstring*(L: PState; s: cstring): cint {.iluaL.} 557 | proc newstate*(): PState {.iluaL.} 558 | proc llen*(L: PState; idx: cint): cint {.iluaL, importc:"luaL_len".} 559 | proc gsub*(L: PState; s: cstring; p: cstring; r: cstring): cstring {.iluaL.} 560 | proc setfuncs*(L: PState; L2: ptr luaL_Reg; nup: cint) {.iluaL.} 561 | proc getsubtable*(L: PState; idx: cint; fname: cstring): cint {.iluaL.} 562 | proc traceback*(L: PState; L1: PState; msg: cstring; level: cint) {.iluaL.} 563 | proc requiref*(L: PState; modname: cstring; openf: TCFunction; glb: cint) {.iluaL.} 564 | # 565 | #* =============================================================== 566 | #* some useful macros 567 | #* =============================================================== 568 | # 569 | 570 | proc newlibtable*(L: PState, arr: openArray[luaL_Reg]){.inline.} = 571 | createtable(L, 0, (arr.len - 1).cint) 572 | 573 | proc newlib*(L: PState, arr: var openArray[luaL_Reg]) {.inline.} = 574 | newlibtable(L, arr) 575 | setfuncs(L, cast[ptr luaL_reg](addr(arr)), 0) 576 | 577 | proc argcheck*(L: PState, cond: bool, numarg: int, extramsg: string) {.inline.} = 578 | if not cond: discard L.argerror(numarg.cint, extramsg) 579 | 580 | proc checkstring*(L: PState, n: int): cstring {.inline.} = L.checklstring(n.cint, nil) 581 | proc optstring*(L: PState, n: int, d: string): cstring {.inline.} = L.optlstring(n.cint, d, nil) 582 | 583 | proc checkint*(L: PState, n: lua_Integer): lua_Integer {.inline.} = L.checkinteger(n.cint) 584 | proc optint*(L: PState, n, d: lua_Integer): lua_Integer {.inline.} = L.optinteger(n.cint, d) 585 | proc checklong*(L: PState, n: int, d: clong): clong {.inline.} = cast[clong](L.checkinteger(n.cint)) 586 | proc optlong*(L: PState, n: int, d: lua_Integer): clong = cast[clong](L.optinteger(n.cint, d)) 587 | 588 | proc Ltypename*(L: PState, i: cint): cstring {.inline.} = 589 | L.typename(L.luatype(i)) 590 | 591 | proc dofile*(L: PState, file: string): cint {.inline, discardable.} = 592 | result = L.loadfile(file) 593 | if result == LUA_OK: 594 | result = L.pcall(0, LUA_MULTRET, 0) 595 | 596 | proc dostring*(L: PState, s: string): cint {.inline, discardable.} = 597 | result = L.loadstring(s) 598 | if result == LUA_OK: 599 | result = L.pcall(0, LUA_MULTRET, 0) 600 | 601 | proc getmetatable*(L: PState, s: string) {.inline.} = 602 | L.getfield(LUA_REGISTRYINDEX, s) 603 | 604 | template opt*(L: PState, f: TCFunction, n, d: typed) = 605 | if L.isnoneornil(n): d else: L.f(n) 606 | 607 | proc loadbuffer*(L: PState, buff: string, name: string): cint = 608 | L.loadbufferx(buff, buff.len.csize_t, name, nil) 609 | 610 | # 611 | #@@ TBufferSIZE is the buffer size used by the lauxlib buffer system. 612 | #* CHANGE it if it uses too much C-stack space. 613 | # 614 | const 615 | Lua_BufferSIZE* = 8192'i32 # BUFSIZ\ 616 | ## COULD NOT FIND BUFSIZE ?? on my machine this is 8192 617 | # 618 | #* {====================================================== 619 | #* Generic Buffer manipulation 620 | #* ======================================================= 621 | # 622 | type 623 | PBuffer* = ptr TBuffer 624 | TBuffer* {.pure, final.} = object 625 | b*: cstring # buffer address 626 | size*: csize_t # buffer size 627 | n*: csize_t # number of characters in buffer 628 | L*: PState 629 | initb*: array[Lua_BufferSIZE, char] # initial buffer 630 | 631 | proc buffinit*(L: PState; B: PBuffer) {.iluaL.} 632 | proc prepbuffsize*(B: PBuffer; sz: csize_t): cstring {.iluaL.} 633 | proc addlstring*(B: PBuffer; s: cstring; len: csize_t) {.iluaL.} 634 | proc addstring*(B: PBuffer; s: cstring) {.iluaL.} 635 | proc addvalue*(B: PBuffer) {.iluaL.} 636 | proc pushresult*(B: PBuffer) {.iluaL.} 637 | proc pushresultsize*(B: PBuffer; sz: csize_t) {.iluaL.} 638 | proc buffinitsize*(L: PState; B: PBuffer; sz: csize_t): cstring {.iluaL.} 639 | proc addchar*(B: PBuffer, c: char) = 640 | if B.n < B.size: discard B.prepbuffsize(1) 641 | B.b[B.n] = c 642 | inc B.n 643 | 644 | proc addsize*(B: PBuffer, s: int) {.inline.} = inc(B.n, s) 645 | proc prepbuffer*(B: PBuffer): cstring {.inline.} = prepbuffsize(B, Lua_BufferSIZE.csize_t) 646 | 647 | # }====================================================== 648 | # 649 | #* {====================================================== 650 | #* File handles for IO library 651 | #* ======================================================= 652 | # 653 | # 654 | #* A file handle is a userdata with metatable 'LUA_FILEHANDLE' and 655 | #* initial structure 'luaL_Stream' (it may contain other fields 656 | #* after that initial structure). 657 | # 658 | const 659 | LUA_FILEHANDLE* = "FILE*" 660 | type 661 | luaL_Stream* {.pure, final.} = object 662 | f*: File # stream (NULL for incompletely created streams) 663 | closef*: TCFunction # to close stream (NULL for closed streams) 664 | 665 | # }====================================================== 666 | # compatibility with old module system 667 | when defined(LUA_COMPAT_MODULE): 668 | proc pushmodule*(L: PState; modname: cstring; sizehint: cint){.iluaL.} 669 | proc openlib*(L: PState; libname: cstring; ls: ptr luaL_Reg; nup: cint){.iluaL.} 670 | proc register*(L: PState, n: string, ls: var openArray[luaL_Reg]) {.inline.} = 671 | L.openlib(n, cast[ptr luaL_reg](addr(ls)), 0) 672 | 673 | when isMainModule: 674 | #import lua52 675 | import strutils 676 | 677 | echo "Starting Lua" 678 | var L = newState() 679 | 680 | proc myPanic(L: PState): cint {.cdecl.} = 681 | echo "panic" 682 | 683 | #discard L.atpanic(myPanic) 684 | 685 | var regs = [ 686 | luaL_Reg(name: "abc", fn: myPanic), 687 | luaL_Reg(name: nil, fn: nil) 688 | ] 689 | 690 | L.newlib(regs) 691 | L.setglobal("mylib") 692 | echo L.dostring("mylib.abc()") 693 | 694 | #echo "Loading libraries" 695 | #L.openLibs 696 | # 697 | #when defined (Lua_REPL): 698 | # import rdstdin 699 | # echo "To leave the REPL, hit ^D, type !!!, or call quit()" 700 | # 701 | # var line: string = "" 702 | # while readlineFromStdin ("> ", line): 703 | # 704 | # if line == "!!!": break 705 | # 706 | # let result = L.loadString(line).TThreadStatus 707 | # if result == Thread_OK: 708 | # let result = L.pcall(0, LUA_MULTRET, 0).TThreadStatus 709 | # case result 710 | # of Thread_OK: discard 711 | # else: echo result 712 | # else: 713 | # echo result 714 | # 715 | #else: 716 | # proc testFunc (L: PState): cint {.cdecl.} = 717 | # #foo 718 | # echo "Hello thar" 719 | # 720 | # echo "Setting testFunc" 721 | # L.pushCfunction testFunc 722 | # L.setGlobal "testFunc" 723 | # 724 | # const LuaScript = "testFunc()" 725 | # echo "Loading script: \"\"\"\L$1\L\"\"\"".format(LuaScript) 726 | # 727 | # let result = L.loadString(LuaScript).TThreadStatus 728 | # echo "return: ", result 729 | # 730 | # if result == Thread_OK: 731 | # echo "Running script" 732 | # let result = L.pcall (0, LUA_MULTRET, 0) 733 | 734 | echo "Closing Lua state" 735 | #L.close 736 | 737 | 738 | -------------------------------------------------------------------------------- /nimLUA.nim: -------------------------------------------------------------------------------- 1 | # nimLUA 2 | # glue code generator to bind Nim and Lua together using Nim's powerful macro 3 | # 4 | # Copyright (c) 2015 Andri Lim 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | # 25 | #------------------------------------- 26 | 27 | import macros, nimLUA/lua, strutils 28 | export lua, macros 29 | 30 | type 31 | BindKind* = enum 32 | isNothing 33 | isClosure 34 | isDestructor 35 | 36 | bindDesc* = ref object 37 | node*: NimNode #this is nnkSym or nnkClosedSymChoice node generated by bindSym, 38 | # bindXXXImpl will then use getImpl to get the AST body of proc/const/enum/etc implementation 39 | name*: string #newname that will be exported, taken from abc -> "newname", or oldname if no newname supplied 40 | lhsKind*: NimNodeKind #op '->' lhs kind, it could be nnkIdent or nnkAccQuoted 41 | rhsKind*: NimNodeKind #op '->' rhs kind, it could be nnkNone or nnkStrLit or nnkIdent 42 | bindKind*: BindKind 43 | genericParams*: seq[NimNode] 44 | 45 | propDesc = tuple [ 46 | node: string, 47 | name: string, 48 | lhsKind: NimNodeKind, 49 | getter: bool, 50 | setter: bool 51 | ] 52 | 53 | proxyDesc* = object 54 | luaCtx* : string 55 | libName* : NimNode 56 | subject* : NimNode 57 | bindList*: seq[bindDesc] 58 | symList* : seq[NimNode] 59 | propList*: seq[propDesc] 60 | 61 | argDesc = object 62 | mName, mType, mVal: NimNode 63 | 64 | bindFlag = enum 65 | nlbUseLib 66 | nlbRegisterObject 67 | nlbRegisterClosure 68 | nlbRegisterGeneric 69 | 70 | bindFlags = set[bindFlag] 71 | 72 | ovProcElem = ref object 73 | retType: NimNode 74 | params: seq[argDesc] 75 | 76 | ovProc = ref object 77 | numArgs: int 78 | procs: seq[ovProcElem] 79 | 80 | ovList = seq[ovProc] 81 | 82 | ovFlag = enum 83 | ovfUseObject 84 | ovfUseRet 85 | ovfConstructor 86 | 87 | ovFlags = set[ovFlag] 88 | 89 | nlOptions* = enum 90 | nloNone 91 | nloDebug 92 | nloAddMember 93 | 94 | NLError* = object 95 | source: string 96 | currentLine: int 97 | msg: string 98 | 99 | NLErrorFunc* = proc(ctx: pointer, err: NLError) {.nimcall.} 100 | 101 | let globalClosure {.compileTime.} = "_gCLV" 102 | 103 | const 104 | IDRegion = 0xEF 105 | IDErrorContext = IDRegion - 1 106 | IDErrorFunc = IDRegion - 2 107 | NLMaxID* = 0xFFF 108 | 109 | template luaError*(x: PState, m: string): untyped = 110 | lua.error(x, m) 111 | 112 | template newuserdata*(L: PState, sz: int): pointer = 113 | newuserdata(L, sz.csize_t) 114 | 115 | template pushlstring*(L: PState, s: cstring, len: int): cstring = 116 | pushlstring(L, s, len.csize_t) 117 | 118 | proc isStrictString*(L: PState, idx: int): bool {.inline.} = 119 | luaType(L, idx.cint) == LUA_TSTRING.cint 120 | 121 | #counter that will be used to generate unique intermediate macro name 122 | #and avoid name collision 123 | var 124 | macroCount {.compileTime.} = 0 125 | proxyCount {.compileTime.} = 0 126 | regsCount {.compileTime.} = 0 127 | nameList {.compileTime.} = newSeq[string]() 128 | outValList {.compileTime.} : seq[string] 129 | gContext {.compileTime.} = "" 130 | gOptions {.compileTime.} = {nloAddMember} 131 | objectMTList {.compileTime.} = newSeq[string]() 132 | 133 | proc convOpt(a: NimNode): nlOptions {.compileTime.} = 134 | case $a: 135 | of "nloDebug": result = nloDebug 136 | of "nloAddMember": result = nloAddMember 137 | else: result = nloNone 138 | 139 | macro nimLuaOptions*(opt: nlOptions, mode: bool): untyped = 140 | if $mode == "true": 141 | gOptions.incl convOpt(opt) 142 | else: 143 | gOptions.excl convOpt(opt) 144 | 145 | result = newEmptyNode() 146 | 147 | proc toString(c: LUA_TYPE): string = 148 | const typeName = ["NIL", "BOOLEAN", "LIGHTUSERDATA", "NUMBER", "STRING", 149 | "TABLE", "FUNCTION", "USERDATA", "THREAD", "NUMTAGS"] 150 | if c in {LNIL..LNUMTAGS}: 151 | return typeName[ord(c)] 152 | elif c == LNONE: 153 | return "NONE" 154 | else: 155 | return "INVALID" 156 | 157 | proc nimDebug(L: PState, idx: cint, eType: string) = 158 | var dbg: TDebug 159 | if L.getStack(1, dbg.addr) != 0: 160 | if L.getInfo("Sln", dbg.addr) != 0: 161 | let gType = L.getType(idx).toString() 162 | let err = NLError(source: $dbg.source, currentLine: dbg.currentLine, 163 | msg: "expected `$1`, got `$2`" % [eType, $gType]) 164 | L.pushLightUserData(cast[pointer](IDErrorContext)) 165 | L.rawGet(LUA_REGISTRYINDEX) # get correct error context 166 | let errCtx = L.toUserData(-1) 167 | L.pushLightUserData(cast[pointer](IDErrorFunc)) 168 | L.rawGet(LUA_REGISTRYINDEX) # get correct error function 169 | let errFunc = cast[NLErrorFunc](L.toUserData(-1)) 170 | L.pop(2) 171 | errFunc(errCtx, err) 172 | 173 | #inside macro, const bool become nnkIntLit, that's why we need to use 174 | #this trick to test for bool type using 'when internalTestForBOOL(n)' 175 | proc internalTestForBOOL*[T](a: T): bool {.compileTime.} = 176 | when a is bool: result = true 177 | else: result = false 178 | 179 | proc parseCode(s: string): NimNode {.compileTime.} = 180 | result = parseStmt(s) 181 | if nloDebug in gOptions: echo s 182 | 183 | #flatten formal param into seq 184 | proc paramsToArgListBasic(params: NimNode, start = 1): seq[argDesc] {.compileTime.} = 185 | var argList = newSeq[argDesc]() 186 | for i in start..params.len-1: 187 | let arg = params[i] 188 | let mType = arg[arg.len - 2] 189 | let mVal = arg[arg.len - 1] 190 | for j in 0..arg.len-3: 191 | argList.add(argDesc(mName: arg[j], mType: mType, mVal: mVal)) 192 | result = argList 193 | 194 | proc paramsToArgList(params: NimNode, subs: seq[NimNode], templateParam: NimNode): seq[argDesc] {.compileTime.} = 195 | var p = paramsToArgListBasic(params) 196 | for i in 0..templateParam.len-1: 197 | for j in 0..p.len-1: 198 | if p[j].mType.kind == nnkVarTy: 199 | if p[j].mType[0].kind == nnkBracketExpr: 200 | if $p[j].mType[0][1] == $templateParam[i]: 201 | if i < subs.len: p[j].mType[0][1] = subs[i] 202 | else: 203 | if $p[j].mType[0] == $templateParam[i]: 204 | if i < subs.len: p[j].mType[0] = subs[i] 205 | else: 206 | if $p[j].mType == $templateParam[i]: 207 | if i < subs.len: p[j].mType = subs[i] 208 | 209 | result = p 210 | 211 | proc replaceRet(ret: NimNode, subs: seq[NimNode], templateParam: NimNode): NimNode {.compileTime.} = 212 | for i in 0..templateParam.len-1: 213 | if ret.kind != nnkEmpty: 214 | if $ret == $templateParam[i]: 215 | if i < subs.len: return subs[i] 216 | result = ret 217 | 218 | proc newBindDesc*(node: NimNode, name: string, lhsKind, rhsKind: NimNodeKind, 219 | kind = isNothing, genericParams: seq[NimNode] = @[]): bindDesc {.compileTime.} = 220 | new(result) 221 | result.node = node 222 | result.name = name 223 | result.lhsKind = lhsKind 224 | result.rhsKind = rhsKind 225 | result.bindKind = kind 226 | result.genericParams = genericParams 227 | 228 | proc collectGenericParam(p: bindDesc, n: NimNode) {.compileTime.} = 229 | for i in 1..n.len-1: 230 | p.genericParams.add n[i] 231 | 232 | #split something like 'ident -> "newName"' into tuple 233 | proc splitElem(n: NimNode, opts: bindFlags, proxyName: string): bindDesc {.compileTime.} = 234 | let 235 | op = n[0] 236 | lhs = n[1] 237 | rhs = n[2] 238 | registerObject = nlbRegisterobject in opts 239 | registerClosure = nlbRegisterClosure in opts 240 | registerGeneric = nlbRegisterGeneric in opts 241 | 242 | if $op != "->": 243 | error("wrong operator, must be '->' and not '" & $op & "'") 244 | if lhs.kind notin {nnkIdent, nnkAccQuoted, nnkCall, nnkBracket, nnkBracketExpr}: 245 | error("param must be an identifier and not " & $lhs.kind) 246 | if not registerObject and lhs.kind == nnkCall: 247 | error("getter/setter not available in bind$1" % [proxyName]) 248 | if not registerClosure and lhs.kind == nnkBracket: 249 | error("closure not available in bind$1" % [proxyName]) 250 | if not registerGeneric and lhs.kind == nnkBracketExpr: 251 | error("generic not available in bind$1" % [proxyName]) 252 | if rhs.kind notin {nnkStrLit, nnkIdent}: 253 | error("alias must be string literal and not " & $rhs.kind) 254 | 255 | if lhs.kind == nnkAccQuoted: 256 | result = newBindDesc(lhs[0], $rhs, lhs.kind, rhs.kind) 257 | elif lhs.kind == nnkBracket: 258 | if lhs[0].kind == nnkAccQuoted: 259 | result = newBindDesc(lhs[0][0], $rhs, lhs[0].kind, rhs.kind, isClosure) 260 | else: 261 | result = newBindDesc(lhs[0], $rhs, lhs.kind, rhs.kind, isClosure) 262 | elif lhs.kind == nnkBracketExpr: 263 | if lhs[0].kind == nnkAccQuoted: 264 | result = newBindDesc(lhs[0][0], $rhs, lhs[0].kind, rhs.kind) 265 | else: 266 | result = newBindDesc(lhs[0], $rhs, lhs.kind, rhs.kind) 267 | collectGenericParam(result, lhs) 268 | else: 269 | result = newBindDesc(lhs, $rhs, lhs.kind, rhs.kind) 270 | 271 | #helper proc to flatten nnkStmtList 272 | proc unwindList(arg: NimNode, elemList: var seq[bindDesc], opts: bindFlags, proxyName: string) {.compileTime.} = 273 | let 274 | registerObject = nlbRegisterobject in opts 275 | registerClosure = nlbRegisterClosure in opts 276 | registerGeneric = nlbRegisterGeneric in opts 277 | 278 | for i in 0..arg.len-1: 279 | let n = arg[i] 280 | case n.kind: 281 | of nnkIdent: 282 | let elem = newBindDesc(n, $n, n.kind, nnkNone) 283 | elemList.add elem 284 | of nnkAccQuoted: 285 | let elem = newBindDesc(n[0], "`" & $n[0] & "`", n.kind, nnkNone) 286 | elemList.add elem 287 | of nnkInfix: 288 | elemList.add splitElem(n, opts, proxyName) 289 | of nnkCall: 290 | if not registerObject: 291 | error("getter/setter not available in bind$1" % [proxyName]) 292 | let elem = newBindDesc(n, $n[0], n.kind, nnkNone) 293 | elemList.add elem 294 | of nnkBracket: 295 | if not registerClosure: 296 | error("closure not available in bind$1" % [proxyName]) 297 | let elem = if n[0].kind == nnkAccQuoted: 298 | newBindDesc(n[0][0], "`" & $n[0][0] & "`", n[0].kind, nnkNone, isClosure) 299 | else: 300 | newBindDesc(n[0], $n[0], n.kind, nnkNone, isClosure) 301 | elemList.add elem 302 | of nnkBracketExpr: 303 | if not registerGeneric: 304 | error("generic not available in bind$1" % [proxyName]) 305 | let elem = if n[0].kind == nnkAccQuoted: 306 | newBindDesc(n[0][0], "`" & $n[0][0] & "`", n[0].kind, nnkNone) 307 | else: 308 | newBindDesc(n[0], $n[0], n.kind, nnkNone) 309 | collectGenericParam(elem, n) 310 | elemList.add elem 311 | of nnkPrefix: 312 | if $n[0] != "~": error("only `~` prefix supported", n[0]) 313 | let elem = newBindDesc(n[1], "__gc", n.kind, nnkNone, isDestructor) 314 | elemList.add elem 315 | else: 316 | error("wrong param type: $1, $2 not allowed here" % [$n.kind, n.toStrLit().strVal], n) 317 | 318 | proc checkDuplicate(list: seq[bindDesc]): string {.compileTime.} = 319 | var checked = newSeq[bindDesc]() 320 | for k in list: 321 | if checked.contains(k): 322 | return $k.node 323 | else: 324 | checked.add k 325 | result = "" 326 | 327 | #here is the factory of second level macro that will be expanded to utilize bindSym 328 | proc genProxyMacro(arg: NimNode, opts: bindFlags, proxyName: string): NimNode {.compileTime.} = 329 | let 330 | useLib = nlbUseLib in opts 331 | registerObject = nlbRegisterobject in opts 332 | registerClosure = nlbRegisterClosure in opts 333 | registerGeneric = nlbRegisterGeneric in opts 334 | 335 | var 336 | luaCtx = "" 337 | libName = "" 338 | libKind: NimNodeKind 339 | objectName = "" 340 | objectNewName = "" 341 | elemList = newSeq[bindDesc]() 342 | propList = newSeq[propDesc]() 343 | 344 | for i in 0..arg.len-1: 345 | let n = arg[i] 346 | case n.kind 347 | of nnkSym: 348 | if i == 0: luaCtx = $n 349 | else: 350 | error("param " & $i & " must be an identifier, not symbol\n" & arg.treeRepr) 351 | of nnkStrLit: 352 | if i == 1 and useLib: 353 | libName = n.strVal 354 | libKind = n.kind 355 | else: 356 | let msg = "bind$1, param: $2" % [proxyName, n.toStrLit().strVal] 357 | error("param " & $i & " must be an identifier, not string literal\n" & msg, n) 358 | of nnkIdent: 359 | if i == 1 and $n == "GLOBAL" and useLib: 360 | libName = $n 361 | libKind = n.kind 362 | elif i == 1 and registerObject: 363 | objectName = $n 364 | objectNewName = $n 365 | else: 366 | let elem = newBindDesc(n, $n, n.kind, nnkNone) 367 | elemList.add elem 368 | of nnkAccQuoted: 369 | let elem = newBindDesc(n[0], "`" & $n[0] & "`", n.kind, nnkNone) 370 | elemList.add elem 371 | of nnkInfix: 372 | if registerObject and i == 1: 373 | let k = splitElem(n, opts, proxyName) 374 | objectName = $k.node 375 | objectNewName = k.name 376 | else: 377 | elemList.add splitElem(n, opts, proxyName) 378 | of nnkCall: 379 | if not registerObject: 380 | error("getter/setter not available in bind$1" % [proxyName]) 381 | let elem = newBindDesc(n, $n[0], n.kind, nnkNone) 382 | elemList.add elem 383 | of nnkBracket: 384 | if not registerClosure: 385 | error("closure not available in bind$1" % [proxyName]) 386 | let elem = if n[0].kind == nnkAccQuoted: 387 | newBindDesc(n[0][0], "`" & $n[0][0] & "`", n[0].kind, nnkNone, isClosure) 388 | else: 389 | newBindDesc(n[0], $n[0], n.kind, nnkNone, isClosure) 390 | elemList.add elem 391 | of nnkBracketExpr: 392 | if not registerGeneric: 393 | error("generic not available in bind$1" % [proxyName]) 394 | let elem = if n[0].kind == nnkAccQuoted: 395 | newBindDesc(n[0][0], "`" & $n[0][0] & "`", n[0].kind, nnkNone) 396 | else: 397 | newBindDesc(n[0], $n[0], n.kind, nnkNone) 398 | collectGenericParam(elem, n) 399 | elemList.add elem 400 | of nnkStmtList: 401 | unwindList(n, elemList, opts, proxyName) 402 | of nnkPrefix: 403 | if $n[0] != "~": error("only `~` prefix supported", n[0]) 404 | let elem = newBindDesc(n[1], "__gc", n.kind, nnkNone, isDestructor) 405 | elemList.add elem 406 | else: 407 | error("wrong param type\n" & n.treeRepr, n) 408 | 409 | if luaCtx == "": 410 | error("need luaState as first param") 411 | 412 | let dup = elemList.checkDuplicate() 413 | if dup != "": 414 | error("bind$1 detected duplicated entries: $2" % [proxyName, dup]) 415 | 416 | #generate intermediate macro to utilize bindSym that can only accept string literal 417 | let macroName = "NLB$1$2" % [proxyName, $macroCount] 418 | var nlb = "macro " & macroName & "(): untyped =\n" 419 | nlb.add " var ctx: proxyDesc\n" 420 | nlb.add " ctx.luaCtx = \"$1\"\n" % [luaCtx] 421 | if registerObject: 422 | nlb.add " ctx.subject = bindSym\"$1\"\n" % [objectName] 423 | else: 424 | nlb.add " ctx.subject = newEmptyNode()\n" 425 | 426 | var numElem = 0 427 | for k in elemList: 428 | if k.node.kind in {nnkAccQuoted, nnkIdent}: 429 | inc numElem 430 | elif k.node.kind == nnkCall: 431 | var v = if k.node[0].kind == nnkAccQuoted: 432 | (node: $k.node[0][0], name: "`" & $k.node[0][0] & "`", lhsKind: k.node[0].kind, getter: false, setter: false) 433 | else: 434 | (node: $k.node[0], name: $k.node[0], lhsKind: k.node[0].kind, getter: false, setter: false) 435 | 436 | if k.rhsKind in {nnkStrLit, nnkIdent}: v.name = k.name 437 | 438 | for i in 1..k.node.len-1: 439 | let n = k.node[i] 440 | if n.kind != nnkIdent: error(v.node & ": getter/setter attribut must be identifier") 441 | if $n == "get": v.getter = true 442 | elif $n == "set": v.setter = true 443 | else: error(v.node & ": getter/setter attribute must be 'get' and/or 'set'") 444 | if v.getter == false and v.setter == false: error(v.node & ": getter/setter attribute must be present") 445 | propList.add v 446 | else: 447 | error("unexpected node kind: " & $k.node.kind) 448 | 449 | if numElem > 0: 450 | nlb.add " ctx.bindList = @[\n" 451 | var i = 0 452 | for k in elemList: 453 | let comma = if i < numElem: "," else: "" 454 | if k.node.kind in {nnkAccQuoted, nnkIdent}: 455 | var gp = "" 456 | if k.genericParams.len > 0: 457 | gp.add "@[" 458 | var ii = 0 459 | for x in k.genericParams: 460 | let cma = if ii < k.genericParams.len-1: "," else: "" 461 | gp.add "bindSym\"$1\"$2" % [$x, cma] 462 | inc ii 463 | gp.add "]" 464 | else: 465 | gp.add "newSeq[NimNode]()" 466 | nlb.add " newBindDesc(bindSym\"$1\", \"$2\", $3, $4, $5, $6)$7\n" % 467 | [$k.node, k.name, $k.lhsKind, $k.rhsKind, $k.bindKind, gp, comma] 468 | inc i 469 | nlb.add " ]\n" 470 | else: 471 | nlb.add " ctx.bindList = @[]\n" 472 | 473 | if propList.len > 0: 474 | nlb.add " ctx.propList = @[\n" 475 | var ii = 0 476 | for k in propList: 477 | let comma = if ii < propList.len-1: "," else: "" 478 | nlb.add " (\"$1\", \"$2\", $3, $4, $5)$6\n" % 479 | [k.node, k.name, $k.lhsKind, $k.getter, $k.setter, comma] 480 | inc ii 481 | nlb.add " ]\n" 482 | else: 483 | nlb.add " ctx.propList = @[]\n" 484 | 485 | if libKind == nnkStrLit: 486 | nlb.add " ctx.libName = newStrLitNode(\"$1\")\n" % [libName] 487 | elif libKind == nnkIdent: 488 | nlb.add " ctx.libName = newIdentNode(\"$1\")\n" % [libName] 489 | elif registerObject: 490 | nlb.add " ctx.libName = newIdentNode(\"$1\")\n" % [objectNewName] 491 | else: 492 | nlb.add " ctx.libName = newEmptyNode()\n" 493 | 494 | nlb.add " result = proxyMixer(ctx, \"$1\")\n" % [proxyName] 495 | nlb.add macroName & "()\n" 496 | 497 | result = parseCode(nlb) 498 | inc macroCount 499 | 500 | #both normal ident and backticks quoted ident converted to string 501 | proc getAccQuotedName(n: NimNode, kind: NimNodeKind): string {.compileTime.} = 502 | let name = if n.kind == nnkClosedSymChoice: $n[0] else: $n 503 | if kind == nnkAccQuoted: result = "`" & name & "`" else: result = name 504 | 505 | proc getAccQuotedName(name: string, kind: NimNodeKind): string {.compileTime.} = 506 | if kind == nnkAccQuoted: result = "`" & name & "`" else: result = name 507 | 508 | proc ignoreGenerics(ids: var seq[string], id: string, generics: NimNode) {.compileTime.} = 509 | var found = false 510 | if generics.kind == nnkGenericParams: 511 | for k in generics: 512 | if $k == id: 513 | found = true 514 | break 515 | if not found: ids.add id 516 | 517 | proc collectSym(ids: var seq[string], arg: NimNode) {.compileTime.} = 518 | if arg.kind == nnkProcDef: 519 | let generics = arg[2] 520 | let params = arg[3] 521 | let retType = params[0] 522 | let argList = paramsToArgListBasic(params) 523 | if retType.kind == nnkIdent: ignoreGenerics(ids, $retType, generics) 524 | for k in argList: 525 | if k.mType.kind == nnkIdent: ignoreGenerics(ids, $k.mType, generics) 526 | if k.mVal.kind == nnkIdent: ignoreGenerics(ids, $k.mVal, generics) 527 | 528 | proc checkProp(subject: NimNode, prop: string): bool {.compileTime.} = 529 | let parent = if subject.kind == nnkRefTy: subject[0][1] else: subject[1] 530 | if parent.kind == nnkOfInherit: 531 | let parentName = parent[0] 532 | var t = getTypeImpl(parentName) 533 | if t.kind == nnkRefTy: t = getTypeImpl(t[0]) 534 | if checkProp(t, prop): return true 535 | 536 | let recList = if subject.kind == nnkRefTy: subject[0][2] else: subject[2] 537 | if recList.kind == nnkEmpty: return false 538 | 539 | for n in recList: 540 | for i in 0..n.len-3: 541 | let k = n[i] 542 | if k.kind in {nnkIdent, nnkSym}: 543 | if $k == prop: return true 544 | elif k.kind == nnkPostfix: 545 | if $k[1] == prop: return true 546 | else: 547 | error("unknown prop construct") 548 | result = false 549 | 550 | proc checkObject(subject: NimNode): bool {.compileTime.} = 551 | if subject[2].kind == nnkDistinctTy: 552 | return subject[2][0].kind == nnkSym and $subject[2][0] == "pointer" 553 | result = subject[2].kind in {nnkObjectTy, nnkRefTy} 554 | 555 | proc proxyMixer*(ctx: proxyDesc, proxyName: string): NimNode {.compileTime.} = 556 | var ids = newSeq[string]() 557 | 558 | for n in ctx.bindList: 559 | if n.node.kind == nnkSym: 560 | let im = getImpl(n.node) 561 | collectSym(ids, im) 562 | else: 563 | for s in children(n.node): 564 | let im = getImpl(s) 565 | collectSym(ids, im) 566 | 567 | let macroName = "NLB$1$2" % [proxyName, $macroCount] 568 | var nlb = "macro " & macroName & "(): untyped =\n" 569 | nlb.add " var ctx: proxyDesc\n" 570 | nlb.add " ctx.luaCtx = \"$1\"\n" % [ctx.luaCtx] 571 | if ctx.subject.kind == nnkSym: 572 | let subject = getImpl(ctx.subject) 573 | if not checkObject(subject): 574 | error($ctx.subject & ": not an object") 575 | nlb.add " ctx.subject = bindSym\"$1\"\n" % [$ctx.subject] 576 | else: 577 | nlb.add " ctx.subject = newEmptyNode()\n" 578 | 579 | if ctx.bindList.len > 0: 580 | nlb.add " ctx.bindList = @[\n" 581 | var i = 0 582 | for k in ctx.bindList: 583 | var gp = "" 584 | if k.genericParams.len > 0: 585 | gp.add "@[" 586 | var ii = 0 587 | for x in k.genericParams: 588 | let cma = if ii < k.genericParams.len-1: "," else: "" 589 | gp.add "bindSym\"$1\"$2" % [$x, cma] 590 | inc ii 591 | gp.add "]" 592 | else: 593 | gp.add "newSeq[NimNode]()" 594 | 595 | let comma = if i < ctx.bindList.len-1: "," else: "" 596 | nlb.add " newBindDesc(bindSym\"$1\", \"$2\", $3, $4, $5, $6)$7\n" % 597 | [$k.node, k.name, $k.lhsKind, $k.rhsKind, $k.bindKind, gp, comma] 598 | inc i 599 | nlb.add " ]\n" 600 | else: 601 | nlb.add " ctx.bindList = @[]\n" 602 | 603 | if ctx.propList.len > 0: 604 | nlb.add " ctx.propList = @[\n" 605 | var ii = 0 606 | let subject = getImpl(ctx.subject) 607 | for k in ctx.propList: 608 | if not checkProp(subject[2], k.node): 609 | error($ctx.subject & ": don't have properties " & k.name) 610 | let comma = if ii < ctx.propList.len-1: "," else: "" 611 | nlb.add " (\"$1\", \"$2\", $3, $4, $5)$6\n" % 612 | [k.node, k.name, $k.lhsKind, $k.getter, $k.setter, comma] 613 | inc ii 614 | nlb.add " ]\n" 615 | else: 616 | nlb.add " ctx.propList = @[]\n" 617 | 618 | if ids.len > 0: 619 | nlb.add " ctx.symList = @[\n" 620 | var i = 0 621 | for k in ids: 622 | let comma = if i < ids.len-1: "," else: "" 623 | nlb.add " bindSym\"$1\"$2\n" % [k, comma] 624 | inc i 625 | nlb.add " ]\n" 626 | else: 627 | nlb.add " ctx.symList = @[]\n" 628 | 629 | if ctx.libName.kind == nnkStrLit: 630 | nlb.add " ctx.libName = newStrLitNode(\"$1\")\n" % [$ctx.libName] 631 | elif ctx.libName.kind == nnkIdent: 632 | nlb.add " ctx.libName = newIdentNode(\"$1\")\n" % [$ctx.libName] 633 | else: 634 | nlb.add " ctx.libName = newEmptyNode()\n" 635 | 636 | nlb.add " result = bind$1Impl(ctx)\n" % [proxyName] 637 | nlb.add macroName & "()\n" 638 | 639 | result = parseCode(nlb) 640 | inc macroCount 641 | 642 | #proc params and return type 643 | proc newProcElem(retType: NimNode, params: seq[argDesc]): ovProcElem {.compileTime.} = 644 | result = new(ovProcElem) 645 | result.retType = retType 646 | result.params = params 647 | 648 | #list of overloaded proc 649 | proc newOvProc(retType: NimNode, params: seq[argDesc]): ovProc {.compileTime.} = 650 | var ovp = new(ovProc) 651 | ovp.numArgs = params.len 652 | ovp.procs = newSeq[ovProcElem]() 653 | ovp.procs.add newProcElem(retType, params) 654 | result = ovp 655 | 656 | #add overloaded proc into ovList 657 | proc addOvProc(ovl: var ovList, retType: NimNode, params: seq[argDesc]) {.compileTime.} = 658 | var found = false 659 | for k in ovl: 660 | if k.numArgs == params.len: 661 | k.procs.add newProcElem(retType, params) 662 | found = true 663 | break 664 | 665 | if not found: 666 | ovl.add newOvProc(retType, params) 667 | 668 | proc isRefType(s: NimNode): bool {.compileTime.} = 669 | let n = getImpl(s) 670 | if n.kind != nnkTypeDef: return false 671 | if n[2].kind != nnkRefTy: return false 672 | result = true 673 | 674 | proc isObjectType(s: NimNode): bool {.compileTime.} = 675 | let n = getImpl(s) 676 | if n.kind != nnkTypeDef: return false 677 | if n[2].kind != nnkObjectTy: return false 678 | result = true 679 | 680 | proc hasName(name: string): bool {.compileTime.} = 681 | for n in nameList: 682 | if n == name: return true 683 | result = false 684 | 685 | proc setName(name: string) {.compileTime.} = 686 | nameList.add(name) 687 | 688 | proc registerObject(subject: NimNode): string {.compileTime.} = 689 | let name = $subject 690 | let prefixedName = "nlobj" & name 691 | for i in 0..nameList.high: 692 | if nameList[i] == prefixedName: 693 | return name & $i 694 | 695 | let subjectID = $nameList.len 696 | let subjectName = name & subjectID 697 | nameList.add prefixedName 698 | objectMTList.add "$1.nimNewMetatable(NL_$2)\n" % ["$1", subjectName] 699 | var glue = "const\n" 700 | glue.add " NL_$1 = $2+$3\n" % [subjectName, $IDRegion, subjectID] 701 | glue.add " NL_$1name = \"$2\"\n" % [subjectName, name] 702 | glue.add "type\n" 703 | glue.add " NL_$1Proxy = object\n" % [subjectName] 704 | glue.add " ud: $1\n" % [name] 705 | gContext.add glue 706 | result = subjectName 707 | 708 | proc genMetaTableList(SL: string): string {.compileTime.} = 709 | result = "" 710 | for n in objectMTList: 711 | result.add(n % [SL]) 712 | objectMTList.setLen 0 713 | 714 | proc checkUD(s, n: string): string {.compileTime.} = 715 | result = "cast[ptr NL_$1Proxy](L.nimCheckUData($2.cint, NL_$1, NL_$1name))\n" % [s, n] 716 | 717 | proc newUD(s: string): string {.compileTime.} = 718 | result = "cast[ptr NL_$1Proxy](L.newUserData(sizeof(NL_$1Proxy)))\n" % [s] 719 | 720 | proc addMemberCap(SL, libName: string, argLen: int): string {.compileTime.} = 721 | when nloAddMember in gOptions: 722 | var glue = "" 723 | glue.add "$1.getGlobal(\"$2\")\n" % [SL, libName] 724 | glue.add "if not $1.isTable(-1):\n" % [SL] 725 | glue.add " $1.pop(1)\n" % [SL] 726 | glue.add " $1.createTable(0.cint, $2.cint)\n" % [SL, $(argLen)] 727 | return glue 728 | else: 729 | result = "$1.createTable(0.cint, $2.cint)\n" % [SL, $(argLen)] 730 | 731 | proc addClosureEnv(SL, procName: string, n: NimNode, bd: bindDesc, ovIdx: int = 0): string {.compileTime.} = 732 | var glue = "" 733 | 734 | var params = copy(n[3]) 735 | params.add newIdentDefs(newIdentNode("clEnv"), bindSym"pointer") 736 | let clv = params.toStrLit.strVal 737 | 738 | glue.add "type clsTyp$1$2 = proc$3 {.nimCall.}\n" % [$proxycount, $ovIdx, clv] 739 | glue.add "$1.getGlobal(\"$2\")\n" % [SL, globalClosure] 740 | glue.add "if not $1.isTable(-1):\n" % [SL] 741 | glue.add " $1.pop(1)\n" % [SL] 742 | glue.add " $1.createTable(0.cint, 2.cint)\n" % [SL] 743 | glue.add "$1.pushLiteral(\"$2$3env$4\")\n" % [SL, procName, $proxycount, $ovIdx] 744 | glue.add "$1.pushLightUserData(rawEnv($2))\n" % [Sl, procName] 745 | glue.add "$1.setTable(-3)\n" % [SL] 746 | glue.add "$1.pushLiteral(\"$2$3proc$4\")\n" % [SL, procName, $proxycount, $ovIdx] 747 | glue.add "$1.pushLightUserData(rawProc($2))\n" % [Sl, procName] 748 | glue.add "$1.setTable(-3)\n" % [SL] 749 | glue.add "$1.setGlobal(\"$2\")\n" % [SL, globalClosure] 750 | result = glue 751 | 752 | proc getClosureEnv(SL, procName: string, ovIdx: int): string {.compileTime.} = 753 | var glue = "" 754 | glue.add " $1.getGlobal(\"$2\")\n" % [SL, globalClosure] 755 | glue.add " let clsTab = $1.getTop()\n" % [SL] 756 | glue.add " $1.pushLiteral(\"$2$3env$4\")\n" % [SL, procName, $proxycount, $ovIdx] 757 | glue.add " $1.getTable(clsTab)\n" % [SL] 758 | glue.add " let clsEnv$2 = $1.toUserData(-1)\n" % [SL, $ovIdx] 759 | glue.add " $1.pushLiteral(\"$2$3proc$4\")\n" % [SL, procName, $proxycount, $ovIdx] 760 | glue.add " $1.getTable(clsTab)\n" % [SL] 761 | glue.add " let clsProc$2 = cast[clsTyp$1$2]($3.toUserData(-1))\n" % [$proxyCount, $ovIdx, SL] 762 | glue.add " $1.pop(3)\n" % [SL] 763 | result = glue 764 | 765 | proc nimLuaPanic(L: PState): cint {.cdecl.} = 766 | echo "panic" 767 | echo L.toString(-1) 768 | L.pop(1) 769 | return 0 770 | 771 | proc stdNimLuaErrFunc(ctx: pointer, err: NLError) = 772 | echo "$1:$2 warning: $3" % [err.source, $err.currentLine, err.msg] 773 | 774 | proc NLSetErrorHandler*(L: PState, errFunc: NLErrorFunc) = 775 | L.pushLightUserData(cast[pointer](IDErrorFunc)) 776 | L.pushLightUserData(cast[pointer](errFunc)) 777 | L.rawSet(LUA_REGISTRYINDEX) 778 | 779 | proc NLSetErrorContext*(L: PState, errCtx: pointer) = 780 | L.pushLightUserData(cast[pointer](IDErrorContext)) 781 | L.pushLightUserData(errCtx) 782 | L.rawSet(LUA_REGISTRYINDEX) 783 | 784 | #call this before you use this library 785 | proc newNimLua*(readOnlyEnum = false): PState = 786 | var L = newState() 787 | L.openLibs 788 | discard L.atPanic(nimLuaPanic) 789 | 790 | L.NLSetErrorHandler(stdNimLuaErrFunc) 791 | L.NLSetErrorContext(nil) 792 | 793 | const roEnum = """ 794 | function readonlytable(table) 795 | return setmetatable({}, { 796 | __index = table, 797 | __newindex = function(table, key, value) error("Attempt to modify read-only table") end, 798 | __metatable = false 799 | }); 800 | end 801 | """ 802 | const rwEnum = """ 803 | function readonlytable(table) 804 | return table 805 | end 806 | """ 807 | 808 | const metaMethods = """ 809 | function __nlbIndex(myobject, key) 810 | local mytable = getmetatable(myobject) 811 | local x = rawget(mytable, "_get_" .. key) 812 | if x ~= nil then 813 | return x(myobject) 814 | else 815 | return mytable[key] 816 | end 817 | end 818 | function __nlbNewIndex(myobject, key, value) 819 | local mytable = getmetatable(myobject) 820 | local x = rawget(mytable, "_set_" .. key) 821 | if x ~= nil then 822 | x(myobject, value) 823 | else 824 | mytable[key] = value 825 | end 826 | end 827 | """ 828 | 829 | const 830 | nimVer = "Nim = { major = $1, minor = $2, patch = $3 }" % 831 | [$NimMajor, $NimMinor, $NimPatch] 832 | 833 | discard L.doString(if readOnlyEnum: roEnum else: rwEnum) 834 | discard L.doString(metaMethods) 835 | discard L.doString(nimVer) 836 | result = L 837 | 838 | proc propsEnd*(L: PState) = 839 | L.getGlobal("__nlbIndex") 840 | L.setField(-2, "__index") 841 | L.getGlobal("__nlbNewIndex") 842 | L.setField(-2, "__newindex") 843 | 844 | # ------------------------------------------------------------------------- 845 | # --------------------------------- bindEnum ------------------------------ 846 | # ------------------------------------------------------------------------- 847 | 848 | proc bindEnumScoped(SL: string, s: NimNode, scopeName: string, kind: NimNodeKind): string {.compileTime.} = 849 | let x = getImpl(s) 850 | var err = false 851 | if x.kind != nnkTypeDef: err = true 852 | if x[0].kind notin {nnkSym, nnkPragmaExpr}: err = true 853 | if x[2].kind != nnkEnumTy: err = true 854 | if err: error("bindEnum: incorrect enum definition") 855 | 856 | var pureEnum = false 857 | var enumName = "" 858 | if x[0].kind == nnkPragmaExpr: 859 | pureEnum = $x[0][1][0] == "pure" 860 | 861 | if pureEnum: 862 | if x[0][0].kind != nnkSym: error("wrong enum definition") 863 | enumName = if kind == nnkAccQuoted: "`" & $x[0][0] & "`" else: $x[0][0] 864 | else: 865 | enumName = if kind == nnkAccQuoted: "`" & $x[0] & "`" else: $x[0] 866 | 867 | let numEnum = x[2].len - 1 868 | var glue = "" 869 | glue.add "$1.getGlobal(\"readonlytable\")\n" % [SL] 870 | glue.add addMemberCap(SL, scopeName, numEnum) 871 | 872 | for i in 1..numEnum: 873 | let 874 | n = x[2][i] 875 | sym = if n.kind == nnkAccQuoted: "`" & $n[0] & "`" else: $n 876 | glue.add "discard $1.pushLString(\"$2\", $3)\n" % [SL, sym, $sym.len] 877 | if pureEnum: glue.add "$1.pushInteger(lua_Integer($2.$3))\n" % [SL, enumName, sym] 878 | else: glue.add "$1.pushInteger(lua_Integer($2))\n" % [SL, sym] 879 | glue.add "$1.setTable(-3)\n" % [SL] 880 | 881 | glue.add "discard $1.pcall(1, 1, 0)\n" % [SL] 882 | glue.add "$1.setGlobal(\"$2\")\n" % [SL, scopeName] 883 | result = glue 884 | 885 | proc bindEnumGlobal(SL: string, s: NimNode, kind: NimNodeKind): string {.compileTime.} = 886 | let x = getImpl(s) 887 | var err = false 888 | if x.kind != nnkTypeDef: err = true 889 | if x[0].kind notin {nnkSym, nnkPragmaExpr}: err = true 890 | if x[2].kind != nnkEnumTy: err = true 891 | if err: error("bindEnum: incorrect enum definition") 892 | 893 | var pureEnum = false 894 | var enumName = "" 895 | if x[0].kind == nnkPragmaExpr: 896 | pureEnum = $x[0][1][0] == "pure" 897 | 898 | if pureEnum: 899 | if x[0][0].kind != nnkSym: error("wrong enum definition") 900 | enumName = if kind == nnkAccQuoted: "`" & $x[0][0] & "`" else: $x[0][0] 901 | else: 902 | enumName = if kind == nnkAccQuoted: "`" & $x[0] & "`" else: $x[0] 903 | 904 | let numEnum = x[2].len - 1 905 | var glue = "" 906 | for i in 1..numEnum: 907 | let 908 | n = x[2][i] 909 | sym = if n.kind == nnkAccQuoted: "`" & $n[0] & "`" else: $n 910 | if pureEnum: glue.add "$1.pushInteger(lua_Integer($2.$3))\n" % [SL, enumName, sym] 911 | else: glue.add "$1.pushInteger(lua_Integer($2))\n" % [SL, sym] 912 | glue.add "$1.setGlobal(\"$2\")\n" % [SL, sym] 913 | 914 | result = glue 915 | 916 | #this proc need to be exported because intermediate macro call this proc from 917 | #callsite module 918 | proc bindEnumImpl*(ctx: proxyDesc): NimNode {.compileTime.} = 919 | let 920 | SL = ctx.luaCtx 921 | arg = ctx.bindList 922 | 923 | var glue = "" 924 | for i in 0..arg.len-1: 925 | let n = arg[i] 926 | if n.name == "GLOBAL" and n.rhsKind == nnkIdent: glue.add bindEnumGlobal(SL, n.node, n.lhsKind) 927 | else: glue.add bindEnumScoped(SL, n.node, n.name, n.lhsKind) 928 | 929 | result = parseCode(glue) 930 | 931 | macro bindEnum*(arg: varargs[untyped]): untyped = 932 | result = genProxyMacro(arg, {}, "Enum") 933 | 934 | # ------------------------------------------------------------------------- 935 | # ----------------------------- bindFunction ------------------------------ 936 | # ------------------------------------------------------------------------- 937 | 938 | # these are runtime type check helper for each type 939 | # supported by Nim and Lua 940 | proc nimCheckString*(L: PState, idx: cint): string = 941 | if L.isStrictString(idx): result = L.toString(idx) 942 | else: 943 | L.nimDebug(idx.cint, "string") 944 | result = "" 945 | 946 | proc nimCheckBool*(L: PState, idx: cint): bool = 947 | if L.isBoolean(idx): result = if L.toBoolean(idx) == 0: false else: true 948 | else: 949 | L.nimDebug(idx.cint, "bool") 950 | result = false 951 | 952 | proc nimCheckInteger*(L: PState, idx: cint): int = 953 | if L.isInteger(idx) != 0: result = L.toInteger(idx).int 954 | else: 955 | L.nimDebug(idx.cint, "int") 956 | result = 0 957 | 958 | proc nimCheckNumber*(L: PState, idx: cint): float64 = 959 | if L.isNumber(idx) != 0: result = L.toNumber(idx).float64 960 | else: 961 | L.nimDebug(idx.cint, "float") 962 | result = 0.0 963 | 964 | proc nimCheckCstring*(L: PState, idx: cint): cstring = 965 | if L.isStrictString(idx): result = L.toLString(idx, nil) 966 | else: 967 | L.nimDebug(idx.cint, "cstring") 968 | result = nil 969 | 970 | proc nimCheckChar*(L: PState, idx: cint): char = 971 | if L.isInteger(idx) != 0: result = L.toInteger(idx).char 972 | else: result = chr(0) 973 | 974 | proc nimNewMetaTable*(L: PState, key: int) = 975 | L.pushLightUserData(cast[pointer](key)) 976 | L.rawGet(LUA_REGISTRYINDEX) 977 | if not L.isNil(-1): # name already in use? 978 | L.pop(1) 979 | return 980 | 981 | L.pop(1) # pop nil 982 | L.pushLightUserData(cast[pointer](key)) 983 | L.newTable() # create metatable 984 | L.rawSet(LUA_REGISTRYINDEX) 985 | 986 | proc nimGetMetaTable*(L: PState, key: int) = 987 | L.pushLightUserData(cast[pointer](key)) 988 | L.rawGet(LUA_REGISTRYINDEX) 989 | 990 | proc nimCheckUData*(L: PState, idx, key: int, name: string): pointer = 991 | let p = L.toUserData(idx.cint) 992 | if p != nil: #value is a userdata? 993 | if L.getMetaTable(idx.cint) != 0.cint: #does it have a metatable? 994 | L.pushLightUserData(cast[pointer](key)) 995 | L.rawGet(LUA_REGISTRYINDEX) # get correct metatable 996 | if L.rawEqual(-1, -2) != 0.cint: # does it have the correct mt? 997 | L.pop(2) # remove both metatables 998 | return p 999 | 1000 | # else error 1001 | L.nimDebug(idx.cint, name) 1002 | result = nil 1003 | 1004 | let 1005 | intTypes {.compileTime.} = ["int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64"] 1006 | floatTypes {.compileTime.} = ["float", "float32", "float64"] 1007 | 1008 | proc constructBasicArg(mType: NimNode, i: int, procName: string): string {.compileTime.} = 1009 | let argType = $mType 1010 | for c in intTypes: 1011 | if c == argType: 1012 | return "L.nimCheckInteger(" & $i & ")." & c & "\n" 1013 | 1014 | for c in floatTypes: 1015 | if c == argType: 1016 | return "L.nimCheckNumber(" & $i & ")." & c & "\n" 1017 | 1018 | if argType == "string": 1019 | return "L.nimCheckString(" & $i & ")\n" 1020 | 1021 | if argType == "cstring": 1022 | return "L.nimCheckCstring(" & $i & ")\n" 1023 | 1024 | if argType == "bool": 1025 | return "L.nimCheckBool(" & $i & ")\n" 1026 | 1027 | if argType == "char": 1028 | return "L.nimCheckChar(" & $i & ")\n" 1029 | 1030 | if argType == "pointer": 1031 | return "L.toUserData(" & $i & ")\n" 1032 | 1033 | result = "" 1034 | 1035 | proc constructBasicRet(mType: NimNode, arg, indent, procName: string): string {.compileTime.} = 1036 | let retType = $mType 1037 | for c in intTypes: 1038 | if c == retType: 1039 | return indent & "L.pushInteger(lua_Integer(" & arg & "))\n" 1040 | 1041 | for c in floatTypes: 1042 | if c == retType: 1043 | return indent & "L.pushNumber(lua_Number(" & arg & "))\n" 1044 | 1045 | if retType == "string": 1046 | return indent & "discard L.pushLiteral(" & arg & ")\n" 1047 | 1048 | if retType == "cstring": 1049 | return indent & "discard L.pushString(" & arg & ")\n" 1050 | 1051 | if retType == "bool": 1052 | return indent & "L.pushBoolean(" & arg & ".cint)\n" 1053 | 1054 | if retType == "char": 1055 | return indent & "L.pushInteger(lua_Integer(" & arg & "))\n" 1056 | 1057 | if retType == "pointer": 1058 | return indent & "L.pushLightUserData(" & arg & ")\n" 1059 | 1060 | result = "" 1061 | 1062 | proc constructArg(ctx: proxyDesc, mType: NimNode, i: int, procName: string, needCheck: var string): string {.compileTime.} 1063 | 1064 | proc argAttr(mType: NimNode): string {.compileTime.} = 1065 | if mType.kind == nnkSym: 1066 | let nType = getImpl(mType) 1067 | if nType.kind == nnkTypeDef and nType[2].kind in {nnkObjectTy, nnkRefTy}: 1068 | return ".ud" 1069 | 1070 | if nType.kind == nnkTypeDef and nType[2].kind in {nnkDistinctTy, nnkEnumTy}: 1071 | if nType[0].kind == nnkPragmaExpr: 1072 | return "." & $nType[0][0] 1073 | else: #nnkSym 1074 | return "." & $nType[0] 1075 | 1076 | if mType.kind == nnkVarTy: 1077 | if getType(mType[0]).kind in {nnkObjectTy, nnkRefTy}: 1078 | return ".ud" 1079 | 1080 | result = "" 1081 | 1082 | proc stackDump*(L: PState) = 1083 | let top = L.getTop() 1084 | echo "total in stack ", top 1085 | for i in 1..top: 1086 | #repeat for each level 1087 | let t = L.luatype(i) 1088 | case t 1089 | of LUA_TSTRING: 1090 | echo "string: '$1'" % L.toString(i) 1091 | of LUA_TBOOLEAN: 1092 | echo "boolean $1" % [if L.toBoolean(i) == 1: "true" else: "false"] 1093 | of LUA_TNUMBER: 1094 | echo "number: $1" % [$(L.toNumber(i).int)] 1095 | else: 1096 | echo "$1" % [$L.typeName(t)] 1097 | 1098 | proc registerArrayCheck(ctx: proxyDesc, s: NimNode, lo, hi: int, procName: string): string {.compileTime.} = 1099 | let name = "checkArray$1$2$3" % [$s, $lo, $hi] 1100 | var needCheck = "" 1101 | if not hasName(name): 1102 | setName(name) 1103 | var glue = "proc $1(L: PState, idx: int): array[$2..$3, $4] =\n" % [name, $lo, $hi, $s] 1104 | glue.add " L.checkType(idx.cint, LUA_TTABLE)\n" 1105 | glue.add " let len = min(L.llen(idx.cint), result.len)\n" 1106 | glue.add " L.pushNil()\n" 1107 | glue.add " var i = 0\n" 1108 | glue.add " while L.next(idx.cint) != 0:\n" 1109 | glue.add " let tmp = " & constructArg(ctx, s, -1, procName, needCheck) 1110 | if needCheck.len != 0: 1111 | glue.add((" " & needCheck) % ["tmp"]) 1112 | glue.add " result[i+$1] = tmp$2\n" % [$lo, argAttr(s)] 1113 | else: 1114 | glue.add " result[i+$1] = tmp$2\n" % [$lo, argAttr(s)] 1115 | glue.add " L.pop(1.cint)\n" 1116 | glue.add " inc i\n" 1117 | glue.add " if i >= len: break\n" 1118 | gContext.add glue 1119 | result = name 1120 | 1121 | proc registerArrayCheck(ctx: proxyDesc, s: NimNode, hi: int, procName: string): string {.compileTime.} = 1122 | let name = "checkArray$1$2" % [$s, $hi] 1123 | var needCheck = "" 1124 | if not hasName(name): 1125 | setName(name) 1126 | var glue = "proc $1(L: PState, idx: int): array[$2, $3] =\n" % [name, $hi, $s] 1127 | glue.add " L.checkType(idx.cint, LUA_TTABLE)\n" 1128 | glue.add " let len = min(L.llen(idx.cint), result.len)\n" 1129 | glue.add " L.pushNil()\n" 1130 | glue.add " var i = 0\n" 1131 | glue.add " while L.next(idx.cint) != 0:\n" 1132 | glue.add " let tmp = " & constructArg(ctx, s, -1, procName, needCheck) 1133 | if needCheck.len != 0: 1134 | glue.add((" " & needCheck) % ["tmp"]) 1135 | glue.add " result[i] = tmp$1\n" % [argAttr(s)] 1136 | else: 1137 | glue.add " result[i] = tmp$1\n" % [argAttr(s)] 1138 | glue.add " L.pop(1.cint)\n" 1139 | glue.add " inc i\n" 1140 | glue.add " if i >= len: break\n" 1141 | gContext.add glue 1142 | 1143 | result = name 1144 | 1145 | proc registerArrayCheck(ctx: proxyDesc, s: NimNode, id: string, procName: string): string {.compileTime.} = 1146 | let name = "checkArray$1$2" % [$s, id] 1147 | var needCheck = "" 1148 | if not hasName(name): 1149 | setName(name) 1150 | var glue = "proc $1(L: PState, idx: int): array[$2, $3] =\n" % [name, id, $s] 1151 | glue.add " L.checkType(idx.cint, LUA_TTABLE)\n" 1152 | glue.add " let len = min(L.llen(idx.cint), result.len)\n" 1153 | glue.add " L.pushNil()\n" 1154 | glue.add " var i = 0\n" 1155 | glue.add " while L.next(idx.cint) != 0:\n" 1156 | glue.add " let tmp = " & constructArg(ctx, s, -1, procName, needCheck) 1157 | if needCheck.len != 0: 1158 | glue.add((" " & needCheck) % ["tmp"]) 1159 | glue.add " result[i] = tmp$1\n" % [argAttr(s)] 1160 | else: 1161 | glue.add " result[i] = tmp$1\n" % [argAttr(s)] 1162 | glue.add " L.pop(1.cint)\n" 1163 | glue.add " inc i\n" 1164 | glue.add " if i >= len: break\n" 1165 | gContext.add glue 1166 | 1167 | result = name 1168 | 1169 | proc genArrayArg(ctx: proxyDesc, nType: NimNode, i: int, procName: string): string {.compileTime.} = 1170 | var lo, hi, mode: int 1171 | 1172 | if nType[1].kind == nnkInfix: 1173 | lo = int(nType[1][1].intVal) 1174 | hi = int(nType[1][2].intVal) 1175 | mode = 1 1176 | elif nType[1].kind == nnkIntLit: 1177 | lo = 0 1178 | hi = int(nType[1].intVal) 1179 | mode = 2 1180 | elif nType[1].kind == nnkIdent: 1181 | mode = 3 1182 | else: 1183 | error(procName & ": unknown array param type: " & nType.treeRepr) 1184 | 1185 | let argType = nType[2] 1186 | 1187 | var needCheck = "" 1188 | let res = constructArg(ctx, argType, -1, procName, needCheck) 1189 | let checkArray = if mode == 1: 1190 | registerArrayCheck(ctx, argType, lo, hi, procName) 1191 | elif mode == 2: 1192 | registerArrayCheck(ctx, argType, hi, procName) 1193 | else: 1194 | registerArrayCheck(ctx, argType, $nType[1], procName) 1195 | 1196 | var glue = "L.$1($2)\n" % [checkArray, $i] 1197 | if res != "": return glue 1198 | 1199 | error(procName & ": unknown array param type: " & $nType.kind & "\n" & nType.treeRepr) 1200 | result = "" 1201 | 1202 | proc registerSetCheck(ctx: proxyDesc, s: NimNode, procName: string): string {.compileTime.} = 1203 | let name = "checkSet$1" % [$s] 1204 | var needCheck = "" 1205 | if not hasName(name): 1206 | setName(name) 1207 | var glue = "proc $1(L: PState, idx: int): set[$2] =\n" % [name, $s] 1208 | glue.add " L.checkType(idx.cint, LUA_TTABLE)\n" 1209 | glue.add " let len = L.llen(idx.cint)\n" 1210 | glue.add " L.pushNil()\n" 1211 | glue.add " var i = 0\n" 1212 | glue.add " while L.next(idx.cint) != 0:\n" 1213 | glue.add " let tmp = " & constructArg(ctx, s, -1, procName, needCheck) 1214 | if needCheck.len != 0: 1215 | glue.add((" " & needCheck) % ["tmp"]) 1216 | glue.add " result.incl(tmp$1)\n" % [argAttr(s)] 1217 | else: 1218 | glue.add " result.incl(tmp$1)\n" % [argAttr(s)] 1219 | glue.add " L.pop(1.cint)\n" 1220 | glue.add " inc i\n" 1221 | glue.add " if i >= len: break\n" 1222 | gContext.add glue 1223 | 1224 | result = name 1225 | 1226 | proc genSetArg(ctx: proxyDesc, nType: NimNode, i: int, procName: string): string {.compileTime.} = 1227 | let argType = nType[1] 1228 | var needCheck = "" 1229 | let res = constructArg(ctx, argType, -1, procName, needCheck) 1230 | let checkSet = registerSetCheck(ctx, argType, procName) 1231 | var glue = "L.$1($2)\n" % [checkSet, $i] 1232 | if res != "": return glue 1233 | error(procName & ": unknown set param type: " & $nType.kind & "\n" & nType.treeRepr) 1234 | result = "" 1235 | 1236 | proc registerSequenceCheck(ctx: proxyDesc, s: NimNode, procName: string): string {.compileTime.} = 1237 | let name = "checkSequence$1" % [$s] 1238 | var needCheck = "" 1239 | if not hasName(name): 1240 | setName(name) 1241 | var glue = "proc $1(L: PState, idx: int): seq[$2] =\n" % [name, $s] 1242 | glue.add " L.checkType(idx.cint, LUA_TTABLE)\n" 1243 | glue.add " let len = L.llen(idx.cint)\n" 1244 | glue.add " L.pushNil()\n" 1245 | glue.add " result = newSeq[$1](len)\n" % [$s] 1246 | glue.add " var i = 0\n" 1247 | glue.add " while L.next(idx.cint) != 0:\n" 1248 | glue.add " let tmp = " & constructArg(ctx, s, -1, procName, needCheck) 1249 | if needCheck.len != 0: 1250 | glue.add((" " & needCheck) % ["tmp"]) 1251 | glue.add " result[i] = tmp$1\n" % [argAttr(s)] 1252 | else: 1253 | glue.add " result[i] = tmp$1\n" % [argAttr(s)] 1254 | glue.add " L.pop(1.cint)\n" 1255 | glue.add " inc i\n" 1256 | glue.add " if i >= len: break\n" 1257 | gContext.add glue 1258 | 1259 | result = name 1260 | 1261 | proc genSequenceArg(ctx: proxyDesc, nType: NimNode, i: int, procName: string): string {.compileTime.} = 1262 | let argType = nType[1] 1263 | var needCheck = "" 1264 | let res = constructArg(ctx, argType, -1, procName, needCheck) 1265 | let checkSeq = registerSequenceCheck(ctx, argType, procName) 1266 | var glue = "L.$1($2)\n" % [checkSeq, $i] 1267 | if res != "": return glue 1268 | error(procName & ": unknown seq param type: " & $nType.kind & "\n" & nType.treeRepr) 1269 | result = "" 1270 | 1271 | proc genPtrArg(nType: NimNode, i: int, procName: string): string {.compileTime.} = 1272 | let argType = $nType[0].toStrLit 1273 | result = "cast[ptr $1](L.toUserData($2.cint))\n" % [argType, $i] 1274 | 1275 | proc genRangeArg(nType: NimNode, i: int, procName: string): string {.compileTime.} = 1276 | result = "L.nimCheckInteger($1.cint).int\n" % [$i] 1277 | 1278 | proc registerTupleCheck(ctx: proxyDesc, nType: NimNode, procName: string): string {.compileTime.} = 1279 | let name = "checkTuple$1" % [$proxyCount] 1280 | 1281 | if not hasName(name): 1282 | setName(name) 1283 | var glue = "proc $1(L: PState, idx: int): $2 =\n" % [name, nType.toStrLit.strVal] 1284 | glue.add " L.checkType(idx.cint, LUA_TTABLE)\n" 1285 | 1286 | let argList = paramsToArgListBasic(nType, 0) 1287 | for x in argList: 1288 | glue.add " L.pushLiteral(\"$1\")\n" % [$x.mName] 1289 | glue.add " L.getTable(idx.cint)\n" 1290 | var needCheck = "" 1291 | let res = constructArg(ctx, x.mType, -1, procName, needCheck) 1292 | if res == "": 1293 | error(procName & ": unknown tuple param type: " & $x.mType.kind & "\n" & x.mType.treeRepr) 1294 | if needCheck.len != 0: 1295 | glue.add((" " & needCheck) % [res]) 1296 | glue.add " result.$1 = $2" % [$x.mName, res] 1297 | else: 1298 | glue.add " result.$1 = $2" % [$x.mName, res] 1299 | glue.add " L.pop($1)\n" % [$argList.len] 1300 | gContext.add glue 1301 | 1302 | result = name 1303 | 1304 | proc genTupleArg(ctx: proxyDesc, nType: NimNode, i: int, procName: string): string {.compileTime.} = 1305 | let checkTup = registerTupleCheck(ctx, nType, procName) 1306 | var glue = "L.$1($2)\n" % [checkTup, $i] 1307 | result = glue 1308 | 1309 | proc constructComplexArg(ctx: proxyDesc, mType: NimNode, i: int, procName: string, needCheck: var string): string {.compileTime.} = 1310 | if mType.kind == nnkSym: 1311 | let nType = getImpl(mType)[2] 1312 | if nType.kind in {nnkObjectTy, nnkRefTy}: 1313 | needCheck = "if not $1.isNil:\n" 1314 | return checkUD(registerObject(mType), $i) 1315 | 1316 | if nType.kind == nnkDistinctTy: 1317 | return constructArg(ctx, nType[0], i, procName, needcheck) 1318 | 1319 | if nType.kind == nnkEnumTy: 1320 | return constructArg(ctx, bindSym"int", i, procName, needCheck) 1321 | 1322 | if nType.kind == nnkTupleTy: 1323 | return genTupleArg(ctx, nType, i, procName) 1324 | 1325 | if nType.kind == nnkBracketExpr: 1326 | if $nType[0] == "array": 1327 | return genArrayArg(ctx, nType, i, procName) 1328 | if $nType[0] == "set": 1329 | return genSetArg(ctx, nType, i, procName) 1330 | if $nType[0] == "seq": 1331 | return genSequenceArg(ctx, nType, i, procName) 1332 | if $nType[0] == "range": 1333 | return genRangeArg(nType, i, procName) 1334 | 1335 | if nType.kind == nnkPtrTy: 1336 | return genPtrArg(nType, i, procName) 1337 | 1338 | if nType.kind == nnkSym: 1339 | return constructBasicArg(nType, i, procName) 1340 | 1341 | if mType.kind == nnkBracketExpr: 1342 | if $mType[0] == "array": 1343 | return genArrayArg(ctx, mType, i, procName) 1344 | if $mType[0] == "set": 1345 | return genSetArg(ctx, mType, i, procName) 1346 | if $mType[0] == "seq": 1347 | return genSequenceArg(ctx, mType, i, procName) 1348 | if $mType[0] == "range": 1349 | return genRangeArg(mType, i, procName) 1350 | if ($mType[0]).toLowerAscii == "openarray": 1351 | return genSequenceArg(ctx, mType, i, procName) 1352 | 1353 | if mType.kind == nnkPtrTy: 1354 | return genPtrArg(mType, i, procName) 1355 | 1356 | if mType.kind == nnkTupleTy: 1357 | return genTupleArg(ctx, mType, i, procName) 1358 | 1359 | if mType.kind == nnkVarTy: 1360 | let nType = getType(mType[0]) 1361 | if nType.kind in {nnkObjectTy, nnkRefTy}: 1362 | needCheck = "if not $1.isNil:\n" 1363 | return checkUD(registerObject(mType[0]), $i) 1364 | if nType.kind == nnkSym: 1365 | outValList.add constructBasicRet(nType, "arg" & $(i-1), "", procName) 1366 | return constructBasicArg(nType, i, procName) 1367 | 1368 | if mType.kind == nnkEnumTy: 1369 | return "L.nimCheckInteger($1.cint).$2\n" % [$i, $mType[0]] 1370 | 1371 | error(procName & ": unknown param type: " & $mType.kind & "\n" & mType.treeRepr) 1372 | result = "" 1373 | 1374 | proc constructRet(retType: NimNode, procCall, indent, procName: string): string {.compileTime.} 1375 | 1376 | proc genArrayRet(nType: NimNode, procCall, indent, procName: string): string {.compileTime.} = 1377 | var lo, hi, mode: int 1378 | 1379 | if nType[1].kind == nnkInfix: 1380 | lo = int(nType[1][1].intVal) 1381 | hi = int(nType[1][2].intVal) 1382 | mode = 1 1383 | elif nType[1].kind == nnkIntLit: 1384 | lo = 0 1385 | hi = int(nType[1].intVal) 1386 | mode = 2 1387 | elif nType[1].kind == nnkIdent: 1388 | mode = 3 1389 | else: 1390 | error(procName & ": unknown array ret type: " & nType.treeRepr) 1391 | 1392 | let retType = nType[2] 1393 | 1394 | var glue = indent & "L.createTable($1+1, 0)\n" % [$hi] 1395 | glue.add indent & "let arrTmp = $1\n" % [procCall] 1396 | var res = "" 1397 | 1398 | if mode == 1: 1399 | glue.add indent & "for i in $1..$2:\n" % [$lo, $hi] 1400 | glue.add indent & " L.pushInteger(i-$1+1)\n" % [$lo] 1401 | res = constructRet(retType, "arrTmp[i-$1]" % [$lo], indent & " ", procName) 1402 | glue.add res 1403 | elif mode == 2: 1404 | glue.add indent & "for i in $1..$2-1:\n" % [$lo, $hi] 1405 | glue.add indent & " L.pushInteger(i-$1+1)\n" % [$lo] 1406 | res = constructRet(retType, "arrTmp[i-$1]" % [$lo], indent & " ", procName) 1407 | glue.add res 1408 | else: 1409 | glue.add indent & "for i in 0..$1-1:\n" % [$nType[1]] 1410 | glue.add indent & " L.pushInteger(i+1)\n" 1411 | res = constructRet(retType, "arrTmp[i]", indent & " ", procName) 1412 | glue.add res 1413 | 1414 | glue.add indent & " L.setTable(-3)\n" 1415 | 1416 | if res != "": return glue 1417 | 1418 | error(procName & ": unknown array ret type: " & $nType.kind & "\n" & nType.treeRepr) 1419 | result = "" 1420 | 1421 | proc genSetRet(nType: NimNode, procCall, indent, procName: string): string {.compileTime.} = 1422 | let retType = nType[1] 1423 | var glue = "" 1424 | 1425 | glue.add indent & "let setTmp = $1\n" % [procCall] 1426 | glue.add indent & "L.createTable((card(setTmp)+1).cint, 0.cint)\n" 1427 | glue.add indent & "var setIdx = 1\n" 1428 | 1429 | var res = "" 1430 | glue.add indent & "for k in setTmp:\n" 1431 | glue.add indent & " L.pushInteger(setIdx)\n" 1432 | res = constructRet(retType, "k", indent & " ", procName) 1433 | glue.add res 1434 | glue.add indent & " L.setTable(-3)\n" 1435 | glue.add indent & " inc setIdx\n" 1436 | if res != "": return glue 1437 | 1438 | error(procName & ": unknown set ret type: " & $nType.kind & "\n" & nType.treeRepr) 1439 | result = "" 1440 | 1441 | proc genSequenceRet(nType: NimNode, procCall, indent, procName: string): string {.compileTime.} = 1442 | let retType = nType[1] 1443 | var glue = "" 1444 | 1445 | glue.add indent & "let seqTmp = $1\n" % [procCall] 1446 | glue.add indent & "L.createTable((seqTmp.len+1).cint, 0.cint)\n" 1447 | glue.add indent & "var seqIdx = 1\n" 1448 | 1449 | var res = "" 1450 | glue.add indent & "for k in seqTmp:\n" 1451 | glue.add indent & " L.pushInteger(seqIdx)\n" 1452 | res = constructRet(retType, "k", indent & " ", procName) 1453 | glue.add res 1454 | glue.add indent & " L.setTable(-3)\n" 1455 | glue.add indent & " inc seqIdx\n" 1456 | if res != "": return glue 1457 | 1458 | error(procName & ": unknown seq ret type: " & $nType.kind & "\n" & nType.treeRepr) 1459 | result = "" 1460 | 1461 | proc genTupleRet(nType: NimNode, procCall, indent, procName: string): string {.compileTime.} = 1462 | var glue = "" 1463 | glue.add indent & "let tupTmp = $1\n" % [procCall] 1464 | glue.add indent & "L.createTable(0.cint, $1.cint)\n" % [$nType.len] 1465 | 1466 | let argList = paramsToArgListBasic(nType, 0) 1467 | 1468 | for x in argList: 1469 | glue.add indent & "L.pushLiteral(\"$1\")\n" % [$x.mName] 1470 | let res = constructRet(x.mType, "tupTmp." & $x.mName, indent, procName) 1471 | if res == "": 1472 | error(procName & ": unknown ret tuple field type: " & $nType.kind & "\n" & nType.treeRepr) 1473 | glue.add res 1474 | glue.add indent & "L.setTable(-3)\n" 1475 | 1476 | result = glue 1477 | 1478 | proc constructComplexRet(mType: NimNode, procCall, indent, procName: string): string {.compileTime.} = 1479 | if mType.kind == nnkSym: 1480 | let nType = getImpl(mType)[2] 1481 | 1482 | if nType.kind == nnkBracketExpr: 1483 | if $nType[0] == "array": 1484 | return genArrayRet(nType, procCall, indent, procName) 1485 | if $nType[0] == "set": 1486 | return genSetRet(nType, procCall, indent, procName) 1487 | if $nType[0] == "seq": 1488 | return genSequenceRet(nType, procCall, indent, procName) 1489 | if $nType[0] == "range": 1490 | return indent & "L.pushInteger(lua_Integer(" & procCall & "))\n" 1491 | 1492 | if nType.kind in {nnkObjectTy, nnkRefTy}: 1493 | let subjectName = registerObject(mType) 1494 | var glue = "" 1495 | if isRefType(mType): 1496 | glue.add indent & "var pxret: NL_$1Proxy\n" % [subjectName] 1497 | glue.add indent & "pxret.ud = $1\n" % [procCall] 1498 | glue.add indent & "let proxyret = getRegisteredUD[NL_$1Proxy](L, pxret)\n" % [subjectName] 1499 | glue.add indent & "if proxyret.isNil: return 0\n" 1500 | else: 1501 | glue.add indent & "var proxyret = " & newUD(subjectName) 1502 | glue.add indent & "proxyret.ud = $1\n" % [procCall] 1503 | 1504 | glue.add indent & "L.nimGetMetatable(NL_$1)\n" % [subjectName] 1505 | glue.add indent & "discard L.setMetatable(-2)\n" 1506 | return glue 1507 | 1508 | if nType.kind == nnkDistinctTy: 1509 | return constructRet(nType[0], procCall, indent, procName) 1510 | 1511 | if nType.kind == nnkEnumTy: 1512 | return indent & "L.pushInteger(lua_Integer(" & procCall & "))\n" 1513 | 1514 | if nType.kind == nnkTupleTy: 1515 | return genTupleRet(nType, procCall, indent, procName) 1516 | 1517 | if nType.kind == nnkPtrTy: 1518 | return indent & "L.pushLightUserData(cast[pointer](" & procCall & "))\n" 1519 | 1520 | if nType.kind == nnkSym: 1521 | return constructRet(nType, procCall, indent, procName) 1522 | 1523 | if mType.kind == nnkTupleTy: 1524 | return genTupleRet(mType, procCall, indent, procName) 1525 | 1526 | if mType.kind == nnkPtrTy: 1527 | return indent & "L.pushLightUserData(cast[pointer](" & procCall & "))\n" 1528 | 1529 | if mType.kind == nnkBracketExpr: 1530 | if $mType[0] == "array": 1531 | return genArrayRet(mType, procCall, indent, procName) 1532 | if $mType[0] == "set": 1533 | return genSetRet(mType, procCall, indent, procName) 1534 | if $mType[0] == "seq": 1535 | return genSequenceRet(mType, procCall, indent, procName) 1536 | if $mType[0] == "range": 1537 | return indent & "L.pushInteger(lua_Integer(" & procCall & "))\n" 1538 | 1539 | if mType.kind == nnkVarTy: 1540 | if getType(mType[0]).kind in {nnkObjectTy, nnkRefTy}: 1541 | let subjectName = registerObject(mType) 1542 | var glue = "" 1543 | 1544 | if getType(mType[0]).kind == nnkRefTy: 1545 | glue.add indent & "var pxvar: NL_$1Proxy\n" % [subjectName] 1546 | glue.add indent & "pxvar.ud = $1\n" % [procCall] 1547 | glue.add indent & "let proxy = getRegisteredUD[NL_$1Proxy](L, pxvar)\n" % [subjectName] 1548 | glue.add indent & "if proxy.isNil: return 0\n" 1549 | else: 1550 | glue.add indent & "var proxy = " & newUD(subjectName) 1551 | glue.add indent & "proxy.ud = $1\n" % [procCall] 1552 | 1553 | glue.add indent & "L.nimGetMetatable(NL_$1)\n" % [subjectName] 1554 | glue.add indent & "discard L.setMetatable(-2)\n" 1555 | return glue 1556 | 1557 | error(procName & ": unknown ret type: " & $mType.kind & "\n" & mType.treeRepr) 1558 | result = "" 1559 | 1560 | proc constructArg(ctx: proxyDesc, mType: NimNode, i: int, procName: string, needCheck: var string): string = 1561 | case mType.kind: 1562 | of nnkSym: 1563 | result = constructBasicArg(mType, i, procName) 1564 | if result == "": result = constructComplexArg(ctx, mType, i, procName, needCheck) 1565 | else: 1566 | result = constructComplexArg(ctx, mType, i, procName, needCheck) 1567 | 1568 | proc constructRet(retType: NimNode, procCall, indent, procName: string): string = 1569 | case retType.kind: 1570 | of nnkSym: 1571 | result = constructBasicRet(retType, procCall, indent, procName) 1572 | if result == "": result = constructComplexRet(retType, procCall, indent, procName) 1573 | else: 1574 | result = constructComplexRet(retType, procCall, indent, procName) 1575 | 1576 | proc findSymbol(ctx: proxyDesc, s: NimNode): NimNode = 1577 | for k in ctx.symList: 1578 | if $s == $k: return getType(k) 1579 | result = newEmptyNode() 1580 | 1581 | proc genOvCallSingle(ctx: proxyDesc, ovp: ovProcElem, procName, indent: string, flags: ovFlags, bd: bindDesc, ovIdx: int = 0): string {.compileTime.} = 1582 | var glueParam = "" 1583 | var glue = "" 1584 | let start = if ovfUseObject in flags: 1 else: 0 1585 | outValList = @[] 1586 | 1587 | if bd.bindKind == isClosure: 1588 | glue.add getClosureEnv("L", procName, ovIdx) 1589 | 1590 | for i in start..ovp.params.len-1: 1591 | var needCheck = "" 1592 | let param = ovp.params[i] 1593 | let pType = if param.mType.kind != nnkEmpty: param.mType else: findSymbol(ctx, param.mVal) 1594 | glue.add indent & " var arg" & $i & " = " & constructArg(ctx, pType, i + 1, procName, needCheck) 1595 | if needCheck.len != 0: 1596 | glue.add(indent & " if arg" & $i & ".isNil: return 0\n") 1597 | glueParam.add "arg" & $i & argAttr(param.mType) 1598 | if i < ovp.params.len-1: glueParam.add ", " 1599 | 1600 | let comma = if glueParam.len == 0: "" else: ", " 1601 | if ovfConstructor in flags: 1602 | let procCall = if bd.bindKind == isClosure: 1603 | "clsProc$3($1$2clsEnv$3)" % [glueParam, comma, $ovIdx] 1604 | else: 1605 | procName & "(" & glueParam & ")" 1606 | glue.add indent & " proxy.ud = " & procCall & "\n" 1607 | else: 1608 | let procCall = if ovfUseObject in flags: 1609 | if bd.bindKind == isClosure: 1610 | "proxy.ud.clsProc$3($1$2clsEnv$3)" % [glueParam, comma, $ovIdx] 1611 | else: 1612 | "proxy.ud.$1($2)" % [procName, glueParam] 1613 | else: 1614 | if bd.bindKind == isClosure: 1615 | "clsProc$3($1$2clsEnv$3)" % [glueParam, comma, $ovIdx] 1616 | else: 1617 | procName & "(" & glueParam & ")" 1618 | 1619 | if ovfUseRet in flags: 1620 | var numRet = 0 1621 | if ovp.retType.kind == nnkEmpty: 1622 | glue.add indent & " " & procCall & "\n" 1623 | else: 1624 | glue.add constructRet(ovp.retType, procCall, indent & " ", procName) 1625 | numRet = 1 1626 | 1627 | inc(numRet, outValList.len) 1628 | for s in outValList: 1629 | glue.add "$1 $2" % [indent, s] 1630 | 1631 | glue.add "$1 return $2\n" % [indent, $numRet] 1632 | result = glue 1633 | 1634 | proc bindSingleFunction(ctx: proxyDesc, bd: bindDesc, n: NimNode, glueProc, procName, SL: string): string {.compileTime.} = 1635 | if n.kind != nnkProcDef: 1636 | error("bindFunction: " & procName & " is not a proc") 1637 | 1638 | let params = n[3] 1639 | let retType = replaceRet(params[0], bd.genericParams, n[2]) 1640 | let argList = paramsToArgList(params, bd.genericParams, n[2]) 1641 | 1642 | var glue = "" 1643 | if bd.bindKind == isClosure: glue.add addClosureEnv(SL, procName, n, bd) 1644 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 1645 | glue.add genOvCallSingle(ctx, newProcElem(retType, argList), procName, "", {ovfUseRet}, bd) 1646 | result = glue 1647 | 1648 | proc genBasicCheck(mType: NimNode, i: int, procNane: string): string {.compileTime.} = 1649 | let argType = $mType 1650 | for c in intTypes: 1651 | if c == argType: 1652 | return "(L.isInteger(" & $i & ") == 1)" 1653 | 1654 | for c in floatTypes: 1655 | if c == argType: 1656 | return "(L.isNumber(" & $i & ") == 1)" 1657 | 1658 | if argType == "string": 1659 | return "(L.isStrictString(" & $i & ") == true)" 1660 | 1661 | if argType == "cstring": 1662 | return "(L.isStrictString(" & $i & ") == true)" 1663 | 1664 | if argType == "bool": 1665 | return "L.isBoolean(" & $i & ")" 1666 | 1667 | if argType == "char": 1668 | return "(L.isInteger(" & $i & ") == 1)" 1669 | 1670 | if argType == "pointer": 1671 | return "(L.isUserData(" & $i & ") == 1)" 1672 | 1673 | result = "" 1674 | 1675 | proc genCheckType(mType: NimNode, i: int, procName: string): string {.compileTime.} 1676 | 1677 | proc genComplexCheck(mType: NimNode, i: int, procName: string): string {.compileTime.} = 1678 | if mType.kind == nnkSym: 1679 | let nType = getImpl(mType)[2] 1680 | if nType.kind in {nnkObjectTy, nnkRefTy}: 1681 | return "(L.isUserData(" & $i & ") == 1)" 1682 | 1683 | if nType.kind == nnkDistinctTy: 1684 | return genCheckType(nType, i, procName) 1685 | 1686 | if nType.kind == nnkEnumTy: 1687 | return "(L.isInteger(" & $i & ") == 1)" 1688 | 1689 | if nType.kind == nnkBracketExpr: 1690 | if $nType[0] == "array": 1691 | return "L.isTable(" & $i & ")" 1692 | if $nType[0] == "set": 1693 | return "L.isTable(" & $i & ")" 1694 | if $nType[0] == "seq": 1695 | return "L.isTable(" & $i & ")" 1696 | if $nType[0] == "range": 1697 | return "L.isTable(" & $i & ")" 1698 | 1699 | if nType.kind == nnkPtrTy: 1700 | return "(L.isLightUserData(" & $i & ") == 1)" 1701 | 1702 | if nType.kind == nnkSym: 1703 | if $nType == "pointer": 1704 | return "(L.isLightUserData(" & $i & ") == 1)" 1705 | 1706 | if mType.kind == nnkBracketExpr: 1707 | if $mType[0] == "array": 1708 | return "L.isTable(" & $i & ")" 1709 | if $mType[0] == "set": 1710 | return "L.isTable(" & $i & ")" 1711 | if $mType[0] == "seq": 1712 | return "L.isTable(" & $i & ")" 1713 | if $mType[0] == "range": 1714 | return "(L.isInteger(" & $i & ") == 1)" 1715 | 1716 | if mType.kind == nnkPtrTy: 1717 | return "(L.isLightUserData(" & $i & ") == 1)" 1718 | 1719 | if mType.kind == nnkVarTy: 1720 | let nType = getType(mType[0]) 1721 | if nType.kind in {nnkObjectTy, nnkRefTy}: 1722 | return "(L.isUserData(" & $i & ") == 1)" 1723 | if nType.kind == nnkSym: 1724 | return genCheckType(nType, i, procName) 1725 | 1726 | if mType.kind == nnkEnumTy: 1727 | return "(L.isInteger(" & $i & ") == 1)" 1728 | 1729 | error(procName & " : unknown param type: " & $mType.kind & "\n" & mType.treeRepr) 1730 | result = "" 1731 | 1732 | proc genCheckType(mType: NimNode, i: int, procName: string): string = 1733 | case mType.kind: 1734 | of nnkSym: 1735 | result = genBasicCheck(mType, i, procName) 1736 | if result == "": result = genComplexCheck(mType, i, procName) 1737 | else: 1738 | result = genComplexCheck(mType, i, procName) 1739 | 1740 | #second level of ov proc resolution 1741 | proc genCheck(params: seq[argDesc], flags: ovFlags, procName: string): string {.compileTime.} = 1742 | var glue = " if " 1743 | let start = if ovfUseObject in flags: 1 else: 0 1744 | for i in start..params.len-1: 1745 | glue.add genCheckType(params[i].mType, i + 1, procName) 1746 | if i < params.len-1: 1747 | glue.add " and " 1748 | else: 1749 | glue.add ":\n" 1750 | result = glue 1751 | 1752 | #overloaded proc need to be resolved by their params count and params type 1753 | #genCheck generate code to check params type 1754 | proc genOvCallMany(ctx: proxyDesc, ovp: seq[ovProcElem], procName: string, flags: ovFlags, bd: bindDesc): string {.compileTime.} = 1755 | var glue = "" 1756 | var i = 0 1757 | for ov in ovp: 1758 | glue.add genCheck(ov.params, flags, procName) 1759 | glue.add genOvCallSingle(ctx, ov, procName, " ", flags, bd, i) 1760 | inc i 1761 | result = glue 1762 | 1763 | proc genOvCall(ctx: proxyDesc, ovp: seq[ovProc], procName: string, flags: ovFlags, bd: bindDesc): string {.compileTime.} = 1764 | var glue = " let numArgs = L.getTop().int\n" 1765 | for i in 0.. export nim function(s) with lua scope named "libName" 1850 | # * bindFunction(luaState, ident1, ident2, .., identN) 1851 | # -> export nim function(s) to lua global scope 1852 | 1853 | macro bindFunction*(arg: varargs[untyped]): untyped = 1854 | result = genProxyMacro(arg, {nlbUSeLib, nlbRegisterClosure, nlbRegisterGeneric}, "Function") 1855 | 1856 | macro bindProc*(arg: varargs[untyped]): untyped = 1857 | result = genProxyMacro(arg, {nlbUSeLib, nlbRegisterClosure, nlbRegisterGeneric}, "Function") 1858 | 1859 | # ---------------------------------------------------------------------- 1860 | # ----------------------------- bindConst ------------------------------ 1861 | # ---------------------------------------------------------------------- 1862 | 1863 | proc constructConstBasic(SL, name, indent: string, n: NimNode): string {.compileTime.} = 1864 | if n.kind in {nnkCharLit..nnkUInt64Lit}: 1865 | var nlb = indent & "when not internalTestForBOOL($1[0]):\n" % [name] 1866 | nlb.add indent & " $1.pushInteger(lua_Integer($2[i]))\n" % [SL, name] 1867 | nlb.add indent & "else:\n" 1868 | nlb.add indent & " $1.pushBoolean($2[i].cint)\n" % [SL, name] 1869 | return nlb 1870 | 1871 | if n.kind in {nnkFloatLit..nnkFloat64Lit}: 1872 | return indent & "$1.pushNumber(lua_Number($2[i]))\n" % [SL, name] 1873 | 1874 | if n.kind in {nnkStrLit, nnkRStrLit, nnkTripleStrLit}: 1875 | return indent & "discard $1.pushLString($2[i], $2[i].len)\n" % [SL, name] 1876 | 1877 | result = "" 1878 | 1879 | proc constructConstParBasic(SL, indent: string, n: NimNode, name: string, idx: int): string {.compileTime.} = 1880 | if n.kind in {nnkCharLit..nnkUInt64Lit}: 1881 | var nlb = indent & "when not internalTestForBOOL($2[0][0]):\n" % [SL, name] 1882 | nlb.add indent & " $1.pushInteger(lua_Integer($2[i][$3]))\n" % [SL, name, $idx] 1883 | nlb.add indent & "else:\n" 1884 | nlb.add indent & " $1.pushBoolean($2[i][$3].cint)\n" % [SL, name, $idx] 1885 | return nlb 1886 | 1887 | if n.kind in {nnkFloatLit..nnkFloat64Lit}: 1888 | return indent & "$1.pushNumber(lua_Number($2[i][$3]))\n" % [SL, name, $idx] 1889 | 1890 | if n.kind in {nnkStrLit, nnkRStrLit, nnkTripleStrLit}: 1891 | return indent & "discard $1.pushLString($2[i][$3], $2[i][$3].len)\n" % [SL, name, $idx] 1892 | 1893 | result = "" 1894 | 1895 | proc constructConstPar(SL, name, indent: string, n: NimNode): string {.compileTime.} = 1896 | result = constructConstParBasic(SL, indent, n[0], name, 0) 1897 | result.add constructConstParBasic(SL, indent, n[1], name, 1) 1898 | 1899 | proc constructConst(SL: string, n: NimNode, name: string): string {.compileTime.} = 1900 | if n.kind in {nnkCharLit..nnkUInt64Lit}: 1901 | var nlb = "when not internalTestForBOOL($1):\n" % [name] 1902 | nlb.add " $1.pushInteger(lua_Integer($2))\n" % [SL, $(n.intVal)] 1903 | nlb.add "else:\n" 1904 | nlb.add " $1.pushBoolean($2.cint)\n" % [SL, $(n.intVal)] 1905 | return nlb 1906 | 1907 | if n.kind in {nnkFloatLit..nnkFloat64Lit}: 1908 | return "$1.pushNumber(lua_Number($2))\n" % [SL, $(n.floatVal)] 1909 | 1910 | if n.kind in {nnkStrLit, nnkRStrLit, nnkTripleStrLit}: 1911 | return "discard $1.pushLString(\"$2\", $3)\n" % [SL, n.strVal, $(n.strVal.len)] 1912 | 1913 | if n.kind == nnkBracket: 1914 | if n[0].kind in {nnkCharLit..nnkTripleStrLit}: 1915 | var nlb = "$1.createTable($2, 0)\n" % [SL, $n.len] 1916 | nlb.add "for i in 0..$1:\n" % [$(n.len-1)] 1917 | nlb.add constructConstBasic(SL, name, " ", n[0]) 1918 | nlb.add " $1.rawSeti(-2, i)\n" % [SL] 1919 | return nlb 1920 | elif n[0].kind in {nnkPar, nnkTupleConstr}: 1921 | if n[0].len == 2: 1922 | var nlb = "$1.createTable(0, $2)\n" % [SL, $n.len] 1923 | nlb.add "for i in 0..$1:\n" % [$(n.len-1)] 1924 | nlb.add constructConstPar(SL, name, " ", n[0]) 1925 | nlb.add " $1.setTable(-3)\n" % [SL] 1926 | return nlb 1927 | 1928 | result = "" 1929 | 1930 | proc bindConstImpl*(ctx: proxyDesc): NimNode {.compileTime.} = 1931 | let 1932 | SL = ctx.luaCtx 1933 | libName = if ctx.libName.kind != nnkEmpty: $ctx.libName else: "" 1934 | libKind = ctx.libName.kind 1935 | arg = ctx.bindList 1936 | exportLib = libName != "" and libName != "GLOBAL" or (libName == "GLOBAL" and libKind == nnkStrLit) 1937 | 1938 | var glue = "" 1939 | if exportLib: 1940 | glue.add addMemberCap(SL, libName, arg.len) 1941 | 1942 | for i in 0..arg.len-1: 1943 | let n = arg[i] 1944 | if n.node.kind != nnkSym: 1945 | error("bindConst: arg[" & $i & "] need symbol not " & $n.node.kind) 1946 | 1947 | let exportedName = n.name 1948 | 1949 | if exportLib: 1950 | glue.add "discard " & SL & ".pushString(\"" & exportedName & "\")\n" 1951 | glue.add constructConst(SL, getImpl(n.node), $n.node) 1952 | glue.add SL & ".setTable(-3)\n" 1953 | else: 1954 | glue.add constructConst(SL, getImpl(n.node), $n.node) 1955 | glue.add SL & ".setGlobal(\"" & exportedName & "\")\n" 1956 | 1957 | if exportLib: 1958 | glue.add SL & ".setGlobal(\"" & libName & "\")\n" 1959 | 1960 | result = parseCode(glue) 1961 | 1962 | macro bindConst*(arg: varargs[untyped]): untyped = 1963 | result = genProxyMacro(arg, {nlbUseLib}, "Const") 1964 | 1965 | # ----------------------------------------------------------------------- 1966 | # ----------------------------- bindObject ------------------------------ 1967 | # ----------------------------------------------------------------------- 1968 | 1969 | proc bindSingleConstructor(ctx: proxyDesc, bd: bindDesc, n: NimNode, glueProc, procName, subjectName: string): string {.compileTime.} = 1970 | if n.kind notin {nnkProcDef, nnkTemplateDef}: 1971 | error("bindFunction: " & procName & " is not a proc") 1972 | 1973 | let 1974 | params = n[3] 1975 | retType = replaceRet(params[0], bd.genericParams, n[2]) 1976 | SL = ctx.luaCtx 1977 | subject = ctx.subject 1978 | 1979 | if subject.kind != retType.kind and $subject != $retType: 1980 | error("invalid constructor ret type") 1981 | 1982 | let argList = paramsToArgList(params, bd.genericParams, n[2]) 1983 | 1984 | var glue = "" 1985 | if bd.bindKind == isClosure: glue.add addClosureEnv(SL, procName, n, bd) 1986 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 1987 | if isRefType(subject): 1988 | glue.add " var proxy: NL_$1Proxy\n" % [subjectName] 1989 | glue.add genOvCallSingle(ctx, newProcElem(retType, argList), procName, "", {ovfConstructor}, bd) 1990 | glue.add " let ret = getRegisteredUD[NL_$1Proxy](L, proxy)\n" % [subjectName] 1991 | glue.add " if ret.isNil: return 0\n" 1992 | else: 1993 | glue.add " var proxy = " & newUD(subjectName) 1994 | #always zeroed the memory if you mix gc code and unmanaged code 1995 | #otherwise, strange things will happened 1996 | if isObjectType(subject): glue.add " zeroMem(proxy, sizeof(NL_$1Proxy))\n" % [subjectName] 1997 | glue.add genOvCallSingle(ctx, newProcElem(retType, argList), procName, "", {ovfConstructor}, bd) 1998 | 1999 | glue.add " L.nimGetMetatable(NL_$1)\n" % [subjectName] 2000 | glue.add " discard L.setMetatable(-2)\n" 2001 | glue.add " result = 1\n" 2002 | result = glue 2003 | 2004 | proc eqType(a, b: NimNode): bool {.compileTime.} = 2005 | if a.kind == nnkSym and b.kind == nnkVarTy: 2006 | if sameType(a, b[0]): return true 2007 | result = sameType(a, b) 2008 | 2009 | proc getParentName(n: NimNode, res: var seq[string]) = 2010 | var prefix = "" 2011 | var a = getImpl(n)[2] 2012 | if a.kind notin {nnkObjectTy, nnkRefTy}: return 2013 | if a.kind == nnkRefTy: 2014 | a = a[0] 2015 | prefix = "ref " 2016 | if a.kind != nnkObjectTy: return 2017 | a = a[1] 2018 | if a.kind != nnkOfInherit: return 2019 | res.add(prefix & $a[0]) 2020 | getParentName(a[0], res) 2021 | 2022 | proc isDescendant(a, b: NimNode): bool {.compileTime.} = 2023 | if b.kind notin {nnkRefTy, nnkSym}: return false 2024 | let bType = if b.kind == nnkRefTy: "ref " & $b[0] else: $b 2025 | var parents = newSeq[string]() 2026 | getParentName(a, parents) 2027 | result = parents.contains(bType) 2028 | 2029 | proc eqTypeOrDescendant(a, b: NimNode): bool {.compileTime.} = 2030 | result = eqType(a, b) or isDescendant(a, b) 2031 | 2032 | proc getRegisteredUD*[T](L: PState, proxy: T): ptr T = 2033 | # get ref type userdata from REGISTRYINDEX 2034 | # if it's already there or create one if it's not there 2035 | # this help us to communicate with lua side from Nim 2036 | if proxy.ud == nil: return nil 2037 | 2038 | # proxy.ud must be larger than NLMaxID 2039 | doAssert((cast[int](proxy.ud) and (not NLMaxID)) != 0) 2040 | 2041 | L.pushLightUserData(cast[pointer](proxy.ud)) 2042 | L.rawGet(LUA_REGISTRYINDEX) 2043 | if not L.isNil(-1): # name already in use? 2044 | result = cast[ptr T](L.toUserData(-1)) 2045 | result.ud = proxy.ud 2046 | return result 2047 | 2048 | L.pop(1) # pop nil 2049 | result = cast[ptr T](L.newUserData(sizeof(T))) 2050 | L.pushLightUserData(cast[pointer](proxy.ud)) 2051 | L.pushValue(-2) 2052 | L.rawSet(LUA_REGISTRYINDEX) 2053 | 2054 | #always zeroed the memory if you mix gc code and non gc code 2055 | zeroMem(result, sizeof(T)) 2056 | result.ud = proxy.ud 2057 | GC_ref(result.ud) 2058 | 2059 | proc bindOverloadedConstructor(ctx: proxyDesc, bd: bindDesc, ov: NimNode, glueProc, procName, subjectName: string): string {.compileTime.} = 2060 | var ovl = newSeq[ovProc]() 2061 | 2062 | let 2063 | SL = ctx.luaCtx 2064 | subject = ctx.subject 2065 | 2066 | var glue = "" 2067 | var i = 0 2068 | 2069 | for s in children(ov): 2070 | let n = getImpl(s) 2071 | if n.kind != nnkProcDef: 2072 | error("bindObject: " & procName & " is not a proc") 2073 | 2074 | if bd.bindKind == isClosure: glue.add addClosureEnv(SL, procName, n, bd, i) 2075 | let params = n[3] 2076 | let retType = replaceRet(params[0], bd.genericParams, n[2]) 2077 | let argList = paramsToArgList(params, bd.genericParams, n[2]) 2078 | 2079 | #not a valid constructor 2080 | if subject.kind != retType.kind and not eqTypeOrDescendant(subject, retType): continue 2081 | ovl.addOvProc(retType, argList) 2082 | inc i 2083 | 2084 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 2085 | if isRefType(subject): 2086 | glue.add " var proxy: NL_$1Proxy\n" % [subjectName] 2087 | glue.add genOvCall(ctx, ovl, procName, {ovfConstructor}, bd) 2088 | glue.add " let ret = getRegisteredUD[NL_$1Proxy](L, proxy)\n" % [subjectName] 2089 | glue.add " if ret.isNil: return 0\n" 2090 | else: 2091 | glue.add " var proxy = " & newUD(subjectName) 2092 | #always zeroed the memory if you mix gc code and unmanaged code 2093 | #otherwise, strange things will happened 2094 | if isObjectType(subject): glue.add " zeroMem(proxy, sizeof(NL_$1Proxy))\n" % [subjectName] 2095 | glue.add genOvCall(ctx, ovl, procName, {ovfConstructor}, bd) 2096 | 2097 | glue.add " L.nimGetMetatable(NL_$1)\n" % [subjectName] 2098 | glue.add " discard L.setMetatable(-2)\n" 2099 | glue.add " result = 1\n" 2100 | result = glue 2101 | 2102 | proc bindObjectSingleMethod(ctx: proxyDesc, bd: bindDesc, n: NimNode, glueProc, procName, subjectName: string): string {.compileTime.} = 2103 | if n.kind notin {nnkProcDef, nnkTemplateDef}: 2104 | error("bindFunction: " & procName & " is not a proc/template") 2105 | 2106 | let 2107 | params = n[3] 2108 | retType = replaceRet(params[0], bd.genericParams, n[2]) 2109 | argList = paramsToArgList(params, bd.genericParams, n[2]) 2110 | SL = ctx.luaCtx 2111 | subject = ctx.subject 2112 | 2113 | if eqTypeOrDescendant(subject, retType): 2114 | return bindSingleConstructor(ctx, bd, n, glueProc, procName, subjectName) 2115 | 2116 | if argList.len == 0: 2117 | error(procName & ": invalid object method") 2118 | 2119 | if not eqTypeOrDescendant(subject, argList[0].mType): 2120 | error("object method need object type as first param: " & procName) 2121 | 2122 | var glue = "" 2123 | if bd.bindKind == isClosure: glue.add addClosureEnv(SL, procName, n, bd) 2124 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 2125 | glue.add " var proxy = " & checkUD(subjectName, "1") 2126 | glue.add " if proxy.isNil: return 0\n" 2127 | glue.add genOvCallSingle(ctx, newProcElem(retType, argList), procName, "", {ovfUseObject, ovfUseRet}, bd) 2128 | result = glue 2129 | 2130 | proc bindObjectOverloadedMethod(ctx: proxyDesc, bd: bindDesc, ov: NimNode, glueProc, procName, subjectName: string): string {.compileTime.} = 2131 | var ovl = newSeq[ovProc]() 2132 | var ovc = newNimNode(nnkClosedSymChoice) 2133 | let 2134 | SL = ctx.luaCtx 2135 | subject = ctx.subject 2136 | 2137 | var glue = "" 2138 | var i = 0 2139 | 2140 | for s in children(ov): 2141 | let n = getImpl(s) 2142 | if n.kind notin {nnkProcDef, nnkTemplateDef}: 2143 | error("bindConstructor: " & procName & " is not a proc/template") 2144 | 2145 | if bd.bindKind == isClosure: glue.add addClosureEnv(SL, procName, n, bd, i) 2146 | let params = n[3] 2147 | let retType = replaceRet(params[0], bd.genericParams, n[2]) 2148 | let argList = paramsToArgList(params, bd.genericParams, n[2]) 2149 | 2150 | if eqTypeOrDescendant(subject, retType): #constructor like 2151 | ovc.add s 2152 | continue 2153 | 2154 | if argList.len == 0: continue #not a valid object method 2155 | if not eqTypeOrDescendant(subject, argList[0].mType): continue 2156 | ovl.addOvProc(retType, argList) 2157 | inc i 2158 | 2159 | if ovc.len > 0: 2160 | glue.add bindOverloadedConstructor(ctx, bd, ovc, glueProc, procName, subjectName) 2161 | return glue 2162 | 2163 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 2164 | glue.add " var proxy = " & checkUD(subjectName, "1") 2165 | glue.add " if proxy.isNil: return 0\n" 2166 | glue.add genOvCall(ctx, ovl, procName, {ovfUseObject, ovfUseRet}, bd) 2167 | glue.add " discard luaError(L, \"$1: invalid param count\")\n" % [procName] 2168 | glue.add " return 0\n" 2169 | result = glue 2170 | 2171 | proc bindGetter(ctx: proxyDesc, glueProc, propName, subjectName: string, propType, subject: NimNode): string {.compileTime.} = 2172 | var glue = "" 2173 | 2174 | let 2175 | procCall = "proxy.ud." & propName 2176 | procName = $subject & "." & propName 2177 | 2178 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 2179 | glue.add " var proxy = " & checkUD(subjectName, "1") 2180 | glue.add " if proxy.isNil: return 0\n" 2181 | glue.add constructRet(propType, procCall, " ", procName) 2182 | glue.add " return 1\n" 2183 | result = glue 2184 | 2185 | proc bindSetter(ctx: proxyDesc, glueProc, propName, subjectName: string, propType, subject: NimNode): string {.compileTime.} = 2186 | var glue = "" 2187 | var needCheck = "" 2188 | 2189 | let 2190 | procCall = "proxy.ud." & propName 2191 | procName = $subject & "." & propName 2192 | 2193 | glue.add "proc " & glueProc & "(L: PState): cint {.cdecl.} =\n" 2194 | glue.add " var proxy = " & checkUD(subjectName, "1") 2195 | glue.add " if proxy.isNil: return 0\n" 2196 | glue.add " $1 = $2" % [procCall, constructArg(ctx, propType, 2, procName, needCheck)] 2197 | glue.add " return 0\n" 2198 | result = glue 2199 | 2200 | proc getPropType(subject: NimNode, prop: string): NimNode {.compileTime.} = 2201 | let parent = if subject.kind == nnkRefTy: subject[0][1] else: subject[1] 2202 | if parent.kind == nnkOfInherit: 2203 | let parentName = parent[0] 2204 | var t = getTypeImpl(parentName) 2205 | if t.kind == nnkRefTy: t = getTypeImpl(t[0]) 2206 | let ret = getPropType(t, prop) 2207 | if ret != nil: return ret 2208 | 2209 | let recList = if subject.kind == nnkRefTy: subject[0][2] else: subject[2] 2210 | for n in recList: 2211 | for i in 0..n.len-3: 2212 | let k = n[i] 2213 | if k.kind in {nnkIdent, nnkSym}: 2214 | if $k == prop: return n[n.len-2] 2215 | elif k.kind == nnkPostfix: 2216 | if $k[1] == prop: return n[n.len-2] 2217 | else: 2218 | error("unknown prop construct") 2219 | 2220 | proc bindObjectImpl*(ctx: proxyDesc): NimNode {.compileTime.} = 2221 | let 2222 | SL = ctx.luaCtx 2223 | newName = $ctx.libName 2224 | subject = ctx.subject 2225 | arg = ctx.bindList 2226 | 2227 | gContext.setLen 0 2228 | let subjectName = registerObject(subject) 2229 | var glue = "" 2230 | var regs = "var regs$1$2 = [\n" % [subjectName, $regsCount] 2231 | 2232 | for i in 0..arg.len-1: 2233 | let n = arg[i] 2234 | if n.node.kind notin {nnkSym, nnkClosedSymChoice}: 2235 | error("bindObject: arg[" & $i & "] need symbol not " & $n.node.kind) 2236 | 2237 | let 2238 | procName = getAccQuotedName(n.node, n.lhsKind) 2239 | glueProc = "nimLUAproxy" & $proxyCount 2240 | exportedName = if n.name == "constructor": "new" else: n.name 2241 | 2242 | regs.add " luaL_Reg(name: \"$1\", fn: $2),\n" % [exportedName, glueProc] 2243 | 2244 | if n.node.kind == nnkSym: 2245 | if n.name == "constructor" and n.lhsKind != nnkStrLit: 2246 | glue.add bindSingleConstructor(ctx, n, getImpl(n.node), glueProc, procName, subjectName) 2247 | else: 2248 | glue.add bindObjectSingleMethod(ctx, n, getImpl(n.node), glueProc, procName, subjectName) 2249 | else: #nnkClosedSymChoice 2250 | if n.name == "constructor" and n.lhsKind != nnkStrLit: 2251 | glue.add bindOverloadedConstructor(ctx, n, n.node, glueProc, procName, subjectName) 2252 | else: 2253 | glue.add bindObjectOverloadedMethod(ctx, n, n.node, glueProc, procName, subjectName) 2254 | 2255 | inc proxyCount 2256 | 2257 | if ctx.propList.len > 0: 2258 | for n in ctx.propList: 2259 | let 2260 | propName = getAccQuotedName(n.node, n.lhsKind) 2261 | getterProc = "nimLUAgetter" & $proxyCount 2262 | setterProc = "nimLUAsetter" & $proxyCount 2263 | subjectT = getImpl(subject) 2264 | propType = getPropType(subjectT[2], propName) 2265 | 2266 | if propType == nil: 2267 | error("'$1': not a prop of $2" % [propName, $subjectT[0]]) 2268 | 2269 | if n.getter: 2270 | regs.add " luaL_reg(name: \"_get_$1\", fn: $2),\n" % [n.name, getterProc] 2271 | glue.add bindGetter(ctx, getterProc, propName, subjectName, propType, subject) 2272 | 2273 | if n.setter: 2274 | regs.add " luaL_reg(name: \"_set_$1\", fn: $2),\n" % [n.name, setterProc] 2275 | glue.add bindSetter(ctx, setterProc, propName, subjectName, propType, subject) 2276 | 2277 | inc proxyCount 2278 | 2279 | if isRefType(subject) and not hasName("dtor" & $subject): 2280 | glue.add "proc $1_destructor(L: PState): cint {.cdecl.} =\n" % [subjectName] 2281 | glue.add " var proxy = " & checkUD(subjectName, "1") 2282 | glue.add " if proxy.isNil: return 0\n" 2283 | glue.add " GC_unref(proxy.ud)\n" 2284 | glue.add " proxy.ud = nil\n" 2285 | regs.add " luaL_reg(name: \"__gc\", fn: $1_destructor),\n" % [subjectName] 2286 | setName("dtor" & $subject) 2287 | 2288 | regs.add " luaL_Reg(name: nil, fn: nil)\n" 2289 | regs.add "]\n" 2290 | 2291 | glue.add regs 2292 | glue.add "$1.nimGetMetatable(NL_$2)\n" % [SL, subjectName] 2293 | glue.add "$1.setFuncs(cast[ptr luaL_reg](addr(regs$2$3)), 0)\n" % [SL, subjectName, $regsCount] 2294 | 2295 | if ctx.propList.len > 0: 2296 | glue.add "$1.propsEnd()\n" % [SL] 2297 | else: 2298 | glue.add "$1.pushValue(-1)\n" % [SL] 2299 | glue.add "$1.setField(-1, \"__index\")\n" % [SL] 2300 | glue.add "$1.setGlobal(\"$2\")\n" % [SL, newName] 2301 | 2302 | inc regsCount 2303 | let mtList = genMetaTableList(SL) 2304 | result = parseCode(gContext & mtList & glue) 2305 | 2306 | macro bindObject*(arg: varargs[untyped]): untyped = 2307 | result = genProxyMacro(arg, {nlbRegisterObject, nlbRegisterClosure, nlbRegisterGeneric}, "Object") 2308 | 2309 | # use this macro to generate alias for object meta table name dan proxy name 2310 | macro getRegisteredType*(obj: typed, metaTableName, proxyName: untyped): untyped = 2311 | let subjectName = registerObject(obj) 2312 | var glue = "type $1 = NL_$2Proxy\n" % [$proxyName, subjectName] 2313 | glue.add "const $1 = NL_$2\n" % [$metaTableName, subjectName] 2314 | result = parseCode(glue) 2315 | 2316 | macro getRegisteredProxy*(obj: typed, proxyName: untyped): untyped = 2317 | let subjectName = registerObject(obj) 2318 | var glue = "type $1 = NL_$2Proxy\n" % [$proxyName, subjectName] 2319 | result = parseCode(glue) 2320 | 2321 | macro getRegisteredMetaTable*(obj: typed, metaTableName: untyped): untyped = 2322 | let subjectName = registerObject(obj) 2323 | var glue = "const $1 = NL_$2\n" % [$metaTableName, subjectName] 2324 | result = parseCode(glue) 2325 | 2326 | macro instantiateRegisteredProxy(obj: typed): untyped = 2327 | let n = getTypeImpl(obj) 2328 | assert(n.kind == nnkBracketExpr) 2329 | var glue = "type pxName = object\n" 2330 | glue.add " ud: $1\n" % [$n[1]] 2331 | glue.add "let px = pxName(ud: objRef)\n" 2332 | glue.add "let proxy = getRegisteredUD[pxName](L, px)\n" 2333 | glue.add "result = proxy.ud\n" 2334 | result = parseCode(glue) 2335 | 2336 | proc getUD*[T: ref](L: PState, objRef: T): T = 2337 | instantiateRegisteredProxy(T) 2338 | --------------------------------------------------------------------------------