# LuaJIT Go Wrapper This is a Go wrapper for LuaJIT that makes it easy to embed Lua in your Go applications. We've focused on making it both performant and developer-friendly, with an API that feels natural to use. ## What's This For? This wrapper lets you run Lua code from Go and easily pass data back and forth between the two languages. You might want this if you're: - Adding scripting support to your application - Building a game engine - Creating a configuration system - Writing an embedded rules engine - Building test automation tools ## Get Started First, grab the package: ```bash go get git.sharkk.net/Sky/LuaJIT-to-Go ``` Here's the simplest thing you can do: ```go L := luajit.New() // pass false to not load standard libs defer L.Close() defer L.Cleanup() err := L.DoString(`print("Hey from Lua!")`) ``` ## Working with Bytecode Need even more performance? You can compile your Lua code to bytecode and reuse it: ```go // Compile once bytecode, err := L.CompileBytecode(` local function calculate(x) return x * x + x + 1 end return calculate(10) `, "calc") // Execute many times for i := 0; i < 1000; i++ { err := L.LoadAndRunBytecode(bytecode, "calc") } // Or do both at once err := L.CompileAndRun(`return "hello"`, "greeting") ``` ``` Benchmark Ops/sec Comparison ---------------------------------------------------------------------------- BenchmarkSimpleDoString 2,561,012 Base BenchmarkSimplePrecompiledBytecode 3,828,841 +49.5% faster BenchmarkFunctionCallDoString 2,021,098 Base BenchmarkFunctionCallPrecompiled 3,482,074 +72.3% faster BenchmarkLoopDoString 188,119 Base BenchmarkLoopPrecompiled 211,081 +12.2% faster BenchmarkTableOperationsDoString 84,086 Base BenchmarkTableOperationsPrecompiled 93,655 +11.4% faster BenchmarkComplexScript 33,133 Base BenchmarkComplexScriptPrecompiled 41,044 +23.9% faster ``` ## Registering Go Functions Want to call Go code from Lua? It's straightforward: ```go // This function adds two numbers and returns the result adder := func(s *luajit.State) int { sum := s.ToNumber(1) + s.ToNumber(2) s.PushNumber(sum) return 1 // we're returning one value } L.RegisterGoFunction("add", adder) ``` Now in Lua: ```lua result = add(40, 2) -- result = 42 ``` ### Function Validation Validate arguments easily: ```go calculator := func(s *luajit.State) int { if err := s.CheckArgs( luajit.ArgSpec{Name: "x", Type: "number", Required: true, Check: luajit.CheckNumber}, luajit.ArgSpec{Name: "y", Type: "number", Required: true, Check: luajit.CheckNumber}, ); err != nil { return s.PushError(err.Error()) } result := s.ToNumber(1) + s.ToNumber(2) s.PushNumber(result) return 1 } ``` ## Working with Tables Lua tables are powerful - they're like a mix of Go's maps and slices. We make it easy to work with them: ```go // Go → Lua stuff := map[string]any{ "name": "Arthur Dent", "age": 30, "items": []float64{1, 2, 3}, } L.PushValue(stuff) // Handles all Go types automatically // Lua → Go with automatic type detection L.GetGlobal("some_table") result, err := L.ToTable(-1) // Returns optimal Go type (typed array, or map[string]any) ``` ### Table Builder Build tables fluently: ```go L.NewTableBuilder(). SetString("name", "John"). SetNumber("age", 30). SetBool("active", true). SetArray("scores", []any{95, 87, 92}). Build() ``` ### Table Field Access Get fields with defaults: ```go L.GetGlobal("config") host := L.GetFieldString(-1, "host", "localhost") port := L.GetFieldNumber(-1, "port", 8080) debug := L.GetFieldBool(-1, "debug", false) ``` ## Error Handling We provide useful errors instead of mysterious panics: ```go if err := L.DoString("this isn't valid Lua!"); err != nil { if luaErr, ok := err.(*luajit.LuaError); ok { fmt.Printf("Error in %s:%d - %s\n", luaErr.File, luaErr.Line, luaErr.Message) fmt.Printf("Stack trace:\n%s\n", luaErr.StackTrace) } } ``` ## Memory Management The wrapper uses bytecode buffer pooling to reduce allocations: ```go // Bytecode buffers are pooled and reused internally for i := 0; i < 1000; i++ { bytecode, _ := L.CompileBytecode(code, "test") // Buffer automatically returned to pool } ``` Function pointers are managed safely: ```go // Functions are registered in a thread-safe registry L.RegisterGoFunction("myFunc", myGoFunc) defer L.Cleanup() // Cleans up all registered functions ``` ## Best Practices ### State Management - Always use `defer L.Close()` and `defer L.Cleanup()` to prevent memory leaks - Each Lua state should stick to one goroutine - For concurrent operations, create multiple states - You can share functions between states safely - Keep an eye on your stack management - pop as many items as you push ### Bytecode Optimization - Use bytecode for frequently executed code paths - Consider compiling critical Lua code to bytecode at startup - For small scripts (< 1024 bytes), direct execution might be faster due to compilation overhead ### Type Conversion - Use `ToTable()` for automagic type detection and optimized Go arrays/maps - Use `PushValue()` for automagic Go-to-Lua conversion - Leverage typed field accessors for config-style tables ## Advanced Features ### Bytecode Serialization You can serialize bytecode for distribution or caching: ```go // Compile once bytecode, _ := L.CompileBytecode(complexScript, "module") // Save to file ioutil.WriteFile("module.luac", bytecode, 0644) // Later, load from file bytecode, _ := ioutil.ReadFile("module.luac") L.LoadAndRunBytecode(bytecode, "module") ``` ### Closures and Upvalues Bytecode properly preserves closures and upvalues: ```go code := ` local counter = 0 return function() counter = counter + 1 return counter end ` bytecode, _ := L.CompileBytecode(code, "counter") L.LoadAndRunBytecodeWithResults(bytecode, "counter", 1) L.SetGlobal("increment") // Later... results, _ := L.CallGlobal("increment") // Returns []any{1} results, _ = L.CallGlobal("increment") // Returns []any{2} ``` ### Batch Execution Execute multiple statements efficiently: ```go statements := []string{ "x = 10", "y = 20", "result = x + y", } err := L.BatchExecute(statements) ``` ### Package Path Management Manage Lua module paths: ```go L.SetPackagePath("./?.lua;./modules/?.lua") L.AddPackagePath("./vendor/?.lua") ``` ### Type Conversion System The wrapper includes a comprehensive type conversion system: ```go // Get typed values with automatic conversion value, ok := luajit.GetTypedValue[int](L, -1) global, ok := luajit.GetGlobalTyped[[]string](L, "myArray") // Convert between compatible types result, ok := luajit.ConvertValue[map[string]int](someMap) ``` ## Performance Tips - Use bytecode for repeated execution - Prefer `CallGlobal()` for simple function calls - Use typed field accessors for configuration parsing - Leverage automatic type detection in `ToTable()` - Pool your Lua states for high-throughput scenarios ## Need Help? Check out the tests in the repository - they're full of examples. If you're stuck, open an issue! We're here to help. ## License MIT Licensed - do whatever you want with it!