1
0

Compare commits

..

2 Commits

Author SHA1 Message Date
c22638b51f update readme 2025-05-31 17:54:08 -05:00
202664f635 update documentation 2025-05-31 17:49:25 -05:00
3 changed files with 326 additions and 92 deletions

82
API.md Normal file
View File

@ -0,0 +1,82 @@
# API Quick Reference
## Core State
- New(openLibs ...bool) *State
- Close()
- Cleanup()
## Stack
- GetTop() int
- SetTop(index int)
- Pop(n int)
- PushCopy(index int)
- Remove(index int)
## Type Checks
- GetType(index int) LuaType
- IsNil/IsBoolean/IsNumber/IsString/IsTable/IsFunction(index int) bool
## Values
- ToString/ToNumber/ToBoolean(index int) T
- ToValue(index int) (any, error)
- ToTable(index int) (any, error)
- PushNil/PushBoolean/PushNumber/PushString/PushValue()
## Tables
- NewTable()
- CreateTable(narr, nrec int)
- GetTable/SetTable(index int)
- GetField/SetField(index int, key string)
- GetFieldString/Number/Bool/Table(index int, key string, default T) T
- GetTableLength(index int) int
- Next(index int) bool
- ForEachTableKV/ForEachArray(index int, fn func)
- NewTableBuilder() *TableBuilder
## Functions
- RegisterGoFunction(name string, fn GoFunction) error
- UnregisterGoFunction(name string)
- PushGoFunction(fn GoFunction) error
- Call(nargs, nresults int) error
- CallGlobal(name string, args ...any) ([]any, error)
## Globals
- GetGlobal/SetGlobal(name string)
## Execution
- LoadString/LoadFile(source string) error
- DoString/DoFile(source string) error
- Execute(code string) (int, error)
- ExecuteWithResult(code string) (any, error)
- BatchExecute(statements []string) error
## Bytecode
- CompileBytecode(code, name string) ([]byte, error)
- LoadBytecode(bytecode []byte, name string) error
- RunBytecode() error
- RunBytecodeWithResults(nresults int) error
- LoadAndRunBytecode(bytecode []byte, name string) error
- LoadAndRunBytecodeWithResults(bytecode []byte, name string, nresults int) error
- CompileAndRun(code, name string) error
## Validation
- CheckArgs(specs ...ArgSpec) error
- CheckMinArgs/CheckExactArgs(n int) error
- SafeToString/Number/Table(index int) (T, error)
## Error Handling
- PushError(format string, args ...any) int
- GetStackTrace() string
- GetErrorInfo(context string) *LuaError
- CreateLuaError(code int, context string) *LuaError
## Package
- SetPackagePath/AddPackagePath(path string) error
## Metatable
- SetMetatable(index int)
- GetMetatable(index int) bool
## Constants
- TypeNil/Boolean/Number/String/Table/Function/UserData/Thread
- LUA_MINSTACK/MAXSTACK/REGISTRYINDEX/GLOBALSINDEX

159
DOCS.md
View File

