update readme

This commit is contained in:
Sky Johnson 2025-05-31 17:54:08 -05:00
parent 202664f635
commit c22638b51f

141
README.md
View File

@ -18,8 +18,6 @@ First, grab the package:
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() // pass false to not load standard libs
@ -51,10 +49,6 @@ for i := 0; i < 1000; i++ {
err := L.CompileAndRun(`return "hello"`, "greeting")
```
### When to Use Bytecode
Bytecode execution is consistently faster than direct execution:
```
Benchmark Ops/sec Comparison
----------------------------------------------------------------------------
@ -70,12 +64,6 @@ 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:
@ -95,9 +83,27 @@ Now in 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 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 → Lua
@ -106,11 +112,33 @@ stuff := map[string]any{
"age": 30,
"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")
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
@ -119,33 +147,30 @@ 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)
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 a custom table pooling system to reduce GC pressure when handling many tables:
The wrapper uses bytecode buffer pooling to reduce allocations:
```go
// Tables are pooled and reused internally for better performance
// Bytecode buffers are pooled and reused internally
for i := 0; i < 1000; i++ {
L.GetGlobal("table")
table, _ := L.ToTable(-1)
// Use table...
L.Pop(1)
// Table is automatically returned to pool
bytecode, _ := L.CompileBytecode(code, "test")
// Buffer automatically returned to pool
}
```
The sandbox also manages its environment efficiently:
Function pointers are managed safely:
```go
// Environment objects are pooled and reused
for i := 0; i < 1000; i++ {
result, _ := sandbox.Run("return i + 1")
}
// Functions are registered in a thread-safe registry
L.RegisterGoFunction("myFunc", myGoFunc)
defer L.Cleanup() // Cleans up all registered functions
```
## Best Practices
@ -160,7 +185,12 @@ for i := 0; i < 1000; i++ {
### 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
- 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
@ -198,14 +228,53 @@ 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
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.