Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error with glua #13

Closed
ghost opened this issue Dec 22, 2017 · 31 comments
Closed

Error with glua #13

ghost opened this issue Dec 22, 2017 · 31 comments

Comments

@ghost
Copy link

ghost commented Dec 22, 2017

This may be completely outside of your realm of interest, but I am working on a project that uses Moonscript and Gopher-Lua (Golang based Lua Interpreter) for plugins. I have my reasons for doing this, but I've created a single Lua script bundle for the Moonscript interpreter (using LuLPeg in place of LPeg) using Amalgamation and some personal tricks which works with both Lua and LuaJIT. This bundle is great for me single it has no deps and makes deployment way simpler. The problem is that it isn't working for Gopher-Lua! I will include the error below, I would love any input you may have as to how I can fix this issue.

Here are the files I am working with, running should be as simple as lua testers.lua | lua (swap the lua version as you see fit, I am trying to get Gopher-Lua to work which can be found here: https://github.com/yuin/gopher-lua#standalone-interpreter)
files.zip

panic: @/usr/share/lua/5.3/lulpeg.lua:1577: bad argument #1 to load (function expected, got string)
stack traceback:
	[G]: in function 'load'
	@/usr/share/lua/5.3/lulpeg.lua:1577: in function 'printers'
	@/usr/share/lua/5.3/lulpeg.lua:2839: in function 'LuLPeg'
	@/usr/share/lua/5.3/lulpeg.lua:2848: in function <@/usr/share/lua/5.3/lulpeg.lua:2794>
	@/usr/share/lua/5.3/lulpeg.lua:22: in function <@/usr/share/lua/5.3/lulpeg.lua:15>
	(tailcall): ?
	[G]: in function 'require'
	./moon-bundle.lua:2970: in function <./moon-bundle.lua:0>
	[G]: in function 'require'
	<string>:1: in main chunk
	[G]: ?

goroutine 1 [running]:
main.main()
	/home/rucuriousyet/go/src/gitlab.com/stackmesh/stackd/plugin/lua/glua.go:48 +0x878
@pygy
Copy link
Owner

pygy commented Dec 22, 2017

Hello, this is most interesting. It looks like GopherLua is not detected as Lua 5.1 because it doesn't set _VERSION as described in the reference manual. I've opened an issue, let's see how it goes:

yuin/gopher-lua#156

@pygy
Copy link
Owner

pygy commented Dec 22, 2017

Looking into gopher-lua, chances are MoonScript still won't work because gopher-lua doesn't support newproxy nor setting a _len metatmethod on tables. As a consequence the # operator (lookahead) won't work.

LuLPeg adds a lpeg.L function as a polyfill for that, but the grammar must be written with it in mind, and I don't think that MoonScript does so.

I see that the LuLPeg docs and comments also mention a debug.setmetatable trick, but I don't remember in details how it works and I don't have the time to dig now.

@ghost
Copy link
Author

ghost commented Dec 22, 2017 via email

@pygy
Copy link
Owner

pygy commented Dec 22, 2017

The proxy API is result = newproxy(x) where result is a userdata and x is optional, either a boolean or another userdata.

  • When x is false or absent, result doesn't have a metatable.
  • When x is true, result gets a new, empty metatable.
  • When x is a userdata, result inherits its metatable.

You can ignore the debug.setmetatable remark above, the comment was written at a time I didn't know about the x parameter, and then you must use debug.setmetatable to modify the userdata, the normal setmetatable only accepts tables as arguments.

Since gopher-lua supports both debug.setmetatable and userdata as a data type (AFAICT they can only be created from the Go side), adding support for newproxy should be easy for someone who knows Go. Now I don't know if the gopher-lua author is interested.

For context, that API exists in Lua 5.1 because the # operator is hardcoded to table length for tables, whereas userdata can set a custom __len handler. So newproxy was added to bypass that limitation. In Lua 5.2+, # honours the __len metamethod on tables, so newproxy was removed.

Edit: actually, the __gc metamethod is also unsupported on tables in Lua 5.1, but not on proxies.

@ghost
Copy link
Author

ghost commented Dec 23, 2017

@pygy You mention that "When x is a userdata, result inherits its metatable." From where is the metatable inherited? I am still trying to wrap my head around Lua userdata types and the newproxy method...

@ghost
Copy link
Author

ghost commented Dec 23, 2017

It seems as if my initial implementation (however incomplete) solves the first issue... here is the code:

        state.SetGlobal("_VERSION", lua.LString("Lua 5.1"))

	// x == false -> result has no metatable
	// x == true -> result gets a new, empty metatable
	// x == userdata -> result inherits metatable
	state.SetFuncs(state.NewTable(), map[string]lua.LGFunction{
		"newproxy": func(substate *lua.LState) int {
			arg := substate.Get(1)
			typ := arg.Type()

			if typ == lua.LTBool {
				udata := substate.NewUserData()
				if lua.LVAsBool(arg) {
					substate.SetMetatable(udata, substate.NewTable())
				}

				substate.Push(udata)
				return 1
			} else if typ == lua.LTUserData {

			}

			return 0
		},
	})

However, I just encountered another issue.... hoping this isn't a lost cause.

@ghost
Copy link
Author

ghost commented Dec 23, 2017

Here is the output from err #2

# plugin git:master ●
$ ../sglua testers.lua 
panic: strings: negative Repeat count
stack traceback:
	[G]: in function 'rep'
	@/usr/share/lua/5.3/lulpeg.lua:596: in function <@/usr/share/lua/5.3/lulpeg.lua:593>
	(tailcall): ?
	@/usr/share/lua/5.3/lulpeg.lua:646: in function 'Range'
	@/usr/share/lua/5.3/lulpeg.lua:2404: in function 'R'
	@/usr/share/lua/5.3/lulpeg.lua:2862: in function 'locale'
	@/usr/share/lua/5.3/lulpeg.lua:2844: in function 'LuLPeg'
	@/usr/share/lua/5.3/lulpeg.lua:2848: in function <@/usr/share/lua/5.3/lulpeg.lua:2794>
	@/usr/share/lua/5.3/lulpeg.lua:22: in function <@/usr/share/lua/5.3/lulpeg.lua:15>
	(tailcall): ?
	[G]: in function 'require'
	./moon-bundle.lua:2970: in function <./moon-bundle.lua:0>
	[G]: in function 'require'
	<string>:1: in main chunk
	[G]: ?

goroutine 1 [running]:
main.main()
	/home/rucuriousyet/go/src/gitlab.com/stackmesh/stackd/plugin/lua/glua.go:74 +0xa64
# plugin git:master ●

@pygy
Copy link
Owner

pygy commented Dec 23, 2017

Err #2 is due to the fact that string.rep(whatever, negative_index) should return the empty string, not error out. I could use string.rep(input, max(0, n)) as a workaround.

Edit: "should return" is too stringstrong (edit2: heh) here... that's what happens in PUC Lua and LuaJIT, but it is not required per spec.

@pygy
Copy link
Owner

pygy commented Dec 23, 2017

The newproxy code is a good start BTW, but LuLPeg uses newproxy(other_userdata) so that branch is needed if you want MoonScript to work.

@pygy
Copy link
Owner

pygy commented Dec 23, 2017

A third thing I notice, which may or may not be relevant in the future: LuLPeg is installed in the lua/5.3 directory. It doesn't matter for LuLPeg which does version detection at run time, but it may prove troublesome for other libs.

I suppose that your base Lua install is Lua 5.3, and Luarocks uses the corresponding install folder. Not sure how to tell it to use another folder without changing the Lua version.

@ghost
Copy link
Author

ghost commented Dec 23, 2017

@pygy I too noticed the version directory... The thing is, I am working with a single lua bundle created using amalgamation with debug stubs to provide those stack traces. In order to remove those, I would need to install lua 5.1 (instead of my current 5.3), reinstall the libraries and build the bundle all over again (I may do it down the road with minified libraries). Also, let me know if the following code is a valid representation of newproxy()... (still guessing as to where the metatable is inherited from).

state.SetFuncs(state.NewTable(), map[string]lua.LGFunction{
		"newproxy": func(substate *lua.LState) int {
			arg := substate.Get(1)
			typ := arg.Type()

			if typ == lua.LTBool {
				udata := substate.NewUserData()
				if lua.LVAsBool(arg) {
					substate.SetMetatable(udata, substate.NewTable())
				}

				substate.Push(udata)
				return 1
			} else if typ == lua.LTUserData {
				metatable := substate.GetMetatable(arg)
				udata := substate.NewUserData()

				substate.SetMetatable(udata, metatable)
				substate.Push(udata)

				return 1
			}

			return 0
		},
	})

@ghost
Copy link
Author

ghost commented Dec 23, 2017

I've added

const strrepfunc = `
function customrep(str, n)
	if n >= 0 then
		local out = ""
		for i = 0, n do
			out = out..out
		end
		return out
	else
		return ""
	end
end

string.rep = customrep
`

which solves error #2 but now there's a third problem... starting to think this uphill battle is pointless. Any ideas for a better approach? Really hoping to support raw Moonscript in this system without needing to compile anything... may not be a viable option if using Gopher Lua.

@pygy
Copy link
Owner

pygy commented Dec 23, 2017

If I were you I'd go

local str_rep = string.rep
string.rep = function(str, n) 
  if type(n) == "number" and n < 0 then n = 0 end
  return str_rep(str, n)
end

It will be more efficient.

What's the third problem? This is actually interesting for me, because it points at places where LuLPeg relies on implementation quirks rather than documented behavior.

What's the third issue?


Edit: for newproxy, this should work for LuLPeg, but to work like PUC Lua 5.1, it has to treat a lack of argument or nil argument as if it were false, and error out with bad argument #1 to 'newproxy' (boolean or proxy expected) where you return 0.

@ghost
Copy link
Author

ghost commented Dec 23, 2017

Okay thx for the tip @pygy, Ill add that to my TODOs. Here is the error:

# plugin git:master ●
$ ../sglua testers.lua 
panic: @/usr/share/lua/5.3/lulpeg.lua:2382: bad argument #1 to 'P' (lpeg-pattern expected, got nil)
stack traceback:
	[G]: in function 'error'
	@/usr/share/lua/5.3/lulpeg.lua:2382: in function 'LL_P'
	@/usr/share/lua/5.3/lulpeg.lua:2498: in function <@/usr/share/lua/5.3/lulpeg.lua:2496>
	@/usr/share/lua/5.3/lulpeg.lua:1885: in function 'factorize_sequence'
	@/usr/share/lua/5.3/lulpeg.lua:2490: in function <@/usr/share/lua/5.3/lulpeg.lua:2486>
	(tailcall): ?
	@/usr/share/lua/5.3/lulpeg.lua:1104: in function 're'
	@/usr/share/lua/5.3/lulpeg.lua:2845: in function 'LuLPeg'
	@/usr/share/lua/5.3/lulpeg.lua:2848: in function <@/usr/share/lua/5.3/lulpeg.lua:2794>
	@/usr/share/lua/5.3/lulpeg.lua:22: in function <@/usr/share/lua/5.3/lulpeg.lua:15>
	(tailcall): ?
	[G]: in function 'require'
	./moon-bundle.lua:2970: in function <./moon-bundle.lua:0>
	[G]: in function 'require'
	<string>:1: in main chunk
	[G]: ?

goroutine 1 [running]:
main.main()
	/home/rucuriousyet/go/src/gitlab.com/stackmesh/stackd/plugin/lua/glua.go:117 +0xb13
# plugin git:master ●

@ghost
Copy link
Author

ghost commented Dec 23, 2017

If you'd prefer, I may just be able to set you up with the project I am working on so you can test/work though stuff yourself. Either way is fine.

@pygy
Copy link
Owner

pygy commented Dec 23, 2017

@rucuriousyet yup, it would help if I could help me set things up locally, the stack trace you posted goes through the re module which uses lpeg to parse its input and generate a grammar using IIRC constant and folding captures among other things (look at the source for a brain melting session).

I wonder how a nil manages to sneak in there.

@ghost
Copy link
Author

ghost commented Dec 23, 2017

Okay, It should be pretty simple for the most part. I would install Golang (golang.org/brew/pacaur) and then clone or use go get to retrieve this repo: https://gitlab.com/stackmesh/stackd (all the code related to this in the plugin directory)

@ghost
Copy link
Author

ghost commented Dec 23, 2017

I am basically doing the following:

go build -o sglua plugin/lua/glua.go
cd plugin
../sglua testers.lua

@pygy
Copy link
Owner

pygy commented Dec 24, 2017

@rucuriousyet https://gitlab.com/stackmesh/stackd prompted me to log in into GitLab only to return a 404... Typo?

@ghost
Copy link
Author

ghost commented Dec 24, 2017 via email

@ghost
Copy link
Author

ghost commented Dec 24, 2017 via email

@ghost
Copy link
Author

ghost commented Dec 24, 2017 via email

@pygy
Copy link
Owner

pygy commented Dec 24, 2017

So, we're making progress, but go get gitlab.com/stackmesh/core.git still ends up looking for stackmesh/stackd at some point:

~/tmp ❯❯❯ env GIT_TERMINAL_PROMPT=1 go get gitlab.com/stackmesh/core     
Username for 'https://gitlab.com': pygy
Password for 'https://[email protected]':
# cd .; git clone https://gitlab.com/stackmesh/stackd.git /Users/pygy/go/src/gitlab.com/stackmesh/stackd
Cloning into '/Users/pygy/go/src/gitlab.com/stackmesh/stackd'...
remote: The project you were looking for could not be found.
fatal: repository 'https://gitlab.com/stackmesh/stackd.git/' not found
package gitlab.com/stackmesh/stackd/fibre: exit status 128
package gitlab.com/stackmesh/stackd/machapi: cannot find package "gitlab.com/stackmesh/stackd/machapi" in any of:
	/usr/local/go/src/gitlab.com/stackmesh/stackd/machapi (from $GOROOT)
	/Users/pygy/go/src/gitlab.com/stackmesh/stackd/machapi (from $GOPATH)
package gitlab.com/stackmesh/stackd/servman: cannot find package "gitlab.com/stackmesh/stackd/servman" in any of:
	/usr/local/go/src/gitlab.com/stackmesh/stackd/servman (from $GOROOT)
	/Users/pygy/go/src/gitlab.com/stackmesh/stackd/servman (from $GOPATH)
package gitlab.com/stackmesh/stackd/utils: cannot find package "gitlab.com/stackmesh/stackd/utils" in any of:
	/usr/local/go/src/gitlab.com/stackmesh/stackd/utils (from $GOROOT)
	/Users/pygy/go/src/gitlab.com/stackmesh/stackd/utils (from $GOPATH)

I had already installed go through the official macOS installer... Not sure if it's related.

~/tmp ❯❯❯ go version
go version go1.9.2 darwin/amd64

@ghost
Copy link
Author

ghost commented Dec 24, 2017 via email

@ghost
Copy link
Author

ghost commented Dec 24, 2017 via email

@pygy
Copy link
Owner

pygy commented Dec 24, 2017

It was failing at go get gitlab.com/stackmesh/core, but you were right, renaming the folder and then running go get from there then following your instructions (go build -o sglua plugin/lua/glua.go etc.) allowed me to reproduce your errors. I'll look at it in the coming days.

@ghost
Copy link
Author

ghost commented Dec 24, 2017 via email

@yuin
Copy link

yuin commented Dec 25, 2017

I've make some changes for GopherLua, now LuLPeg should work on GopherLua.

@pygy
Copy link
Owner

pygy commented Dec 25, 2017

The issue stems from yuin/gopher-lua#158, an interpreter bug. The gopher-lua author is quite responsive, this will hopefully be solved soon.

Also, no need to add newproxy, actually. The MoonScript parser is LuLPeg-aware (I found this while exploring this issue), and defines L like this:

local L = lpeg.luversion and lpeg.L or function(p) return #p end

@pygy
Copy link
Owner

pygy commented Dec 25, 2017

@yuin I hadn't seen your message here before posting yuin/gopher-lua#158 (I was on the road this afternoon and investigated/wrote the report offline and posted it without refreshing this page).

Assuming you fixed it as well, many thanks for the quick fixes :-)

@pygy
Copy link
Owner

pygy commented Feb 11, 2018

@rucuriousyet Closing now as it seems fixed. Feel free to ping me if there are still issues.

@pygy pygy closed this as completed Feb 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants