summaryrefslogtreecommitdiff
path: root/lua_common.go
diff options
context:
space:
mode:
authorunwox <me@unwox.com>2025-09-09 16:37:32 +0600
committerunwox <me@unwox.com>2025-09-09 16:41:39 +0600
commitc26739ce130bc40ded6ff9dfea1be1eba1a2e4eb (patch)
tree83a150bbea75a174418dae25628c09f8b5b42106 /lua_common.go
parent40abca541e331c213a5cc94c676d58b3ca84b7ad (diff)
support ntables in Lua.Scan
Diffstat (limited to 'lua_common.go')
-rw-r--r--lua_common.go139
1 files changed, 139 insertions, 0 deletions
diff --git a/lua_common.go b/lua_common.go
new file mode 100644
index 0000000..4e58d69
--- /dev/null
+++ b/lua_common.go
@@ -0,0 +1,139 @@
+package main
+
+import (
+ "reflect"
+ "slices"
+ "fmt"
+ "runtime/cgo"
+)
+
+// Scan scans values from the Lua stack into vars according to their types.
+func (l *Lua) Scan(vars ...any) error {
+ slices.Reverse(vars)
+ for i, v := range vars {
+ t := reflect.TypeOf(v)
+ // unwrap pointers
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ tk := t.Kind()
+ if t.Name() == "LuaRef" {
+ if l.IsFunction(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be function",
+ len(vars)-i,
+ )
+ }
+ *v.(*LuaRef) = l.PopToRef()
+ } else if t.String() == "cgo.Handle" {
+ if l.IsNumber(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be Go handler",
+ len(vars)-i,
+ )
+ }
+ *v.(*cgo.Handle) = cgo.Handle(uintptr(l.ToInt(-1)))
+ l.Pop(1)
+ } else if tk == reflect.String {
+ if l.IsString(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be string",
+ len(vars)-i,
+ )
+ }
+ *v.(*string) = l.ToString(-1)
+ l.Pop(1)
+ } else if tk == reflect.Int {
+ if l.IsNumber(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be number",
+ len(vars)-i,
+ )
+ }
+ *v.(*int) = l.ToInt(-1)
+ l.Pop(1)
+ } else if tk == reflect.Map && t.Key().Kind() == reflect.String {
+ // TODO: should be possible to use maps with any types
+ // of value, not only strings.
+ vm, _ := v.(*map[string]string)
+ l.PushNil()
+ for l.Next() {
+ if !l.IsString(-1) {
+ return fmt.Errorf(
+ "map arg #%d must only have string values",
+ len(vars)-i,
+ )
+ }
+ if !l.IsString(-2) {
+ return fmt.Errorf(
+ "map arg #%d must only have string keys",
+ len(vars)-i,
+ )
+ }
+ v := l.ToString(-1)
+ l.Pop(1)
+ // We must not pop the item key from the stack
+ // because otherwise C.lua_next won't work
+ // properly.
+ k := l.ToString(-1)
+ (*vm)[k] = v
+ }
+ l.Pop(1)
+ } else if tk == reflect.Slice && t.Elem().Kind() == reflect.Interface {
+ if !l.IsTable(-1) {
+ return fmt.Errorf(
+ "passed arg #%d must be a table",
+ len(vars)-i,
+ )
+ }
+ va, _ := v.(*[]any)
+
+ // Check if table contains element "n" with
+ // a count of elements. Useful when an array may
+ // contain nils.
+ l.GetTableItem("n")
+ n := 0
+ if l.IsNumber(-1) {
+ n = l.ToInt(-1)
+ }
+ l.Pop(1)
+
+ scanAndAppend := func () error {
+ var v any = nil
+ if l.IsString(-1) {
+ v = l.ToString(-1)
+ } else if l.IsNumber(-1) {
+ v = l.ToInt(-1)
+ } else if l.IsBoolean(-1) {
+ v = l.ToBoolean(-1)
+ } else if l.IsNil(-1) {
+ v = nil
+ } else {
+ return fmt.Errorf("unknown value in array")
+ }
+ l.Pop(1)
+ (*va) = append((*va), v)
+ return nil
+ }
+ if n > 0 {
+ for i := 0; i < n; i++ {
+ l.RawGetI(-1, i + 1)
+ if err := scanAndAppend(); err != nil {
+ return err
+ }
+ }
+ } else {
+ l.PushNil()
+ for l.Next() {
+ if err := scanAndAppend(); err != nil {
+ return err
+ }
+ }
+ }
+ l.Pop(1)
+ } else {
+ return fmt.Errorf("unknown var type: %s", t)
+ }
+ }
+ return nil
+}