github.com/assemblaj/gopher-lua@v0.0.0-20221116224352-d57295a0d9e8/baselib.go (about) 1 package lua 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "runtime" 8 "strconv" 9 "strings" 10 ) 11 12 /* basic functions {{{ */ 13 14 func OpenBase(L *LState) int { 15 global := L.Get(GlobalsIndex).(*LTable) 16 L.SetGlobal("_G", global) 17 L.SetGlobal("_VERSION", LString(LuaVersion)) 18 L.SetGlobal("_GOPHER_LUA_VERSION", LString(PackageName+" "+PackageVersion)) 19 basemod := L.RegisterModule("_G", baseFuncs) 20 global.RawSetString("ipairs", L.NewClosure(baseIpairs, L.NewFunction(ipairsaux))) 21 global.RawSetString("pairs", L.NewClosure(basePairs, L.NewFunction(pairsaux))) 22 L.Push(basemod) 23 return 1 24 } 25 26 var baseFuncs = map[string]LGFunction{ 27 "assert": baseAssert, 28 "collectgarbage": baseCollectGarbage, 29 "dofile": baseDoFile, 30 "error": baseError, 31 "getfenv": baseGetFEnv, 32 "getmetatable": baseGetMetatable, 33 "load": baseLoad, 34 "loadfile": baseLoadFile, 35 "loadstring": baseLoadString, 36 "next": baseNext, 37 "pcall": basePCall, 38 "print": basePrint, 39 "rawequal": baseRawEqual, 40 "rawget": baseRawGet, 41 "rawset": baseRawSet, 42 "select": baseSelect, 43 "_printregs": base_PrintRegs, 44 "setfenv": baseSetFEnv, 45 "setmetatable": baseSetMetatable, 46 "tonumber": baseToNumber, 47 "tostring": baseToString, 48 "type": baseType, 49 "unpack": baseUnpack, 50 "xpcall": baseXPCall, 51 // loadlib 52 "module": loModule, 53 "require": loRequire, 54 // hidden features 55 "newproxy": baseNewProxy, 56 } 57 58 func baseAssert(L *LState) int { 59 if !L.ToBool(1) { 60 L.RaiseError(L.OptString(2, "assertion failed!")) 61 return 0 62 } 63 return L.GetTop() 64 } 65 66 func baseCollectGarbage(L *LState) int { 67 runtime.GC() 68 return 0 69 } 70 71 func baseDoFile(L *LState) int { 72 src := L.ToString(1) 73 top := L.GetTop() 74 fn, err := L.LoadFile(src) 75 if err != nil { 76 L.Push(LString(err.Error())) 77 L.Panic(L) 78 } 79 L.Push(fn) 80 L.Call(0, MultRet) 81 return L.GetTop() - top 82 } 83 84 func baseError(L *LState) int { 85 obj := L.CheckAny(1) 86 level := L.OptInt(2, 1) 87 L.Error(obj, level) 88 return 0 89 } 90 91 func baseGetFEnv(L *LState) int { 92 var value LValue 93 if L.GetTop() == 0 { 94 value = LNumber(1) 95 } else { 96 value = L.Get(1) 97 } 98 99 if fn, ok := value.(*LFunction); ok { 100 if !fn.IsG { 101 L.Push(fn.Env) 102 } else { 103 L.Push(L.G.Global) 104 } 105 return 1 106 } 107 108 if number, ok := value.(LNumber); ok { 109 level := int(float64(number)) 110 if level <= 0 { 111 L.Push(L.Env) 112 } else { 113 cf := L.currentFrame 114 for i := 0; i < level && cf != nil; i++ { 115 cf = cf.Parent 116 } 117 if cf == nil || cf.Fn.IsG { 118 L.Push(L.G.Global) 119 } else { 120 L.Push(cf.Fn.Env) 121 } 122 } 123 return 1 124 } 125 126 L.Push(L.G.Global) 127 return 1 128 } 129 130 func baseGetMetatable(L *LState) int { 131 L.Push(L.GetMetatable(L.CheckAny(1))) 132 return 1 133 } 134 135 func ipairsaux(L *LState) int { 136 tb := L.CheckTable(1) 137 i := L.CheckInt(2) 138 i++ 139 v := tb.RawGetInt(i) 140 if v == LNil { 141 return 0 142 } else { 143 L.Pop(1) 144 L.Push(LNumber(i)) 145 L.Push(LNumber(i)) 146 L.Push(v) 147 return 2 148 } 149 } 150 151 func baseIpairs(L *LState) int { 152 tb := L.CheckTable(1) 153 L.Push(L.Get(UpvalueIndex(1))) 154 L.Push(tb) 155 L.Push(LNumber(0)) 156 return 3 157 } 158 159 func loadaux(L *LState, reader io.Reader, chunkname string) int { 160 if fn, err := L.Load(reader, chunkname); err != nil { 161 L.Push(LNil) 162 L.Push(LString(err.Error())) 163 return 2 164 } else { 165 L.Push(fn) 166 return 1 167 } 168 } 169 170 func baseLoad(L *LState) int { 171 fn := L.CheckFunction(1) 172 chunkname := L.OptString(2, "?") 173 top := L.GetTop() 174 buf := []string{} 175 for { 176 L.SetTop(top) 177 L.Push(fn) 178 L.Call(0, 1) 179 ret := L.reg.Pop() 180 if ret == LNil { 181 break 182 } else if LVCanConvToString(ret) { 183 str := ret.String() 184 if len(str) > 0 { 185 buf = append(buf, string(str)) 186 } else { 187 break 188 } 189 } else { 190 L.Push(LNil) 191 L.Push(LString("reader function must return a string")) 192 return 2 193 } 194 } 195 return loadaux(L, strings.NewReader(strings.Join(buf, "")), chunkname) 196 } 197 198 func baseLoadFile(L *LState) int { 199 var reader io.Reader 200 var chunkname string 201 var err error 202 if L.GetTop() < 1 { 203 reader = os.Stdin 204 chunkname = "<stdin>" 205 } else { 206 chunkname = L.CheckString(1) 207 reader, err = os.Open(chunkname) 208 if err != nil { 209 L.Push(LNil) 210 L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname))) 211 return 2 212 } 213 defer reader.(*os.File).Close() 214 } 215 return loadaux(L, reader, chunkname) 216 } 217 218 func baseLoadString(L *LState) int { 219 return loadaux(L, strings.NewReader(L.CheckString(1)), L.OptString(2, "<string>")) 220 } 221 222 func baseNext(L *LState) int { 223 tb := L.CheckTable(1) 224 index := LNil 225 if L.GetTop() >= 2 { 226 index = L.Get(2) 227 } 228 key, value := tb.Next(index) 229 if key == LNil { 230 L.Push(LNil) 231 return 1 232 } 233 L.Push(key) 234 L.Push(value) 235 return 2 236 } 237 238 func pairsaux(L *LState) int { 239 tb := L.CheckTable(1) 240 key, value := tb.Next(L.Get(2)) 241 if key == LNil { 242 return 0 243 } else { 244 L.Pop(1) 245 L.Push(key) 246 L.Push(key) 247 L.Push(value) 248 return 2 249 } 250 } 251 252 func basePairs(L *LState) int { 253 tb := L.CheckTable(1) 254 L.Push(L.Get(UpvalueIndex(1))) 255 L.Push(tb) 256 L.Push(LNil) 257 return 3 258 } 259 260 func basePCall(L *LState) int { 261 L.CheckAny(1) 262 v := L.Get(1) 263 if v.Type() != LTFunction && L.GetMetaField(v, "__call").Type() != LTFunction { 264 L.Push(LFalse) 265 L.Push(LString("attempt to call a " + v.Type().String() + " value")) 266 return 2 267 } 268 nargs := L.GetTop() - 1 269 if err := L.PCall(nargs, MultRet, nil); err != nil { 270 L.Push(LFalse) 271 if aerr, ok := err.(*ApiError); ok { 272 L.Push(aerr.Object) 273 } else { 274 L.Push(LString(err.Error())) 275 } 276 return 2 277 } else { 278 L.Insert(LTrue, 1) 279 return L.GetTop() 280 } 281 } 282 283 func basePrint(L *LState) int { 284 top := L.GetTop() 285 for i := 1; i <= top; i++ { 286 fmt.Print(L.ToStringMeta(L.Get(i)).String()) 287 if i != top { 288 fmt.Print("\t") 289 } 290 } 291 fmt.Println("") 292 return 0 293 } 294 295 func base_PrintRegs(L *LState) int { 296 L.printReg() 297 return 0 298 } 299 300 func baseRawEqual(L *LState) int { 301 if L.CheckAny(1) == L.CheckAny(2) { 302 L.Push(LTrue) 303 } else { 304 L.Push(LFalse) 305 } 306 return 1 307 } 308 309 func baseRawGet(L *LState) int { 310 L.Push(L.RawGet(L.CheckTable(1), L.CheckAny(2))) 311 return 1 312 } 313 314 func baseRawSet(L *LState) int { 315 L.RawSet(L.CheckTable(1), L.CheckAny(2), L.CheckAny(3)) 316 return 0 317 } 318 319 func baseSelect(L *LState) int { 320 L.CheckTypes(1, LTNumber, LTString) 321 switch lv := L.Get(1).(type) { 322 case LNumber: 323 idx := int(lv) 324 num := L.GetTop() 325 if idx < 0 { 326 idx = num + idx 327 } else if idx > num { 328 idx = num 329 } 330 if 1 > idx { 331 L.ArgError(1, "index out of range") 332 } 333 return num - idx 334 case LString: 335 if string(lv) != "#" { 336 L.ArgError(1, "invalid string '"+string(lv)+"'") 337 } 338 L.Push(LNumber(L.GetTop() - 1)) 339 return 1 340 } 341 return 0 342 } 343 344 func baseSetFEnv(L *LState) int { 345 var value LValue 346 if L.GetTop() == 0 { 347 value = LNumber(1) 348 } else { 349 value = L.Get(1) 350 } 351 env := L.CheckTable(2) 352 353 if fn, ok := value.(*LFunction); ok { 354 if fn.IsG { 355 L.RaiseError("cannot change the environment of given object") 356 } else { 357 fn.Env = env 358 L.Push(fn) 359 return 1 360 } 361 } 362 363 if number, ok := value.(LNumber); ok { 364 level := int(float64(number)) 365 if level <= 0 { 366 L.Env = env 367 return 0 368 } 369 370 cf := L.currentFrame 371 for i := 0; i < level && cf != nil; i++ { 372 cf = cf.Parent 373 } 374 if cf == nil || cf.Fn.IsG { 375 L.RaiseError("cannot change the environment of given object") 376 } else { 377 cf.Fn.Env = env 378 L.Push(cf.Fn) 379 return 1 380 } 381 } 382 383 L.RaiseError("cannot change the environment of given object") 384 return 0 385 } 386 387 func baseSetMetatable(L *LState) int { 388 L.CheckTypes(2, LTNil, LTTable) 389 obj := L.Get(1) 390 if obj == LNil { 391 L.RaiseError("cannot set metatable to a nil object.") 392 } 393 mt := L.Get(2) 394 if m := L.metatable(obj, true); m != LNil { 395 if tb, ok := m.(*LTable); ok && tb.RawGetString("__metatable") != LNil { 396 L.RaiseError("cannot change a protected metatable") 397 } 398 } 399 L.SetMetatable(obj, mt) 400 L.SetTop(1) 401 return 1 402 } 403 404 func baseToNumber(L *LState) int { 405 base := L.OptInt(2, 10) 406 noBase := L.Get(2) == LNil 407 408 switch lv := L.CheckAny(1).(type) { 409 case LNumber: 410 L.Push(lv) 411 case LString: 412 str := strings.Trim(string(lv), " \n\t") 413 if strings.Index(str, ".") > -1 { 414 if v, err := strconv.ParseFloat(str, LNumberBit); err != nil { 415 L.Push(LNil) 416 } else { 417 L.Push(LNumber(v)) 418 } 419 } else { 420 if noBase && strings.HasPrefix(strings.ToLower(str), "0x") { 421 base, str = 16, str[2:] // Hex number 422 } 423 if v, err := strconv.ParseInt(str, base, LNumberBit); err != nil { 424 L.Push(LNil) 425 } else { 426 L.Push(LNumber(v)) 427 } 428 } 429 default: 430 L.Push(LNil) 431 } 432 return 1 433 } 434 435 func baseToString(L *LState) int { 436 v1 := L.CheckAny(1) 437 L.Push(L.ToStringMeta(v1)) 438 return 1 439 } 440 441 func baseType(L *LState) int { 442 L.Push(LString(L.CheckAny(1).Type().String())) 443 return 1 444 } 445 446 func baseUnpack(L *LState) int { 447 tb := L.CheckTable(1) 448 start := L.OptInt(2, 1) 449 end := L.OptInt(3, tb.Len()) 450 for i := start; i <= end; i++ { 451 L.Push(tb.RawGetInt(i)) 452 } 453 ret := end - start + 1 454 if ret < 0 { 455 return 0 456 } 457 return ret 458 } 459 460 func baseXPCall(L *LState) int { 461 fn := L.CheckFunction(1) 462 errfunc := L.CheckFunction(2) 463 464 top := L.GetTop() 465 L.Push(fn) 466 if err := L.PCall(0, MultRet, errfunc); err != nil { 467 L.Push(LFalse) 468 if aerr, ok := err.(*ApiError); ok { 469 L.Push(aerr.Object) 470 } else { 471 L.Push(LString(err.Error())) 472 } 473 return 2 474 } else { 475 L.Insert(LTrue, top+1) 476 return L.GetTop() - top 477 } 478 } 479 480 /* }}} */ 481 482 /* load lib {{{ */ 483 484 func loModule(L *LState) int { 485 name := L.CheckString(1) 486 loaded := L.GetField(L.Get(RegistryIndex), "_LOADED") 487 tb := L.GetField(loaded, name) 488 if _, ok := tb.(*LTable); !ok { 489 tb = L.FindTable(L.Get(GlobalsIndex).(*LTable), name, 1) 490 if tb == LNil { 491 L.RaiseError("name conflict for module: %v", name) 492 } 493 L.SetField(loaded, name, tb) 494 } 495 if L.GetField(tb, "_NAME") == LNil { 496 L.SetField(tb, "_M", tb) 497 L.SetField(tb, "_NAME", LString(name)) 498 names := strings.Split(name, ".") 499 pname := "" 500 if len(names) > 1 { 501 pname = strings.Join(names[:len(names)-1], ".") + "." 502 } 503 L.SetField(tb, "_PACKAGE", LString(pname)) 504 } 505 506 caller := L.currentFrame.Parent 507 if caller == nil { 508 L.RaiseError("no calling stack.") 509 } else if caller.Fn.IsG { 510 L.RaiseError("module() can not be called from GFunctions.") 511 } 512 L.SetFEnv(caller.Fn, tb) 513 514 top := L.GetTop() 515 for i := 2; i <= top; i++ { 516 L.Push(L.Get(i)) 517 L.Push(tb) 518 L.Call(1, 0) 519 } 520 L.Push(tb) 521 return 1 522 } 523 524 var loopdetection = &LUserData{} 525 526 func loRequire(L *LState) int { 527 name := L.CheckString(1) 528 loaded := L.GetField(L.Get(RegistryIndex), "_LOADED") 529 lv := L.GetField(loaded, name) 530 if LVAsBool(lv) { 531 if lv == loopdetection { 532 L.RaiseError("loop or previous error loading module: %s", name) 533 } 534 L.Push(lv) 535 return 1 536 } 537 loaders, ok := L.GetField(L.Get(RegistryIndex), "_LOADERS").(*LTable) 538 if !ok { 539 L.RaiseError("package.loaders must be a table") 540 } 541 messages := []string{} 542 var modasfunc LValue 543 for i := 1; ; i++ { 544 loader := L.RawGetInt(loaders, i) 545 if loader == LNil { 546 L.RaiseError("module %s not found:\n\t%s, ", name, strings.Join(messages, "\n\t")) 547 } 548 L.Push(loader) 549 L.Push(LString(name)) 550 L.Call(1, 1) 551 ret := L.reg.Pop() 552 switch retv := ret.(type) { 553 case *LFunction: 554 modasfunc = retv 555 goto loopbreak 556 case LString: 557 messages = append(messages, string(retv)) 558 } 559 } 560 loopbreak: 561 L.SetField(loaded, name, loopdetection) 562 L.Push(modasfunc) 563 L.Push(LString(name)) 564 L.Call(1, 1) 565 ret := L.reg.Pop() 566 modv := L.GetField(loaded, name) 567 if ret != LNil && modv == loopdetection { 568 L.SetField(loaded, name, ret) 569 L.Push(ret) 570 } else if modv == loopdetection { 571 L.SetField(loaded, name, LTrue) 572 L.Push(LTrue) 573 } else { 574 L.Push(modv) 575 } 576 return 1 577 } 578 579 /* }}} */ 580 581 /* hidden features {{{ */ 582 583 func baseNewProxy(L *LState) int { 584 ud := L.NewUserData() 585 L.SetTop(1) 586 if L.Get(1) == LTrue { 587 L.SetMetatable(ud, L.NewTable()) 588 } else if d, ok := L.Get(1).(*LUserData); ok { 589 L.SetMetatable(ud, L.GetMetatable(d)) 590 } 591 L.Push(ud) 592 return 1 593 } 594 595 /* }}} */ 596 597 //