Documentation ¶
Overview ¶
Package luar provides custom type reflection to gopher-lua.
Notice ¶
This package is currently in development, and its behavior may change. This message will be removed once the package is considered stable.
Basic types ¶
Go bool, number types, string types, and nil values are converted to the equivalent Lua type.
Example:
New(L, "Hello World") = lua.LString("Hello World") New(L, uint(834)) = lua.LNumber(uint(834)) New(L, map[string]int(nil)) = lua.LNil
Channels ¶
Channels have the following methods defined:
receive(): Receives data from the channel. Returns nil plus false if the channel is closed. send(data): Sends data to the channel. close(): Closes the channel.
Taking the length (#) of a channel returns how many unread items are in its buffer.
Example:
ch := make(chan string) L.SetGlobal("ch", New(L, ch)) --- ch:receive() -- equivalent to v, ok := ch ch:send("hello") -- equivalent to ch <- "hello" ch:close() -- equivalent to close(ch)
Functions ¶
Functions can be converted and called from Lua. The function arguments and return values are automatically converted from and to Lua types, respectively (see exception below).
Example:
fn := func(name string, age uint) string { return fmt.Sprintf("Hello %s, age %d", name, age) } L.SetGlobal("fn", New(L, fn)) --- print(fn("Tim", 5)) -- prints "Hello Tim, age 5"
A function that has the signature func(*luar.LState) int can bypass the automatic argument and return value conversion (see luar.LState documentation for example).
A special conversion case happens when function returns a lua.LValue slice. In that case, luar automatically unpacks the slice.
Example:
fn := func() []lua.LValue { return []lua.LValue{lua.LString("Hello"), lua.LNumber(2.5)} } L.SetGlobal("fn", New(L, fn)) --- x, y = fn() print(x) -- prints "Hello" print(y) -- prints "2.5"
Maps ¶
Maps can be accessed and modified like a normal Lua table. The map's length can also be queried using the # operator.
Rather than using Lua's pairs function to create an map iterator, calling the value (e.g. map_variable()) returns an iterator for the map.
Example:
places := map[string]string{ "NA": "North America", "EU": "European Union", } L.SetGlobal("places", New(L, places)) --- print(#places) -- prints "2" print(places.NA) -- prints "North America" print(places["EU"]) -- prints "European Union" for k, v in places() do -- prints all keys and values of places print(k .. ": " .. v) end
Slices ¶
Like maps, slices be indexed, be modified, and have their length queried. Additionally, the following methods are defined for slices:
append(items...): Appends the items to the slice. Returns a slice with the items appended. capacity(): Returns the slice capacity.
For consistency with other Lua code, slices use one-based indexing.
Example:
letters := []string{"a", "e", "i"} L.SetGlobal("letters", New(L, letters)) --- letters = letters:append("o", "u")
Like maps, calling a slice (e.g. slice()) returns an iterator over its values.
Arrays ¶
Arrays can be indexed and have their length queried. Only pointers to arrays can their contents modified.
Like slices and maps, calling an array (e.g. array()) returns an iterator over its values.
Example:
var arr [2]string L.SetGlobal("arr", New(L, &arr)) --- arr[1] = "Hello" arr[2] = "World"
Structs ¶
Structs can have their fields accessed and modified and their methods called.
Example:
type Person { Name string } func (p Person) SayHello() { fmt.Printf("Hello, %s\n", p.Name) } tim := Person{"Tim"} L.SetGlobal("tim", New(L, tim)) --- tim:SayHello() -- same as tim:sayHello()
By default, the name of a struct field is determined by its tag:
"": the field is accessed by its name and its name with a lowercase first letter "-": the field is not accessible else: the field is accessed by that value
Example:
type Person struct { Name string `luar:"name"` Age int Hidden bool `luar:"-"` } --- Person.Name -> "name" Person.Age -> "Age", "age" Person.Hidden -> Not accessible
Pointers ¶
Pointers can be dereferenced using the unary minus (-) operator.
Example:
str := "hello" L.SetGlobal("strptr", New(L, &str)) --- print(-strptr) -- prints "hello"
The pointed to value can changed using the pow (^) operator.
Example:
str := "hello" L.SetGlobal("strptr", New(L, &str)) --- print(str^"world") -- prints "world", and str's value is now "world"
Pointers to struct and array values are returned when accessed via a struct field, array index, or slice index.
Transparent Pointers ¶
You can instruct luar to transparently dereference pointers as if they are regular values, by passing ReflectOptions to New:
Example:
type Person struct { Name *string } name := "Tim" tim := &Person{&name} L.SetGlobal("tim", New(L, tim, ReflectOptions{TransparentPointers: true})) --- print(tim.Name) -- prints "Tim"; note that no dereference via - is req'd tim.Name = "Timothy" -- assignment works transparently too
Additionally, automatic population can be enabled; this takes care of populating zero values when pointers are first accessed. This is to mimic the behavior of zero values on non-pointer fields. Objects behave as if all fields are regular values.
Example:
type Parent struct { Person Child *Person } // No initialization of Name: tim := &Parent{} L.SetGlobal("tim", New(L, tim, ReflectOptions{ TransparentPointers: true, AutoPopulate: true})) --- -- prints an empty string; does not raise an error, even though Child and -- Name are both nil when accessed print(tim.Child.Name) -- afterwards, tim.Child will be set to a Person object, with a pointer to -- an empty string in Name
Any behavior is inherited by objects that are accessed through the original reflected item. For example, functions reflected with TransparentPointers will return objects that transparently dereference.
Type methods ¶
Any array, channel, map, slice, or struct type that has methods defined on it can be called from Lua.
On maps with key strings, map elements are returned before type methods.
Example:
type mySlice []string func (s mySlice) Len() int { return len(s) } var s mySlice = []string{"Hello", "world"} L.SetGlobal("s", New(L, s)) --- print(s:len()) -- prints "2"
Lua to Go conversions ¶
The Lua types are automatically converted to match the output Go type, as described below:
Lua type Go kind/type ----------------------------------------------------- LBool bool string ("true" or "false") LChannel chan lua.LValue LNumber numeric value string (strconv.Itoa) LFunction func LNilType chan, func, interface, map, ptr, slice, unsafe pointer LState *lua.LState LString string LTable slice map struct *struct LUserData underlying lua.LUserData.Value type
Example creating a Go slice from Lua:
type Group struct { Names []string } g := new(Group) L.SetGlobal("g", luar.New(L, g)) --- g.Names = {"Tim", "Frank", "George"}
New types ¶
Type constructors can be created using NewType. When called, it returns a new variable which is of the same type that was passed to NewType. Its behavior is dependent on the kind of value passed, as described below:
Kind Constructor arguments Return value ----------------------------------------------------- Channel Buffer size (opt) Channel Map None Map Slice Length (opt), Capacity (opt) Slice Default None Pointer to the newly allocated value
Example:
type Person struct { Name string } L.SetGlobal("Person", NewType(L, Person{})) --- p = Person() p.Name = "John" print("Hello, " .. p.Name) // prints "Hello, John"
Immutability ¶
luar supports making a number of reflected object types immutable. This means that e.g. a struct's fields cannot be modified, or a map cannot be assigned new values. Attempting such a modification results in an error. Types are still fully readable/writeable in Go, this only affects Lua access. It's useful for locking down inputs that you want to be read-only from Lua.
Example:
type Person struct { Name string } tim := &Person{"Tim"} L.SetGlobal("tim", New(L, tim, ReflectOptions{Immutable: true}) --- tim.Name = "Bob" -- raises an error!
Calling a pointer method on a struct is invalid when using immutability, since pointer methods can modify the struct's internal state.
Thread safety ¶
This package accesses and modifies the Lua state's registry. This happens when functions like New are called, and potentially when luar-created values are used. It is your responsibility to ensure that concurrent access of the state's registry does not happen.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(L *lua.LState, value interface{}, opts ...ReflectOptions) lua.LValue
New creates and returns a new lua.LValue for the given value.
The following table shows how Go types are converted to Lua types:
Kind gopher-lua Type Custom Metatable -------------------------------------------------- nil LNil No Bool LBool No Int LNumber No Int8 LNumber No Int16 LNumber No Int32 LNumber No Int64 LNumber No Uint LNumber No Uint8 LNumber No Uint16 LNumber No Uint32 LNumber No Uint64 LNumber No Uintptr *LUserData No Float32 LNumber No Float64 LNumber No Complex64 *LUserData No Complex128 *LUserData No Array *LUserData Yes Chan *LUserData Yes Func *lua.LFunction No Map *LUserData Yes Ptr *LUserData Yes Slice *LUserData Yes String LString No Struct *LUserData Yes UnsafePointer *LUserData No
func NewType ¶
func NewType(L *lua.LState, value interface{}) lua.LValue
NewType returns a new type creator for the given value's type.
When the returned lua.LValue is called, a new value will be created that is the same type as value's type.
Example ¶
L := lua.NewState() defer L.Close() type Song struct { Title string Artist string } L.SetGlobal("Song", NewType(L, Song{})) L.DoString(` s = Song() s.Title = "Montana" s.Artist = "Tycho" print(s.Artist .. " - " .. s.Title) `)
Output: Tycho - Montana
func ToReflect ¶
ToReflect converts the lua.LValue to a reflect.Value.
Whenever possible, this will be a strongly-typed Go object matching an object that was passed to luar.New. A type hint must be specified to indicate the type that the variable is expected to be. This will be used to e.g. downcast an LString to a string if that is what is expected. A regular LTable will be converted to a map, slice, struct, etc (as per the hint) if possible.
Types ¶
type Config ¶
type Config struct { // The name generating function that defines under which names Go // struct fields will be accessed. // // If nil, the default behaviour is used: // - if the "luar" tag of the field is "", the field name and its name // with a lowercase first letter is returned // - if the tag is "-", no name is returned (i.e. the field is not // accessible) // - for any other tag value, that value is returned FieldNames func(s reflect.Type, f reflect.StructField) []string // The name generating function that defines under which names Go // methods will be accessed. // // If nil, the default behaviour is used: // - the method name and its name with a lowercase first letter MethodNames func(t reflect.Type, m reflect.Method) []string // contains filtered or unexported fields }
Config is used to define luar behaviour for a particular *lua.LState.
type LState ¶
type LState struct {
*lua.LState
}
LState is an wrapper for gopher-lua's LState. It should be used when you wish to have a function/method with the standard "func(*lua.LState) int" signature.
Example ¶
const code = ` print(sum(1, 2, 3, 4, 5)) ` L := lua.NewState() defer L.Close() sum := func(L *LState) int { total := 0 for i := 1; i <= L.GetTop(); i++ { total += L.CheckInt(i) } L.Push(lua.LNumber(total)) return 1 } L.SetGlobal("sum", New(L, sum)) if err := L.DoString(code); err != nil { panic(err) }
Output: 15
type Metatable ¶
type Metatable struct {
*lua.LTable
}
Metatable holds the Lua metatable for a Go type.
type ReflectOptions ¶
type ReflectOptions struct { // Controls whether or not the value of the reflected object can be modified. // Only works for a subset of types that utilize a custom metatable - arrays, // channels, maps, pointers, slices and structs. Child elements/fields inherit // the immutable property, even when assigned to new variables. Immutable // channels may send/receive, but cannot be closed from Lua. Immutable bool // For structs, will auto-indirect pointer fields. This makes structs with // pointer fields behave like their non-pointer counterparts. Fields can // have values assigned directly without use of the pow (^) operator. Note // that fields can only be set if the struct is reflected by reference. TransparentPointers bool // For structs, will auto-populate pointer fields with their appropriate Go // type. This is only applicable if TransparentPointers is on. AutoPopulate bool }
ReflectOptions is a configuration that can be used to alter the behavior of a reflected gopher-luar object.