LuaJIT-to-Go/README.md
2025-02-26 07:00:01 -06:00

202 lines
5.1 KiB
Markdown

# 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
```
You'll need LuaJIT's development files, but don't worry - we include libraries for Windows and Linux in the vendor directory.
Here's the simplest thing you can do:
```go
L := luajit.New()
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")
```
### When to Use Bytecode
Bytecode execution is consistently faster than direct execution:
```
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
```
Use bytecode when you:
- Have code that runs frequently
- Need maximum performance
- Want to precompile your Lua code
- Are distributing Lua code to many instances
## 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
```
## Working with Tables
Lua tables are pretty 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]interface{}{
"name": "Arthur Dent",
"age": 30,
"items": []float64{1, 2, 3},
}
L.PushTable(stuff)
// Lua → Go
L.GetGlobal("some_table")
result, err := L.ToTable(-1)
```
## 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: %s\n", luaErr.Message)
}
}
```
## Memory Management
The wrapper uses a custom table pooling system to reduce GC pressure when handling many tables:
```go
// Tables are pooled and reused internally for better performance
for i := 0; i < 1000; i++ {
L.GetGlobal("table")
table, _ := L.ToTable(-1)
// Use table...
L.Pop(1)
// Table is automatically returned to pool
}
```
## Best Practices
- 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
- Use bytecode for frequently executed code paths
- Consider compiling critical Lua code to bytecode at startup
## 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...
L.GetGlobal("increment")
L.Call(0, 1) // Returns 1
L.Pop(1)
L.GetGlobal("increment")
L.Call(0, 1) // Returns 2
```
## 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!