// Also for arrays. func copySliceToTable(L *lua.State, vslice reflect.Value, visited visitor) int { ref := vslice for vslice.Kind() == reflect.Ptr { // For arrays. vslice = vslice.Elem() } if vslice.IsValid() && (vslice.Kind() == reflect.Slice || vslice.Kind() == reflect.Array) { n := vslice.Len() L.CreateTable(n, 0) if vslice.Kind() == reflect.Slice { visited.mark(vslice) } else if ref.Kind() == reflect.Ptr { visited.mark(ref) } for i := 0; i < n; i++ { L.PushInteger(int64(i + 1)) v := vslice.Index(i) if isNil(v) { v = nullv } goToLua(L, nil, v, true, visited) L.SetTable(-3) } return 1 } L.PushNil() L.PushString("not a slice/array") return 2 }
func copyStructToTable(L *lua.State, vstruct reflect.Value, visited visitor) int { // If 'vstruct' is a pointer to struct, use the pointer to mark as visited. ref := vstruct for vstruct.Kind() == reflect.Ptr { vstruct = vstruct.Elem() } if vstruct.IsValid() && vstruct.Type().Kind() == reflect.Struct { n := vstruct.NumField() L.CreateTable(n, 0) if ref.Kind() == reflect.Ptr { visited.mark(ref) } for i := 0; i < n; i++ { st := vstruct.Type() field := st.Field(i) key := field.Name tag := field.Tag.Get("lua") if tag != "" { key = tag } goToLua(L, nil, reflect.ValueOf(key), true, visited) v := vstruct.Field(i) goToLua(L, nil, v, true, visited) L.SetTable(-3) } return 1 } L.PushNil() L.PushString("not a struct") return 2 }
// Registers a Go function as a global variable and add it to the sandbox. func sandboxRegister(L *lua.State, name string, f interface{}) { goToLua(L, name, f) L.PushString(registryWhitelist) L.GetTable(lua.LUA_REGISTRYINDEX) L.GetGlobal(name) L.SetField(-2, name) }
func GetTableInt(L *lua.State, name string) int { // Remember to check stack for 1 extra location L.PushString(name) L.GetTable(-2) r := L.ToInteger(-1) L.Pop(1) return r }
func LuaTableGetString(L *lua.State, key string) string { L.PushString(key) L.GetTable(-2) r := "" if !L.IsNil(-1) { r = L.ToString(-1) } L.Pop(1) return r }
// GoLuaFunc converts an arbitrary Go function into a Lua-compatible GoFunction. // There are special optimized cases for functions that go from strings to strings, // and doubles to doubles, but otherwise Go // reflection is used to provide a generic wrapper function func GoLuaFunc(L *lua.State, fun interface{}) lua.LuaGoFunction { switch f := fun.(type) { case func(*lua.State) int: return f case func(string) string: return func(L *lua.State) int { L.PushString(f(L.ToString(1))) return 1 } case func(float64) float64: return func(L *lua.State) int { L.PushNumber(f(L.ToNumber(1))) return 1 } default: } var funv reflect.Value switch ff := fun.(type) { case reflect.Value: funv = ff default: funv = valueOf(fun) } funt := funv.Type() targs, tout := functionArgRetTypes(funt) return func(L *lua.State) int { var lastT reflect.Type orig_targs := targs isVariadic := funt.IsVariadic() if isVariadic { n := len(targs) lastT = targs[n-1].Elem() targs = targs[0 : n-1] } args := make([]reflect.Value, len(targs)) for i, t := range targs { val := LuaToGo(L, t, i+1) args[i] = valueOfNil(val) //println(i,args[i].String()) } if isVariadic { n := L.GetTop() for i := len(targs) + 1; i <= n; i++ { ival := LuaToGo(L, lastT, i) args = append(args, valueOfNil(ival)) } targs = orig_targs } resv := callGo(L, funv, args) for i, val := range resv { GoToLua(L, tout[i], val, false) } return len(resv) } }
func sandboxCompile(L *lua.State, registryIndex string, name, code string) { L.PushString(registryIndex) L.GetTable(lua.LUA_REGISTRYINDEX) L.PushString(name) err := L.LoadString(code) if err != 0 { log.Fatalf("%s: %s", name, L.ToString(-1)) L.Pop(2) } else { L.SetTable(-3) } }
// copy a Go slice to a Lua table func CopySliceToTable(L *lua.State, vslice reflect.Value) int { if vslice.IsValid() && vslice.Type().Kind() == reflect.Slice { n := vslice.Len() L.CreateTable(n, 0) for i := 0; i < n; i++ { L.PushInteger(int64(i + 1)) GoToLua(L, nil, vslice.Index(i)) L.SetTable(-3) } return 1 } else { L.PushNil() L.PushString("not a slice!") } return 2 }
// copy a Go map to a Lua table func CopyMapToTable(L *lua.State, vmap reflect.Value) int { if vmap.IsValid() && vmap.Type().Kind() == reflect.Map { n := vmap.Len() L.CreateTable(0, n) for _, key := range vmap.MapKeys() { val := vmap.MapIndex(key) GoToLua(L, nil, key) GoToLua(L, nil, val) L.SetTable(-3) } return 1 } else { L.PushNil() L.PushString("not a map!") } return 2 }
func copyMapToTable(L *lua.State, vmap reflect.Value, visited visitor) int { if vmap.IsValid() && vmap.Type().Kind() == reflect.Map { n := vmap.Len() L.CreateTable(0, n) visited.mark(vmap) for _, key := range vmap.MapKeys() { v := vmap.MapIndex(key) goToLua(L, nil, key, false, visited) if isNil(v) { v = nullv } goToLua(L, nil, v, true, visited) L.SetTable(-3) } return 1 } L.PushNil() L.PushString("not a map!") return 2 }
func MessThingPronounsubMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { thing := checkThing(state, 1) text := state.CheckString(2) for code, pronoun := range thing.Pronouns() { lowerCode := fmt.Sprintf(`%%%s`, code) upperCode := fmt.Sprintf(`%%%s`, strings.ToUpper(code)) text = strings.Replace(text, lowerCode, pronoun, -1) text = strings.Replace(text, upperCode, strings.ToTitle(pronoun), -1) } text = strings.Replace(text, `%n`, thing.Name, -1) text = strings.Replace(text, `%N`, thing.Name, -1) state.Pop(2) // ( udataThing str -- ) state.PushString(text) // ( -- str' ) return 1 }) return 1 }
func pushMap(L *lua.State, m map[string]interface{}, lower bool) { L.CreateTable(0, len(m)) for k, v := range m { if lower { L.PushString(strings.ToLower(k)) } else { L.PushString(k) } switch t := v.(type) { case string: L.PushString(t) case int64: L.PushInteger(t) case int: L.PushInteger(int64(t)) case float64: L.PushNumber(t) case bool: L.PushBoolean(t) case map[string]interface{}: pushMap(L, t, false) default: L.PushNil() } L.SetTable(-3) } }
func LuaIntGetterSetterFunction(fname string, L *lua.State, getter func(tl *Tasklist, entry *Entry) string, setter func(tl *Tasklist, entry *Entry, value string)) int { argNum := L.GetTop() if argNum == 0 { entry := GetEntryFromLua(L, CURSOR, fname) tl := GetTasklistFromLua(L) L.PushString(getter(tl, entry)) return 1 } else if argNum == 1 { value := L.ToString(1) entry := GetEntryFromLua(L, CURSOR, fname) tl := GetTasklistFromLua(L) setter(tl, entry, value) if !tl.luaFlags.cursorCloned { tl.luaFlags.cursorEdited = true } return 0 } panic(errors.New(fmt.Sprintf("Incorrect number of argoments to %s (only 0 or 1 accepted)", fname))) return 0 }
func LuaIntColumn(L *lua.State) int { argNum := L.GetTop() if argNum == 1 { name := L.ToString(1) entry := GetEntryFromLua(L, CURSOR, "column()") L.PushString(entry.Column(name)) return 1 } else if argNum == 2 { name := L.ToString(1) value := L.ToString(2) entry := GetEntryFromLua(L, CURSOR, "column()") entry.SetColumn(name, value) tl := GetTasklistFromLua(L) if !tl.luaFlags.cursorCloned { tl.luaFlags.cursorEdited = true } return 0 } panic(errors.New("Incorrect number of arguments to column (only 1 or 2 accepted)")) return 0 }
// Copy a Go struct to a Lua table. nils in both slices and structs // are represented as luar.null. Defines luar.struct2table // Use tags to set field names. func CopyStructToTable(L *lua.State, vstruct reflect.Value) int { if vstruct.IsValid() && vstruct.Type().Kind() == reflect.Struct { n := vstruct.NumField() L.CreateTable(n, 0) for i := 0; i < n; i++ { st := vstruct.Type() field := st.Field(i) key := field.Name tag := field.Tag.Get("lua") if tag != "" { key = tag } GoToLua(L, nil, reflect.ValueOf(key), true) v := vstruct.Field(i) GoToLua(L, nil, v, true) L.SetTable(-3) } return 1 } else { L.PushNil() L.PushString("not a struct!") } return 2 }
func TableFormat(state *lua.State) int { state.CheckType(1, lua.LUA_TTABLE) state.CheckType(2, lua.LUA_TTABLE) // ( ??? -- tbl tblFields ) log.Println("Formatting a table by a table") printStackTypes(state) numFields := int(state.ObjLen(-1)) fields := make([]string, numFields) maxFieldLen := make(map[string]int) for i := 0; i < numFields; i++ { state.RawGeti(-1, i+1) // ( tbl tblFields -- tbl tblFields strHeader ) fieldName := state.ToString(-1) fields[i] = fieldName maxFieldLen[fieldName] = len(fieldName) state.Pop(1) // ( tbl tblFields strField -- tbl tblFields ) } state.Pop(1) // ( tbl tblFields -- tbl ) log.Println("Slurped up the fields list (table #2)") printStackTypes(state) numRows := int(state.ObjLen(-1)) rows := make([]map[string]string, numRows) for i := 0; i < numRows; i++ { state.RawGeti(-1, i+1) // ( tbl -- tbl tblRow ) row := make(map[string]string) for _, field := range fields { state.PushString(field) // ( tbl tblRow -- tbl tblRow strField ) state.RawGet(-2) // ( tbl tblRow strField -- tbl tblRow tblField ) row[field] = state.ToString(-1) if maxFieldLen[field] < len(row[field]) { maxFieldLen[field] = len(row[field]) } state.Pop(1) // ( tbl tblRow tblField -- tbl tblRow ) } rows[i] = row state.Pop(1) // ( tbl tblRow -- tbl ) } state.Pop(1) // ( tbl -- ) log.Println("Slurped up the data table (table #1)") printStackTypes(state) // %5s %10s %13s fmtStrings := make([]string, numFields) for i, field := range fields { fmtStrings[i] = fmt.Sprintf("%%-%ds", maxFieldLen[field]) } fmtString := strings.Join(fmtStrings, " ") log.Println("Figured out the format string:", fmtString) rowStrings := make([]string, numRows+1) rowFields := make([]interface{}, numFields) for i, row := range rows { for j, field := range fields { rowFields[j] = row[field] } rowStrings[i+1] = fmt.Sprintf(fmtString, rowFields...) } for i := 0; i < numFields; i++ { rowFields[i] = strings.Title(fields[i]) } rowStrings[0] = fmt.Sprintf(fmtString, rowFields...) log.Println("Yay formatted all the strings") formattedTable := strings.Join(rowStrings, "\n") state.PushString(formattedTable) // ( -- str ) log.Println("All done formatting this table!") printStackTypes(state) return 1 }
// Push a Go value 'val' of type 't' on the Lua stack. // If we haven't been given a concrete type, use the type of the value // and unbox any interfaces. You can force slices and maps to be copied // over as tables by setting 'dontproxify' to true. func GoToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool) { if !val.IsValid() { L.PushNil() return } if t == nil { t = val.Type() } if t.Kind() == reflect.Interface && !val.IsNil() { // unbox interfaces! val = valueOf(val.Interface()) t = val.Type() } if t.Kind() == reflect.Ptr { t = t.Elem() } kind := t.Kind() // underlying type is 'primitive' ? wrap it as a proxy! if isPrimitiveDerived(t, kind) != nil { makeValueProxy(L, val, cINTERFACE_META) return } switch kind { case reflect.Float64, reflect.Float32: { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: { L.PushNumber(float64(val.Uint())) } case reflect.String: { L.PushString(val.String()) } case reflect.Bool: { L.PushBoolean(val.Bool()) } case reflect.Slice: { if !dontproxify { makeValueProxy(L, val, cSLICE_META) } else { CopySliceToTable(L, val) } } case reflect.Map: { if !dontproxify { makeValueProxy(L, val, cMAP_META) } else { CopyMapToTable(L, val) } } case reflect.Struct: { if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if v, ok := val.Interface().(*LuaObject); ok { v.Push() } else { if (val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface) && !val.Elem().IsValid() { L.PushNil() return } makeValueProxy(L, val, cSTRUCT_META) } } default: { if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, val, cINTERFACE_META) } } } }
func proxy__tostring(L *lua.State) int { obj, _ := valueOfProxy(L, 1) L.PushString(obj.Type().String()) return 1 }
func SetTableIntString(L *lua.State, idx int64, value string) { L.PushInteger(idx) L.PushString(value) L.SetTable(-3) }
// TODO: Check if we really need multiple pointer levels since pointer methods // can be called on non-pointers. func goToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool, visited visitor) { if !val.IsValid() { L.PushNil() return } // Unbox interface. if val.Kind() == reflect.Interface && !val.IsNil() { val = reflect.ValueOf(val.Interface()) } // Follow pointers if not proxifying. We save the original pointer Value in case we proxify. ptrVal := val for val.Kind() == reflect.Ptr { val = val.Elem() } if !val.IsValid() { L.PushNil() return } // As a special case, we always proxify nullv, the empty element for slices and maps. if val.CanInterface() && val.Interface() == nullv.Interface() { makeValueProxy(L, val, cInterfaceMeta) return } switch val.Kind() { case reflect.Float64, reflect.Float32: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Uint())) } case reflect.String: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cStringMeta) } else { L.PushString(val.String()) } case reflect.Bool: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cInterfaceMeta) } else { L.PushBoolean(val.Bool()) } case reflect.Complex128, reflect.Complex64: makeValueProxy(L, ptrVal, cComplexMeta) case reflect.Array: // It needs be a pointer to be a proxy, otherwise values won't be settable. if !dontproxify && ptrVal.Kind() == reflect.Ptr { makeValueProxy(L, ptrVal, cSliceMeta) } else { // See the case of struct. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copySliceToTable(L, ptrVal, visited) } case reflect.Slice: if !dontproxify { makeValueProxy(L, ptrVal, cSliceMeta) } else { if visited.push(val) { return } copySliceToTable(L, val, visited) } case reflect.Map: if !dontproxify { makeValueProxy(L, ptrVal, cMapMeta) } else { if visited.push(val) { return } copyMapToTable(L, val, visited) } case reflect.Struct: if !dontproxify && ptrVal.Kind() == reflect.Ptr { if ptrVal.CanInterface() { switch v := ptrVal.Interface().(type) { case error: L.PushString(v.Error()) case *LuaObject: v.Push() default: makeValueProxy(L, ptrVal, cStructMeta) } } else { makeValueProxy(L, ptrVal, cStructMeta) } } else { // Use ptrVal instead of val to detect cycles from the very first element, if a pointer. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copyStructToTable(L, ptrVal, visited) } case reflect.Chan: makeValueProxy(L, ptrVal, cChannelMeta) case reflect.Func: L.PushGoFunction(goLuaFunc(L, val)) default: if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, ptrVal, cInterfaceMeta) } } }
// Push a Go value 'val' of type 't' on the Lua stack. // If we haven't been given a concrete type, use the type of the value // and unbox any interfaces. func GoToLua(L *lua.State, t reflect.Type, val reflect.Value) { proxify := true if t == nil { t = val.Type() if t.Kind() == reflect.Interface { // unbox interfaces! val = valueOf(val.Interface()) t = val.Type() } proxify = false } if t.Kind() == reflect.Ptr { t = t.Elem() } switch t.Kind() { case reflect.Float64: case reflect.Float32: { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8: { L.PushNumber(float64(val.Uint())) } case reflect.String: { L.PushString(val.String()) } case reflect.Bool: { L.PushBoolean(val.Bool()) } case reflect.Slice: { if proxify { makeValueProxy(L, val, SLICE_META) } else { CopySliceToTable(L, val) } } case reflect.Map: { if proxify { makeValueProxy(L, val, MAP_META) } else { CopyMapToTable(L, val) } } case reflect.Struct: { makeValueProxy(L, val, STRUCT_META) } default: { if val.IsNil() { L.PushNil() } else { makeValueProxy(L, val, INTERFACE_META) } } } }
// 'exist' is optional. func run(L *lua.State, registryIndex string, code string, input *inputInfo, output *outputInfo, exist *inputInfo) error { // Restore the sandbox. err := L.DoString(luaRestoreSandbox) if err != nil { log.Fatal("Cannot load function to restore sandbox", err) } L.PushString(registryWhitelist) L.GetTable(lua.LUA_REGISTRYINDEX) err = L.Call(1, 0) if err != nil { log.Fatal("Failed to restore sandbox", err) } goToLua(L, "input", *input) goToLua(L, "output", *output) if exist != nil { goToLua(L, "existinfo", *exist) } // Shortcut (mostly for prescript and postscript). L.GetGlobal("input") L.GetField(-1, "tags") L.SetGlobal("i") L.Pop(1) L.GetGlobal("output") L.GetField(-1, "tags") L.SetGlobal("o") L.Pop(1) // Call the compiled script. L.PushString(registryIndex) L.GetTable(lua.LUA_REGISTRYINDEX) L.PushString(code) if L.IsTable(-2) { L.GetTable(-2) if L.IsFunction(-1) { err := L.Call(0, 0) if err != nil { L.SetTop(0) return fmt.Errorf("%s", err) } } else { L.Pop(1) } } else { L.Pop(1) } L.Pop(1) // Allow tags to be numbers for convenience. outputNumbersToStrings(L) L.GetGlobal("output") r := luar.LuaToGo(L, reflect.TypeOf(*output), -1) L.Pop(1) *output = r.(outputInfo) return nil }
func SetTableInt(L *lua.State, name string, value int64) { // Remember to check stack for 2 extra locations L.PushString(name) L.PushInteger(value) L.SetTable(-3) }
func pushValue(state *lua.State, value interface{}) error { switch v := value.(type) { default: return fmt.Errorf("An item of unknown type was included in the environment or arguments of a Lua call (skipping it): %v", value) case nil: log.Println("Pushing nil onto lua stack") state.PushNil() case string: log.Println("Pushing string onto lua stack") state.PushString(v) case int: log.Println("Pushing int onto lua stack") state.PushInteger(int64(v)) case int64: log.Println("Pushing int64 onto lua stack") state.PushInteger(v) case float64: log.Println("Pushing float64 onto lua stack") state.PushNumber(v) case bool: log.Println("Pushing bool onto lua stack") state.PushBoolean(v) case map[string]interface{}: log.Println("Pushing map[string]interface{} onto lua stack") state.CreateTable(0, len(v)) for name, value := range v { err := pushValue(state, value) if err != nil { // error means nothing was added to stack. So pop our new table so *we* leave nothing added to the stack. state.Pop(1) return err } state.SetField(-2, name) } // then leave the table on the stack case ThingType: // These are singleton sentinel values, so load them from Lua-land. state.GetGlobal("world") state.GetField(-1, strings.Title(v.String())) state.Remove(-2) case *Thing: log.Println("Pushing *Thing onto lua stack") return pushValue(state, v.Id) case ThingId: log.Println("Pushing ThingId onto lua stack") // We're pushing a ThingId, so make a new userdata for it, with the Thing metatable. userdata := state.NewUserdata(uintptr(unsafe.Sizeof(int64(0)))) thingPtr := (*int64)(userdata) *thingPtr = int64(v) if !state.IsUserdata(-1) { log.Println("!!! HOGAD JUST PUSHED NEW USERDATA BUT IT ISN'T OMG !!!") } log.Println("Pushed ThingId", *thingPtr, "onto lua stack") // Now make it act like a Thing. state.LGetMetaTable(ThingMetaTableName) // ( udata -- udata mtbl ) state.SetMetaTable(-2) // ( udata mtbl -- udata ) // Let's just check that it's that, for sures. if !state.IsUserdata(-1) { log.Println("!!! WOOP WOOP DID NOT SET METATABLE RIGHT :( !!!") } } return nil }
func MessThingName(state *lua.State, thing *Thing) int { state.PushString(thing.Name) return 1 }