{"id":13741855,"url":"https://github.com/ranciere/zoltan","last_synced_at":"2025-05-08T22:32:32.653Z","repository":{"id":40238284,"uuid":"432123627","full_name":"ranciere/zoltan","owner":"ranciere","description":"A Sol-inspired minimalist Lua binding for Zig.","archived":false,"fork":false,"pushed_at":"2023-06-21T16:55:12.000Z","size":445,"stargazers_count":94,"open_issues_count":7,"forks_count":8,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-15T12:37:07.731Z","etag":null,"topics":["lua","metaprogramming","zig"],"latest_commit_sha":null,"homepage":"","language":"C","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/ranciere.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-26T09:33:05.000Z","updated_at":"2024-09-19T15:18:05.000Z","dependencies_parsed_at":"2024-08-03T04:18:32.441Z","dependency_job_id":null,"html_url":"https://github.com/ranciere/zoltan","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranciere%2Fzoltan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranciere%2Fzoltan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranciere%2Fzoltan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranciere%2Fzoltan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ranciere","download_url":"https://codeload.github.com/ranciere/zoltan/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253158376,"owners_count":21863285,"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":["lua","metaprogramming","zig"],"created_at":"2024-08-03T04:01:03.362Z","updated_at":"2025-05-08T22:32:32.176Z","avatar_url":"https://github.com/ranciere.png","language":"C","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"![zoltan](zoltan.png)\n# zoltan\n\nA Sol-inspired minimalist Lua binding for Zig.\n\n## Features\n\n- Supports Zig 0.9.0\n- Lua tables\n  - table creation from Zig\n  - get/set/create methods\n  - possible key types: numerical, string\n- Scalars\n  - int, float, bool\n  - Lua string (equals `[] const u8`)\n- Functions\n  - calling Zig function from Lua and vice-versa\n  - Zig functions can accept\n    - scalars, Lua types (table, functions, custom types)\n- Custom types\n  - registered types could be instantiated from Lua\n  - public functions are registered in Lua\n  - supports `self`\n- Scalar array support (equals Lua tables without metatables + numeric keys)\n- All of the supported use-cases are intensively tested \u0026 leak-free\n\n## Tutorial\n\n### Installing\n\nIn addition to the binding facilities, `zoltan` contains the vanilla Lua source (v5.4.3) and the necessary code to compile it. If `zoltan` is installed in the `third_party` library in your application's root, then you have to add to your `build.zig` the following:\n\n```zig\n// At the beginning of the file\nconst addLuaLib = @import(\"third_party/zoltan/build.zig\").addLuaLibrary;\n\npub fn build(b: *std.build.Builder) void {\n...\n  // Exe part\n  exe.addPackage(.{ .name = \"lua\", .path = .{ .path=\"third_party/zoltan/src/lua.zig\" }});\n  addLuaLib(exe, \"third_party/zoltan/\");\n...\n  // Test part\n  const lua_tests = b.addTest(\"third_party/zoltan/src/tests.zig\");\n  addLuaLib(lua_tests, \"third_party/zoltan/\");\n  lua_tests.setBuildMode(mode);\n```\nYou can found an example integration [here](https://github.com/ranciere/zoltan_example_app).\n\n### Instantiating and destroying Lua engine\n\n```zig\nconst Lua = @import(\"lua\").Lua;\n...\npub fn main() anyerror!void {\n  ...\n  var lua = try Lua.init(std.testing.allocator);\n  defer lua.destroy();\n\n  lua.openLibs();  // Open common standard libraries\n```\n\n### Running Lua code\n\n```zig\n_ = lua.run(\"print('Hello World!')\");\n```\n\n### Getting/setting Lua global varibles\n\n```zig\nlua.set(\"int32\", 42);\nvar int = lua.get(i32, \"int32\");\nstd.log.info(\"Int: {}\", .{int});  // 42\n\nlua.set(\"string\", \"I'm a string\");\nconst str = lua.get([] const u8, \"string\");\nstd.log.info(\"String: {s}\", .{str});  // I'm a string\n```\n\n### Resource handling\n\nThe following functions acquire some kind of resource (heap, Lua reference):\n\n- `getResource`\n- `createTable`\n- `createUserType`\n\nYou have to release the acquired resources by calling the `release` method:\n\n```zig\nvar tbl = try lua.createTable();\n....\nlua.release(tbl);         // You have to release\n```\n\n### Lua tables\n\n```zig\nvar tbl = try lua.createTable();\ndefer lua.release(tbl);\n\nlua.set(\"tbl\", tbl);\n\nvar inTbl = try lua.createTable();\n\n// Set, integer key\ninTbl.set(1, \"string\");\ninTbl.set(2, 3.1415);\ninTbl.set(3, 42);\n\nvar tst1 = inTbl.get([]const u8, 1);\nvar tst2 = inTbl.get(f32, 2);\nvar tst3 = inTbl.get(i32, 3);\n\ntry std.testing.expect(std.mem.eql(u8, test1, \"string\"));\ntry std.testing.expect(tst2 == 3.1415);\ntry std.testing.expect(tst3 == 42);\n\n// Set, string key\ninTbl.set(\"bool\", true);\n\nvar tst4 = inTbl.get(bool, \"bool\");\ntry std.testing.expect(tst4 == true);\n\n// Set table in parent\ntbl.set(\"inner\", inTbl);\n// Now we can release the inTbl directly (tbl refers it)\nlua.release(inTbl);\n```\n\n### Calling Lua function from Zig\n\n```zig\n_ = lua.run(\"function double_me(a) return 2*a; end\");\n\nvar doubleMe = lua.get(Lua.Function(fn(a: i32) i32), \"double_me\");\n// As Zig doesn't handle variable args, one should pass the arguments as anonymous struct\nvar res = doubleMe.call(.{42});\n\nstd.log.info(\"Result: {}\", .{res});   // 84\n```\n\n### Calling Zig function from Lua\n\n```zig\nvar testResult: i32 = 0;\n\nfn test_fun(a: i32, b: i32) void {\n    std.log.info(\"I'm a test: {}\", .{a*b});\n    testResult = a*b;\n}\n...\nlua.set(\"test_fun\", test_fun);\n\nlua.run(\"test_fun(3,15)\");\ntry std.testing.expect(testResult == 45);\n\n```\n\n### Passing Lua function to Zig function\n\n```zig\nfn testLuaInnerFun(fun: Lua.Function(fn(a: i32) i32)) i32 {\n    var res = fun.call(.{42}) catch unreachable;\n    std.log.warn(\"Result: {}\", .{res});\n    return res;\n}\n...\n```\n\n#### Mechanism on Zig side\n\n```zig\nlua.run(\"function getInt(a) print(a); return a+1; end\");\nvar luafun = try lua.getResource(Lua.Function(fn(a: i32) i32), \"getInt\");\ndefer lua.release(luafun);\n\nvar result = testLuaInnerFun(luafun);\nstd.log.info(\"Zig Result: {}\", .{result});\n```\n\n#### Mechanism on Lua side\n\n```zig\nlua.set(\"zigFunction\", testLuaInnerFun);\n\nconst lua_command =\n    \\\\function getInt(a) print(a); return a+1; end\n    \\\\print(\"Preppare\");\n    \\\\zigFunction(getInt);\n    \\\\print(\"Oppare\");\n;\nlua.run(lua_command);\n```\n\n### Custom types\n\n#### Registering Zig structs in Lua\n\n```zig\nconst TestCustomType = struct {\n    a: i32,\n    b: f32,\n    c: []const u8,\n    d: bool,\n\n    pub fn init(_a: i32, _b: f32, _c: []const u8, _d: bool) TestCustomType {\n        return TestCustomType{ ... };\n    }\n\n    pub fn destroy(_: *TestCustomType) void {}\n\n    pub fn getA(self: *TestCustomType) i32 { return self.a; }\n    pub fn getB(self: *TestCustomType) f32 { return self.b; }\n    pub fn getC(self: *TestCustomType) []const u8 { return self.c; }\n    pub fn getD(self: *TestCustomType) bool { return self.d; }\n\n    pub fn reset(self: *TestCustomType) void {\n        self.a = 0;\n        self.b = 0;\n        self.c = \"\";\n        self.d = false;\n    }\n\n    pub fn store(self: *TestCustomType, _a: i32, _b: f32, _c: []const u8, _d: bool) void {\n        self.a = _a;\n        self.b = _b;\n        self.c = _c;\n        self.d = _d;\n    }\n};\n...\n// ******************************************\ntry lua.newUserType(TestCustomType);\n// ******************************************\n```\n\n#### Instantiating custom type in Zig\n\n```zig\nvar obj = try lua.createUserType(TestCustomType, .{42, 42.0, \"life\", true});\ndefer lua.release(obj);\n\n// One can access the inner struct via the ptr field\nstd.testing.expect(obj.ptr.getA() == 42);\n\n// One can set as global\nlua.set(\"zigObj\", obj);\n\n// And then use it\nlua.run(\"zigObj:reset()\");\n\nstd.testing.expect(obj.ptr.getA() == 0);\n\n```\n\n#### Instantiating custom type in Lua\n\n```zig\nlua.run(\"obj = TestCustomType.new(42, 42.0, 'life', true)\");\n\n// Get as a reference (it doesn't hold reference to the inner object,\n// therefore the lifetime is managed totally by the Lua engine\n// =\u003e storing is dangerous)\nvar ptr = try lua.get(*TestCustomType, \"obj\");\nstd.testing.expect(ptr.getA() == 42);\n```\n\n## TODO\nIn order of importance:\n- The current error handling is a little bit rustic, sometimes rough :) A proper error handling strategy would be better.\n- Run Lua code from file\n- zigmod support\n- LuaJIT support\n- `Lua.Table` should deep-copy between table and user structs\n- Lua Coroutine support\n- `Lua.Table` should support JSON\n- Option for building without libc (if possible)\n- Performance benchmarks\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Franciere%2Fzoltan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Franciere%2Fzoltan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Franciere%2Fzoltan/lists"}