layeh / gopher-luar Goto Github PK
View Code? Open in Web Editor NEWsimplifies data passing to and from gopher-lua
Home Page: https://godoc.org/layeh.com/gopher-luar
License: Mozilla Public License 2.0
simplifies data passing to and from gopher-lua
Home Page: https://godoc.org/layeh.com/gopher-luar
License: Mozilla Public License 2.0
In Lua code that I've seen written before, and some tutorial, it seems that Objects are generally created where the Table has a method called "new" defined on it. Allowing code like:
d = Dog:new()
While the NewType
function in luar adds a type where you create instances by calling the named function like:
d = Dog()
Was this done for a specific reason, would it be simple to provide the first option as an alternative?
Hi,
This is a fantastic project thanks for it.
I'm having some trouble that I think is being caused by luar's translation of my go functions.
panic:
[G]: bad argument #1 to Respond (userdata expected, got string)
stack traceback:
[G]: in Respond
test.lua:6: in main chunk
[G]: ?
test .lua is this:
synfunc = function (msg)
msg.reply("ack")
end
robot.Respond("syn",synfunc)
The general idea is that users will call robot.Respond with a regex they want to match, and a reference to a lua function they want to be called when the regex is detected by the upstream go program.
robot is a very simple struct type that enables the upstream Go to index this request back to the lua state that made it (there will be many):
type Robot struct{
ID int
}
the respond function on the go-side is also pretty simple:
func (r Robot)Respond(pat string, lfunc lua.LValue){
newMsgCallback(r.ID, pat, lfunc, true)
}
As you can see in the error message, this panic's in runtime because the lua state seems to think my Respond function wants lua.LUserData instead of a string, which I assume(?) is probably being caused by luar's translation.
I've written some test scripts and I don't seem to be able to replicate this issue (methods on go structs with this signature (string,lval) seem to work in general), so I'm not sure if I'm doing something wrong in this codebase or if I've found a bug or what. You can take a look at the actual codebase here
Thanks in advance for your help.
-dave
gopher-luar is able to automatically convert lua-types into go - an example for that is given in the documentation.
However, this mechanism is not exposed in the API. While there is a luar.New()
, the counterpart is missing.
This becomes necessary when calling lua-functions from go, and trying to get the function's return value:
// Convert from go-to-LUA
var args []interface{} = ...
lArgs := make([]lua.LValue, len(args))
for i, a := range args {
lArgs[i] = luar.New(L, a)
}
var numberOfReturnArgs = 4
vm.state.CallByParam(lua.P{
Fn: L.GetGlobal("functionName"),
NRet: numberOfReturnArgs,
}, lArgs...)
// Collect return values:
retValues := make([]interface{}, returnArgs)
for i := returnArgs; i > 0; i-- {
ret := vm.state.Get(-1)
vm.state.Pop(1)
retValues[i-1] = ret // <--- how to convert to go-type?
}
How can I convert these return-values to go-types?
How can I use luar to make a const table to lua?
Can I use some struct config like luar:"cosnt"
to do this?
I am trying the following code
package main
import (
"github.com/yuin/gopher-lua"
"layeh.com/gopher-luar"
)
const script = `
print(issue)
`
type Issue struct {
ID int
Title string
}
func main() {
issues := []Issue{
{
ID: 1,
Title: "uno",
},
{
ID: 2,
Title: "due",
},
}
for _, issue := range issues {
L := lua.NewState()
defer L.Close()
L.SetGlobal("issue", luar.New(L, issue))
if err := L.DoString(script); err != nil {
panic(err)
}
}
}
I would expect to have tables in Lua, but I get userdata. Is this expected? is there a way to automatically map a simple struct to a table in Lua?
I also tried using luar.NewType but it doesn't work either
issue: using an table carrying string value to set a struct that is expecting a integer will cause panic instead of throwing type error
go Version: go.17
luar version: layeh.com/gopher-luar v1.0.10
gopher-lua: github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583
example:
type B struct {
I int
}
type A struct {
B *B
}
func test(a *A) {
if a != nil && a.B != nil {
print(a.B.I)
}
}
func main() {
L := lua.NewState()
defer L.Close()
L.SetGlobal("test", luar.New(L, test))
code := `
local a = { B = {I = "10"} }
test(a)
`
if err := L.DoString(code); err != nil {
panic(err)
}
}
result in
panic: reflect: call of reflect.Value.Set on zero Value
stack traceback:
[G]: in function 'test'
<string>:3: in main chunk
[G]: ?
goroutine 1 [running]:
main.main()
caused by this line:
https://github.com/layeh/gopher-luar/blob/6b0665c71bb633083e19fff076ce70c384b71a9c/luar.go#LL444C33-L444C33
This one is related to #10 and how to handle errors from Go functions in Lua.
I have a program, which registers new Lua types from Go as providers, which return some result and an error.
As I don't want to do the error handling in Lua, I thought I'd use a closure and close the Go provider function where I could do the error handling and raise errors in Lua in case I have an error from my provider function. That way I only return the result from the Go function to Lua and do the error handling in the wrapper function.
What I've noticed though is that whenever I register my providers in Lua the wrapper function is always the same, resulting in overwriting the wrappers for previous providers.
Here's an example code:
package main
import (
"fmt"
"github.com/layeh/gopher-luar"
"github.com/yuin/gopher-lua"
)
// provider type
type provider func(name string) (string, error)
// registry contains all registered providers
var registry = map[string]provider{
"provider1": provider_1,
"provider2": provider_2,
}
func provider_1(name string) (string, error) {
return "provider_1 result", nil
}
func provider_2(name string) (string, error) {
return "provider_2 results", nil
}
func main() {
L := lua.NewState()
defer L.Close()
// Register the providers in Lua
for typ, provider := range registry {
wrapper := func(L *lua.LState) int {
result, err := provider(L.CheckString(1))
if err != nil {
L.RaiseError(err.Error()) // Do the error handling here, so that we don't have to return the error to Lua
}
L.Push(luar.New(L, result))
return 1 // Number of arguments
}
tbl := L.NewTable()
tbl.RawSetH(lua.LString("new"), L.NewFunction(wrapper)) // New wrapper overwrites all previous ones
L.SetGlobal(typ, tbl)
fmt.Printf("type %s -> wrapper %s\n", typ, wrapper)
}
// Run some Lua code
code := `
print(provider1.new("some input for provider1"))
print(provider2.new("some input for provider2"))
`
if err := L.DoString(code); err != nil {
panic(err)
}
}
And here's the output it generates.
type provider1 -> wrapper %!s(func(*lua.LState) int=0x401a90)
type provider2 -> wrapper %!s(func(*lua.LState) int=0x401a90)
provider_2 results
provider_2 results
As you can see the wrapper function is always the same one, but I'd expect them to be different on each iteration. As a result the last wrapper function overwrites all previous ones, and the returned result is always from the last registered provider (in this case provider2).
Is this something I've overlooked or a bug in gopher-luar?
Thanks,
Marin
Hi. I executed this snippet:
package main
import (
"fmt"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
const script = `
acceptArray({16.24, 13.23})
`
func acceptArray(array interface{}) {
fmt.Println(array)
}
func main() {
state := lua.NewState()
defer state.Close()
state.SetGlobal("acceptArray", luar.New(state, acceptArray))
if err := state.DoString(script); err != nil {
panic(err)
}
}
I placed a breakpoint on the line with fmt.Println(array)
and inspected a value of array. The Go debugger from VS Code tells me it's (unreadable could not resolve interface type)
:
I expected it to be []interface{}
First, I'm not sure if this is a bug, or correct behaviour of luar.
I have an array of structs. These structs have methods with a pointer receiver. In Go, if you call a method on a lvalue instead of a reference, Go will automatically call (&lvalue).method.
"A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &xโs method set contains m, x.m() is shorthand for (&x).m()".
This does not work automatically with luar. The below code will print:
panic: :3: attempt to call a non-function object
Changing the Hello method to have a non-pointer receiver makes it work as expected.
package main
import (
"fmt"
"github.com/yuin/gopher-lua"
"github.com/layeh/gopher-luar"
)
type Foo struct {}
func (f *Foo) Hello() {
fmt.Printf("Hello\n")
}
const code = `
f = somevar[1]
f:Hello()
`
func main() {
L := lua.NewState()
defer L.Close()
somevar := []Foo { Foo{} }
L.SetGlobal("somevar", luar.New(L, somevar))
if err := L.DoString(code); err != nil {
panic(err)
}
}
In Go it is a common practice to have functions, which return errors in case something went wrong, e.g. func Foo() (FooResult, error)
.
Doing so it is up to the caller to decide whether this is a fatal error or something that can be handled properly.
Considering the following Go code, which registers a function in Lua, what would be the best way to handle errors? Should we just panic
, which would raise an exception in Lua or is there a better way to do that?
package main
import (
"log"
"os/user"
"github.com/layeh/gopher-luar"
"github.com/yuin/gopher-lua"
)
type Person struct {
Name string
Home string
}
func main() {
L := lua.NewState()
defer L.Close()
person := L.NewTable()
person.RawSetH(lua.LString("new"), luar.New(L, NewPerson))
L.SetGlobal("person", person)
if err := L.DoFile("create-person.lua"); err != nil {
log.Fatal(err)
}
}
func NewPerson(name string) *Person {
user, err := user.Lookup(name)
if err != nil {
// How to propagate the error to Lua ??
// panic ??
}
p := &Person{
Name: name,
Home: user.HomeDir,
}
return p
}
And the following example Lua code.
p = person.new("nonexisting-user")
print("username: " .. p.Name)
print("homedir: " .. p.Home)
Thanks,
Marin
If I have a Go struct with one field in it - a slice of strings - is there a way to automatically convert a Lua table to Go slice, if I want to change the contents of my struct from Lua?
I've noticed that stevedonovan/luar does this, so wondering if layeh/gopher-luar supports this as well?
Thanks,
Marin
Go:
type GameEntity struct {
ID int
Components map[string]Component
}
func (e *GameEntity) setupComponentsMap() {
e.Components = make(map[string]Component)
}
L.SetGlobal("Ent", luar.NewType(L, GameEntity{}))
Lua:
--test Go interop
ent = Ent()
print(ent)
--(calling functions)
ent:setupComponentsMap()
error: attempt to call a non-function object
If I create ent on Go side and follow e.g. the example in the readme, functions are called no problem. This kind of beats the whole purpose of using luar in the first place to avoid manually writing glue code...
I'm calling a Go method on an object that returns a slice.. but it seems that slice is not converted to a lua table. What am I doing wrong?
package main
import (
"github.com/yuin/gopher-lua"
"github.com/layeh/gopher-luar"
)
type Foo struct {}
func (f *Foo) GetValues() (v []string) {
return []string{ "one", "two", "three" }
}
const code = `
f = somevar:GetValues()
for i, v in ipairs(f) do
print (v)
end
`
func main() {
L := lua.NewState()
defer L.Close()
somevar := &Foo{}
L.SetGlobal("somevar", luar.New(L, somevar))
if err := L.DoString(code); err != nil {
panic(err)
}
}
This results in
panic: :3: bad argument #1 to ipairs (table expected, got userdata)
If I replace ipairs(f) with f(), I get:
panic: :3: attempt to call a non-function object
Thanks
Mike.
currently, New() creates a userdata for slice and array, it would be nice if it instead create a new table.
I'm seeing the above test failing in master, not sure if it's just something in my setup...
helpers_test.go:80: error substring 'attempt to call' not found in 'runtime error: invalid memory address or nil pointer dereference
stack traceback:
<string>:1: in main chunk
[G]: ?'
FAIL
FAIL layeh.com/gopher-luar 4.028s```
Hey Layeh, thanks for Luar, it's amazing. :)
I've just encountered a security problem, though, which I was hoping you could help with. I'm using luar with my new project, listless, which implements a discussion-mailing-list scripted in Lua.
At present, there are three separate lua contexts;
All of these modes are fairly owner-controlled, and it's always in the power of the owner to open security holes unwittingly through bad code. So, I'm leaving io
and os
and debug
alone, and the latter two scripts even get full database access.
I'd also like to implement a 4th mode, where moderators can email the list to issue commands in a more restricted lua environment; no io
, no os
, no debug
, and more closely limited options, because email is a poorly authenticated platform and abuse would be too easy otherwise.
Coming to the problem:
I just realised that several of the objects I'm exposing to the Lua engines are embedded-struct wrappers around rich structs that have Go io
access, and that potentially the parent methods are exposed. So, I tried shadowing one of these dangerous methods, email.Email.AttachFile(filename) (*email.Attachment, error)
, then went to Lua and accessed message.AttachFile
and message.Email.AttachFile
; different memory addresses.
So, it seems Luar is doing (as it should) a great job of iterating over methods including parent struct methods. This behaviour would normally be great, but in this case it's a problem!
Is there any way in luar to either:
Thanks, sorry for the long post but I figure extra context helps frame the question!
I love this library, but currently am using a personally modified version of it so that when I inject Go values with methods/fields I get only one style of methods in return. It would be awesome to have the the exported style and un-exported style behind a set of options. The default can be it's current behavior.
Perhaps something along the lines of:
luar.Configure(luar.Options{
MethodNames: luar.ExportedOnly,
})
Other values could be luar.UnexportedOnly
, or luar.Default
(I'm drawing a blank at a better name for this one).
Hi,
I am trying to build the gopher-luar with newest version of go on mac, and it fails with error expecting import of layeh.com/gopher-luar
. Please see below:
$ make gopher-luar/git/master
go test -coverprofile=coverage.out
can't load package: package github.com/layeh/gopher-luar: code in directory /Users/gpolek/workspace/src/github.com/layeh/gopher-luar expects import "layeh.com/gopher-luar"
Why the comments introduced in 24588ee were added in first place? It breaks the build for me. After removing // import "layeh.com/gopher-luar"
everything compiles correctly.
Here is my version of go:
go version
go version go1.7.4 darwin/amd64
Best,
Greg
404 Not Found
I'm trying to find a way to add some metamethods such as __len
to my Go types, so I can extend their functionality.
Considering the following code below I'd like to retain access to my Go type's methods, but also add a __len
metamethod to it, so I can get the length of items/persons using the hash #
operator.
Here's the example code I have.
package main
import (
"log"
"github.com/layeh/gopher-luar"
"github.com/yuin/gopher-lua"
)
type Person struct {
Name string `luar:"name"`
Age int `luar:"age"`
}
type People struct {
everyone []*Person `luar:"-"` // Hidden field
}
func (p *People) Add(who ...*Person) {
p.everyone = append(p.everyone, who...)
}
func (p *People) Len() int {
return len(p.everyone)
}
func main() {
L := lua.NewState()
defer L.Close()
people := &People{
everyone: make([]*Person, 0),
}
L.SetGlobal("Person", luar.NewType(L, Person{}))
L.SetGlobal("People", luar.New(L, people))
code := `
kevin = Person()
kevin.name = "Kevin"
kevin.age = 30
bob = Person()
bob.name = "Bob"
bob.age = 31
People:add(kevin, bob)
print("The number of persons I know is " .. People:len())
-- How to add __len metamethod to People so we can use #People as well?
`
if err := L.DoString(code); err != nil {
log.Fatal(err)
}
}
Considering this code, is there a way to get the length of persons using #People
instead of calling my custom People.Len()
method?
Thanks,
Marin
I'm trying to iterate over a Go maps keys and values but I keep running into this problem.
The simple version of the Lua
function doWork(config)
for k, v = config() do
print(k .. ": " .. v)
end
end
The Go:
func main() {
// .. set up state, load script, etc...
config := map[string]interface{}{
"from": "name",
"to": "other_name",
}
err := L.CallByParam(lua.P{
Fn: L.GetGlobal("doWork"),
NRet: 0,
Protect: true,
}, luar.New(L, config))
if err != nil {
panic(err) // Panic happens here
}
}
Not sure what I'm doing wrong, this is per the documentation. I've also tried a map[string]string
but ultimately this data is fed in from a JSON parse so it's a map[string]interface{}
As a side note, if I call config()
outside of the loop header everything works fine. It just returns a function. I'm not quite sure what's breaking here, it's only when trying to do this loop.
What about pointers, like this
print( sql:Table (inspection task): count (NIL))
Considering the luar.NewType
example from https://github.com/layeh/gopher-luar/blob/master/example_test.go#L1037, is there a way to set default values for the Song
struct, so that you don't have to initialize each struct field later on?
Thanks,
Marin
since there is FieldNames
and MethodNames
in Config
, and the default export the upper case and lower case field and method name. but in my usage, only upper case is needed. so is there a way to set the two field in Config
and how? thanks for your help.
Thanks for working on my previous bug report. I think the fix was too specific and not quite right. That's probably my fault, as I didn't reduce the code that reproduced to bug to its minimum. It is still the case that calling a method that has a pointer-receiver on an lvalue doesn't always work with luar while it works in Go. Example code:
package main
import (
"fmt"
"github.com/yuin/gopher-lua"
"github.com/layeh/gopher-luar"
)
type Foo struct {}
func (f *Foo) Hello() {
fmt.Printf("Hello\n")
}
const code = `
somevar:Hello()
`
func main() {
L := lua.NewState()
defer L.Close()
var somevar Foo
L.SetGlobal("somevar", luar.New(L, somevar))
if err := L.DoString(code); err != nil {
panic(err)
}
}
This results in:
panic: <string>:2: attempt to call a non-function object
stack traceback:
<string>:2: in main chunk
[G]: ?
I have tried to fix this myself, and I have a proof of concept that works, at least for structs. The code is at https://github.com/miquels/gopher-luar/commit/c2c3f66f865516d517c19865c9dfe4c7dd1b9b64
It is rough and wrong, I know. I think that what I did in struct.go might also have to be done in other places where mt.method / mt.ptrMethod are used together. However I think this better illustrates the issue.
Thanks
Mike.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.