@ -56,17 +56,6 @@ L.Remove(-1) // Remove top element
L.Remove(1) // Remove first element L.Remove(1) // Remove first element
``` ```
### absIndex(index int) int
Internal function that converts a possibly negative index to its absolute position.
### checkStack(n int) error
Internal function that ensures there's enough space for n new elements.
```go
if err := L.checkStack(2); err != nil {
return err
}
```
## Type Checks ## Type Checks
### GetType(index int) LuaType ### GetType(index int) LuaType
@ -111,7 +100,7 @@ bool := L.ToBoolean(-1)
``` ```
### ToValue(index int) (any, error) ### ToValue(index int) (any, error)
Converts any Lua value to its Go equivalent. Converts any Lua value to its Go equivalent with automatic type detection.
```go ```go
val, err := L.ToValue(-1) val, err := L.ToValue(-1)
if err != nil { if err != nil {
@ -119,8 +108,8 @@ if err != nil {
} }
``` ```
### ToTable(index int) (map[string]any, error) ### ToTable(index int) (any, error)
Converts a Lua table to a Go map. Converts a Lua table to optimal Go type ([]int, []string, map[string]any, etc.).
```go ```go
table, err := L.ToTable(-1) table, err := L.ToTable(-1)
if err != nil { if err != nil {
@ -149,19 +138,13 @@ L.PushNil()
``` ```
### PushValue(v any) error ### PushValue(v any) error
Pushes any Go value onto the stack. Pushes any Go value onto the stack with comprehensive type support.
```go ```go
err := L.PushValue(myValue) // Supports: primitives, slices, maps with various type combinations
``` err := L.PushValue(map[string]any{
### PushTable(table map[string]any) error
Pushes a Go map as a Lua table.
```go
data := map[string]any{
"key": "value", "key": "value",
"numbers": []float64{1, 2, 3}, "numbers": []float64{1, 2, 3},
} })
err := L.PushTable(data)
``` ```
## Table Operations ## Table Operations
@ -218,6 +201,25 @@ for L.Next(-2) {
} }
``` ```
### GetFieldString/Number/Bool/Table(index int, key string, default T) T
Get typed fields from tables with default values.
```go
name := L.GetFieldString(-1, "name", "unknown")
age := L.GetFieldNumber(-1, "age", 0)
active := L.GetFieldBool(-1, "active", false)
config, ok := L.GetFieldTable(-1, "config")
```
### ForEachTableKV(index int, fn func(key, value string) bool)
### ForEachArray(index int, fn func(i int, state *State) bool)
Convenient iteration helpers.
```go
L.ForEachTableKV(-1, func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true // continue iteration
})
```
## Function Registration and Calling ## Function Registration and Calling
### GoFunction ### GoFunction
@ -258,6 +260,12 @@ L.PushNumber(2)
err := L.Call(2, 1) // Call with 2 args, expect 1 result err := L.Call(2, 1) // Call with 2 args, expect 1 result
``` ```
### CallGlobal(name string, args ...any) ([]any, error)
Calls a global function with arguments and returns all results.
```go
results, err := L.CallGlobal("myfunction", 1, 2, "hello")
```
## Global Operations ## Global Operations
### GetGlobal(name string) ### GetGlobal(name string)
@ -317,6 +325,16 @@ result, err := L.ExecuteWithResult("return 'hello'")
// result would be "hello" // result would be "hello"
``` ```
### BatchExecute(statements []string) error
Executes multiple statements as a single batch.
```go
err := L.BatchExecute([]string{
"x = 10",
"y = 20",
"result = x + y",
})
```
## Bytecode Operations ## Bytecode Operations
### CompileBytecode(code string, name string) ([]byte, error) ### CompileBytecode(code string, name string) ([]byte, error)
@ -375,14 +393,35 @@ Adds a path to package.path.
err := L.AddPackagePath("./modules/?.lua") err := L.AddPackagePath("./modules/?.lua")
``` ```
## Metatable Operations
### SetMetatable(index int)
Sets the metatable for the value at the given index.
```go
L.SetMetatable(-1)
```
### GetMetatable(index int) bool
Gets the metatable for the value at the given index.
```go
if L.GetMetatable(-1) {
// Metatable is now on stack
L.Pop(1)
}
```
## Error Handling ## Error Handling
### LuaError ### LuaError
Error type containing both an error code and message. Enhanced error type with detailed context information.
```go ```go
type LuaError struct { type LuaError struct {
Code int Code int
Message string Message string
File string
Line int
StackTrace string
Context string
} }
``` ```
@ -393,12 +432,62 @@ trace := L.GetStackTrace()
fmt.Println(trace) fmt.Println(trace)
``` ```
### safeCall(f func() C.int) error ### GetErrorInfo(context string) *LuaError
Internal function that wraps a potentially dangerous C call with stack checking. Extracts detailed error information from the Lua stack.
```go ```go
err := s.safeCall(func() C.int { err := L.GetErrorInfo("MyFunction")
return C.lua_pcall(s.L, C.int(nargs), C.int(nresults), 0) ```
})
### CreateLuaError(code int, context string) *LuaError
Creates a LuaError with full context information.
```go
err := L.CreateLuaError(status, "DoString")
```
### PushError(format string, args ...any) int
Pushes an error string and returns -1.
```go
return s.PushError("invalid argument: %v", arg)
```
## Validation
### CheckArgs(specs ...ArgSpec) error
Validates function arguments against specifications.
```go
err := s.CheckArgs(
ArgSpec{Name: "name", Type: "string", Required: true, Check: CheckString},
ArgSpec{Name: "age", Type: "number", Required: false, Check: CheckNumber},
)
```
### CheckMinArgs/CheckExactArgs(n int) error
Argument count validation.
```go
if err := s.CheckMinArgs(2); err != nil {
return s.PushError(err.Error())
}
```
### SafeToString/Number/Table(index int) (T, error)
Safe value conversion with error handling.
```go
str, err := s.SafeToString(1)
if err != nil {
return s.PushError(err.Error())
}
```
## Table Building
### NewTableBuilder() *TableBuilder
Creates a new table builder for fluent table construction.
```go
L.NewTableBuilder().
SetString("name", "John").
SetNumber("age", 30).
SetBool("active", true).
Build()
``` ```
## Thread Safety Notes ## Thread Safety Notes
@ -423,9 +512,3 @@ L.PushString("hello")
// ... use the string // ... use the string
L.Pop(1) // Clean up when done L.Pop(1) // Clean up when done
``` ```
Sandbox management:
```go
sandbox := luajit.NewSandbox()
defer sandbox.Close()
```

177
README.md
View File

@ -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.