diff --git a/c/lua/_demo/error/error.go b/c/lua/_demo/error/error.go new file mode 100644 index 00000000..ff8a0c86 --- /dev/null +++ b/c/lua/_demo/error/error.go @@ -0,0 +1,21 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + L.OpenLibs() + if res := L.LoadString(c.Str("function doubleNumber(x) ! return x * 2 end")); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } +} + +/* Expected output: +error: [string "function doubleNumber(x) ! return x * 2 end"]:1: unexpected symbol near '!' +*/ diff --git a/c/lua/_demo/funccall-concat/funccall.go b/c/lua/_demo/funccall-concat/funccall.go new file mode 100644 index 00000000..d313314e --- /dev/null +++ b/c/lua/_demo/funccall-concat/funccall.go @@ -0,0 +1,32 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + + L.OpenLibs() + if res := L.Dostring(c.Str("function combineParams(num, str) return 'Result: ' .. str .. ' ' .. num end")); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + L.GetGlobal(c.Str("combineParams")) + L.PushNumber(3.14159) + L.PushString(c.Str("Hello, World!")) + if res := L.PCall(2, 1, 0); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + if res := L.IsString(-1); res != 0 { + result := L.ToString(-1) + c.Printf(result) + } +} + +/* Expected output: +Result: Hello, World! 3.14159 +*/ diff --git a/c/lua/_demo/funccall-number/funccall.go b/c/lua/_demo/funccall-number/funccall.go new file mode 100644 index 00000000..ddcc6e02 --- /dev/null +++ b/c/lua/_demo/funccall-number/funccall.go @@ -0,0 +1,39 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + + L.OpenLibs() + if res := L.LoadString(c.Str("function doubleNumber(x) return x * 2 end")); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + if res := L.PCall(0, 0, 0); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + + L.GetGlobal(c.Str("doubleNumber")) + L.PushNumber(10) + + if res := L.PCall(1, 1, 0); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + + if res := L.IsNumber(-1); res != 0 { + result := L.ToInteger(-1) + c.Printf(c.Str("result: %lld\n"), result) + } else { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } +} + +/* Expected output: +result: 20 +*/ diff --git a/c/lua/_demo/funccall-string/funccall.go b/c/lua/_demo/funccall-string/funccall.go new file mode 100644 index 00000000..91d220de --- /dev/null +++ b/c/lua/_demo/funccall-string/funccall.go @@ -0,0 +1,48 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + + L.OpenLibs() + code := c.Str( + `function processStrings(a, b, c) + print('Received string a: ' .. a) + print('Received string b: ', b) + print('Received string c (formatted): ' .. c) + return a .. b .. c +end`) + + if res := L.Dostring(code); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + + L.GetGlobal(c.Str("processStrings")) + + L.PushString(c.Str("Hello, World!")) + L.PushLString(c.Str(`Hello Lua In LLGO`), 17) + L.PushFString(c.Str(`Hello %s In %d`), c.Str("LLGO"), 2024) + + if res := L.PCall(3, 1, 0); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + + if res := L.IsString(-1); res != 0 { + result := L.ToString(-1) + c.Printf(c.Str("result: %s\n"), result) + } +} + +/* Expected output: +Received string a: Hello, World! +Received string b: Hello Lua In LLGO +Received string c (formatted): Hello LLGO In 2024 +result: Hello, World!Hello Lua In LLGOHello LLGO In 2024 +*/ diff --git a/c/lua/_demo/hello/hello.go b/c/lua/_demo/hello/hello.go new file mode 100644 index 00000000..cb96ddde --- /dev/null +++ b/c/lua/_demo/hello/hello.go @@ -0,0 +1,17 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + L.OpenLibs() + if res := L.Dostring(c.Str("print('hello world')")); res != lua.OK { + println("error") + } +} diff --git a/c/lua/_demo/loadcall/loadcall.go b/c/lua/_demo/loadcall/loadcall.go new file mode 100644 index 00000000..9fd04671 --- /dev/null +++ b/c/lua/_demo/loadcall/loadcall.go @@ -0,0 +1,22 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + + L.OpenLibs() + if res := L.LoadString(c.Str("print('hello world')")); res != lua.OK { + println("error") + } + if res := L.PCall(0, 0, 0); res != lua.OK { + println("error") + } + +} diff --git a/c/lua/_demo/problem/pushinteger/pushinteger.go b/c/lua/_demo/problem/pushinteger/pushinteger.go new file mode 100644 index 00000000..815d3fe0 --- /dev/null +++ b/c/lua/_demo/problem/pushinteger/pushinteger.go @@ -0,0 +1,42 @@ +package main + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func main() { + L := lua.NewState() + defer L.Close() + + L.OpenLibs() + // TODO(zzy): fix push interger got stuck + code := c.Str(`function combineParams(x) + return x * 2 +end`) + if res := L.Dostring(code); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + L.GetGlobal(c.Str("combineParams")) + c.Printf(c.Str("stack lens:%d\n"), L.GetTop()) // stack lens:1 + + L.PushInteger(lua.Integer(42)) + pushed := L.ToInteger(-1) + + c.Printf(c.Str("pushed: %lld\n"), pushed) // pushed: 0 + c.Printf(c.Str("stack lens:%d\n"), L.GetTop()) // stack lens:1 + + // L.PushNumber(42) + // pushed := L.ToNumber(-1) + // c.Printf(c.Str("pushed: %f\n"), pushed) + + if res := L.PCall(1, 1, 0); res != lua.OK { + c.Printf(c.Str("error: %s\n"), L.ToString(-1)) + } + if res := L.IsNumber(-1); res != 0 { + result := L.ToInteger(-1) + c.Printf(c.Str("result %f"), result) + } +} diff --git a/c/lua/_demo/stack/stack.go b/c/lua/_demo/stack/stack.go new file mode 100644 index 00000000..ef1fc556 --- /dev/null +++ b/c/lua/_demo/stack/stack.go @@ -0,0 +1,80 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +// printStack prints the current stack of the given Lua state. +func printStack(L *lua.Lua_State, stateName *c.Char) { + top := L.GetTop() + // c.Printf(c.Str("%s stack (top=%d): "), c.GoStringData("sdasd"), top) + c.Printf(c.Str("%s stack (top=%d):"), stateName, top) + for i := 1; i <= int(top); i++ { + c.Printf(c.Str("%s "), L.ToString(c.Int(i))) + } + c.Printf(c.Str("\n")) +} + +func main() { + // Create a new Lua state and open libraries + L := lua.NewState() + defer L.Close() + L.OpenLibs() + + // Push initial values onto the stack + L.PushString(c.Str("Hello")) + L.PushString(c.Str("LLGO")) + L.PushNumber(2024) + + // Print initial stack + c.Printf(c.Str("Initial stack:\n")) + printStack(L, c.Str("L1")) + + // Use absindex to ensure the index is positive + idx := -2 + absIdx := L.AbsIndex(c.Int(idx)) + c.Printf(c.Str("Absolute index of 'LLGO': %d\n"), absIdx) + + // Copy 'LLGO' to the top of the stack + L.PushValue(absIdx) + c.Printf(c.Str("\nAfter pushing 'LLGO' to the top:\n")) + printStack(L, c.Str("L1")) + + // Rotate stack elements + L.Rotate(c.Int(1), c.Int(-1)) + c.Printf(c.Str("\nAfter rotating the stack:\n")) + printStack(L, c.Str("L1")) + + // Copy the top element to index 2 + L.Copy(c.Int(-1), c.Int(2)) + c.Printf(c.Str("\nAfter copying the top element to index 2:\n")) + printStack(L, c.Str("L1")) + + // Check if we can grow the stack + if L.CheckStack(c.Int(2)) == 0 { + c.Printf(c.Str("Cannot grow stack\n")) + return + } + + // Push additional elements + L.PushNumber(3.14) + L.PushString(c.Str("Lua")) + c.Printf(c.Str("\nAfter pushing more elements:\n")) + printStack(L, c.Str("L1")) + + // Set the top of the stack, clearing extra elements + L.SetTop(c.Int(5)) + c.Printf(c.Str("\nAfter setting top to 5:\n")) + printStack(L, c.Str("L1")) + + // Create a second Lua state + L1 := lua.NewState() + defer L1.Close() + + // Move two elements to the new state + L.Xmove(L1, c.Int(2)) + c.Printf(c.Str("\nAfter moving two elements to L1:\n")) + printStack(L, c.Str("L1")) + printStack(L1, c.Str("L2")) +} diff --git a/c/lua/lauxlib.go b/c/lua/lauxlib.go new file mode 100644 index 00000000..81bcd475 --- /dev/null +++ b/c/lua/lauxlib.go @@ -0,0 +1,101 @@ +package lua + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +// /* global table */ + +// /* extra error code for 'luaL_loadfilex' */ + +// /* key, in the registry, for table of loaded modules */ + +// /* key, in the registry, for table of preloaded loaders */ + +// /* predefined references */ + +// llgo:link (*Lua_State).LoadFilex C.luaL_loadfilex +func (L *Lua_State) LoadFilex(filename *c.Char, mode *c.Char) c.Int { return 0 } + +func (L *Lua_State) LoadFile(filename *c.Char) c.Int { return L.LoadFilex(filename, nil) } + +// llgo:link (*Lua_State).LoadString C.luaL_loadstring +func (L *Lua_State) LoadString(s *c.Char) c.Int { return 0 } + +//go:linkname NewState C.luaL_newstate +func NewState() *Lua_State + +// /* +// ** =============================================================== +// ** some useful macros +// ** =============================================================== +// */ + +func (L *Lua_State) DoFile(filename *c.Char) c.Int { + if loadResult := L.LoadFile(filename); loadResult != 0 { + return loadResult + } + return L.PCall(c.Int(0), c.Int(MULTRET), c.Int(0)) +} + +func (L *Lua_State) Dostring(str *c.Char) c.Int { + if loadResult := L.LoadString(str); loadResult != 0 { + return loadResult + } + return L.PCall(c.Int(0), c.Int(MULTRET), c.Int(0)) +} + +// /* +// ** Perform arithmetic operations on lua_Integer values with wrap-around +// ** semantics, as the Lua core does. +// */ + +// /* push the value used to represent failure/error */ + +// /* +// ** {====================================================== +// ** Generic Buffer manipulation +// ** ======================================================= +// */ + +// /* }====================================================== */ + +// /* +// ** {====================================================== +// ** File handles for IO library +// ** ======================================================= +// */ + +// /* +// ** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and +// ** initial structure 'luaL_Stream' (it may contain other fields +// ** after that initial structure). +// */ + +// #define LUA_FILEHANDLE "FILE*" + +// /* }====================================================== */ + +// /* +// ** {================================================================== +// ** "Abstraction Layer" for basic report of messages and errors +// ** =================================================================== +// */ + +// /* print a string */ + +// /* print a newline and flush the output */ + +// /* print an error message */ + +// /* }================================================================== */ + +// /* +// ** {============================================================ +// ** Compatibility with deprecated conversions +// ** ============================================================= +// */ + +// /* }============================================================ */ diff --git a/c/lua/lua.go b/c/lua/lua.go new file mode 100644 index 00000000..ab1bd960 --- /dev/null +++ b/c/lua/lua.go @@ -0,0 +1,269 @@ +package lua + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "link: $(pkg-config --libs lua); -llua -lm" +) + +// /* mark for precompiled code ('Lua') */ + +// /* option for multiple returns in 'lua_pcall' and 'lua_call' */ +const ( + MULTRET = -1 +) + +// /* +// ** Pseudo-indices +// ** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty +// ** space after that to help overflow detection) +// */ + +// /* thread status */ +const ( + OK = 0 + YIELD = 1 + ERRRUN = 2 + ERRSYNTAX = 3 + ERRMEM = 4 + ERRERR = 5 +) + +type Lua_State struct { + Unused [8]byte +} + +// /* +// ** basic types +// */ +const ( + TNONE = -1 + TNIL = 0 + TBOOLEAN = 1 + TLIGHTUSERDATA = 2 + TNUMBER = 3 + TSTRING = 4 + TTABLE = 5 + TFUNCTION = 6 + TUSERDATA = 7 + TTHREAD = 8 + NUMTYPES = 9 +) + +// /* minimum Lua stack available to a C function */ +const ( + MINSTACK = 20 +) + +// /* predefined values in the registry */ + +// /* type of numbers in Lua */ +type Number c.Double + +// /* type for integer functions */ +// TODO(zzy):consider dynamic size +type Integer c.LongLong + +// /* unsigned integer type */ +type Unsigned c.UlongLong + +// /* type for continuation-function contexts */ +// TODO(zzy): Context may not be c.Int +type KContext c.Int + +// /* +// ** Type for C functions registered with Lua +// */ + +// /* +// ** Type for continuation functions +// */ + +// TODO(zzy): KFunction does not currently support +type KFunction func(L *Lua_State, status c.Int, ctx KContext) c.Int + +// /* +// ** Type for functions that read/write blocks when loading/dumping Lua chunks +// */ + +// /* +// ** Type for memory-allocation functions +// */ + +// /* +// ** Type for warning functions +// */ + +// /* +// ** Type used by the debug API to collect debug information +// */ + +// /* +// ** Functions to be called by the debugger in specific events +// */ + +// /* +// ** generic extra include file +// */ + +// /* +// ** RCS ident string +// */ + +// /* +// ** state manipulation +// */ +// llgo:link (*Lua_State).Close C.lua_close +func (L *Lua_State) Close() {} + +// /* +// ** basic stack manipulation +// */ + +// llgo:link (*Lua_State).AbsIndex C.lua_absindex +func (L *Lua_State) AbsIndex(idx c.Int) c.Int { return 0 } + +// llgo:link (*Lua_State).GetTop C.lua_gettop +func (L *Lua_State) GetTop() c.Int { return 0 } + +// llgo:link (*Lua_State).SetTop C.lua_settop +func (L *Lua_State) SetTop(idx c.Int) {} + +// llgo:link (*Lua_State).PushValue C.lua_pushvalue +func (L *Lua_State) PushValue(idx c.Int) {} + +// llgo:link (*Lua_State).Rotate C.lua_rotate +func (L *Lua_State) Rotate(idx c.Int, n c.Int) {} + +// llgo:link (*Lua_State).Copy C.lua_copy +func (L *Lua_State) Copy(fromidx c.Int, toidx c.Int) {} + +// llgo:link (*Lua_State).CheckStack C.lua_checkstack +func (L *Lua_State) CheckStack(n c.Int) c.Int { return 0 } + +// llgo:link (*Lua_State).Xmove C.lua_xmove +func (L *Lua_State) Xmove(to *Lua_State, n c.Int) {} + +// /* +// ** access functions (stack -> C) +// */ + +// llgo:link (*Lua_State).ToNumberx C.lua_tonumberx +func (L *Lua_State) ToNumberx(idx c.Int, isnum *c.Int) Number { return 0 } + +// llgo:link (*Lua_State).IsNumber C.lua_isnumber +func (L *Lua_State) IsNumber(idx c.Int) c.Int { return 0 } + +// llgo:link (*Lua_State).IsString C.lua_isstring +func (L *Lua_State) IsString(idx c.Int) c.Int { return 0 } + +// llgo:link (*Lua_State).ToIntegerx C.lua_tointegerx +func (L *Lua_State) ToIntegerx(idx c.Int, isnum *c.Int) Integer { return 0 } + +// llgo:link (*Lua_State).ToLString C.lua_tolstring +func (L *Lua_State) ToLString(idx c.Int, len *c.Ulong) *c.Char { return nil } + +// /* +// ** Comparison and arithmetic functions +// */ + +// /* +// ** push functions (C -> stack) +// */ +// llgo:link (*Lua_State).PushNil C.lua_pushnil +func (L *Lua_State) PushNil() {} + +// llgo:link (*Lua_State).PushNumber C.lua_pushnumber +func (L *Lua_State) PushNumber(n Number) {} + +// TODO(zzy): will get stuck +// llgo:link (*Lua_Stage).PushInteger C.lua_pushinteger +func (L *Lua_State) PushInteger(n Integer) {} + +// llgo:link (*Lua_State).PushLString C.lua_pushlstring +func (L *Lua_State) PushLString(s *c.Char, len c.Ulong) *c.Char { + return nil +} + +// llgo:link (*Lua_State).PushString C.lua_pushstring +func (L *Lua_State) PushString(s *c.Char) *c.Char { + return nil +} + +// llgo:link (*Lua_State).PushFString C.lua_pushfstring +func (L *Lua_State) PushFString(format *c.Char, __llgo_va_list ...any) *c.Char { return nil } + +// /* +// ** get functions (Lua -> stack) +// */ +// int (lua_getglobal) (lua_State *L, const char *name); +// llgo:link (*Lua_State).GetGlobal C.lua_getglobal +func (L *Lua_State) GetGlobal(name *c.Char) c.Int { return 0 } + +// /* +// ** set functions (stack -> Lua) +// */ + +// /* +// ** 'load' and 'call' functions (load and run Lua code) +// */ + +// llgo:link (*Lua_State).PCallk C.lua_pcallk +func (L *Lua_State) PCallk(nargs c.Int, nresults c.Int, errfunc c.Int, ctx KContext, k *KFunction) c.Int { + return 0 +} + +func (L *Lua_State) PCall(nargs c.Int, nresults c.Int, errfunc c.Int) c.Int { + return L.PCallk(nargs, nresults, errfunc, KContext(c.Int(0)), nil) +} + +// /* +// ** coroutine functions +// */ + +// /* +// ** Warning-related functions +// */ + +// /* +// ** garbage-collection function and options +// */ + +// /* +// ** miscellaneous functions +// */ + +// /* +// ** {============================================================== +// ** some useful macros +// ** =============================================================== +// */ +// /* }============================================================== */ + +func (L *Lua_State) ToNumber(idx c.Int) Number { return L.ToNumberx(idx, nil) } +func (L *Lua_State) ToString(idx c.Int) *c.Char { return L.ToLString(idx, nil) } +func (L *Lua_State) ToInteger(idx c.Int) Integer { return L.ToIntegerx(idx, nil) } + +// /* +// ** {============================================================== +// ** compatibility macros +// ** =============================================================== +// */ +// /* }============================================================== */ + +// /* +// ** {====================================================================== +// ** Debug API +// ** ======================================================================= +// */ +// /* +// ** Event codes +// */ +// /* +// ** Event masks +// */ +// /* }====================================================================== */ diff --git a/c/lua/lualib.go b/c/lua/lualib.go new file mode 100644 index 00000000..ef49a3bf --- /dev/null +++ b/c/lua/lualib.go @@ -0,0 +1,8 @@ +package lua + +import ( + _ "unsafe" +) + +// llgo:link (*Lua_State).OpenLibs C.luaL_openlibs +func (L *Lua_State) OpenLibs() {}