update readme
This commit is contained in:
parent
202664f635
commit
c22638b51f
177
README.md
177
README.md
@ -18,8 +18,6 @@ First, grab the package:
|
|||||||
go get git.sharkk.net/Sky/LuaJIT-to-Go
|
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:
|
Here's the simplest thing you can do:
|
||||||
```go
|
```go
|
||||||
L := luajit.New() // pass false to not load standard libs
|
L := luajit.New() // pass false to not load standard libs
|
||||||
@ -36,25 +34,21 @@ Need even more performance? You can compile your Lua code to bytecode and reuse
|
|||||||
```go
|
```go
|
||||||
// Compile once
|
// Compile once
|
||||||
bytecode, err := L.CompileBytecode(`
|
bytecode, err := L.CompileBytecode(`
|
||||||
local function calculate(x)
|
local function calculate(x)
|
||||||
return x * x + x + 1
|
return x * x + x + 1
|
||||||
end
|
end
|
||||||
return calculate(10)
|
return calculate(10)
|
||||||
`, "calc")
|
`, "calc")
|
||||||
|
|
||||||
// Execute many times
|
// Execute many times
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
err := L.LoadAndRunBytecode(bytecode, "calc")
|
err := L.LoadAndRunBytecode(bytecode, "calc")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Or do both at once
|
// Or do both at once
|
||||||
err := L.CompileAndRun(`return "hello"`, "greeting")
|
err := L.CompileAndRun(`return "hello"`, "greeting")
|
||||||
```
|
```
|
||||||
|
|
||||||
### When to Use Bytecode
|
|
||||||
|
|
||||||
Bytecode execution is consistently faster than direct execution:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Benchmark Ops/sec Comparison
|
Benchmark Ops/sec Comparison
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
@ -70,21 +64,15 @@ BenchmarkComplexScript 33,133 Base
|
|||||||
BenchmarkComplexScriptPrecompiled 41,044 +23.9% faster
|
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
|
## Registering Go Functions
|
||||||
|
|
||||||
Want to call Go code from Lua? It's straightforward:
|
Want to call Go code from Lua? It's straightforward:
|
||||||
```go
|
```go
|
||||||
// This function adds two numbers and returns the result
|
// This function adds two numbers and returns the result
|
||||||
adder := func(s *luajit.State) int {
|
adder := func(s *luajit.State) int {
|
||||||
sum := s.ToNumber(1) + s.ToNumber(2)
|
sum := s.ToNumber(1) + s.ToNumber(2)
|
||||||
s.PushNumber(sum)
|
s.PushNumber(sum)
|
||||||
return 1 // we're returning one value
|
return 1 // we're returning one value
|
||||||
}
|
}
|
||||||
|
|
||||||
L.RegisterGoFunction("add", adder)
|
L.RegisterGoFunction("add", adder)
|
||||||
@ -95,22 +83,62 @@ Now in Lua:
|
|||||||
result = add(40, 2) -- result = 42
|
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
|
## 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:
|
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
|
||||||
// Go → Lua
|
// Go → Lua
|
||||||
stuff := map[string]any{
|
stuff := map[string]any{
|
||||||
"name": "Arthur Dent",
|
"name": "Arthur Dent",
|
||||||
"age": 30,
|
"age": 30,
|
||||||
"items": []float64{1, 2, 3},
|
"items": []float64{1, 2, 3},
|
||||||
}
|
}
|
||||||
L.PushTable(stuff)
|
L.PushValue(stuff) // Handles all Go types automatically
|
||||||
|
|
||||||
// Lua → Go
|
// Lua → Go with automatic type detection
|
||||||
L.GetGlobal("some_table")
|
L.GetGlobal("some_table")
|
||||||
result, err := L.ToTable(-1)
|
result, err := L.ToTable(-1) // Returns optimal Go type ([]int, map[string]string, etc.)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
## Error Handling
|
||||||
@ -118,34 +146,31 @@ result, err := L.ToTable(-1)
|
|||||||
We provide useful errors instead of mysterious panics:
|
We provide useful errors instead of mysterious panics:
|
||||||
```go
|
```go
|
||||||
if err := L.DoString("this isn't valid Lua!"); err != nil {
|
if err := L.DoString("this isn't valid Lua!"); err != nil {
|
||||||
if luaErr, ok := err.(*luajit.LuaError); ok {
|
if luaErr, ok := err.(*luajit.LuaError); ok {
|
||||||
fmt.Printf("Error: %s\n", luaErr.Message)
|
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
|
## Memory Management
|
||||||
|
|
||||||
The wrapper uses a custom table pooling system to reduce GC pressure when handling many tables:
|
The wrapper uses bytecode buffer pooling to reduce allocations:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Tables are pooled and reused internally for better performance
|
// Bytecode buffers are pooled and reused internally
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
L.GetGlobal("table")
|
bytecode, _ := L.CompileBytecode(code, "test")
|
||||||
table, _ := L.ToTable(-1)
|
// Buffer automatically returned to pool
|
||||||
// Use table...
|
|
||||||
L.Pop(1)
|
|
||||||
// Table is automatically returned to pool
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The sandbox also manages its environment efficiently:
|
Function pointers are managed safely:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Environment objects are pooled and reused
|
// Functions are registered in a thread-safe registry
|
||||||
for i := 0; i < 1000; i++ {
|
L.RegisterGoFunction("myFunc", myGoFunc)
|
||||||
result, _ := sandbox.Run("return i + 1")
|
defer L.Cleanup() // Cleans up all registered functions
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
@ -160,7 +185,12 @@ for i := 0; i < 1000; i++ {
|
|||||||
### Bytecode Optimization
|
### Bytecode Optimization
|
||||||
- Use bytecode for frequently executed code paths
|
- Use bytecode for frequently executed code paths
|
||||||
- Consider compiling critical Lua code to bytecode at startup
|
- Consider compiling critical Lua code to bytecode at startup
|
||||||
- For small scripts (< 1024 bytes), direct execution might be faster
|
- 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
|
## Advanced Features
|
||||||
|
|
||||||
@ -186,11 +216,11 @@ Bytecode properly preserves closures and upvalues:
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
code := `
|
code := `
|
||||||
local counter = 0
|
local counter = 0
|
||||||
return function()
|
return function()
|
||||||
counter = counter + 1
|
counter = counter + 1
|
||||||
return counter
|
return counter
|
||||||
end
|
end
|
||||||
`
|
`
|
||||||
|
|
||||||
bytecode, _ := L.CompileBytecode(code, "counter")
|
bytecode, _ := L.CompileBytecode(code, "counter")
|
||||||
@ -198,14 +228,53 @@ L.LoadAndRunBytecodeWithResults(bytecode, "counter", 1)
|
|||||||
L.SetGlobal("increment")
|
L.SetGlobal("increment")
|
||||||
|
|
||||||
// Later...
|
// Later...
|
||||||
L.GetGlobal("increment")
|
results, _ := L.CallGlobal("increment") // Returns []any{1}
|
||||||
L.Call(0, 1) // Returns 1
|
results, _ = L.CallGlobal("increment") // Returns []any{2}
|
||||||
L.Pop(1)
|
|
||||||
|
|
||||||
L.GetGlobal("increment")
|
|
||||||
L.Call(0, 1) // Returns 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?
|
## 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.
|
Check out the tests in the repository - they're full of examples. If you're stuck, open an issue! We're here to help.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user