{"id":19275283,"url":"https://github.com/jangko/nimlua","last_synced_at":"2025-10-12T19:45:43.935Z","repository":{"id":37269796,"uuid":"47669472","full_name":"jangko/nimLUA","owner":"jangko","description":"glue code generator to bind Nim and Lua together using Nim's powerful macro","archived":false,"fork":false,"pushed_at":"2023-12-04T13:37:02.000Z","size":350,"stargazers_count":104,"open_issues_count":4,"forks_count":13,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-01T16:33:39.651Z","etag":null,"topics":["glue","lua","nim","wrapper-library"],"latest_commit_sha":null,"homepage":null,"language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jangko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-12-09T04:56:08.000Z","updated_at":"2025-02-25T22:50:43.000Z","dependencies_parsed_at":"2023-12-04T14:50:14.256Z","dependency_job_id":"5f6047b2-0ead-4842-a032-911a03a209a6","html_url":"https://github.com/jangko/nimLUA","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jangko%2FnimLUA","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jangko%2FnimLUA/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jangko%2FnimLUA/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jangko%2FnimLUA/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jangko","download_url":"https://codeload.github.com/jangko/nimLUA/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250150549,"owners_count":21383185,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["glue","lua","nim","wrapper-library"],"created_at":"2024-11-09T20:50:02.611Z","updated_at":"2025-10-12T19:45:43.874Z","avatar_url":"https://github.com/jangko.png","language":"Nim","readme":"# nimLua\nglue code generator to bind Nim and Lua together using Nim's powerful macro\n\n[![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)\n[![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)\n![nimble](https://img.shields.io/badge/available%20on-nimble-yellow.svg?style=flat-square)\n![license](https://img.shields.io/github/license/citycide/cascade.svg?style=flat-square)\n![Github action](https://github.com/jangko/nimLUA/workflows/nimLUA%20CI/badge.svg)\n- - -\n\n**Features**:\n\n* bind free proc\n* bind proc as Lua method\n* bind const\n* bind enum\n* bind object\n* generic proc binding\n* closure binding\n* properties getter/setter\n* automatic resolve overloaded proc\n* easy namespace creation\n* easy debugging\n* consistent simple API\n* can rename exported symbol\n* support automatic type conversion\n* can change binding dynamically at runtime too\n* generate clean and optimized glue code that you can inspect at compile time\n* it's free\n\n**planned features**:\n\n* complex data types conversion, at least standard container\n* access Lua code/data from Nim\n\n- - -\n**Current version API**:\nno need to remember complicated API, the API is simple but powerful\n\n* newNimLua\n* bindEnum\n* bindConst\n* bindFunction/bindProc\n* bindObject\n\n- - -\n\n## **DATA TYPE CONVERSION**\n\n| Nim | Lua |\n|--------------------------------|----------------------------------|\n| char,int,uint,int8-64,uint8-64 | integer/number |\n| float, float32, float64 | number |\n| array[0..n, T], [n, T] | array[1..n] |\n| enum | integer/number |\n| string, cstring | string |\n| ref/object | userdata |\n| bool | boolean |\n| seq[T] | array[1..n] |\n| set[T] | table with unique element |\n| pointer | light user data |\n| ptr T | light user data |\n| range/subrange | integer |\n| openArray[T] | table -\u003e seq[T] |\n| tuple | assoc-table or array |\n| varargs[T] | not supported |\n---\n## **HOW TO USE**\n\n### **1. bindEnum**\n\n```nimrod\nimport nimLUA, os\n\ntype\n  FRUIT = enum\n    APPLE, BANANA, PEACH, PLUM\n  SUBATOM = enum\n    ELECTRON, PROTON, NEUTRON\n  GENE = enum\n    ADENINE, CYTOSINE, GUANINE, THYMINE\n\nproc test(L: PState, fileName: string) =\n  if L.doFile(\"test\" \u0026 DirSep \u0026 fileName) != 0.cint:\n    echo L.toString(-1)\n    L.pop(1)\n  else:\n    echo fileName \u0026 \" .. OK\"\n\nproc main() =\n  var L = newNimLua()\n  L.bindEnum(FRUIT, SUBATOM, GENE)\n  L.test(\"test.lua\")\n  L.close()\n\nmain()\n```\nand you can access them at Lua side like this:\n\n```Lua\nassert(FRUIT.APPLE == 0)\nassert(FRUIT.BANANA == 1)\nassert(FRUIT.PEACH == 2)\nassert(FRUIT.PLUM == 3)\n\nassert(GENE.ADENINE == 0)\nassert(GENE.CYTOSINE == 1)\nassert(GENE.GUANINE == 2)\nassert(GENE.THYMINE == 3)\n\nassert(SUBATOM.ELECTRON == 0)\nassert(SUBATOM.PROTON == 1)\nassert(SUBATOM.NEUTRON == 2)\n```\n\nanother style:\n\n```nimrod\nL.bindEnum:\n  FRUIT\n  SUBATOM\n  GENE\n```\nif you want to rename the namespace, you can do this:\n```nimrod\nL.bindEnum:\n  GENE -\u003e \"DNA\"\n  SUBATOM -\u003e GLOBAL\n```\nor\n```nimrod\nL.bindEnum(GENE -\u003e \"DNA\", SUBATOM -\u003e GLOBAL)\n```\n\na note on **GLOBAL** and \"GLOBAL\":\n\n* **GLOBAL** without quote will not create namespace on Lua side but will bind the symbol in Lua globalspace\n* \"GLOBAL\" with quote, will create \"GLOBAL\" namespace on Lua side\n\nnow Lua side will become:\n\n```Lua\nassert(DNA.ADENINE == 0)\nassert(DNA.CYTOSINE == 1)\nassert(DNA.GUANINE == 2)\nassert(DNA.THYMINE == 3)\n\nassert(ELECTRON == 0)\nassert(PROTON == 1)\nassert(NEUTRON == 2)\n```\n\n### **2. bindConst**\n\n```nimrod\nimport nimLUA\n\nconst\n  MANGOES = 10.0\n  PAPAYA = 11.0'f64\n  LEMON = 12.0'f32\n  GREET = \"hello world\"\n  connected = true\n\nproc main() =\n  var L = newNimLua()\n  L.bindConst(MANGOES, PAPAYA, LEMON)\n  L.bindConst:\n    GREET\n    connected\n  L.close()\n\nmain()\n```\n\nby default, bindConst will not generate namespace, so how do you create namespace for const? easy:\n\n```nimrod\nL.bindConst(\"fruites\", MANGOES, PAPAYA, LEMON)\nL.bindConst(\"status\"):\n  GREET\n  connected\n```\nfirst argument(actually second) to bindConst will become the namespace. Without namespace, symbol will be put into global namespace\n\nif you use **GLOBAL** without quote as namespace, it will have no effect\n\noperator `-\u003e` have same meaning with bindEnum, to rename exported symbol on Lua side\n\n### **3. bindFunction/bindProc**\n\nbindFunction is an alias to bindProc, they behave identically\n\n```nimrod\nimport nimLUA\n\nproc abc(a, b: int): int =\n  result = a + b\n\nvar L = newNimLua()\nL.bindFunction(abc)\nL.bindFunction:\n  abc -\u003e \"cba\"\nL.bindFunction(\"alphabet\", abc)\n```\n\nbindFunction more or less behave like bindConst, without namespace, it will bind symbol to global namespace.\n\noverloaded procs will be automatically resolved by their params count and types\n\noperator `-\u003e` have same meaning with bindEnum, to rename exported symbol on Lua side\n\n### **4. bindObject**\n\n```nimrod\nimport nimLUA\n\ntype\n  Foo = ref object\n    name: string\n\nproc newFoo(name: string): Foo =\n  new(result)\n  result.name = name\n\nproc addv(f: Foo, a, b: int): int =\n  result = 2 * (a + b)\n\nproc addv(f: Foo, a, b: string): string =\n  result = \"hello: my name is $1, here is my message: $2, $3\" % [f.name, a, b]\n\nproc addk(f: Foo, a, b: int): string =\n  result = f.name \u0026 \": \" \u0026 $a \u0026 \" + \" \u0026 $b \u0026 \" = \" \u0026 $(a+b)\n\nproc main() =\n  var L = newNimLua()\n  L.bindObject(Foo):\n    newFoo -\u003e constructor\n    addv\n    addk -\u003e \"add\"\n  L.close()\n\nmain()\n```\nthis time, Foo will become object name and also namespace name in Lua\n\n\"newFoo `-\u003e` constructor\" have special meaning, it will create constructor on Lua side with special name: `new`(this is an artefact)\nbut any other constructor like procs will be treated as constructor too:\n\n```nimrod\nL.bindObject(Foo):\n  newFoo                    #constructor #1 'newFoo'\n  newFoo -\u003e constructor     #constructor #2 'new'\n  newFoo -\u003e \"whatever\"      #constructor #3 'whatever'\n  makeFoo -\u003e \"constructor\"  #constructor #4 'constructor'\n```\n\noperator `-\u003e` on non constructor will behave the same as other binder.\n\noverloaded proc will be automatically resolved by their params count and types, including overloaded constructor\n\ndestructor will be generated automatically for ref object, none for regular object.\nGC safety works as usual on both side of Nim and Lua, no need to worry, except when you manually allocated memory\n\n```Lua\nlocal foo = Foo.new(\"fred\")\nlocal m = foo:add(3, 4)\n\n-- \"fred: 3 + 4 = 7\"\nprint(m)\n\nassert(foo:addv(4,5) == 2 * (4+5))\n\n-- \"hello: my name is fred, here is my message: abc, nop\"\nprint(foo:addv(\"abc\", \"nop\"))\n```\n\noperator `-\u003e` when applied to object, will rename exported symbol on Lua side:\n\n```nimrod\nL.bindObject(Foo -\u003e \"cat\"):\n  newFoo -\u003e constructor\n```\n\non Lua side:\n\n```lua\nlocal c = cat.new(\"fred\") --not 'Foo' anymore\n```\n\nboth **bindObject** and **bindFunction** and **bindConst** can add member to existing namespace\n\nif you want to turn off this functionality, call **nimLuaOptions**(nloAddMember, false)\n\n```nimrod\nL.bindObject(Foo): #namespace creation\n  newFoo -\u003e constructor\n\nL.bindObject(Foo): #add new member\n  addv\n  addk -\u003e \"add\"\n\nL.bindFunction(\"gem\"): #namespace \"gem\" creation\n  mining\n\nL.bindFunction(\"gem\"): #add 'polish' member\n  polish\n```\n\n#### **4.1. bindObject without member**\n\nIt's ok to call bindObject without any additional member/method if you want to\nregister object type and use it later. For example if you want to create your own\nobject constructor\n\n#### **4.2. bindObject for opaque C pointer**\n\nUsually a C library have constructor(s) and destructor function.\nThe constructor will return an opaque pointer.\n\nOn Nim side, we usually use something like:\n\n```Nim\ntype\n  CContext* = distinct pointer\n\nproc createCContext*(): CContext {.cdecl, importc.}\nproc deleteCContext*(ctx: CContext) {.cdecl, importc.}\n```\n\nOf course this is not an object or ref object, but we treat it as an object in this case.\nTherefore bindObject will work like usual.\nOnly this time, we also need to specify the destructor function using `~` operator.\n\n```Nim\nL.bindObject(CContext):\n  createCContext -\u003e \"create\"\n  ~deleteCContext\n```\n\n## **PASSING BY REFERENCE**\n\nLua basic data types cannot be passed by reference, but Nim does\n\nif you have something like this in Nim:\n\n```nimrod\nproc abc(a, b: var int) =\n  a = a + 1\n  b = b + 5\n```\n\nthen on Lua side:\n\n```lua\na = 10\nb = 20\na, b = abc(a, b)\nassert(a == 11)\nassert(b == 25)\n```\n\nbasically, outval will become retval, FIFO ordered\n\n## **GENERIC PROC BINDING**\n```nimrod\nproc mew[T, K](a: T, b: K): T =\n  discard\n\nL.bindFunction:\n  mew[int, string]\n  mew[int, string] -\u003e \"mewt\"\n```\n\n## **CLOSURE BINDING**\n```nimrod\nproc main() =\n  ...\n\n  var test = 1237\n  proc cl() =\n    echo test\n\n  L.bindFunction:\n    [cl]\n    [cl] -\u003e \"clever\"\n```\n\n## **GETTER/SETTER**\n```nimrod\ntype\n  Ship = object\n    speed*: int\n    power: int\n\nL.bindObject(Ship):\n  speed(set)\n  speed(get) -\u003e \"currentSpeed\"\n  speed(get, set) -\u003e \"velocity\"\n```\n\nthen you can access the object's properties on lua side using '.' (dot) and not ':' (colon)\n```lua\nlocal b = Ship.newShip()\nb.speed = 19\nassert(b.speed == nil) -- setter only\nassert(b.currentSpeed == 19) -- getter only\nb.velocity = 20\nassert(b.velocity == 20) -- getter \u0026 setter\n```\n\n## **HOW TO DEBUG**\n\nyou can call **nimLuaOptions**(nloDebug, true/false)\n\n```nimrod\nnimLuaOptions(nloDebug, true) #turn on debug\nL.bindEnum:\n  GENE\n  SUBATOM\n\nnimLuaOptions(nloDebug, false) #turn off debug mode\nL.bindFunction:\n  machine\n  engine\n```\n\n## **DANGEROUS ZONE**\nlua_error, lua_checkstring, lua_checkint, lua_checkudata and other lua C API that can throw error\nare dangerous functions when called from Nim context. lua_error use `longjmp` when compiled to C\nor `throw` when compiled to C++.\n\nAlthough Nim compiled to C, Nim have it's own stack frame. Calling lua_error and other functions\nthat can throw error will disrupt Nim stack frame, and application will crash.\n\nnimLUA avoid using those dangerous functions and and use it's own set of functions that is considerably\nsafe. those functions are:\n\n| lua | nimLUA |\n|-----|--------|\n| lua_error | N/A |\n| lua_checkstring | nimCheckString |\n| lua_checkinteger | nimCheckInteger |\n| lua_checkbool | nimCheckBool |\n| lua_checknumber | nimCheckNumber |\n| N/A | nimCheckCstring |\n| N/A | nimCheckChar |\n| lua_newmetatable | nimNewMetaTable |\n| lua_getmetatable | nimGetMetaTable |\n| lua_checkudata | nimCheckUData |\n\n## **Error Handling**\n```Nim\n  NLError* = object\n    source: string\n    currentLine: int\n    msg: string\n\n  NLErrorFunc* = proc(ctx: pointer, err: NLError) {.nimcall.}\n\nproc NLSetErrorHandler*(L: PState, errFunc: NLErrorFunc)\nproc NLSetErrorContext*(L: PState, errCtx: pointer)\n```\n\nThis is actually not a real error handler, because you cannot use raise exception.\nThe purpose of this function is to provide information to user about wrong argument type\npassed from Lua to Nim.\n\nnimLUA already provide a default error handler in case you forget to provide one.\n\n## **HOW TO ACCESS LUA CODE FROM NIM?**\n\nstill under development, contributions are welcome\n\n## Installation via nimble\n\u003e nimble install nimLUA\n\n## Override shared library name\n\nYou can use compiler switch `-d:SHARED_LIB_NAME=\"yourlibname\"`\n\n```bash\n$\u003e nim c -r -d:SHARED_LIB_NAME=\"lua534.dll\" test/test\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjangko%2Fnimlua","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjangko%2Fnimlua","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjangko%2Fnimlua/lists"}