package lua import ( "fmt" "io" "os" "runtime" "strconv" "strings" ) /* basic functions {{{ */ func OpenBase(L *LState) int { global := L.Get(GlobalsIndex).(*LTable) L.SetGlobal("_G", global) L.SetGlobal("_VERSION", LString(PackageName+" "+PackageVersion)) basemod := L.RegisterModule("_G", baseFuncs) global.RawSetString("ipairs", L.NewClosure(baseIpairs, L.NewFunction(ipairsaux))) global.RawSetString("pairs", L.NewClosure(basePairs, L.NewFunction(pairsaux))) L.Push(basemod) return 1 } var baseFuncs = map[string]LGFunction{ "assert": baseAssert, "collectgarbage": baseCollectGarbage, "dofile": baseDoFile, "error": baseError, "getfenv": baseGetFEnv, "getmetatable": baseGetMetatable, "load": baseLoad, "loadfile": baseLoadFile, "loadstring": baseLoadString, "next": baseNext, "pcall": basePCall, "print": basePrint, "rawequal": baseRawEqual, "rawget": baseRawGet, "rawset": baseRawSet, "select": baseSelect, "_printregs": base_PrintRegs, "setfenv": baseSetFEnv, "setmetatable": baseSetMetatable, "tonumber": baseToNumber, "tostring": baseToString, "type": baseType, "unpack": baseUnpack, "xpcall": baseXPCall, // loadlib "module": loModule, "require": loRequire, } func baseAssert(L *LState) int { if !L.ToBool(1) { L.RaiseError(L.OptString(2, "assertion failed!")) return 0 } return L.GetTop() } func baseCollectGarbage(L *LState) int { runtime.GC() return 0 } func baseDoFile(L *LState) int { src := L.ToString(1) top := L.GetTop() fn, err := L.LoadFile(src) if err != nil { L.Push(LString(err.Error())) L.Panic(L) } L.Push(fn) L.Call(0, MultRet) return L.GetTop() - top } func baseError(L *LState) int { obj := L.CheckAny(1) level := L.OptInt(2, 1) L.Error(obj, level) return 0 } func baseGetFEnv(L *LState) int { var value LValue if L.GetTop() == 0 { value = LNumber(1) } else { value = L.Get(1) } if fn, ok := value.(*LFunction); ok { if !fn.IsG { L.Push(fn.Env) } else { L.Push(L.G.Global) } return 1 } if number, ok := value.(LNumber); ok { level := int(float64(number)) if level <= 0 { L.Push(L.Env) } else { cf := L.currentFrame for i := 0; i < level && cf != nil; i++ { cf = cf.Parent } if cf == nil || cf.Fn.IsG { L.Push(L.G.Global) } else { L.Push(cf.Fn.Env) } } return 1 } L.Push(L.G.Global) return 1 } func baseGetMetatable(L *LState) int { L.Push(L.GetMetatable(L.CheckAny(1))) return 1 } func ipairsaux(L *LState) int { tb := L.CheckTable(1) i := L.CheckInt(2) i++ v := tb.RawGetInt(i) if v == LNil { return 0 } else { L.Pop(1) L.Push(LNumber(i)) L.Push(LNumber(i)) L.Push(v) return 2 } } func baseIpairs(L *LState) int { tb := L.CheckTable(1) L.Push(L.Get(UpvalueIndex(1))) L.Push(tb) L.Push(LNumber(0)) return 3 } func loadaux(L *LState, reader io.Reader, chunkname string) int { if fn, err := L.Load(reader, chunkname); err != nil { L.Push(LNil) L.Push(LString(err.Error())) return 2 } else { L.Push(fn) return 1 } } func baseLoad(L *LState) int { fn := L.CheckFunction(1) chunkname := L.OptString(2, "?") top := L.GetTop() buf := []string{} for { L.SetTop(top) L.Push(fn) L.Call(0, 1) ret := L.reg.Pop() if ret == LNil { break } else if LVCanConvToString(ret) { str := ret.String() if len(str) > 0 { buf = append(buf, string(str)) } else { break } } else { L.Push(LNil) L.Push(LString("reader function must return a string")) return 2 } } return loadaux(L, strings.NewReader(strings.Join(buf, "")), chunkname) } func baseLoadFile(L *LState) int { var reader io.Reader var chunkname string var err error if L.GetTop() < 1 { reader = os.Stdin chunkname = "" } else { chunkname = L.CheckString(1) reader, err = os.Open(chunkname) if err != nil { L.Push(LNil) L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname))) return 2 } defer reader.(*os.File).Close() } return loadaux(L, reader, chunkname) } func baseLoadString(L *LState) int { return loadaux(L, strings.NewReader(L.CheckString(1)), L.OptString(2, "")) } func baseNext(L *LState) int { tb := L.CheckTable(1) index := LNil if L.GetTop() >= 2 { index = L.Get(2) } key, value := tb.Next(index) if key == LNil { L.Push(LNil) return 1 } L.Push(key) L.Push(value) return 2 } func pairsaux(L *LState) int { tb := L.CheckTable(1) key, value := tb.Next(L.Get(2)) if key == LNil { return 0 } else { L.Pop(1) L.Push(key) L.Push(key) L.Push(value) return 2 } } func basePairs(L *LState) int { tb := L.CheckTable(1) L.Push(L.Get(UpvalueIndex(1))) L.Push(tb) L.Push(LNil) return 3 } func basePCall(L *LState) int { L.CheckFunction(1) nargs := L.GetTop() - 1 if err := L.PCall(nargs, MultRet, nil); err != nil { L.Push(LFalse) if aerr, ok := err.(*ApiError); ok { L.Push(aerr.Object) } else { L.Push(LString(err.Error())) } return 2 } else { L.Insert(LTrue, 1) return L.GetTop() } } func basePrint(L *LState) int { top := L.GetTop() for i := 1; i <= top; i++ { fmt.Print(L.ToStringMeta(L.Get(i)).String()) if i != top { fmt.Print("\t") } } fmt.Println("") return 0 } func base_PrintRegs(L *LState) int { L.printReg() return 0 } func baseRawEqual(L *LState) int { if L.CheckAny(1) == L.CheckAny(2) { L.Push(LTrue) } else { L.Push(LFalse) } return 1 } func baseRawGet(L *LState) int { L.Push(L.RawGet(L.CheckTable(1), L.CheckAny(2))) return 1 } func baseRawSet(L *LState) int { L.RawSet(L.CheckTable(1), L.CheckAny(2), L.CheckAny(3)) return 0 } func baseSelect(L *LState) int { L.CheckTypes(1, LTNumber, LTString) switch lv := L.Get(1).(type) { case LNumber: idx := int(lv) num := L.reg.Top() - L.indexToReg(int(lv)) - 1 if idx < 0 { num++ } return num case LString: if string(lv) != "#" { L.ArgError(1, "invalid string '"+string(lv)+"'") } L.Push(LNumber(L.GetTop() - 1)) return 1 } return 0 } func baseSetFEnv(L *LState) int { var value LValue if L.GetTop() == 0 { value = LNumber(1) } else { value = L.Get(1) } env := L.CheckTable(2) if fn, ok := value.(*LFunction); ok { if fn.IsG { L.RaiseError("cannot change the environment of given object") } else { fn.Env = env L.Push(fn) return 1 } } if number, ok := value.(LNumber); ok { level := int(float64(number)) if level <= 0 { L.Env = env return 0 } cf := L.currentFrame for i := 0; i < level && cf != nil; i++ { cf = cf.Parent } if cf == nil || cf.Fn.IsG { L.RaiseError("cannot change the environment of given object") } else { cf.Fn.Env = env L.Push(cf.Fn) return 1 } } L.RaiseError("cannot change the environment of given object") return 0 } func baseSetMetatable(L *LState) int { L.CheckTypes(2, LTNil, LTTable) obj := L.Get(1) if obj == LNil { L.RaiseError("cannot set metatable to a nil object.") } mt := L.Get(2) if m := L.metatable(obj, true); m != LNil { if tb, ok := m.(*LTable); ok && tb.RawGetString("__metatable") != LNil { L.RaiseError("cannot change a protected metatable") } } L.SetMetatable(obj, mt) L.SetTop(1) return 1 } func baseToNumber(L *LState) int { base := L.OptInt(2, 10) noBase := L.Get(2) == LNil switch lv := L.CheckAny(1).(type) { case LNumber: L.Push(lv) case LString: str := strings.Trim(string(lv), " \n\t") if strings.Index(str, ".") > -1 { if v, err := strconv.ParseFloat(str, LNumberBit); err != nil { L.Push(LNil) } else { L.Push(LNumber(v)) } } else { if noBase && strings.HasPrefix(strings.ToLower(str), "0x") { base, str = 16, str[2:] // Hex number } if v, err := strconv.ParseInt(str, base, LNumberBit); err != nil { L.Push(LNil) } else { L.Push(LNumber(v)) } } default: L.Push(LNil) } return 1 } func baseToString(L *LState) int { v1 := L.CheckAny(1) L.Push(L.ToStringMeta(v1)) return 1 } func baseType(L *LState) int { L.Push(LString(L.CheckAny(1).Type().String())) return 1 } func baseUnpack(L *LState) int { tb := L.CheckTable(1) start := L.OptInt(2, 1) end := L.OptInt(3, tb.Len()) for i := start; i <= end; i++ { L.Push(tb.RawGetInt(i)) } ret := end - start + 1 if ret < 0 { return 0 } return ret } func baseXPCall(L *LState) int { fn := L.CheckFunction(1) errfunc := L.CheckFunction(2) top := L.GetTop() L.Push(fn) if err := L.PCall(0, MultRet, errfunc); err != nil { L.Push(LFalse) if aerr, ok := err.(*ApiError); ok { L.Push(aerr.Object) } else { L.Push(LString(err.Error())) } return 2 } else { L.Insert(LTrue, top+1) return L.GetTop() - top } } /* }}} */ /* load lib {{{ */ func loModule(L *LState) int { name := L.CheckString(1) loaded := L.GetField(L.Get(RegistryIndex), "_LOADED") tb := L.GetField(loaded, name) if _, ok := tb.(*LTable); !ok { tb = L.FindTable(L.Get(GlobalsIndex).(*LTable), name, 1) if tb == LNil { L.RaiseError("name conflict for module: %v", name) } L.SetField(loaded, name, tb) } if L.GetField(tb, "_NAME") == LNil { L.SetField(tb, "_M", tb) L.SetField(tb, "_NAME", LString(name)) names := strings.Split(name, ".") pname := "" if len(names) > 1 { pname = strings.Join(names[:len(names)-1], ".") + "." } L.SetField(tb, "_PACKAGE", LString(pname)) } caller := L.currentFrame.Parent if caller == nil { L.RaiseError("no calling stack.") } else if caller.Fn.IsG { L.RaiseError("module() can not be called from GFunctions.") } L.SetFEnv(caller.Fn, tb) top := L.GetTop() for i := 2; i <= top; i++ { L.Push(L.Get(i)) L.Push(tb) L.Call(1, 0) } L.Push(tb) return 1 } var loopdetection = &LUserData{} func loRequire(L *LState) int { name := L.CheckString(1) loaded := L.GetField(L.Get(RegistryIndex), "_LOADED") lv := L.GetField(loaded, name) if LVAsBool(lv) { if lv == loopdetection { L.RaiseError("loop or previous error loading module: %s", name) } L.Push(lv) return 1 } loaders, ok := L.GetField(L.Get(RegistryIndex), "_LOADERS").(*LTable) if !ok { L.RaiseError("package.loaders must be a table") } messages := []string{} var modasfunc LValue for i := 1; ; i++ { loader := L.RawGetInt(loaders, i) if loader == LNil { L.RaiseError("module %s not found:\n\t%s, ", name, strings.Join(messages, "\n\t")) } L.Push(loader) L.Push(LString(name)) L.Call(1, 1) ret := L.reg.Pop() switch retv := ret.(type) { case *LFunction: modasfunc = retv goto loopbreak case LString: messages = append(messages, string(retv)) } } loopbreak: L.SetField(loaded, name, loopdetection) L.Push(modasfunc) L.Push(LString(name)) L.Call(1, 1) ret := L.reg.Pop() modv := L.GetField(loaded, name) if ret != LNil && modv == loopdetection { L.SetField(loaded, name, ret) L.Push(ret) } else if modv == loopdetection { L.SetField(loaded, name, LTrue) L.Push(LTrue) } else { L.Push(modv) } return 1 } /* }}} */ //