luar

package module
v0.0.0-...-b1dc89e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 22, 2016 License: MIT Imports: 8 Imported by: 0

README

Luar: Lua reflection bindings for Go

Luar is designed to make using Lua from Go more convenient. Go structs, slices and maps can be automatically converted to Lua tables and vice-versa. The resulting conversion can either be a copy or a proxy. In the latter case, any change made to the result will reflect on the source.

Any Go function can be made available to Lua scripts, without having to write C-style wrappers.

Luar support cyclic structures (map[string]interface{}, lists, etc.).

User-defined types can be made available to Lua as well: their exported methods can be called and usual operations such as indexing or arithmetic can be performed.

See the documentation for usage instructions and examples.

Installation

Install with

go get <repo>/luar

The original Luar uses Alessandro Arzilli's golua. This fork of Luar uses D.Nestorov's golua. See golua's homepage for further installation details.

Usage

This is a fork of https://github.com/stevedonovan/luar that runs with Lua 5.3.3 and supports FFI (https://github.com/dnestorov/luaffifb). The FFI interface is a fork from Facebook's luaffifb (https://github.com/facebook/luaffifb). The only difference is that the former builds a static library with Premake.

The final results is:

package main

import (
	"fmt"
	"log"

	"github.com/dnestorov/luar"
)

var lcode = `
local ffi = require("ffi")
ffi.cdef[[
	int printf(const char *fmt, ...);
]]
ffi.C.printf("Hello %s from FFI!\n", "world")

print("Hello world from Lua!")

Print("Hello world from Go!")
`

func main() {
	l := luar.Init()
	defer l.Close()

	l.OpenFFI()

	luar.Register(l, "", luar.Map{
		// Go functions may be registered directly.
		"Print": fmt.Println,
	})

	err := l.DoString(lcode)
	if err != nil {
		log.Fatal(err)
	}
}

REPL

An example REPL is available in the cmd folder.

Issues

The GoToLua and LuaToGo functions take a reflect.Type parameter, which is bad design. Sadly changing this would break backward compatibility.

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

Examples

Constants

View Source
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

View Source
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

func ArrayToTable(L *lua.State) int

ArrayToTable defines 'luar.array2table' when 'Init' is called.

WARNING: Deprecated, use luar.unproxify instead.

func Complex

func Complex(L *lua.State) int

Complex pushes a proxy to a Go complex on the stack.

Arguments: real (number), imag (number)

Returns: proxy (complex128)

func ComplexImag

func ComplexImag(L *lua.State) int

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

func ComplexReal(L *lua.State) int

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

func CopyArrayToTable(L *lua.State, v reflect.Value) int

CopyArrayToTable copies a Go array to a Lua table. 'nil' is represented as 'luar.null'.

WARNING: Deprecated, use GoToLua instead.

func CopyMapToTable

func CopyMapToTable(L *lua.State, vmap reflect.Value) int

CopyMapToTable copies a Go map to a Lua table.

WARNING: Deprecated, use GoToLua instead.

func CopySliceToTable

func CopySliceToTable(L *lua.State, vslice reflect.Value) int

CopySliceToTable copies a Go slice to a Lua table. 'nil' is represented as 'luar.null'.

WARNING: Deprecated, use GoToLua instead.

func CopyStructToTable

func CopyStructToTable(L *lua.State, vstruct reflect.Value) int

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

func CopyTableToMap(L *lua.State, t reflect.Type, idx int) interface{}

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

func CopyTableToSlice(L *lua.State, t reflect.Type, idx int) interface{}

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

func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{}

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

func GoToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool)

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

func Init() *lua.State

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

func InitProxies(L *lua.State)

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

func Lookup(L *lua.State, path string, idx int)

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

func LuaToGo(L *lua.State, t reflect.Type, idx int) interface{}

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

func MakeChan(L *lua.State) int

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

func MakeMap(L *lua.State) int

MakeMap creates a 'map[string]interface{}' proxy and pushes it on the stack.

Returns: proxy (map[string]interface{})

func MakeSlice

func MakeSlice(L *lua.State) int

MakeSlice creates a '[]interface{}' proxy and pushes it on the stack.

Optional argument: size (number)

Returns: proxy ([]interface{})

func MapToTable

func MapToTable(L *lua.State) int

