yuin / gopher-lua Goto Github PK
View Code? Open in Web Editor NEWGopherLua: VM and compiler for Lua in Go
License: MIT License
GopherLua: VM and compiler for Lua in Go
License: MIT License
Hello yuin.
defer
doesn't run when you use os.Exit()
to terminate a process.
Therefore, LState.Close()
doesn't run in some cases.
This behavior causes an issue. For instance. A temporary file that is created by io.tmpfile()
is not deleted automatically by using glua
. Please see the following steps.
Create test.lua
-- test.lua
local t = io.tmpfile()
t:write("This is a tempfile.")
Run the test.lua
file by glua
.
glua test.lua
Look for the file. At my environment(OSX 10.10.4), the temporary file generated under the /var/folders/bt/xwh9qmcj00dctz53_rxclgtr0000gn/T
. It is not deleted.
ls -ltr /var/folders/bt/xwh9qmcj00dctz53_rxclgtr0000gn/T
...
-rw------- 1 kohkimakimoto staff 6 8 19 13:41 311400111
-rw------- 1 kohkimakimoto staff 6 8 19 13:42 445911440
-rw------- 1 kohkimakimoto staff 6 8 19 13:44 305149766
-rw------- 1 kohkimakimoto staff 6 8 19 13:45 960108122
-rw------- 1 kohkimakimoto staff 6 8 19 13:45 607108666
-rw------- 1 kohkimakimoto staff 6 8 19 13:45 731094900
-rw------- 1 kohkimakimoto staff 6 8 19 13:45 166451425
-rw------- 1 kohkimakimoto staff 8 8 19 13:48 977724667
-rw-r--r-- 1 kohkimakimoto staff 132 8 19 18:53 lsuseractivityd.log
drwx------@ 3 kohkimakimoto staff 102 8 19 19:59 com.apple.mail
drwx------@ 2 kohkimakimoto staff 68 8 20 06:08 com.apple.corerecents.recentsd
-rw------- 1 kohkimakimoto staff 4011 8 20 06:12 xcrun_db
drwxr-xr-x 2 kohkimakimoto staff 68 8 20 06:24 TemporaryItems
-rw------- 1 kohkimakimoto staff 19 8 20 07:39 440567778
cat 440567778
This is a tempfile.
The following code causes an infinite loop for me on master/Go 1.7/Linux:
local tbl = {
[-1] = "a",
[0] = "b",
[1] = "c",
}
for k, v in pairs(tbl) do
print(k, v)
end
Output:
-1 a
0 b
1 c
-1 a
0 b
1 c
... and so on
It only happens with numeric zero (not string). I know Lua indexing starts with 1 instead of 0. But in some calculations a zero may be stored in tables, and this loop does not happen in reference Lua 5.1.
Minimal test case:
local tbl = { [0] = true }
assert(next(tbl, 0) == nil) -- should be nil, but is 0
I started playing with this library, and have really liked it so far. Except for this annoying method of creating Go funcs that can be called from Lua. Its been constructed that you have to write these methods with the lua state in mind.
I have written a proof on concept function that essentially replaces LState.NewFunction with an interface{} that is typed checked to ensure it has been passed a function, then essentially does some reflection magic to automatically get all the required arguments from the state in their proper types, call the function with the arguments, and push any and all of the results back onto the stack in their respective orders.
Heres an example
func Double(i int) int {
return i * 2
}
func main() {
L := lua.NewState()
defer L.Close()
L.SetGlobal("double", L.NewFunc(Double)) /* wraps Double and called newLFunction */
}
If this is something that you'd accept into the codebase I will finish my implementation, currently it only works with ints as arguments and returns (Not for any particular purpose, I just chose a type to test with)
I have the following code:
package main
import (
"github.com/yuin/gopher-lua"
)
var luaProgram = `
-- defines a factorial function
function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end
print("enter a number:")
a = io.read("*number") -- read a number
print(fact(5))
`
func main() {
L := lua.NewState()
defer L.Close()
if err := L.DoString(luaProgram); err != nil {
panic(err)
}
}
When I run it, I get the following error:
enter a number:
15
panic: <string>:12: bad argument #2 to read (invalid options:u)
stack traceback:
[G]: in read
<string>:12: in function 'main chunk'
[G]: ?
goroutine 1 [running]:
main.main()
C:/Users/Bruno.Panuto/Desktop/lua.go:27 +0xc7
By using the glua interpreter, I get the same panic.
Using Windows may be the case, but I am just wondering what went wrong.
Thanks!
When reusing error
variables with gopher-lua, the "Why is my nil error value not equal to nil?" issue arises.
It would be a good idea to change any *ApiError
return value to error
to prevent this from happening. Most Go libraries take this approach (e.g. 1, 2).
Hi! Awesome work on this! I've been prototyping a project which uses gopher-lua to run user-defined scripts in a sandbox, and everything has been going pretty smoothly so far, however I would like to implement some resource constraints on scripts which are run, and based on previous experience, the way this is done is with debug hooks. Is there a specific reason why they are not exposed in gopher-lua? Is it possible to add them? I see that the shopify/go-lua project does implement them, but they are missing other things that gopher-lua has, and as far as I can tell, is not particularly active. Rather than cut over to using shopify/go-lua, I'm wondering if it's feasible to implement the debug hooks in gopher-lua, and wanted to get some background on why they were omitted before I started working on anything.
Thanks!
The baseLoadFile
function in baselib.go
is using the incorrect Sprint version of the fmt
package.
L.Push(LString(fmt.Sprint("can not open file: %v", chunkname)))
Should either be:
L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname)))
or:
L.Push(LString(fmt.Sprint("can not open file:", chunkname)))
If I wanted to "lockdown" the Lua thread running so that it only had access to a select number of files that are predetermined or determined by an outside source how would I go about doing so?
I can't seem to find any functions that would allow this behavior so the only course of action I can see is replacing loRequire
with something else (reassigning that global) and removing the load*
series and dofile
methods.
Do you have a clean way to do this other than what my thoughts are?
Also, secondary, but you don't provide any examples of LUserData
on the README, perhaps add some? As I become more familiar with this library, if you haven't done them already I'll see about doing it to help out.
test case:
print(string.find('hello.world', '.', 0))
print('str = ' .. string.sub('hello.world', 0, 2))
Output of Lua:
1 1
str = he
Output of gopher-lua:
nil
str =
0 start index is 1 in Lua.
0 start index is the end(length) of the string in gopher-lua.
Closure serialization, which means code and it's context partly evaluated.
In my business, If a closure can be serialized, it can be passed to remote service to apply with the input provided by the remote service.
a closure(args1...N) -----> remote service( apply(closure,input) )
This feature is quite useful in distributed system, for example:
Without this, I can only implement Context(some arguments provided) + Code Snippet String (not evaluated) to remote service and Apply(Context, Args, Code)
How do you think of this?
The basePrint
function in baselib.go
currently writes its output directly to os.Stdout
through fmt.Print
and friends. This will cause problems when calling print
from multiple goroutines/Lua threads. Notably, that multiple Print
calls can have their output interwoven on the console when they both run in parallel. For example:
fmt.Println("foo and bar")
go fmt.Println("bar and foo")
Can, under certain conditions, give output like this:
foo abar and foo
nd bar
Additionally, it would be nice if the host application could specify a different output target, aside from the default os.Stdout
. For this reason, I'm wondering if it is not more convenient and safer to have it use Go's log
package instead. This package takes care of both problems at once.
The changes to this package can be as simple as:
func basePrint(L *LState) int {
var buf bytes.Buffer
top := L.GetTop()
for i := 1; i <= top; i++ {
fmt.Fprint(&buf, L.Get(i).String(), " ")
}
log.Println(buf.String())
return 0
}
The host now has the guarantee that logging stuff is thread safe and the output can be redirected to any target by calling log.SetOutput(io.Writer)
.
Hi,
I love working with gopher-lua so far. The documentation is lacking tough.
What would be the best way to set the LUA_PATH? In order to load modules from subdirectories located relative to the executed .lua file, I need to set it to the directory of that .lua file.
I discovered that the Lua vm uses system env variables set via os.Setenv(). Is there a way to set / append to the LUA_PATH just for one vm (e.g. vm.Setenv())?
Thanks
Thanks for writing gopher-lua, I'm using in it algernon and it works great!
Best regards,
Alexander F Rødseth
I tested the following 2 files code by using glua
.
a.lua
dofile "b.lua"
b.lua
error("error!")
Run the command. Got a incorrect message: nil
as the following.
$ glua a.lua
nil
stack traceback:
[G]: in error
b.lua:1: in <b.lua:0>
[G]: in dofile
a.lua:1: in function 'main chunk'
[G]: ?
And I also tested running the original C lua5.1 in the same way. Got a correct error message.
$ lua5.1 a.lua
lua5.1: b.lua:1: error!
stack traceback:
[C]: in function 'error'
b.lua:1: in main chunk
[C]: in function 'dofile'
a.lua:1: in main chunk
[C]: ?
Oh well, sorry, I ran into a second issue :)
When I use a coroutine together with a for-loop, the second value is wrong - the first value is duplicated. If I call the coroutine manually, everything is fine.
Example:
local cr = function()
return coroutine.wrap(function()
coroutine.yield(1, "a")
coroutine.yield(2, "b")
end)
end
for k, v in cr() do
print("for:", k, v)
end
print("------")
local f = cr()
print("call:", f())
print("call:", f())
Output:
for: 1 1
for: 2 2
------
call: 1 a
call: 2 b
Thanks again!
Could you write a readme heading describing how go routines interact with gopher-lua, and what is/isn't thread safe.
I found a bug: os.execute
doesn't work.
I created like the following lua file.
-- execute_test.lua
os.execute("echo OK")
And I tried to run it, but I got a error.
$ go build cmd/glua/glua.go
$ ./glua execute_test.lua
-c: echo OK: No such file or directory
NOTE:My go version is 1.4.2
$ go version
go version go1.4.2 darwin/amd64
Hi,
I want integrate it on my project:
https://github.com/prsolucoes/goci
It is a continuous integration system that use script files to make the build steps. I want use LUA now. How i can export some Go packages to this implementation? How multiple return works in this LUA implementation?
Thanks.
I have an LState in which a user script has defined several functions. Is there a way to clone this LState in order to have two individual LStates that can get executed concurrently safely (they can eventually get killed individually, without affecting the other)?
Example:
L := lua.NewState()
L.DoString(script)
L2 := L.Clone()
go run(L)
go run(L2)
// "run()" is an arbitrary function that may call anything on L or L2 and also might crash
Currently you can have all, or no standard modules. This produces problems when, for example, you need everything but os
and io
to be available.
The fix for this is trivial, just export the openXYZ
functions so that users can install modules piecemeal if they wish.
Currently, the following code runs without error:
local x = {}
x[nil] = "test"
To be consistent with Lua 5.1, it should raise a "table index is nil" error.
Im currently running a simple LUA script with a module
func (c *CharacterModule) characterDeaths(L *lua.LState) int {
name := L.ToString(1)
deaths, err := models.GetCharacterDeaths(name)
if err != nil {
L.Push(lua.LString(err.Error()))
L.Push(lua.LNil)
return 2
}
death_list := &lua.LTable{}
for _, val := range deaths {
t := &lua.LTable{}
t.RawSetString("time", lua.LNumber(val.Time))
t.RawSetString("level", lua.LNumber(val.Level))
t.RawSetString("killed_by", lua.LString(val.Killed_by))
t.RawSetString("most_damage_by", lua.LString(val.Mostdamage_by))
death_list.Append(t)
}
L.Push(lua.LBool(true))
L.Push(death_list)
return 2
}
local character_module = require("character")
local errorx, deaths = character_module.getCharacterDeaths("Kassem G")
if(errorx ~= true) then
return false
end
local template = {}
template["list"] = {}
template["list"]["killed_by"] = "load test"
return template
Im using a http load test tool and at a certain points my app goes from 0-1% CPU usage to 50%
Im doing something wrong while creating the lua table?
Im sure the error is in the getCharacterDeaths function since removing it makes my CPU usage go quiet
Also im sure its not this part
models.GetCharacterDeaths(name)
Since I teste that one alone and nothing happened
So maybe im missing something while working with lua tables?
Original Lua5.1 implementation supports shebang.
But GopherLua does not support it.
Example: example.lua
#!/path/to/glua
print("hoge")
$ ./example.lua
./example.lua line:1(column:1) near '#': syntax error
my environment:
go version go1.6 darwin/amd64
Two examples from the reference are wrong:
1、This example has two wrong. One is missing 'lua' package name for 'LTrue'. Another is 'bl,ok == ' should be ':='
lv := L.Get(-1) // get the value at the top of the stack
if lv == LTrue { // correct
}
if bl, ok == lv.(lua.LBool); ok && bool(bl) { // wrong
}
2、Missing 'lua' package again for the two functions.
lv := L.Get(-1) // get the value at the top of the stack
if LVIsFalse(lv) { // lv is nil or false
}
if LVAsBool(lv) { // lv is neither nil nor false
}
Hi, I've just played with inspect.lua (https://github.com/kikito/inspect.lua) but it seems it doesn't work correctly.
for example)
macbook% ../bin/glua bug.lua
inspect.lua:300: attempt to index a non-table object(nil)
stack traceback:
inspect.lua:300: in inspect.lua:297
(tailcall): ?
bug.lua:2: in main chunk
[G]: ?
macbook% lua bug.lua
"hello"
https://gist.github.com/chobie/bcff5c74c915422a3075
also, I've tested above script with lua 5.1.5 and lua 5.2.3 and it works fine.
can you check this if you have a chance?
Thanks,
Do you have any plan to make the changes necessary to be a compliant environment for newer (minor) versions of Lua? Of course Lua 5.3.2 being the latest version as of today. I don't really have a need for it. But I think it would be nice.
One thing I have noticed as a difference between the lua C programs is that the lua5.3 interactive interpreter (REPL) works much better than the lua5.1 repl. Surely more work has gone into it's presentation but, having implemented a simple REPL in 5.1 and seen the limitations, I wasn't sure if it was actually just easier to build a good REPL using the 5.3 C API.
From what I understand the Go API has some functions that correspond to functions added to the C API in Lua 5.2. But I'm not entirely sure about C API changes in Lua 5.3.
Is the existing Lua module API strictly compliant exactly with Lua 5.1? Or is in somewhere in-between versions?
It would be good if the benchmark would compare with http://luadist.org/ that seems a more reasonable baseline to see how much improvemnt can be done in terms of performance.
Does it support luarocks?
When using RawSet function to add a LTable inside another LTable, I got error like
cannot use *llTable (type lua.LTable) as type lua.LValue in argument to ltable.RawSet: lua.LTable does not implement lua.LValue (String method has pointer receiver)
but I see lua.LTable do implement lua.LValue interface. any idea?
why it requires the return type to be int, can not it be float?
I injected a bug by #73
When GopherLua loads an empty source file, it outputs error 'EOF'.
$ glua empty.lua
EOF
I added a failed test case by kohkimakimoto@9e9a27c
And I will fix it by new PR (now, working in progress in my local machine). sorry!
Hi,
I tested agora (https://github.com/PuerkitoBio/agora/) a year or two ago, and found it much faster than otto. As a suggestion, I thought you may want a benchmark against that as a comparison, too.
There is no documentation that os.setenv
has been implemented by gopher-lua.
How would I go about to implement the following Lua function in Go? I can't figure out how to extend the string global.
function string:split(sep, cb)
local sep, fields = sep or ":", {}
local pattern = string.format("([^%s]+)", sep)
if cb then
assert(type(cb) == "function")
self:gsub(pattern, cb)
end
self:gsub(pattern, function(c) fields[#fields+1] = c end)
i = 0
n = table.getn(fields)
return function()
i = i + 1
if i <= n then return fields[i] end
end
end
Hi. Really loving gopher-lua so far and excited to keep using it! Thanks for making it.
I'm struggling with error handling at the moment. Basically, I'd like to do my own error reporting. The error
returned from the Do functions could be a concrete type that has structured access to the line and column number and error message.
if err := L.DoString(luaCode); err != nil {
// access to line and column number here would be awesome!
}
If you're too busy, I'm willing to make the changes but am not sure the best way to go about it. The DoString function could still return error
type, but the underlying type (after a type assertion) would ideally expose the line, column numbers as well as the error message and maybe error type (syntax, etc.). Right now the error is like this:
&lua.ApiError{Type:0, Object:"<string> line:3(column:5) near 'if': syntax error\n", StackTrace:""}
But I'd like to be able to do this:
if err := L.DoString(luaCode); err != nil {
luaErr := err.(lua.Error) // or whatever
fmt.Println("Error type:", luaErr.Type)
fmt.Println("Line:", luaErr.Line)
fmt.Println("Col:", luaErr.Column)
fmt.Println("Message:", luaErr.Message)
}
What do you think?
Please answer the following before submitting your issue:
This is the test code:
package main
import (
"fmt"
"sync"
"github.com/hoisie/web"
lua "github.com/yuin/gopher-lua"
)
type lStatePool struct {
m sync.Mutex
saved []*lua.LState
}
func (pl *lStatePool) Get() *lua.LState {
pl.m.Lock()
defer pl.m.Unlock()
n := len(pl.saved)
if n == 0 {
return pl.New()
}
x := pl.saved[n-1]
pl.saved = pl.saved[0 : n-1]
return x
}
func (pl *lStatePool) New() *lua.LState {
L := lua.NewState(lua.Options{
IncludeGoStackTrace: true,
})
// setting the L up here.
// load scripts, set global variables, share channels, etc...
L.PreloadModule("web", Loader)
return L
}
func (pl *lStatePool) Put(L *lua.LState) {
pl.m.Lock()
defer pl.m.Unlock()
pl.saved = append(pl.saved, L)
}
func (pl *lStatePool) Shutdown() {
for _, L := range pl.saved {
L.Close()
}
}
// Global LState pool
var luaPool = &lStatePool{
saved: make([]*lua.LState, 0, 4),
}
func main() {
defer func() {
luaPool.Shutdown()
}()
/*
L := lua.NewState(lua.Options{
IncludeGoStackTrace: true,
})
defer L.Close()
L.PreloadModule("web", Loader)
*/
go vm()
for {}
}
func vm() {
L := luaPool.Get()
defer luaPool.Put(L)
if err := L.DoString(`
local web = require("web")
print("lua>> web", web.version)
print("lua>> web.app()")
local app = web.app()
local hello = 0
print("lua>> web.app:get()")
app:get("/", function ()
hello = hello + 1
print("lua>print:",hello)
return ctx.render(hello)
end)
print("lua>> web.app:listen()")
app:listen("0.0.0.0", "8000")
`); err != nil {
panic(err)
}
}
//----------------------------------------------------//
const (
lContextTypeName = "context"
lAppClassName = "App"
)
var exports = map[string]lua.LGFunction{
"app": app,
}
var appMethods = map[string]lua.LGFunction{
"get": get,
"listen": listen,
}
func Loader(L *lua.LState) int {
mod := L.SetFuncs(L.NewTable(), exports)
L.SetField(mod, "version", lua.LString("0.0.1"))
// returns the module
L.Push(mod)
mod2 := L.SetFuncs(L.NewTable(), selfMethods)
L.SetGlobal("ctx", mod2)
// returns the module
L.Push(mod2)
return 2
}
var selfMethods = map[string]lua.LGFunction{
"render": render,
}
func render(L *lua.LState) int {
s := L.CheckNumber(1)
L.Push(lua.LNumber(s))
return 1
}
func app(L *lua.LState) int {
mt := L.NewTypeMetatable(lAppClassName)
mt.RawSetString("__index", mt)
L.SetFuncs(mt, appMethods)
s := web.NewServer()
ud := L.NewUserData()
ud.Value = s
L.SetMetatable(ud, L.GetTypeMetatable(lAppClassName))
L.Push(ud)
return 1
}
func checkServer(L *lua.LState) *web.Server {
fmt.Println("L.GetTop():", L.GetTop())
ud := L.CheckUserData(1)
if v, ok := ud.Value.(*web.Server); ok {
return v
}
L.ArgError(1, "*web.Server expected")
return nil
}
func get(L *lua.LState) int {
s := checkServer(L)
path := L.CheckString(2)
fn := L.CheckFunction(3)
s.Get(path, func(self *web.Context) {
L.Push(fn)
L.Call(0, 1)
hello := L.CheckNumber(L.GetTop())
self.WriteString(hello.String())
})
return 0
}
func listen(L *lua.LState) int {
s := checkServer(L)
host := L.CheckString(2)
port := L.CheckString(3)
addr := fmt.Sprintf("%v:%v", host, port)
s.Run(addr)
return 0
}
local t = {
[123456789] = 'A',
}
if I set lua table key = 123456789, it is not worked
local t = {
[12345678] = 'A',
}
if i set lua table key = 12345678, it takes long time, almost 7 sec
how can i fix this problem??
The debug.traceback function does not support the level argument as described in the reference manual.
This seems like it would be helpful for writing error handling functions (edit: specifically for use with PCall). As it is now, I think the error handling function is included in any stracktrace the error handling function produces.
I see sometimes lua requires to write the function 'module(...) when developing a lua module, but sometimes returning a table is enough. please advise....
I discovered this when I was integrating gopher-lua and I'm able to reproduce it with glua as well. To work around the issue I had to assign the string.format
to a temporary variable and then do the var1 = var1 or var2
statement.
function test(a, b, c)
b = b or string.format("test %v", a)
c = c or string.format("test %v", a)
print("a:", a)
print("b:", b)
print("c:", c)
end
glua test.lua hello
a: hello
b: test hello
c: test hello
a: hello
b: test hello
c: test %v
I would like to annotate Lua code through comments, much like Go to generate documentation. However, I don't see the comments when I call parse.Dump
.
How should I access the comments through the AST?
-- relase <var> [optionalVar]
function target.relase(var, optionalVar)
end
I was trying to iterate over a string's characters with gsub
and found this particular implementation difference (which I assume is considered a bug):
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> function newline(str) str:gsub(".", function(c) print(c == '\n') end) end
> newline("a\nb")
false
true
false
GopherLua 0.1 Copyright (C) 2015 Yusuke Inuzuka
> function newline(str) str:gsub(".", function(c) print(c == '\n') end) end
> newline("a\nb")
false
false
Turns out RE2 regular expressions do not match newlines by default - the s
flag needs to be set. As far as I can tell, this is done by prepending (?s)
to the regex string. In other words:
--- a/utils.go
+++ b/utils.go
@@ -286,7 +286,7 @@ func compileLuaRegex(pattern string) (*regexp.Regexp, error) {
- gopattern := sc.String()
+ gopattern := "(?s)" + sc.String()
Pull request incoming.
Hi,
gopher-lua works great. I actually found out about it when Shopify announced its version (go-lua).
One thing I'd like to be able to do it to provide a sandbox environment for executing user scripts, e.g. preventing access to files and executing commands. Is this possible with gopher-lua?
Thanks!
From the Lua 5.1 manual (emphasis mine):
table.remove (table [, pos])
Removes from table the element at position pos, shifting down other elements to close the space, if necessary. Returns the value of the removed element. The default value for pos is n, where n is the length of the table, so that a call table.remove(t) removes the last element of table t.
This should probably be fixed for capability.
I'm writing a plugin for Piepan which uses gopher-lua for its Go Lua plugins. I use string.match()
in such a plugin with a regular expression to sanitize user input. However, this does not work with non-latin characters such as the Danish æ
, ø
and å
. Regular expression implementations usually support this.
A trivial example:
print(string.match("abc", "abc"))
abc
print(string.match("æøå", "æøå"))
nil
Is this a bug in gopher-lua? Or do I need to do something special to be able to use these non-latin characters?
(I originally filed this bug against Piepan here: layeh/piepan#31. The author sent me here.)
The current implementation of os.clock uses a simple "seconds since the app started" but the LUA reading material I'm seeing (e.g. http://www.lua.org/pil/22.1.html) says it should be CPU-seconds.
I don't know of a clean way to get this via the Go runtime, so this would require a kernel call to getrusage (http://linux.die.net/man/2/getrusage). For some reason getrusage returns wall clock time on Windows, so you'll need to use GetProcessTimes (https://msdn.microsoft.com/en-us/library/windows/desktop/ms683223.aspx).
Hi, I'm trying to define a __call
on a module like:
mt := L.NewTypeMetatable("CUSTOM_MAP")
L.SetGlobal("Map", mt)
// static attributes
L.SetField(mt, "__call", L.NewFunction(newLuaMap))
L.SetField(mt, "create", L.NewFunction(newLuaMap))
If I call:
m = Map.create()
everything works as expected. But if I do
m = Map()
I get attempt to call a non-function object
.
Is there a way I can get this to work?
It seems that coroutines and pcalls don't quite work right when nested. I'm not sure why it is, but pcalls and coroutine don't play nice together.
This is a complete example program, that reproduces the problem. It should be spitting out several Tick: lines, instead it's spitting nothing out at all.
If you comment out the pcall stuff, it functions as expected.
package main
import (
"fmt"
"github.com/yuin/gopher-lua"
)
func main() {
state := lua.NewState()
state.OpenLibs()
main, err := state.LoadString(threadLua)
if err != nil {
panic(err)
}
thread := state.NewThread()
s, err, _ := state.Resume(thread, main)
if err != nil {
panic(err)
}
for i := 0; i < 20; i++ {
if s != lua.ResumeError {
s, err, _ = state.Resume(thread, main, lua.LString("tick"), lua.LNumber(i))
if err != nil {
fmt.Println("Error: ", err)
break
}
}
}
}
var threadLua = `
local ok, err = pcall(
function()
local evt, n = coroutine.yield()
print("Tick: " .. n)
if n > 10 then
error("N is > 10")
end
end)
if not ok then
print("Got Error: " .. err)
error("Dying.")
end
`
I have a simple test:
--test.lua
local a,b
a = function ()
require "inityx_dtba"
print("a")
coroutine.yield()
b()
end
b = function ()
print("b")
end
local co = coroutine.create(a)
local ok = coroutine.resume(co)
while ok do
ok = coroutine.resume(co)
end
It's ok in clua5.1, but when run in glua, I got these error info
It's very important for me to load module in coroutine, how can solve this issue?
[G]: can not yield from outside of a coroutine
stack traceback:
[G]: in yield
test.lua:11: in main chunk
[G]: ?
goroutine 1 [running]:
github.com/yuin/gopher-lua.func·007()
e:/fei/go/src/github.com/yuin/gopher-lua/state.go:1365 +0x17e
github.com/yuin/gopher-lua.func·010()
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:118 +0x1a2
github.com/yuin/gopher-lua.func·002(0xc082014410)
e:/fei/go/src/github.com/yuin/gopher-lua/coroutinelib.go:75 +0x72
github.com/yuin/gopher-lua.(_LState).raiseError(0xc082014410, 0x1, 0x650570, 0x2
9, 0x0, 0x0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/state.go:354 +0x364
github.com/yuin/gopher-lua.(_LState).RaiseError(0xc082014410, 0x650570, 0x29, 0x
0, 0x0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/state.go:1065 +0x6a
github.com/yuin/gopher-lua.switchToParentThread(0xc082014410, 0x0, 0xc0820d0000)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:44 +0x71
github.com/yuin/gopher-lua.callGFunction(0xc082014410, 0x0, 0xc082006dc0)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:74 +0xe6
github.com/yuin/gopher-lua.func·033(0xc082014410, 0xc070000201, 0x0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:493 +0x22c
github.com/yuin/gopher-lua.mainLoop(0xc082014410, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:27 +0x119
github.com/yuin/gopher-lua.threadRun(0xc082014410)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:122 +0x96
github.com/yuin/gopher-lua.coResume(0xc0820143c0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/coroutinelib.go:82 +0x450
github.com/yuin/gopher-lua.callGFunction(0xc0820143c0, 0x0, 0xc082006e00)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:67 +0x40
github.com/yuin/gopher-lua.func·033(0xc0820143c0, 0xc0700c0402, 0x0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:493 +0x22c
github.com/yuin/gopher-lua.mainLoop(0xc0820143c0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/vm.go:27 +0x119
github.com/yuin/gopher-lua.(_LState).callR(0xc0820143c0, 0x0, 0xffffffffffffffff
, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/state.go:712 +0x21f
github.com/yuin/gopher-lua.(_LState).Call(0xc0820143c0, 0x0, 0xffffffffffffffff)
e:/fei/go/src/github.com/yuin/gopher-lua/state.go:1348 +0x4c
github.com/yuin/gopher-lua.(_LState).PCall(0xc0820143c0, 0x0, 0xffffffffffffffff
, 0x0, 0x0, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/state.go:1396 +0x15b
github.com/yuin/gopher-lua.(_LState).DoFile(0xc0820143c0, 0xc0820021f0, 0x8, 0x0
, 0x0)
e:/fei/go/src/github.com/yuin/gopher-lua/auxlib.go:385 +0xec
main.main()
e:/fei/go/src/github.com/yuin/gopher-lua/cmd/glua/glua.go:89 +0x769
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.