### Install GopherLua Source: https://github.com/yuin/gopher-lua/blob/master/README.md Use `go get` to install the GopherLua package. ```bash $ go get github.com/yuin/gopher-lua ``` -------------------------------- ### Install GopherLua Interpreter Source: https://github.com/yuin/gopher-lua/blob/master/README.md Use `go get` to install the standalone GopherLua interpreter, `glua`. This command fetches and installs the executable, making it available in your system's PATH. ```bash go get github.com/yuin/gopher-lua/cmd/glua ``` -------------------------------- ### Compare Performance: GopherLua with and without Context Source: https://github.com/yuin/gopher-lua/blob/master/README.md Illustrates the performance difference when using Go contexts for LState termination compared to running without context. The example shows that context usage can lead to increased execution time. ```shell time ./glua-with-context.exe fib.lua 9227465 0.01s user 0.11s system 1% cpu 7.505 total time ./glua-without-context.exe fib.lua 9227465 0.01s user 0.01s system 0% cpu 5.306 total ``` -------------------------------- ### GopherLua Coroutine Example Source: https://github.com/yuin/gopher-lua/blob/master/README.md Illustrates how to create, resume, and handle results/errors from Lua coroutines within Go. ```go co, _ := L.NewThread() /* create a new thread */ fn := L.GetGlobal("coro").(*lua.LFunction) /* get function from lua */ for { st, err, values := L.Resume(co, fn) if st == lua.ResumeError { fmt.Println("yield break(error)") fmt.Println(err.Error()) break } for i, lv := range values { fmt.Printf("%v : %v\n", i, lv) } if st == lua.ResumeOK { fmt.Println("yield break(ok)") break } } ``` -------------------------------- ### Go Goroutine and Channel Example Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates sending and receiving data between Go and Lua using channels and the select statement. Ensure LState is not shared across goroutines. ```go func receiver(ch, quit chan lua.LValue) { L := lua.NewState() defer L.Close() L.SetGlobal("ch", lua.LChannel(ch)) L.SetGlobal("quit", lua.LChannel(quit)) if err := L.DoString(` local exit = false while not exit do channel.select( {"|<-", ch, function(ok, v) if not ok then print("channel closed") exit = true else print("received:", v) end end}, {"|<-", quit, function(ok, v) print("quit") exit = true end} ) end `); err != nil { panic(err) } } func sender(ch, quit chan lua.LValue) { L := lua.NewState() defer L.Close() L.SetGlobal("ch", lua.LChannel(ch)) L.SetGlobal("quit", lua.LChannel(quit)) if err := L.DoString(` ch:send("1") ch:send("2") `); err != nil { panic(err) } ch <- lua.LString("3") quit <- lua.LTrue } func main() { ch := make(chan lua.LValue) quit := make(chan lua.LValue) go receiver(ch, quit) go sender(ch, quit) time.Sleep(3 * time.Second) } ``` -------------------------------- ### Lua Channel Select Example Source: https://github.com/yuin/gopher-lua/blob/master/README.md Illustrates using channel.select in Lua to receive from multiple channels or handle default cases. The handler functions are called upon successful operation. ```lua local idx, recv, ok = channel.select( {"|<-", ch1}, {"|<-", ch2} ) if not ok then print("closed") elseif idx == 1 then -- received from ch1 print(recv) elseif idx == 2 then -- received from ch2 print(recv) end ``` ```lua channel.select( {"|<-", ch1, function(ok, data) print(ok, data) end}, {"<-|", ch2, "value", function(data) print(data) end}, {"default", function() print("default action") end} ) ``` -------------------------------- ### Import GopherLua Package Source: https://github.com/yuin/gopher-lua/blob/master/README.md Import the GopherLua package into your Go project to start using its functionalities. ```go import ( "github.com/yuin/gopher-lua" ) ``` -------------------------------- ### Integrate Go Channels with Lua using LChannel and OpenChannel Source: https://context7.com/yuin/gopher-lua/llms.txt Exposes Go channels as a Lua type, allowing Lua scripts to send, receive, and select on channels. This example shows a producer-consumer pattern using channels. ```go package main import ( "fmt" "sync" lua "github.com/yuin/gopher-lua" ) func newVM() *lua.LState { L := lua.NewState() return L } func main() { ch := make(chan lua.LValue, 4) quit := make(chan lua.LValue, 1) var wg sync.WaitGroup // Producer goroutine wg.Add(1) go func() { defer wg.Done() L := newVM() defer L.Close() L.SetGlobal("ch", lua.LChannel(ch)) L.DoString(` for i = 1, 4 do ch:send(i * 10) end `) }() // Consumer goroutine wg.Add(1) go func() { defer wg.Done() L := newVM() defer L.Close() L.SetGlobal("ch", lua.LChannel(ch)) L.SetGlobal("quit", lua.LChannel(quit)) L.DoString(` for i = 1, 4 do local ok, v = ch:receive() if ok then print("got", v) end end `) quit <- lua.LTrue }() wg.Wait() fmt.Println("done") // Output: got 10 / got 20 / got 30 / got 40 } ``` -------------------------------- ### Share Compiled Lua Bytecode Between GopherLua LStates Source: https://github.com/yuin/gopher-lua/blob/master/README.md Example demonstrating how to compile a Lua script once and then execute the resulting bytecode in multiple independent `LState` instances. This optimizes memory usage by avoiding recompilation for each `LState`. Requires `CompileLua` and `DoCompiledFile`. ```go // Example shows how to share the compiled byte code from a lua script between multiple VMs. func Example() { codeToShare, err := CompileLua("mylua.lua") if err != nil { panic(err) } a := lua.NewState() b := lua.NewState() c := lua.NewState() DoCompiledFile(a, codeToShare) DoCompiledFile(b, codeToShare) DoCompiledFile(c, codeToShare) } ``` -------------------------------- ### LState Pool Implementation Source: https://github.com/yuin/gopher-lua/blob/master/README.md Provides a thread-safe pool for managing LState instances using sync.Pool. Use Get to retrieve an LState and Put to return it. ```go type lStatePool struct { m sync.Mutex saved []*lua.LState } func (pl *lStatePool) Get() *lua.LState { pl.m.Lock() defer pl.m.Unlock() n := len(pl.saved) if n == 0 { return pl.New() } x := pl.saved[n-1] pl.saved = pl.saved[0 : n-1] return x } func (pl *lStatePool) New() *lua.LState { L := lua.NewState() // setting the L up here. // load scripts, set global variables, share channels, etc... return L } func (pl *lStatePool) Put(L *lua.LState) { pl.m.Lock() defer pl.m.Unlock() pl.saved = append(pl.saved, L) } func (pl *lStatePool) Shutdown() { for _, L := range pl.saved { L.Close() } } // Global LState pool var luaPool = &lStatePool{ saved: make([]*lua.LState, 0, 4), } ``` ```go func MyWorker() { L := luaPool.Get() defer luaPool.Put(L) /* your code here */ } func main() { defer luaPool.Shutdown() go MyWorker() go MyWorker() /* etc... */ } ``` -------------------------------- ### Create and Configure Lua VM with NewState Source: https://context7.com/yuin/gopher-lua/llms.txt Use `NewState` to create a new Lua VM. An optional `Options` struct can configure call-stack size, registry size, auto-growth, and library loading. ```go package main import ( "fmt" lua "github.com/yuin/gopher-lua" ) func main() { // Default state — all standard libraries loaded L := lua.NewState() defer L.Close() // Custom state — large registry with auto-growth, no libs Lopt := lua.NewState(lua.Options{ CallStackSize: 512, RegistrySize: 1024 * 20, RegistryMaxSize: 1024 * 80, RegistryGrowStep: 32, SkipOpenLibs: true, MinimizeStackMemory: true, }) defer Lopt.Close() fmt.Println("states created") } ``` -------------------------------- ### Open Subset of Built-in Modules Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates initializing a Lua state with only specific built-in modules, skipping others for security or performance. ```go func main() { L := lua.NewState(lua.Options{SkipOpenLibs: true}) defer L.Close() for _, pair := range []struct { n string f lua.LGFunction }{ {lua.LoadLibName, lua.OpenPackage}, // Must be first {lua.BaseLibName, lua.OpenBase}, {lua.TabLibName, lua.OpenTable}, } { if err := L.CallByParam(lua.P{ Fn: L.NewFunction(pair.f), NRet: 0, Protect: true, }, lua.LString(pair.n)); err != nil { panic(err) } } if err := L.DoFile("main.lua"); err != nil { panic(err) } } ``` -------------------------------- ### Run Lua Script from File Source: https://github.com/yuin/gopher-lua/blob/master/README.md Initialize a new Lua state, defer its closing, and execute a Lua script from a file using `DoFile`. ```go L := lua.NewState() deferr L.Close() if err := L.DoFile("hello.lua"); err != nil { panic(err) } ``` -------------------------------- ### Create Go Module for Lua Source: https://github.com/yuin/gopher-lua/blob/master/README.md Shows how to define a Go package that can be loaded as a Lua module, including functions and module metadata. ```go package mymodule import ( "github.com/yuin/gopher-lua" ) func Loader(L *lua.LState) int { // register functions to the table mod := L.SetFuncs(L.NewTable(), exports) // register other stuff L.SetField(mod, "name", lua.LString("value")) // returns the module L.Push(mod) return 1 } var exports = map[string]lua.LGFunction{ "myfunc": myfunc, } func myfunc(L *lua.LState) int { return 0 } ``` ```go package main import ( "./mymodule" "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() L.PreloadModule("mymodule", mymodule.Loader) if err := L.DoFile("main.lua"); err != nil { panic(err) } } ``` ```lua local m = require("mymodule") m.myfunc() print(m.name) ``` -------------------------------- ### Run Lua Script from String Source: https://github.com/yuin/gopher-lua/blob/master/README.md Initialize a new Lua state, defer its closing, and execute a Lua script provided as a string using `DoString`. ```go L := lua.NewState() deferr L.Close() if err := L.DoString(`print("hello")`); err != nil { panic(err) } ``` -------------------------------- ### Execute Lua Code with DoString and DoFile Source: https://context7.com/yuin/gopher-lua/llms.txt `DoString` compiles and runs Lua code from a string. `DoFile` loads and runs a `.lua` file. Both return an error on syntax or runtime failure. ```go package main import ( "fmt" "log" lua "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() // Run inline Lua if err := L.DoString(` local sum = 0 for i = 1, 10 do sum = sum + i end print("sum =", sum) -- sum = 55 `); err != nil { log.Fatal(err) } // Run a file (errors include file + line info) if err := L.DoFile("script.lua"); err != nil { if apiErr, ok := err.(*lua.ApiError); ok { fmt.Printf("type=%d object=%s\n", apiErr.Type, apiErr.Object) } log.Fatal(err) } } ``` -------------------------------- ### Call Go Function from Lua Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates how to register a Go function to be callable from Lua and then call it. ```go func Double(L *lua.LState) int { lv := L.ToInt(1) /* get argument */ L.Push(lua.LNumber(lv * 2)) /* push result */ return 1 /* number of results */ } func main() { L := lua.NewState() defer L.Close() L.SetGlobal("double", L.NewFunction(Double)) /* Original lua_setglobal uses stack... */ } ``` ```lua print(double(20)) -- > "40" ``` -------------------------------- ### NewState Source: https://context7.com/yuin/gopher-lua/llms.txt Creates a new Lua VM state (`*LState`). By default, all standard libraries are opened. Options can be provided to customize aspects like call-stack size, registry size, and library loading. ```APIDOC ## NewState — Create and configure a Lua VM `NewState` allocates a new `*LState` with all standard libraries opened. An optional `Options` struct controls call-stack size, registry size, auto-growth, and whether libraries are skipped. ```go package main import ( "fmt" lua "github.com/yuin/gopher-lua" ) func main() { // Default state — all standard libraries loaded L := lua.NewState() defer L.Close() // Custom state — large registry with auto-growth, no libs Lopt := lua.NewState(lua.Options{ CallStackSize: 512, RegistrySize: 1024 * 20, RegistryMaxSize: 1024 * 80, RegistryGrowStep: 32, SkipOpenLibs: true, MinimizeStackMemory: true, }) defer Lopt.Close() fmt.Println("states created") } ``` ``` -------------------------------- ### Argument Handling in LGFunctions with Check*, Opt*, To* Source: https://context7.com/yuin/gopher-lua/llms.txt Illustrates how to handle arguments within LGFunctions using `CheckString`, `OptString`, and other variants for robust input validation and conversion. `Check*` raises errors on type mismatch, while `Opt*` provides defaults. ```go package main import ( "strings" lua "github.com/yuin/gopher-lua" ) func joinStrings(L *lua.LState) int { sep := L.OptString(1, ",") // optional separator, default "," n := L.GetTop() - 1 // remaining args parts := make([]string, 0, n) for i := 2; i <= L.GetTop(); i++ { parts = append(parts, L.CheckString(i)) // raises error if not string } L.Push(lua.LString(strings.Join(parts, sep))) return 1 } func main() { L := lua.NewState() defer L.Close() L.Register("join", joinStrings) L.DoString(` print(join("-", "a", "b", "c")) // a-b-c print(join(",", "x", "y")) // x,y print(join(nil, "p", "q")) // p,q (nil → default sep) `) } ``` -------------------------------- ### Call Lua Script from Go Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates executing a Lua script file from Go and retrieving a return value. ```go L := lua.NewState() defer L.Close() if err := L.DoFile("double.lua"); err != nil { panic(err) } if err := L.CallByParam(lua.P{ Fn: L.GetGlobal("double"), NRet: 1, Protect: true, }, lua.LNumber(10)); err != nil { panic(err) } ret := L.Get(-1) // returned value L.Pop(1) // remove received value ``` -------------------------------- ### Configuring Initial and Max Registry Size Source: https://github.com/yuin/gopher-lua/blob/master/README.md Sets the initial size, maximum size, and growth step for the Lua registry when creating a new `LState`. This helps manage memory and prevent panics due to insufficient registry space. ```go L := lua.NewState(lua.Options{ RegistrySize: 1024 * 20, // this is the initial size of the registry RegistryMaxSize: 1024 * 80, // this is the maximum size that the registry can grow to. If set to `0` (the default) then the registry will not auto grow RegistryGrowStep: 32, // this is how much to step up the registry by each time it runs out of space. The default is `32`. }) defer L.Close() ``` -------------------------------- ### Registering Go Functions for Lua (NewFunction, LGFunction) Source: https://context7.com/yuin/gopher-lua/llms.txt Explains how to register Go functions with the `func(*LState) int` signature as `LGFunction`s, making them callable from Lua. The return value indicates the number of results pushed to the stack. ```go package main import ( "fmt" "math" lua "github.com/yuin/gopher-lua" ) // hypot(a, b) -> c exposed to Lua func hypot(L *lua.LState) int { a := L.CheckNumber(1) b := L.CheckNumber(2) c := math.Sqrt(float64(a*a + b*b)) L.Push(lua.LNumber(c)) return 1 } func main() { L := lua.NewState() defer L.Close() L.Register("hypot", hypot) // shorthand for SetGlobal + NewFunction if err := L.DoString(` local h = hypot(3, 4) assert(h == 5.0, "expected 5") print("hypot(3,4) =", h) // 5 `); err != nil { panic(err) } fmt.Println("ok") } ``` -------------------------------- ### Create Lua Modules with Go using PreloadModule and SetFuncs Source: https://context7.com/yuin/gopher-lua/llms.txt Register a Go function as a Lua module loader. `SetFuncs` is used to efficiently add multiple functions to the module table. ```go package main import ( "strings" lua "github.com/yuin/gopher-lua" ) func mathExtraLoader(L *lua.LState) int { mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{ "clamp": func(L *lua.LState) int { v := L.CheckNumber(1) lo := L.CheckNumber(2) hi := L.CheckNumber(3) if v < lo { v = lo } if v > hi { v = hi } L.Push(v) return 1 }, "sign": func(L *lua.LState) int { v := float64(L.CheckNumber(1)) switch { case v > 0: L.Push(lua.LNumber(1)) case v < 0: L.Push(lua.LNumber(-1)) default: L.Push(lua.LNumber(0)) } return 1 }, }) L.Push(mod) return 1 } func main() { L := lua.NewState() defer L.Close() L.PreloadModule("mathx", mathExtraLoader) L.DoString(` local mx = require("mathx") print(mx.clamp(15, 0, 10)) -- 10 print(mx.sign(-42)) -- -1 `) } ``` -------------------------------- ### Register and Use User-Defined Types in GopherLua Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates how to define a custom Go struct, register it as a new type in GopherLua, and use its constructor and methods from Lua. Ensure the type name is unique and consistently used. ```go type Person struct { Name string } const luaPersonTypeName = "person" // Registers my person type to given L. func registerPersonType(L *lua.LState) { mt := L.NewTypeMetatable(luaPersonTypeName) L.SetGlobal("person", mt) // static attributes L.SetField(mt, "new", L.NewFunction(newPerson)) // methods L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), personMethods)) } // Constructor func newPerson(L *lua.LState) int { person := &Person{L.CheckString(1)} ud := L.NewUserData() ud.Value = person L.SetMetatable(ud, L.GetTypeMetatable(luaPersonTypeName)) L.Push(ud) return 1 } // Checks whether the first lua argument is a *LUserData with *Person and returns this *Person. func checkPerson(L *lua.LState) *Person { ud := L.CheckUserData(1) if v, ok := ud.Value.(*Person); ok { return v } L.ArgError(1, "person expected") return nil } var personMethods = map[string]lua.LGFunction{ "name": personGetSetName, } // Getter and setter for the Person#Name func personGetSetName(L *lua.LState) int { p := checkPerson(L) if L.GetTop() == 2 { p.Name = L.CheckString(2) return 0 } L.Push(lua.LString(p.Name)) return 1 } func main() { L := lua.NewState() defer L.Close() registerPersonType(L) if err := L.DoString(` p = person.new("Steeve") print(p:name()) -- "Steeve" p:name("Alice") print(p:name()) -- "Alice" `); err != nil { panic(err) } } ``` -------------------------------- ### DoString / DoFile Source: https://context7.com/yuin/gopher-lua/llms.txt Executes Lua code. `DoString` compiles and runs a Lua source string, while `DoFile` loads and runs a `.lua` file. Both functions return an `error` (specifically an `*ApiError`) if there are any syntax or runtime failures during execution. ```APIDOC ## DoString / DoFile — Execute Lua code `DoString` compiles and runs a Lua source string. `DoFile` loads and runs a `.lua` file. Both return an `error` (an `*ApiError`) on syntax or runtime failure. ```go package main import ( "fmt" "log" lua "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() // Run inline Lua if err := L.DoString(` local sum = 0 for i = 1, 10 do sum = sum + i end print("sum =", sum) -- sum = 55 `); err != nil { log.Fatal(err) } // Run a file (errors include file + line info) if err := L.DoFile("script.lua"); err != nil { if apiErr, ok := err.(*lua.ApiError); ok { fmt.Printf("type=%d object=%s\n", apiErr.Type, apiErr.Object) } log.Fatal(err) } } ``` ``` -------------------------------- ### Checking for Lua Nil or False Source: https://github.com/yuin/gopher-lua/blob/master/README.md Provides methods `LVIsFalse` and `LVAsBool` to correctly evaluate Lua's truthiness, where both `nil` and `false` are considered false. ```go lv := L.Get(-1) // get the value at the top of the stack if lua.LVIsFalse(lv) { // lv is nil or false } if lua.LVAsBool(lv) { // lv is neither nil nor false } ``` -------------------------------- ### Protected Function Calls (CallByParam, PCall) in Go Source: https://context7.com/yuin/gopher-lua/llms.txt Demonstrates how to call Lua functions from Go with optional error protection using CallByParam and PCall. PCall allows for custom error handling. ```go package main import ( "fmt" "log" lua "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() if err := L.DoString(` function add(a, b) return a + b end function risky(x) if x < 0 then error("negative input: " .. x) end return x * 2 end `); err != nil { log.Fatal(err) } // CallByParam with protection err := L.CallByParam(lua.P{ Fn: L.GetGlobal("add"), NRet: 1, Protect: true, }, lua.LNumber(10), lua.LNumber(32)) if err != nil { log.Fatal(err) } fmt.Println(L.ToInt(-1)) // 42 L.Pop(1) // PCall with custom error handler errHandler := L.NewFunction(func(L *lua.LState) int { msg := L.ToString(1) L.Push(lua.LString("CAUGHT: " + msg)) return 1 }) L.Push(L.GetGlobal("risky")) L.Push(lua.LNumber(-5)) if err := L.PCall(1, 1, errHandler); err != nil { fmt.Println(err) // CAUGHT: input:2: negative input: -5 } } ``` -------------------------------- ### CallByParam / PCall Source: https://context7.com/yuin/gopher-lua/llms.txt Demonstrates protected function calls from Go to Lua using CallByParam and PCall. CallByParam is a high-level way to call Lua functions by name, while PCall is a lower-level protected call that handles errors without panicking. ```APIDOC ## CallByParam / PCall — Protected function calls from Go `CallByParam` is the high-level way to call a Lua function by name with arguments and a chosen number of return values. `PCall` is the lower-level protected call (no panic on error). Both return an `error` instead of panicking when `Protect: true`. ### Example Usage: ```go // CallByParam with protection err := L.CallByParam(lua.P{ Fn: L.GetGlobal("add"), NRet: 1, Protect: true, }, lua.LNumber(10), lua.LNumber(32)) if err != nil { log.Fatal(err) } fmt.Println(L.ToInt(-1)) // 42 L.Pop(1) // PCall with custom error handler errHandler := L.NewFunction(func(L *lua.LState) int { msg := L.ToString(1) L.Push(lua.LString("CAUGHT: " + msg)) return 1 }) L.Push(L.GetGlobal("risky")) L.Push(lua.LNumber(-5)) if err := L.PCall(1, 1, errHandler); err != nil { fmt.Println(err) // CAUGHT: input:2: negative input: -5 } ``` ``` -------------------------------- ### Exchanging Values with Lua Globals (SetGlobal, GetGlobal) Source: https://context7.com/yuin/gopher-lua/llms.txt Shows how to set Go functions and values as Lua globals using SetGlobal, and retrieve Lua globals back into Go using GetGlobal. Useful for exposing Go APIs to Lua. ```go package main import ( "fmt" "strings" lua "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() // Expose a Go function to Lua L.SetGlobal("upper", L.NewFunction(func(L *lua.LState) int { s := L.CheckString(1) L.Push(lua.LString(strings.ToUpper(s))) return 1 // number of return values })) // Expose a Go value L.SetGlobal("version", lua.LString("1.0.0")) if err := L.DoString(` print(upper("hello")) // HELLO print(version) // 1.0.0 `); err != nil { panic(err) } // Read a Lua global back into Go v := L.GetGlobal("version") fmt.Println(v.(lua.LString)) // 1.0.0 } ``` -------------------------------- ### Exposing Go Structs as Lua Objects (LUserData, NewTypeMetatable) Source: https://context7.com/yuin/gopher-lua/llms.txt Demonstrates wrapping Go structs in `LUserData` and defining metatables to create an object-oriented interface in Lua. This allows Lua to call methods on Go objects. ```go package main import ( "fmt" lua "github.com/yuin/gopher-lua" ) type Counter struct{ n int } const counterType = "Counter" func registerCounter(L *lua.LState) { mt := L.NewTypeMetatable(counterType) L.SetGlobal(counterType, mt) L.SetField(mt, "new", L.NewFunction(func(L *lua.LState) int { init := L.OptInt(1, 0) ud := L.NewUserData() ud.Value = &Counter{n: init} L.SetMetatable(ud, L.GetTypeMetatable(counterType)) L.Push(ud) return 1 })) L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{ "inc": func(L *lua.LState) int { c := L.CheckUserData(1).Value.(*Counter) c.n++ return 0 }, "get": func(L *lua.LState) int { c := L.CheckUserData(1).Value.(*Counter) L.Push(lua.LNumber(c.n)) return 1 }, })) } func main() { L := lua.NewState() defer L.Close() registerCounter(L) if err := L.DoString(` local c = Counter.new(10) c:inc() c:inc() print(c:get()) // 12 `); err != nil { panic(err) } fmt.Println("done") } ``` -------------------------------- ### Manage Coroutines with NewThread, Resume, and Yield Source: https://context7.com/yuin/gopher-lua/llms.txt Demonstrates creating a new Lua coroutine, resuming its execution, and handling yielded values. The `Resume` function returns the coroutine's state, errors, and any returned values. ```go package main import ( "fmt" lua "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() L.DoString(` function gen(max) for i = 1, max do coroutine.yield(i * i) end end `) co, _ := L.NewThread() fn := L.GetGlobal("gen").(*lua.LFunction) for { st, err, values := L.Resume(co, fn, lua.LNumber(5)) if err != nil { fmt.Println("error:", err) break } if st == lua.ResumeYield { fmt.Println("yield:", values[0]) // 1, 4, 9, 16, 25 } else { // ResumeOK — coroutine finished break } fn = nil // only pass fn on first Resume } } ``` -------------------------------- ### Configuring Fixed or Auto-Sizing Callstack Source: https://github.com/yuin/gopher-lua/blob/master/README.md Configures the callstack size for an `LState`, allowing for a fixed size for maximum performance or an auto-sizing mode (`MinimizeStackMemory: true`) that grows and shrinks as needed. ```go L := lua.NewState(lua.Options{ CallStackSize: 120, // this is the maximum callstack size of this LState MinimizeStackMemory: true, // Defaults to `false` if not specified. If set, the callstack will auto grow and shrink as needed up to a max of `CallStackSize`. If not set, the callstack will be fixed at `CallStackSize`. }) defer L.Close() ``` -------------------------------- ### Compile Lua Script to Bytecode in Go Source: https://github.com/yuin/gopher-lua/blob/master/README.md Provides a Go function to read a Lua file, parse it, and compile it into a `FunctionProto` (bytecode). This function is a prerequisite for sharing compiled code between `LState` instances. Ensure `os`, `bufio`, `parse`, and `lua` packages are imported. ```go // CompileLua reads the passed lua file from disk and compiles it. func CompileLua(filePath string) (*lua.FunctionProto, error) { file, err := os.Open(filePath) defer file.Close() if err != nil { return nil, err } reader := bufio.NewReader(file) chunk, err := parse.Parse(reader, filePath) if err != nil { return nil, err } proto, err := lua.Compile(chunk, filePath) if err != nil { return nil, err } return proto, nil } ``` -------------------------------- ### Define Go Function Signature Source: https://github.com/yuin/gopher-lua/blob/master/README.md Shows the type definition for functions that can be registered from Go to Lua. ```go type LGFunction func(*LState) int ``` -------------------------------- ### Implement Script Timeouts with SetContext and RemoveContext Source: https://context7.com/yuin/gopher-lua/llms.txt Attaches a `context.Context` to the Lua state to enable cancellation and timeouts. Use `RemoveContext` to detach the context and allow the state to be reused. ```go package main import ( "context" "fmt" "time" lua "github.com/yuin/gopher-lua" ) func main() { L := lua.NewState() defer L.Close() ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) defer cancel() L.SetContext(ctx) err := L.DoString(` local t = os.clock() while os.clock() - t < 10 do end -- infinite-ish loop `) if err != nil { fmt.Println("caught:", err) // caught: context deadline exceeded } // Detach context so the state can be reused L.RemoveContext() L.DoString(`print("still alive")`) } ``` -------------------------------- ### Type Assertions for LString in Go Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates how to retrieve a value from the stack and assert if it is an LString using Go's type assertion. This is useful when you expect a string value from Lua. ```go lv := L.Get(-1) // get the value at the top of the stack if str, ok := lv.(lua.LString); ok { // lv is LString fmt.Println(string(str)) } if lv.Type() != lua.LTString { panic("string required.") } ``` -------------------------------- ### LUserData / NewTypeMetatable Source: https://context7.com/yuin/gopher-lua/llms.txt Exposes Go structs as Lua objects by wrapping them in `LUserData` and associating them with a named metatable. ```APIDOC ## LUserData / NewTypeMetatable — Expose Go structs as Lua objects `LUserData` wraps an arbitrary Go value. Combined with a named metatable and `__index`, this creates a full object-oriented interface accessible from Lua. ### Example Usage: ```go type Counter struct{ n int } const counterType = "Counter" func registerCounter(L *lua.LState) { mt := L.NewTypeMetatable(counterType) L.SetGlobal(counterType, mt) L.SetField(mt, "new", L.NewFunction(func(L *lua.LState) int { init := L.OptInt(1, 0) ud := L.NewUserData() ud.Value = &Counter{n: init} L.SetMetatable(ud, L.GetTypeMetatable(counterType)) L.Push(ud) return 1 })) L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{ "inc": func(L *lua.LState) int { c := L.CheckUserData(1).Value.(*Counter) c.n++ return 0 }, "get": func(L *lua.LState) int { c := L.CheckUserData(1).Value.(*Counter) L.Push(lua.LNumber(c.n)) return 1 }, })) } // Lua script usage: // local c = Counter.new(10) // c:inc() // c:inc() // print(c:get()) -- 12 ``` ``` -------------------------------- ### Terminate GopherLua LState with Context Cancel (Coroutines) Source: https://github.com/yuin/gopher-lua/blob/master/README.md Demonstrates canceling a Lua coroutine's execution using a Go context. This allows for graceful termination of long-running or infinite loops within coroutines. Remember that context usage can impact performance. ```go L := lua.NewState() deffer L.Close() c, cancel := context.WithCancel(context.Background()) L.SetContext(ctx) deffer cancel() L.DoString(` function coro() local i = 0 while true do coroutine.yield(i) i = i+1 end return i end `) co, cocancel := L.NewThread() deffer cocancel() fn := L.GetGlobal("coro").(*LFunction) _, err, values := L.Resume(co, fn) // err is nil cancel() // cancel the parent context _, err, values = L.Resume(co, fn) // err is NOT nil : child context was canceled ``` -------------------------------- ### Terminate GopherLua LState with Context Timeout Source: https://github.com/yuin/gopher-lua/blob/master/README.md Use Go's context package to set a timeout for Lua script execution. This is useful for preventing long-running scripts from blocking indefinitely. Ensure `context` and `time` packages are imported. ```go L := lua.NewState() defer L.Close() c, cancel := context.WithTimeout(context.Background(), 1*time.Second) deffer cancel() // set the context to our LState L.SetContext(ctx) err := L.DoString(` local clock = os.clock function sleep(n) -- seconds local t0 = clock() while clock() - t0 <= n do end end sleep(3) `) // err.Error() contains "context deadline exceeded" ``` -------------------------------- ### Trace Function Calls with GopherLua Debug API Source: https://context7.com/yuin/gopher-lua/llms.txt This Go code demonstrates how to use GopherLua's debug API to walk the call stack and print information about each frame, including source file, line number, and function name. It registers a 'trace' function in Lua and calls it from nested Lua functions to show stack unwinding. ```go package main import ( "fmt" lua "github.com/yuin/gopher-lua" ) func tracingFunc(L *lua.LState) int { // Walk the call stack and print each frame for level := 0; ; level++ { dbg, ok := L.GetStack(level) if !ok { break } L.GetInfo("Sln", dbg, lua.LNil) fmt.Printf(" [%d] %s:%d in %s\n", level, dbg.Source, dbg.CurrentLine, dbg.Name) } return 0 } func main() { L := lua.NewState() defer L.Close() L.Register("trace", tracingFunc) L.DoString(` local function inner() trace() end local function outer() inner() end outer() `) } ``` -------------------------------- ### LState Pool Pattern for Goroutine Safety Source: https://context7.com/yuin/gopher-lua/llms.txt Demonstrates a pool of pre-initialized Lua states to manage concurrent access from multiple goroutines, as LState is not goroutine-safe. Each goroutine checks out a state and returns it when done. ```go package main import ( "fmt" "sync" lua "github.com/yuin/gopher-lua" ) type LStatePool struct { mu sync.Mutex saved []*lua.LState } func (p *LStatePool) Get() *lua.LState { p.mu.Lock() defer p.mu.Unlock() if n := len(p.saved); n > 0 { x := p.saved[n-1] p.saved = p.saved[:n-1] return x } L := lua.NewState() L.DoString(`function process(x) return x * x end`) return L } func (p *LStatePool) Put(L *lua.LState) { p.mu.Lock() p.saved = append(p.saved, L) p.mu.Unlock() } func (p *LStatePool) Shutdown() { for _, L := range p.saved { L.Close() } } var pool = &LStatePool{} func worker(n int, wg *sync.WaitGroup) { defer wg.Done() L := pool.Get() defer pool.Put(L) L.CallByParam(lua.P{Fn: L.GetGlobal("process"), NRet: 1, Protect: true}, lua.LNumber(n)) fmt.Printf("process(%d) = %d\n", n, L.ToInt(-1)) L.Pop(1) } func main() { defer pool.Shutdown() var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) } wg.Wait() } ``` -------------------------------- ### Run Compiled Lua Bytecode in GopherLua LState Source: https://github.com/yuin/gopher-lua/blob/master/README.md A Go function that takes a pre-compiled Lua `FunctionProto` and executes it within a given `LState`. This is used in conjunction with `CompileLua` to run shared bytecode. It's equivalent to `L.DoFile` but operates on compiled code. ```go // DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent // to calling DoFile on the LState with the original source file. func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error { lfunc := L.NewFunctionFromProto(proto) L.Push(lfunc) return L.PCall(0, lua.MultRet, nil) } ``` -------------------------------- ### Go API for Channels Source: https://github.com/yuin/gopher-lua/blob/master/README.md Functions available in the Go API for interacting with GopherLua channels. ```APIDOC ## Go API `ToChannel`, `CheckChannel`, `OptChannel` are available. Refer to [Go doc(LState methods)](http://godoc.org/github.com/yuin/gopher-lua) for further information. ``` -------------------------------- ### Correctly Comparing LBool Constants Source: https://github.com/yuin/gopher-lua/blob/master/README.md Illustrates the correct way to check for Lua's true value using the pre-defined `lua.LTrue` constant. Avoid direct type assertion for `LBool` when checking against true/false. ```go lv := L.Get(-1) // get the value at the top of the stack if lv == lua.LTrue { // correct } if bl, ok := lv.(lua.LBool); ok && bool(bl) { // wrong } ``` -------------------------------- ### Load / LoadFile / LoadString Source: https://context7.com/yuin/gopher-lua/llms.txt Compiles Lua source code into an `*LFunction` without executing it immediately. The compiled function can be pushed onto the stack and called later, or even shared across multiple `LState` instances. This is useful for pre-compiling scripts. ```APIDOC ## Load / LoadFile / LoadString — Compile without executing These functions compile Lua source into an `*LFunction` without running it. The result can be pushed and called later, or shared across multiple `LState` instances. ```go package main import ( "bufio" "log" "strings" lua "github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua/parse" ) // CompileLua compiles a file to a shareable FunctionProto. func CompileLua(path string) (*lua.FunctionProto, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() chunk, err := parse.Parse(bufio.NewReader(f), path) if err != nil { return nil, err } return lua.Compile(chunk, path) } func main() { L := lua.NewState() defer L.Close() // LoadString — compile from string fn, err := L.LoadString(`return 2 + 2`) if err != nil { log.Fatal(err) } L.Push(fn) if err := L.PCall(0, lua.MultRet, nil); err != nil { log.Fatal(err) } fmt.Println(L.ToInt(-1)) // 4 L.Pop(1) // Share compiled proto across states proto, err := CompileLua("heavy.lua") if err != nil { log.Fatal(err) } for i := 0; i < 5; i++ { worker := lua.NewState() lfn := worker.NewFunctionFromProto(proto) worker.Push(lfn) worker.PCall(0, lua.MultRet, nil) worker.Close() } } ``` ```