MapToTable defines 'luar.map2table' when 'Init' is called.

WARNING: Deprecated, use luar.unproxify instead.

func ProxyIpairs

func ProxyIpairs(L *lua.State) int

ProxyIpairs implements Lua 5.2 'ipairs' functions. It respects the __ipairs metamethod.

It is only useful for compatibility with Lua 5.1.

func ProxyMethod

func ProxyMethod(L *lua.State) int

ProxyMethod pushes the proxy method on the stack.

Argument: proxy

Returns: method (function)

func ProxyPairs

func ProxyPairs(L *lua.State) int

ProxyPairs implements Lua 5.2 'pairs' functions. It respects the __pairs metamethod.

It is only useful for compatibility with Lua 5.1.

func ProxyRaw

func ProxyRaw(L *lua.State) int

ProxyRaw unproxifies a value.

WARNING: Deprecated, use luar.unproxify instead.

func ProxyType

func ProxyType(L *lua.State) int

ProxyType pushes the proxy type on the stack.

Argument: proxy

Returns: type (string)

func RaiseError

func RaiseError(L *lua.State, msg string)

RaiseError raises a Lua error from Go code.

func RawRegister

func RawRegister(L *lua.State, table string, values Map)

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

func Register(L *lua.State, table string, values Map)

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

func SliceAppend(L *lua.State) int

SliceAppend defines 'luar.append' when 'Init' is called.

WARNING: Deprecated, use the 'append' method instead.

func SliceSub

func SliceSub(L *lua.State) int

SliceSub defines 'luar.sub' when 'Init' is called.

WARNING: Deprecated, use the 'sub' method instead.

func SliceToTable

func SliceToTable(L *lua.State) int

SliceToTable defines 'luar.slice2table' when 'Init' is called.

WARNING: Deprecated, use luar.unproxify instead.

func StructToTable

func StructToTable(L *lua.State) int

StructToTable defines 'luar.struct2table' when 'Init' is called.

WARNING: Deprecated, use luar.unproxify instead.

func Types

func Types(values ...interface{}) []reflect.Type

Types is a convenience function for converting a set of values into a corresponding slice of their types.

func Unproxify

func Unproxify(L *lua.State) int

Unproxify converts a proxy to an unproxified Lua value.

Argument: proxy

Returns: value (Lua value)

Types

type LuaObject

type LuaObject struct {
	L    *lua.State
	Ref  int
	Type string
}

LuaObject encapsulates a Lua object like a table or a function.

func Global

func Global(L *lua.State) *LuaObject

Global creates a new LuaObject refering to the global environment.

func NewLuaObject

func NewLuaObject(L *lua.State, idx int) *LuaObject

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

func NewLuaObjectFromName(L *lua.State, path string) *LuaObject

NewLuaObjectFromName creates a new LuaObject from global qualified name, using Lookup.

func NewLuaObjectFromValue

func NewLuaObjectFromValue(L *lua.State, val interface{}) *LuaObject

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

func (lo *LuaObject) Call(args ...interface{}) (res interface{}, err error)

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) Get

func (lo *LuaObject) Get(key string) interface{}

Get returns the Go value indexed at 'key' in the Lua object.

func (*LuaObject) GetObject

func (lo *LuaObject) GetObject(key string) *LuaObject

GetObject returns the Lua object indexed at 'key' in the Lua object.

func (*LuaObject) Geti

func (lo *LuaObject) Geti(idx int64) interface{}

Geti return the value indexed at 'idx'.

func (*LuaObject) Iter

func (lo *LuaObject) Iter() *LuaTableIter

Iter creates a Lua table iterator.

func (*LuaObject) Push

func (lo *LuaObject) Push()

Push pushes this Lua object on the stack.

func (*LuaObject) Set

func (lo *LuaObject) Set(idx interface{}, val interface{}) interface{}

Set sets the value at a given index 'idx'.

func (*LuaObject) Setv

func (lo *LuaObject) Setv(src *LuaObject, keys ...string)

Setv copies values between two tables in the same state.

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

type NullT

type NullT int

NullT is the type of 'luar.null'.

Directories

Path Synopsis
cmd
luar
A golua REPL with line editing, pretty-printing and tab completion.
A golua REPL with line editing, pretty-printing and tab completion.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL