Documentation
¶
Overview ¶
Package luar provides a convenient interface between Lua and Go.
It uses Alessandro Arzilli's golua (https://github.com/aarzilli/golua).
Most Go values can be passed to Lua: basic types, strings, complex numbers, user-defined types, pointers, composite types, functions, channels, etc. Conversely, most Lua values can be converted to Go values.
Composite types are processed recursively.
Methods can be called on user-defined types. These methods will be callable using _dot-notation_ rather than colon notation.
Arrays, slices, maps and structs can be copied as tables, or alternatively passed over as Lua proxy objects which can be naturally indexed.
In the case of structs and string maps, fields have priority over methods. Use 'luar.method(<value>, <method>)(<params>...)' to call shadowed methods.
Unexported struct fields are ignored. The "lua" tag is used to match fields in struct conversion.
You may pass a Lua table to an imported Go function; if the table is 'array-like' then it is converted to a Go slice; if it is 'map-like' then it is converted to a Go map.
Pointer values encode as the value pointed to when unproxified.
Usual operators (arithmetic, string concatenation, pairs/ipairs, etc.) work on proxies too. The type of the result depends on the type of the operands. The rules are as follows:
- If the operands are of the same type, use this type.
- If one type is a Lua number, use the other, user-defined type.
- If the types are different and not Lua numbers, convert to a complex proxy, a Lua number, or a Lua string according to the result kind.
Channels ¶
Channel proxies can be manipulated with the following methods:
- close(): Close the channel.
- recv() value: Fetch and return a value from the channel.
- send(x value): Send a value in the channel.
Complex numbers ¶
Complex proxies can be manipulated with the following attributes:
- real: The real part.
- imag: The imaginary part.
Slices ¶
Slice proxies can be manipulated with the following methods/attributes:
- append(x ...value) sliceProxy: Append the elements and return the new slice. The elements must be convertible to the slice element type.
- cap: The capacity of the slice.
- sub(i, j integer) sliceProxy: Return the sub-slice that ranges from 'i' to 'j' included. This matches Lua's behaviour, but not Go's.
Strings ¶
String proxies can be browsed rune by rune with the pairs/ipairs functions. These runes are encoded as strings in Lua.
String proxies can be manipulated with the following method:
- sub(i, j integer) sliceProxy: Return the sub-string that ranges from 'i' to 'j' included. This matches Lua's behaviour, but not Go's.
Example ¶
package main import ( "fmt" "github.com/stevedonovan/luar" ) func main() { const test = ` for i = 1, 3 do Print(msg, i) end Print(user) Print(user.Name, user.Age) ` type person struct { Name string Age int } L := luar.Init() defer L.Close() user := &person{"Dolly", 46} luar.Register(L, "", luar.Map{ // Go functions may be registered directly. "Print": fmt.Println, // Constants can be registered. "msg": "foo", // And other values as well. "user": user, }) L.DoString(test) }
Output: foo 1 foo 2 foo 3 &{Dolly 46} Dolly 46
Example (Pointers) ¶
package main import ( "fmt" "github.com/stevedonovan/luar" ) func main() { const test = ` -- Pointers to structs and structs within pointers are automatically dereferenced. local t = newRef() Print(t.Index, t.Number, t.Title) ` type Ref struct { Index int Number *int Title *string } newRef := func() *Ref { n := new(int) *n = 10 t := new(string) *t = "foo" return &Ref{Index: 17, Number: n, Title: t} } L := luar.Init() defer L.Close() luar.Register(L, "", luar.Map{ "Print": fmt.Println, "newRef": newRef, }) L.DoString(test) }
Output: 17 10 foo
Example (Slices) ¶
Slices must be looped with 'ipairs'.
package main import ( "fmt" "github.com/stevedonovan/luar" ) func main() { const test = ` for i, v in ipairs(names) do Print(i, v) end ` L := luar.Init() defer L.Close() names := []string{"alfred", "alice", "bob", "frodo"} luar.Register(L, "", luar.Map{ "Print": fmt.Println, "names": names, }) L.DoString(test) }
Output: 1 alfred 2 alice 3 bob 4 frodo
Index ¶
- Constants
- Variables
- func ArrayToTable(L *lua.State) int
- func Complex(L *lua.State) int
- func ComplexImag(L *lua.State) int
- func ComplexReal(L *lua.State) int
- func CopyArrayToTable(L *lua.State, v reflect.Value) int
- func CopyMapToTable(L *lua.State, vmap reflect.Value) int
- func CopySliceToTable(L *lua.State, vslice reflect.Value) int
- func CopyStructToTable(L *lua.State, vstruct reflect.Value) int
- func CopyTableToMap(L *lua.State, t reflect.Type, idx int) interface{}
- func CopyTableToSlice(L *lua.State, t reflect.Type, idx int) interface{}
- func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{}
- func GoLuaFunc(L *lua.State, fun interface{}) lua.LuaGoFunction
- func GoToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool)
- func Init() *lua.State
- func InitProxies(L *lua.State)
- func Lookup(L *lua.State, path string, idx int)
- func LuaToGo(L *lua.State, t reflect.Type, idx int) interface{}
- func MakeChan(L *lua.State) int
- func MakeMap(L *lua.State) int
- func MakeSlice(L *lua.State) int
- func MapToTable(L *lua.State) int
- func ProxyIpairs(L *lua.State) int
- func ProxyMethod(L *lua.State) int
- func ProxyPairs(L *lua.State) int
- func ProxyRaw(L *lua.State) int
- func ProxyType(L *lua.State) int
- func RaiseError(L *lua.State, msg string)
- func RawRegister(L *lua.State, table string, values Map)
- func Register(L *lua.State, table string, values Map)
- func SliceAppend(L *lua.State) int
- func SliceSub(L *lua.State) int
- func SliceToTable(L *lua.State) int
- func StructToTable(L *lua.State) int
- func Types(values ...interface{}) []reflect.Type
- func Unproxify(L *lua.State) int
- type LuaObject
- func (lo *LuaObject) Call(args ...interface{}) (res interface{}, err error)
- func (lo *LuaObject) Callf(rtypes []reflect.Type, args ...interface{}) (res []interface{}, err error)
- func (lo *LuaObject) Close()
- func (lo *LuaObject) Get(key string) interface{}
- func (lo *LuaObject) GetObject(key string) *LuaObject
- func (lo *LuaObject) Geti(idx int64) interface{}
- func (lo *LuaObject) Iter() *LuaTableIter
- func (lo *LuaObject) Push()
- func (lo *LuaObject) Set(idx interface{}, val interface{}) interface{}
- func (lo *LuaObject) Setv(src *LuaObject, keys ...string)
- type LuaTableIter
- type Map
- type NullT
Examples ¶
Constants ¶
const LuarSetup = `` /* 309-byte string literal not displayed */
LuarSetup replaces the 'pairs' and 'ipairs' so they work on proxies as well.
WARNING: Deprecated, register ProxyIpairs and ProxyPairs instead.
Variables ¶
var ( // Null is the definition of 'luar.null' which is used in place of 'nil' when // converting slices and structs. Null = NullT(0) )
Functions ¶
func ArrayToTable ¶
ArrayToTable defines 'luar.array2table' when 'Init' is called.
WARNING: Deprecated, use luar.unproxify instead.
func Complex ¶
Complex pushes a proxy to a Go complex on the stack.
Arguments: real (number), imag (number)
Returns: proxy (complex128)
func ComplexImag ¶
ComplexImag defines 'luar.imag' when 'Init' is called. It is the equivalent of Go's 'imag' function.
WARNING: Deprecated, use the 'imag' index instead.
func ComplexReal ¶
ComplexReal defines 'luar.real' when 'Init' is called. It is the equivalent of Go's 'real' function.
WARNING: Deprecated, use the 'real' index instead.
func CopyArrayToTable ¶
CopyArrayToTable copies a Go array to a Lua table. 'nil' is represented as 'luar.null'.
WARNING: Deprecated, use GoToLua instead.
func CopyMapToTable ¶
CopyMapToTable copies a Go map to a Lua table.
WARNING: Deprecated, use GoToLua instead.
func CopySliceToTable ¶
CopySliceToTable copies a Go slice to a Lua table. 'nil' is represented as 'luar.null'.
WARNING: Deprecated, use GoToLua instead.
func CopyStructToTable ¶
CopyStructToTable copies a Go struct to a Lua table. 'nil' is represented as 'luar.null'. Use the "lua" tag to set field names.
WARNING: Deprecated, use GoToLua instead.
func CopyTableToMap ¶
CopyTableToMap returns the Lua table at 'idx' as a copied Go map. If 't' is nil then the map type is map[string]interface{}.
WARNING: Deprecated, use LuaToGo instead.
Example ¶
Read configuration in Lua format.
WARNING: Deprecated.
package main import ( "fmt" "log" "github.com/stevedonovan/luar" ) // TODO: If ExampleCopy* get removed, remove this as well. const config = `return { baggins = true, age = 24, name = 'dumbo' , marked = {1,2}, options = { leave = true, cancel = 'always', tags = {strong=true, foolish=true}, } }` func main() { L := luar.Init() defer L.Close() err := L.DoString(config) if err != nil { log.Fatal(err) } // There should be a table on the Lua stack. if !L.IsTable(-1) { log.Fatal("no table on stack") } v := luar.CopyTableToMap(L, nil, -1) // Extract table from the returned interface. m := v.(map[string]interface{}) marked := m["marked"].([]interface{}) options := m["options"].(map[string]interface{}) fmt.Printf("%#v\n", m["baggins"]) fmt.Printf("%#v\n", m["name"]) fmt.Printf("%#v\n", len(marked)) fmt.Printf("%.1f\n", marked[0]) fmt.Printf("%.1f\n", marked[1]) fmt.Printf("%#v\n", options["leave"]) }
Output: true "dumbo" 2 1.0 2.0 true
func CopyTableToSlice ¶
CopyTableToSlice returns the Lua table at 'idx' as a copied Go slice. If 't' is nil then the slice type is []interface{}
WARNING: Deprecated, use LuaToGo instead.
func CopyTableToStruct ¶
CopyTableToStruct copies matching Lua table entries to a struct, given the struct type and the index on the Lua stack. Use the "lua" tag to set field names.
WARNING: Deprecated, use LuaToGo instead.
Example ¶
WARNING: Deprecated.
package main import ( "fmt" "log" "reflect" "github.com/stevedonovan/luar" ) // TODO: If ExampleCopy* get removed, remove this as well. const config = `return { baggins = true, age = 24, name = 'dumbo' , marked = {1,2}, options = { leave = true, cancel = 'always', tags = {strong=true, foolish=true}, } }` func main() { L := luar.Init() defer L.Close() err := L.DoString(config) if err != nil { log.Fatal(err) } // There should be a table on the Lua stack. if !L.IsTable(-1) { log.Fatal("no table on stack") } type conf struct { Baggins bool `lua:"baggins"` Age int `lua:"age"` Name string `lua:"name"` Marked []int `lua:"marked"` Options struct { Leave bool `lua:"leave"` cancel string // Ingored since it is unexported. Tags map[string]bool `lua:"tags"` } `lua:"options"` } var s conf v := luar.CopyTableToStruct(L, reflect.TypeOf(s), -1) s = v.(conf) fmt.Println(s.Baggins) fmt.Println(s.Age) fmt.Println(s.Name) fmt.Println(s.Marked) fmt.Println(s.Options.Leave) fmt.Println(s.Options.Tags["foolish"], s.Options.Tags["strong"]) }
Output: true 24 dumbo [1 2] true true true
func GoLuaFunc ¶
func GoLuaFunc(L *lua.State, fun interface{}) lua.LuaGoFunction
GoLuaFunc converts an arbitrary Go function into a Lua-compatible GoFunction.
WARNING: Deprecated, use GoToLua instead.
func GoToLua ¶
GoToLua pushes a Go value 'val' on the Lua stack.
It unboxes interfaces. 't' is here for backward-compatibility and will be ignored.
If not proxifying, pointers are followed recursively. Slices, structs and maps are copied over as tables.
When proxifying, pointers are preserved. Structs and arrays need to be passed as pointers to be proxified, otherwise they will be copied as tables.
Predeclared scalar types are never proxified (dontproxify is ignored) as they have no methods.
Example ¶
package main import ( "fmt" "reflect" "github.com/dnestorov/golua/lua" "github.com/stevedonovan/luar" ) func main() { // The luar's Init function is only required for proxy use. L := lua.NewState() defer L.Close() L.OpenLibs() input := "Hello world!" luar.GoToLua(L, nil, reflect.ValueOf(input), true) L.SetGlobal("input") luar.GoToLua(L, nil, reflect.ValueOf(fmt.Println), true) L.SetGlobal("Print") L.DoString("Print(input)") }
Output: Hello world!
func Init ¶
Init makes and initialize a new pre-configured Lua state.
It populates the 'luar' table with some helper functions/values:
method: ProxyMethod type: ProxyType unproxify: Unproxify chan: MakeChan complex: MakeComplex map: MakeMap slice: MakeSlice null: Null
It replaces the pairs/ipairs functions so that __pairs/__ipairs can be used, Lua 5.2 style. It allows for looping over Go composite types and strings.
It is not required for using the 'GoToLua' and 'LuaToGo' functions.
Example ¶
This example shows how Go slices and maps are marshalled to Lua tables and vice versa. This requires the Lua state to be initialized with `luar.Init()`.
An arbitrary Go function is callable from Lua, and list-like tables become slices on the Go side. The Go function returns a map, which is wrapped as a proxy object. You can however then copy this to a Lua table explicitly. There is also `luar.unproxify` on the Lua side.
package main import ( "fmt" "strconv" "github.com/stevedonovan/luar" ) func main() { const code = ` -- Lua tables auto-convert to slices. local res = foo {10,20,30,40} -- The result is a map-proxy. print(res['1'], res['2']) -- Which we may explicitly convert to a table. res = luar.unproxify(res) for k,v in pairs(res) do print(k,v) end ` foo := func(args []int) (res map[string]int) { res = make(map[string]int) for i, val := range args { res[strconv.Itoa(i)] = val * val } return } L := luar.Init() defer L.Close() luar.Register(L, "", luar.Map{ "foo": foo, "print": fmt.Println, }) res := L.DoString(code) if res != nil { fmt.Println("Error:", res) } }
Output: 400 900 1 400 0 100 3 1600 2 900
func InitProxies ¶
InitProxies sets up a Lua state for using Go<->Lua proxies. This need not be called if the Lua state was created with Init(). This function is useful if you want to set up your Lua state manually, e.g. with a custom allocator.
WARNING: Deprecated, this function is not needed anymore.
func Lookup ¶
Lookup will search a Lua value by its full name.
If idx is 0, then this name is assumed to start in the global table, e.g. "string.gsub". With non-zero idx, it can be used to look up subfields of a table. It terminates with a nil value if we cannot continue the lookup.
func LuaToGo ¶
LuaToGo converts the Lua value at index 'idx' to the Go value of desired type 't'. Handles numerical and string types in a straightforward way, and will convert tables to either map or slice types. If 't' is nil or an interface, the type is inferred from the Lua value.
func MakeChan ¶
MakeChan creates a 'chan interface{}' proxy and pushes it on the stack.
Optional argument: size (number)
Returns: proxy (chan interface{})
Example ¶
package main import ( "fmt" "sync" "github.com/stevedonovan/luar" ) func main() { L1 := luar.Init() defer L1.Close() L2 := luar.Init() defer L2.Close() luar.MakeChan(L1) L1.SetGlobal("c") L1.GetGlobal("c") c := luar.LuaToGo(L1, nil, -1) luar.Register(L2, "", luar.Map{ "c": c, "Print": fmt.Println, }) const code1 = ` c.send(17) ` const code2 = ` v = c.recv() Print(v) ` var wg sync.WaitGroup wg.Add(1) go func() { err := L1.DoString(code1) if err != nil { fmt.Println(err) } wg.Done() }() err := L2.DoString(code2) if err != nil { fmt.Println(err) } wg.Wait() }
Output: 17
func MakeMap ¶
MakeMap creates a 'map[string]interface{}' proxy and pushes it on the stack.
Returns: proxy (map[string]interface{})
func MakeSlice ¶
MakeSlice creates a '[]interface{}' proxy and pushes it on the stack.
Optional argument: size (number)
Returns: proxy ([]interface{})
func MapToTable ¶
MapToTable defines 'luar.map2table' when 'Init' is called.
WARNING: Deprecated, use luar.unproxify instead.
func ProxyIpairs ¶
ProxyIpairs implements Lua 5.2 'ipairs' functions. It respects the __ipairs metamethod.
It is only useful for compatibility with Lua 5.1.
func ProxyMethod ¶
ProxyMethod pushes the proxy method on the stack.
Argument: proxy
Returns: method (function)
func ProxyPairs ¶
ProxyPairs implements Lua 5.2 'pairs' functions. It respects the __pairs metamethod.
It is only useful for compatibility with Lua 5.1.
func ProxyType ¶
ProxyType pushes the proxy type on the stack.
Argument: proxy
Returns: type (string)
func RaiseError ¶
RaiseError raises a Lua error from Go code.
func RawRegister ¶
RawRegister makes a number of 'raw' Go functions or values available in Lua code. Raw Go functions access the Lua state directly and have signature '(*lua.State) int'.
WARNING: Deprecated, use Register instead.
func Register ¶
Register makes a number of Go values available in Lua code. 'values' is a map of strings to Go values.
- If table is non-nil, then create or reuse a global table of that name and put the values in it.
- If table is ” then put the values in the global table (_G).
- If table is '*' then assume that the table is already on the stack.
Example (Sandbox) ¶
package main import ( "fmt" "github.com/stevedonovan/luar" ) func main() { const code = ` Print("foo") Print(io ~= nil) Print(os == nil) ` L := luar.Init() defer L.Close() res := L.LoadString(code) if res != 0 { msg := L.ToString(-1) fmt.Println("could not compile", msg) } // Create a empty sandbox. L.NewTable() // "*" means "use table on top of the stack." luar.Register(L, "*", luar.Map{ "Print": fmt.Println, }) env := luar.NewLuaObject(L, -1) G := luar.Global(L) // We can copy any Lua object from "G" to env with 'Set', e.g.: // env.Set("print", G.Get("print")) // A more convenient and efficient way is to do a bulk copy with 'Setv': env.Setv(G, "print", "io") // Set up sandbox. L.SetfEnv(-2) // Run 'code' chunk. err := L.Call(0, 0) if err != nil { fmt.Println("could not run", err) } }
Output: foo true true
func SliceAppend ¶
SliceAppend defines 'luar.append' when 'Init' is called.
WARNING: Deprecated, use the 'append' method instead.
func SliceSub ¶
SliceSub defines 'luar.sub' when 'Init' is called.
WARNING: Deprecated, use the 'sub' method instead.
func SliceToTable ¶
SliceToTable defines 'luar.slice2table' when 'Init' is called.
WARNING: Deprecated, use luar.unproxify instead.
func StructToTable ¶
StructToTable defines 'luar.struct2table' when 'Init' is called.
WARNING: Deprecated, use luar.unproxify instead.
Types ¶
type LuaObject ¶
LuaObject encapsulates a Lua object like a table or a function.
func NewLuaObject ¶
NewLuaObject creates a new LuaObject from stack index.
Example ¶
Another way to do parse configs: using LuaObject to manipulate the table.
package main import ( "fmt" "log" "sort" "github.com/stevedonovan/luar" ) func main() { L := luar.Init() defer L.Close() // Using Lua to parse configuration files. const config = `return { baggins = true, age = 24, name = 'dumbo' , marked = {1,2}, options = { leave = true, cancel = 'always', tags = {strong=true, foolish=true}, } }` err := L.DoString(config) if err != nil { log.Fatal(err) } lo := luar.NewLuaObject(L, -1) // Can get the field itself as a Lua object, and so forth. opts := lo.GetObject("options") marked := lo.GetObject("marked") fmt.Printf("%#v\n", lo.Get("baggins")) fmt.Printf("%#v\n", lo.Get("name")) fmt.Printf("%#v\n", opts.Get("leave")) // Note that these Get methods understand nested fields. fmt.Printf("%#v\n", lo.Get("options.leave")) fmt.Printf("%#v\n", lo.Get("options.tags.strong")) // Non-existent nested fields don't crash but return nil. fmt.Printf("%#v\n", lo.Get("options.tags.extra.flakey")) fmt.Printf("%.1f\n", marked.Geti(1)) iter := lo.Iter() keys := []string{} for iter.Next() { keys = append(keys, iter.Key.(string)) } sort.Strings(keys) fmt.Println("Keys:") for _, v := range keys { fmt.Println(v) } }
Output: true "dumbo" true true true <nil> 1.0 Keys: age baggins marked name options
func NewLuaObjectFromName ¶
NewLuaObjectFromName creates a new LuaObject from global qualified name, using Lookup.
func NewLuaObjectFromValue ¶
NewLuaObjectFromValue creates a new LuaObject from a Go value. Note that this _will_ convert any slices or maps into Lua tables.
Example ¶
package main import ( "fmt" "log" "github.com/stevedonovan/luar" ) func main() { L := luar.Init() defer L.Close() gsub := luar.NewLuaObjectFromName(L, "string.gsub") // We do have to explicitly copy the map to a Lua table, because `gsub` // will not handle userdata types. gmap := luar.NewLuaObjectFromValue(L, luar.Map{ "NAME": "Dolly", "HOME": "where you belong", }) res, err := gsub.Call("hello $NAME go $HOME", "%$(%u+)", gmap) if err != nil { log.Fatal(err) } fmt.Println(res) }
Output: hello Dolly go where you belong
func (*LuaObject) Call ¶
Call calls a Lua function and return a single value, converted in a default way.
func (*LuaObject) Callf ¶
func (lo *LuaObject) Callf(rtypes []reflect.Type, args ...interface{}) (res []interface{}, err error)
Callf calls a Lua function, given the desired return types and the arguments.
Callf is used whenever:
- the Lua function has multiple return values;
- and/or you have exact types for these values.
The first argument may be `nil` and can be used to access multiple return values without caring about the exact conversion.
Example ¶
package main import ( "fmt" "log" "github.com/stevedonovan/luar" ) func main() { L := luar.Init() defer L.Close() returns := luar.Types([]string{}) // []reflect.Type const code = ` function return_strings() return {'one', luar.null, 'three'} end` err := L.DoString(code) if err != nil { log.Fatal(err) } fun := luar.NewLuaObjectFromName(L, "return_strings") // Using `Call` we would get a generic `[]interface{}`, which is awkward to // work with. But the return type can be specified: results, err := fun.Callf(returns) if err != nil { log.Fatal(err) } strs := results[0].([]string) fmt.Println(strs[0]) // We get an empty string corresponding to a luar.null in a table, // since that's the empty 'zero' value for a string. fmt.Println(strs[1]) fmt.Println(strs[2]) }
Output: one three
func (*LuaObject) Close ¶
func (lo *LuaObject) Close()
Close frees the Lua reference of this object.
func (*LuaObject) Iter ¶
func (lo *LuaObject) Iter() *LuaTableIter
Iter creates a Lua table iterator.
type LuaTableIter ¶
type LuaTableIter struct { Key interface{} Value interface{} // contains filtered or unexported fields }
LuaTableIter is the Go equivalent of a Lua table iterator.
func (*LuaTableIter) Next ¶
func (ti *LuaTableIter) Next() bool
Next gets the next key/value pair from the table.
Example ¶
package main import ( "fmt" "log" "sort" "github.com/stevedonovan/luar" ) func main() { const code = ` return { foo = 17, bar = 18, } ` L := luar.Init() defer L.Close() err := L.DoString(code) if err != nil { log.Fatal(err) } lo := luar.NewLuaObject(L, -1) iter := lo.Iter() keys := []string{} values := map[string]float64{} for iter.Next() { k := iter.Key.(string) keys = append(keys, k) values[k] = iter.Value.(float64) } sort.Strings(keys) for _, v := range keys { fmt.Println(v, values[v]) } }
Output: bar 18 foo 17
type Map ¶
type Map map[string]interface{}
Map is an alias for passing maps of strings to values to luar.
Example ¶
package main import ( "fmt" "github.com/stevedonovan/luar" ) func main() { const code = ` print(#M) print(M.one) print(M.two) print(M.three) ` L := luar.Init() defer L.Close() M := luar.Map{ "one": "ein", "two": "zwei", "three": "drei", } luar.Register(L, "", luar.Map{ "M": M, "print": fmt.Println, }) err := L.DoString(code) if err != nil { fmt.Println("error", err.Error()) } }
Output: 3 ein zwei drei