// Copy matching Lua table entries to a struct, given the struct type // and the index on the Lua stack. func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { was_ptr := t.Kind() == reflect.Ptr if was_ptr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(strings.Title(key)) if f.IsValid() { val := luaToGoValue(L, f.Type(), -1) f.Set(val) } L.Pop(1) } if was_ptr { return s.Interface() } return s.Elem().Interface() }
func popMap(L *lua.State) map[string]interface{} { m := make(map[string]interface{}) L.PushNil() for L.Next(-2) != 0 { if L.IsString(-2) { var v interface{} if L.IsBoolean(-1) { v = L.ToBoolean(-1) } else if L.IsNumber(-1) { var t float64 t = L.ToNumber(-1) if t == float64(int64(t)) { v = int(t) } else { v = t } } else if L.IsString(-1) { v = L.ToString(-1) } else if L.IsNil(-1) { v = nil } else if L.IsTable(-1) { v = popMap(L) } m[L.ToString(-2)] = v } L.Pop(1) } return m }
func (p *Plugin) apiProcessNew(l *lua.State) int { callback := luar.NewLuaObject(l, 1) command := l.ToString(2) args := make([]string, l.GetTop()-2) for i := 3; i <= l.GetTop(); i++ { args[i-3] = l.ToString(i) } proc := &process{ cmd: exec.Command(command, args...), } go func() { var str string bytes, err := proc.cmd.Output() if err == nil { if bytes != nil { str = string(bytes) } p.callValue(callback, proc.cmd.ProcessState.Success(), str) } else { p.callValue(callback, false, "") } callback.Close() }() obj := luar.NewLuaObjectFromValue(l, proc) obj.Push() obj.Close() return 1 }
func outputNumbersToStrings(L *lua.State) { L.GetGlobal("output") if !L.IsTable(-1) { L.NewTable() L.SetGlobal("output") } L.GetField(-1, "tags") if L.IsTable(-1) { // First key. L.PushNil() for L.Next(-2) != 0 { // Use 'key' at index -2 and 'value' at index -1. if L.IsString(-2) && L.IsString(-1) { // Convert numbers to strings. L.ToString(-1) L.SetField(-3, L.ToString(-2)) } else { // Remove 'value' and keep 'key' for next iteration. L.Pop(1) } } } L.Pop(1) L.Pop(1) }
// ProxyMethod pushes the proxy method on the stack. // // Argument: proxy // // Returns: method (function) func ProxyMethod(L *lua.State) int { if !isValueProxy(L, 1) { L.PushNil() return 1 } v, _ := valueOfProxy(L, 1) name := L.ToString(2) pushGoMethod(L, name, v) return 1 }
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 struct__newindex(L *lua.State) int { st, t := valueOfProxy(L, 1) name := L.ToString(2) if t.Kind() == reflect.Ptr { st = st.Elem() } field := st.FieldByName(name) val := LuaToGo(L, field.Type(), 3) field.Set(valueOf(val)) return 0 }
func LuaIntRmColumn(L *lua.State) int { luaAssertArgnum(L, 1, "rmcolumn()") name := L.ToString(1) entry := GetEntryFromLua(L, CURSOR, "rmcolumn()") entry.RemoveColumn(name) tl := GetTasklistFromLua(L) if !tl.luaFlags.cursorCloned { tl.luaFlags.cursorEdited = true } return 0 }
func copyTableToStruct(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { if t == nil { RaiseError(L, "type argument must be non-nill") } wasPtr := t.Kind() == reflect.Ptr if wasPtr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() // See copyTableToSlice. ptr := L.ToPointer(idx) if !luaIsEmpty(L, idx) { if wasPtr { visited[ptr] = s.Interface() } else { visited[ptr] = s.Elem().Interface() } } // Associate Lua keys with Go fields: tags have priority over matching field // name. fields := map[string]string{} st := ref.Type() for i := 0; i < ref.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("lua") if tag != "" { fields[tag] = field.Name continue } fields[field.Name] = field.Name } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(fields[key]) if f.CanSet() && f.IsValid() { val := reflect.ValueOf(luaToGo(L, f.Type(), -1, visited)) f.Set(val) } L.Pop(1) } if wasPtr { return s.Interface() } return s.Elem().Interface() }
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) } }
func LuaIntPriorityQuery(L *lua.State) int { luaAssertArgnum(L, 1, "priorityq()") priority := L.ToString(1) L.CheckStack(1) tl := GetTasklistFromLua(L) tl.luaState.PushGoStruct(&SimpleExpr{":priority", "=", priority, nil, ParsePriority(priority), ""}) return 1 }
func LuaIntStringFunction(L *lua.State, name string, n int, fn func(tl *Tasklist, argv []string) int) int { luaAssertArgnum(L, n, name) argv := make([]string, 0) for i := 1; i <= n; i++ { argv = append(argv, L.ToString(i)) } L.Pop(n) L.CheckStack(1) tl := GetTasklistFromLua(L) return fn(tl, argv) }
func struct__index(L *lua.State) int { st, t := valueOfProxy(L, 1) name := L.ToString(2) est := st if t.Kind() == reflect.Ptr { est = st.Elem() } ret := est.FieldByName(name) if !ret.IsValid() { // no such field, try for method? callGoMethod(L, name, st) } else { GoToLua(L, ret.Type(), ret) } return 1 }
func struct__newindex(L *lua.State) int { st, t := valueOfProxy(L, 1) name := L.ToString(2) if t.Kind() == reflect.Ptr { st = st.Elem() } field := st.FieldByName(name) assertValid(L, field, st, name, "field") val := luaToGoValue(L, field.Type(), 3) if isPointerToPrimitive(field) { field.Elem().Set(val) } else { field.Set(val) } return 0 }
func slice__index(L *lua.State) int { slice, _ := valueOfProxy(L, 1) if L.IsNumber(2) { idx := L.ToInteger(2) ret := slice.Index(idx - 1) GoToLua(L, ret.Type(), ret) } else { name := L.ToString(2) switch name { case "Slice": L.PushGoFunction(slice_slice) default: fmt.Println("unknown slice method") } } return 1 }
func LuaIntParseDateTime(L *lua.State) int { luaAssertArgnum(L, 1, "parsedatetime()") L.CheckStack(1) input := L.ToString(-1) tl := GetTasklistFromLua(L) out, _ := ParseDateTime(input, tl.GetTimezone()) if out != nil { L.PushInteger(int64(out.Unix())) } else { L.PushInteger(0) } return 1 }
// Copy matching Lua table entries to a struct, given the struct type // and the index on the Lua stack. func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { was_ptr := t.Kind() == reflect.Ptr if was_ptr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() // Associate Lua keys with Go fields: tags have priority over matching field // name. fields := map[string]string{} st := ref.Type() for i := 0; i < ref.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("lua") if tag != "" { fields[tag] = field.Name continue } fields[field.Name] = field.Name } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(fields[key]) if f.CanSet() && f.IsValid() { val := luaToGoValue(L, f.Type(), -1) f.Set(val) } L.Pop(1) } if was_ptr { return s.Interface() } return s.Elem().Interface() }
func LuaIntColumnQuery(L *lua.State) int { if (L.GetTop() != 1) && (L.GetTop() != 3) { panic(errors.New("Wrong number of arguments to columnq")) return 0 } name := L.ToString(1) op := "" value := "" if L.GetTop() == 3 { op = L.ToString(2) value = L.ToString(3) L.Pop(3) } else { L.Pop(1) } if name[0] == ':' { panic(errors.New("Column name can not start with ':'")) return 0 } L.CheckStack(1) tl := GetTasklistFromLua(L) tl.luaState.PushGoStruct(&SimpleExpr{name, op, value, nil, 0, ""}) return 1 }
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 LuaIntSearch(L *lua.State) int { if (L.GetTop() < 1) || (L.GetTop() > 2) { panic(errors.New("Wrong number of arguments to search()")) return 0 } L.CheckStack(2) tl := GetTasklistFromLua(L) if !tl.luaFlags.freeCursor { panic(errors.New("search() function only available on a free cursor")) return 0 } query := L.ToString(1) var luaClausable Clausable = nil if L.GetTop() == 2 { luaClausable = GetQueryObject(tl, 2) } theselect, _, _, _, _, _, _, perr := tl.ParseSearch(query, luaClausable) Must(perr) entries, serr := tl.Retrieve(theselect, "", false) Must(serr) Logf(INFO, "Searching from lua interface <%s> clausable: <%v> yields %d results\n", query, luaClausable, len(entries)) r := []string{} for _, entry := range entries { r = append(r, entry.Id()) } PushStringVec(L, r) return 1 }
func LuaIntSplit(L *lua.State) int { if L.GetTop() < 2 { panic(errors.New("Wrong number of arguments to split()")) return 0 } instr := L.ToString(1) sepstr := L.ToString(2) n := -1 if L.GetTop() == 3 { n = L.ToInteger(3) } if L.GetTop() > 3 { panic(errors.New("Wrong number of arguments to split()")) return 0 } PushStringVec(L, strings.SplitN(instr, sepstr, n)) return 1 }
func LuaIntVisit(L *lua.State) int { L.CheckStack(1) tl := GetTasklistFromLua(L) luaAssertNotFreeCursor(tl, "visit()") id := L.ToString(1) Logf(DEBUG, "Lua visiting: <%s>\n", id) var error interface{} = nil { defer func() { if rerr := recover(); rerr != nil { error = rerr } }() cursor := tl.Get(id) tl.SetEntryInLua(CURSOR, cursor) } if error != nil { tl.SetEntryInLua(CURSOR, nil) } 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 }
func luaToGo(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { var value interface{} var kind reflect.Kind if t != nil { if t.Kind() == reflect.Ptr { kind = t.Elem().Kind() } else if t.Kind() == reflect.Interface { // Let the Lua type drive the conversion. t = nil } else { kind = t.Kind() } } switch L.Type(idx) { case lua.LUA_TNIL: if t == nil { return nil } switch kind { default: value = reflect.Zero(t).Interface() } case lua.LUA_TBOOLEAN: if t == nil { kind = reflect.Bool } switch kind { case reflect.Bool: ptr := new(bool) *ptr = L.ToBoolean(idx) value = *ptr default: value = reflect.Zero(t).Interface() } case lua.LUA_TSTRING: if t == nil { kind = reflect.String } switch kind { case reflect.String: tos := L.ToString(idx) ptr := new(string) *ptr = tos value = *ptr default: value = reflect.Zero(t).Interface() } case lua.LUA_TNUMBER: if t == nil { // Infering the type here (e.g. int if round value) would break backward // compatibility and drift away from Lua's design: the numeric type is // specified at compile time. kind = reflect.Float64 } switch kind { case reflect.Float64: ptr := new(float64) *ptr = L.ToNumber(idx) value = *ptr case reflect.Float32: ptr := new(float32) *ptr = float32(L.ToNumber(idx)) value = *ptr case reflect.Int: ptr := new(int) *ptr = int(L.ToNumber(idx)) value = *ptr case reflect.Int8: ptr := new(int8) *ptr = int8(L.ToNumber(idx)) value = *ptr case reflect.Int16: ptr := new(int16) *ptr = int16(L.ToNumber(idx)) value = *ptr case reflect.Int32: ptr := new(int32) *ptr = int32(L.ToNumber(idx)) value = *ptr case reflect.Int64: ptr := new(int64) *ptr = int64(L.ToNumber(idx)) value = *ptr case reflect.Uint: ptr := new(uint) *ptr = uint(L.ToNumber(idx)) value = *ptr case reflect.Uint8: ptr := new(uint8) *ptr = uint8(L.ToNumber(idx)) value = *ptr case reflect.Uint16: ptr := new(uint16) *ptr = uint16(L.ToNumber(idx)) value = *ptr case reflect.Uint32: ptr := new(uint32) *ptr = uint32(L.ToNumber(idx)) value = *ptr case reflect.Uint64: ptr := new(uint64) *ptr = uint64(L.ToNumber(idx)) value = *ptr default: value = reflect.Zero(t).Interface() } case lua.LUA_TTABLE: if t == nil { kind = reflect.Interface } fallthrough default: istable := L.IsTable(idx) // If we don't know the type and the Lua object is userdata, // then it might be a proxy for a Go object. Otherwise wrap // it up as a LuaObject. if t == nil && !istable { if isValueProxy(L, idx) { v, _ := valueOfProxy(L, idx) return v.Interface() } return NewLuaObject(L, idx) } switch kind { case reflect.Array: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToSlice(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Slice: // if we get a table, then copy its values to a new slice if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToSlice(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Map: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToMap(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Struct: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToStruct(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Interface: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } // We have to make an executive decision here: tables with non-zero // length are assumed to be slices! if L.ObjLen(idx) > 0 { value = copyTableToSlice(L, nil, idx, visited) } else { value = copyTableToMap(L, nil, idx, visited) } } else if L.IsNumber(idx) { value = L.ToNumber(idx) } else if L.IsString(idx) { value = L.ToString(idx) } else if L.IsBoolean(idx) { value = L.ToBoolean(idx) } else if L.IsNil(idx) { return nil } else { value = mustUnwrapProxy(L, idx) } default: value = mustUnwrapProxy(L, idx) } } return value }
// Convert a Lua value 'idx' on the stack to the Go value of desired type 't'. Handles // numerical and string types in a straightforward way, and will convert tables to // either map or slice types. func LuaToGo(L *lua.State, t reflect.Type, idx int) interface{} { var value interface{} var kind reflect.Kind if t != nil { // let the Lua type drive the conversion... if t.Kind() == reflect.Ptr { kind = t.Elem().Kind() } else if t.Kind() == reflect.Interface { t = nil } else { kind = t.Kind() } } switch L.Type(idx) { case lua.LUA_TNIL: if t == nil { return nil } switch kind { default: cannotConvert(L, idx, "nil", kind, t) } case lua.LUA_TBOOLEAN: if t == nil { kind = reflect.Bool } switch kind { case reflect.Bool: { ptr := new(bool) *ptr = bool(L.ToBoolean(idx)) value = *ptr } default: cannotConvert(L, idx, "bool", kind, t) } case lua.LUA_TSTRING: if t == nil { kind = reflect.String } switch kind { case reflect.String: { tos := L.ToString(idx) ptr := new(string) *ptr = tos value = *ptr } default: cannotConvert(L, idx, "string", kind, t) } case lua.LUA_TNUMBER: if t == nil { kind = reflect.Float64 } switch kind { case reflect.Float64: { ptr := new(float64) *ptr = L.ToNumber(idx) value = *ptr } case reflect.Float32: { ptr := new(float32) *ptr = float32(L.ToNumber(idx)) value = *ptr } case reflect.Int: { ptr := new(int) *ptr = int(L.ToNumber(idx)) value = *ptr } case reflect.Int8: { ptr := new(int8) *ptr = int8(L.ToNumber(idx)) value = *ptr } case reflect.Int16: { ptr := new(int16) *ptr = int16(L.ToNumber(idx)) value = *ptr } case reflect.Int32: { ptr := new(int32) *ptr = int32(L.ToNumber(idx)) value = *ptr } case reflect.Int64: { ptr := new(int64) *ptr = int64(L.ToNumber(idx)) value = *ptr } case reflect.Uint: { ptr := new(uint) *ptr = uint(L.ToNumber(idx)) value = *ptr } case reflect.Uint8: { ptr := new(uint8) *ptr = uint8(L.ToNumber(idx)) value = *ptr } case reflect.Uint16: { ptr := new(uint16) *ptr = uint16(L.ToNumber(idx)) value = *ptr } case reflect.Uint32: { ptr := new(uint32) *ptr = uint32(L.ToNumber(idx)) value = *ptr } case reflect.Uint64: { ptr := new(uint64) *ptr = uint64(L.ToNumber(idx)) value = *ptr } default: cannotConvert(L, idx, "number", kind, t) } case lua.LUA_TTABLE: if t == nil { kind = reflect.Interface } fallthrough default: istable := L.IsTable(idx) // if we don't know the type and the Lua object is userdata, // then it might be a proxy for a Go object. Otherwise wrap // it up as a LuaObject. if t == nil && !istable { if isValueProxy(L, idx) { return unwrapProxy(L, idx) } else { return NewLuaObject(L, idx) } } switch kind { case reflect.Slice: { // if we get a table, then copy its values to a new slice if istable { value = CopyTableToSlice(L, t, idx) } else { value = unwrapProxyOrComplain(L, idx) } } case reflect.Map: { if istable { value = CopyTableToMap(L, t, idx) } else { value = unwrapProxyOrComplain(L, idx) } } case reflect.Struct: { if istable { value = CopyTableToStruct(L, t, idx) } else { value = unwrapProxyOrComplain(L, idx) } } case reflect.Interface: { if istable { // have to make an executive decision here: tables with non-zero // length are assumed to be slices! if L.ObjLen(idx) > 0 { value = CopyTableToSlice(L, nil, idx) } else { value = CopyTableToMap(L, nil, idx) } } else if L.IsNumber(idx) { value = L.ToNumber(idx) } else if L.IsString(idx) { value = L.ToString(idx) } else if L.IsBoolean(idx) { value = L.ToBoolean(idx) } else if L.IsNil(idx) { return nil } else { value = unwrapProxyOrComplain(L, idx) } } default: value = unwrapProxyOrComplain(L, idx) //cannotConvert(L,idx,"unknown",kind,t) } } return value }
func interface__index(L *lua.State) int { st, _ := valueOfProxy(L, 1) name := L.ToString(2) callGoMethod(L, name, st) return 1 }
// raise a Lua error from Go code func RaiseError(L *lua.State, msg string) { L.Where(1) pos := L.ToString(-1) L.Pop(1) panic(L.NewError(pos + " " + msg)) }
// function that lua will call before aborting. // we override this because normally lua longjmps, // but that breaks go's defer/panic. so we just panic. func LuaAtPanic(L *lua.State) int { panic(errors.New(L.ToString(-1))) return 0 }
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 }