github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/base/base.go (about) 1 package base 2 3 import ( 4 "fmt" 5 "runtime" 6 "strconv" 7 "strings" 8 9 "github.com/hirochachacha/plua/compiler" 10 "github.com/hirochachacha/plua/internal/compiler_pool" 11 "github.com/hirochachacha/plua/internal/errors" 12 "github.com/hirochachacha/plua/internal/limits" 13 "github.com/hirochachacha/plua/internal/version" 14 "github.com/hirochachacha/plua/object" 15 "github.com/hirochachacha/plua/object/fnutil" 16 ) 17 18 // assert(v [, message], ...) -> ((v [, message], ...) | panic) 19 func assert(th object.Thread, args ...object.Value) (rets []object.Value, err *object.RuntimeError) { 20 ap := fnutil.NewArgParser(th, args) 21 22 ok, err := ap.ToGoBool(0) 23 if err != nil { 24 return nil, err 25 } 26 27 if !ok { 28 val, ok := ap.Get(1) 29 if !ok { 30 return nil, object.NewRuntimeError("assertion failed!") 31 } 32 33 return nil, &object.RuntimeError{RawValue: val, Level: 1} 34 } 35 36 return args, nil 37 } 38 39 // collectgarbage([opt [, arg]]) 40 func collectgarbage(th object.Thread, args ...object.Value) (rets []object.Value, err *object.RuntimeError) { 41 ap := fnutil.NewArgParser(th, args) 42 43 opt := "collect" 44 45 if len(args) > 0 { 46 opt, err = ap.ToGoString(0) 47 if err != nil { 48 return nil, err 49 } 50 } 51 52 switch opt { 53 case "collect": 54 runtime.GC() 55 return []object.Value{object.Integer(0)}, nil 56 case "stop": 57 return nil, object.NewRuntimeError("not implemented") 58 case "restart": 59 return nil, object.NewRuntimeError("not implemented") 60 case "count": 61 m := runtime.MemStats{} 62 runtime.ReadMemStats(&m) 63 return []object.Value{object.Number(m.Alloc / 1024.0)}, nil 64 case "step": 65 return nil, object.NewRuntimeError("not implemented") 66 case "setpause": 67 return nil, object.NewRuntimeError("not implemented") 68 case "setstepmul": 69 return nil, object.NewRuntimeError("not implemented") 70 case "isrunning": 71 return []object.Value{object.True}, nil 72 } 73 74 return nil, ap.OptionError(0, opt) 75 } 76 77 // dofile([filename]) -> (... | panic) 78 func dofile(th object.Thread, args ...object.Value) (rets []object.Value, err *object.RuntimeError) { 79 ap := fnutil.NewArgParser(th, args) 80 81 fname := "" 82 83 if len(args) > 0 { 84 fname, err = ap.ToGoString(0) 85 if err != nil { 86 return nil, err 87 } 88 } 89 90 p, err := compiler_pool.CompileFile(fname, 0) 91 if err != nil { 92 return nil, err 93 } 94 95 return th.Call(th.NewClosure(p)) 96 } 97 98 // error(message [, level]) -> panic 99 func _error(th object.Thread, args ...object.Value) (rets []object.Value, err *object.RuntimeError) { 100 ap := fnutil.NewArgParser(th, args) 101 102 val, ok := ap.Get(0) 103 if !ok { 104 return nil, &object.RuntimeError{RawValue: nil} 105 } 106 107 level, err := ap.OptGoInt(1, 1) 108 if err != nil { 109 return nil, err 110 } 111 112 if _, ok := val.(object.String); ok && level > 0 { 113 return nil, &object.RuntimeError{RawValue: val, Level: level} 114 } 115 116 return nil, &object.RuntimeError{RawValue: val} 117 } 118 119 // getmetatable(object) -> Value 120 func getmetatable(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 121 ap := fnutil.NewArgParser(th, args) 122 123 val, err := ap.ToValue(0) 124 if err != nil { 125 return nil, err 126 } 127 128 mt := th.GetMetatable(val) 129 if mt != nil { 130 if _mt := mt.Get(object.String("__metatable")); _mt != nil { 131 return []object.Value{_mt}, nil 132 } 133 } 134 135 return []object.Value{mt}, nil 136 } 137 138 func inext(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 139 ap := fnutil.NewArgParser(th, args) 140 141 t, err := ap.ToTable(0) 142 if err != nil { 143 return nil, err 144 } 145 146 i, err := ap.ToGoInt(1) 147 if err != nil { 148 return nil, err 149 } 150 151 i++ 152 153 if i <= t.Len() { 154 v := t.Get(object.Integer(i)) 155 if v == nil { 156 return nil, nil 157 } 158 return []object.Value{object.Integer(i), v}, nil 159 } 160 161 return nil, nil 162 } 163 164 func makeINext(tm object.Value) object.GoFunction { 165 inext := func(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 166 ap := fnutil.NewArgParser(th, args) 167 168 t, err := ap.ToTable(0) 169 if err != nil { 170 return nil, err 171 } 172 173 i, err := ap.ToGoInt(1) 174 if err != nil { 175 return nil, err 176 } 177 178 i++ 179 180 rets, err := th.Call(tm, t, object.Integer(i)) 181 if err != nil { 182 return nil, err 183 } 184 185 if len(rets) == 0 || rets[0] == nil { 186 return nil, nil 187 } 188 189 return append([]object.Value{object.Integer(i)}, rets...), nil 190 } 191 192 return object.GoFunction(inext) 193 } 194 195 // ipairs(t) -> (inext, t, 0) 196 func ipairs(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 197 ap := fnutil.NewArgParser(th, args) 198 199 t, err := ap.ToValue(0) 200 if err != nil { 201 return nil, err 202 } 203 204 mt := th.GetMetatable(t) 205 206 if mt == nil { 207 return []object.Value{object.GoFunction(inext), t, object.Integer(0)}, nil 208 } 209 210 if tm := mt.Get(object.TM_IPAIRS); tm != nil { 211 return th.Call(tm, args...) 212 } 213 214 for i := 0; i < version.MAX_TAG_LOOP; i++ { 215 tm := mt.Get(object.TM_INDEX) 216 if tm == nil { 217 return []object.Value{object.GoFunction(inext), t, object.Integer(0)}, nil 218 } 219 220 if object.ToType(tm) == object.TFUNCTION { 221 return []object.Value{makeINext(tm), t, object.Integer(0)}, nil 222 } 223 224 t = tm 225 } 226 227 return nil, object.NewRuntimeError("gettable chain too long; possible loop") 228 } 229 230 // next(t, key) -> (nkey, val) 231 func next(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 232 ap := fnutil.NewArgParser(th, args) 233 234 t, err := ap.ToTable(0) 235 if err != nil { 236 return nil, err 237 } 238 239 var key object.Value 240 if len(args) > 1 { 241 key = args[1] 242 } 243 244 k, v, ok := t.Next(key) 245 if !ok { 246 return nil, object.NewRuntimeError("invalid key to 'next'") 247 } 248 249 if v == nil { 250 return nil, nil 251 } 252 253 return []object.Value{k, v}, nil 254 } 255 256 // pairs(t) -> (next, t, nil) 257 func pairs(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 258 ap := fnutil.NewArgParser(th, args) 259 260 t, err := ap.ToValue(0) 261 if err != nil { 262 return nil, err 263 } 264 265 if mt := th.GetMetatable(t); mt != nil { 266 if tm := mt.Get(object.TM_PAIRS); tm != nil { 267 return th.Call(tm, args...) 268 } 269 } 270 271 return []object.Value{object.GoFunction(next), t, nil}, nil 272 } 273 274 // loadfile(fname [, mode [, env]]]) -> (closure | (nil, errmessage)) 275 func loadfile(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 276 ap := fnutil.NewArgParser(th, args) 277 278 fname, err := ap.OptGoString(0, "") 279 if err != nil { 280 return nil, err 281 } 282 283 mode, err := ap.OptGoString(1, "bt") 284 if err != nil { 285 return nil, err 286 } 287 288 var p *object.Proto 289 290 switch mode { 291 case "b": 292 p, err = compiler_pool.CompileFile(fname, compiler.Binary) 293 case "t": 294 p, err = compiler_pool.CompileFile(fname, compiler.Text) 295 case "bt": 296 p, err = compiler_pool.CompileFile(fname, 0) 297 default: 298 return nil, ap.OptionError(1, mode) 299 } 300 301 if err != nil { 302 return []object.Value{nil, err.Value()}, nil 303 } 304 305 cl := th.NewClosure(p) 306 307 if env, ok := ap.Get(2); ok { 308 cl.SetUpvalue(0, env) 309 } 310 311 return []object.Value{cl}, nil 312 } 313 314 // load(chunk [, chunkname [, mode [, env]]]) -> (closure | (nil, errmessage)) 315 func load(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 316 ap := fnutil.NewArgParser(th, args) 317 318 s, err := ap.ToTypes(0, object.TSTRING, object.TFUNCTION) 319 if err != nil { 320 return nil, err 321 } 322 323 var chunk string 324 var chunkname string 325 326 switch s := s.(type) { 327 case object.String: 328 chunk = string(s) 329 330 chunkname, err = ap.OptGoString(1, string(s)) 331 if err != nil { 332 return nil, err 333 } 334 case object.GoFunction, object.Closure: 335 for { 336 rets, err := th.Call(s) 337 if err != nil { 338 return []object.Value{nil, err.Value()}, nil 339 } 340 if len(rets) == 0 || rets[0] == nil { 341 break 342 } 343 s, ok := object.ToGoString(rets[0]) 344 if !ok { 345 return []object.Value{nil, object.String("reader function must return a string")}, nil 346 } 347 if s == "" { 348 break 349 } 350 chunk += s 351 } 352 353 chunkname, err = ap.OptGoString(1, "=(load)") 354 if err != nil { 355 return nil, err 356 } 357 } 358 359 mode, err := ap.OptGoString(2, "bt") 360 if err != nil { 361 return nil, err 362 } 363 364 var p *object.Proto 365 switch mode { 366 case "b": 367 p, err = compiler_pool.CompileString(chunk, chunkname, compiler.Binary) 368 case "t": 369 p, err = compiler_pool.CompileString(chunk, chunkname, compiler.Text) 370 case "bt": 371 p, err = compiler_pool.CompileString(chunk, chunkname, 0) 372 default: 373 return nil, ap.OptionError(2, mode) 374 } 375 376 if err != nil { 377 return []object.Value{nil, err.Value()}, nil 378 } 379 380 cl := th.NewClosure(p) 381 382 if env, ok := ap.Get(3); ok { 383 cl.SetUpvalue(0, env) 384 } 385 386 return []object.Value{cl}, nil 387 } 388 389 func _print(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 390 if len(args) == 0 { 391 fmt.Println("") 392 393 return nil, nil 394 } 395 396 tostring := th.Globals().Get(object.String("tostring")) 397 398 for _, arg := range args[:len(args)-1] { 399 rets, err := th.Call(tostring, arg) 400 if err != nil { 401 return nil, err 402 } 403 404 if len(rets) == 0 { 405 return nil, object.NewRuntimeError("'tostring' must return a string to 'print'") 406 } 407 408 s, ok := object.ToGoString(rets[0]) 409 if !ok { 410 return nil, object.NewRuntimeError("'tostring' must return a string to 'print'") 411 } 412 413 fmt.Print(s) 414 fmt.Print("\t") 415 } 416 417 rets, err := th.Call(tostring, args[len(args)-1]) 418 if err != nil { 419 return nil, err 420 } 421 422 if len(rets) == 0 { 423 return nil, object.NewRuntimeError("'tostring' must return a string to 'print'") 424 } 425 426 s, ok := object.ToGoString(rets[0]) 427 if !ok { 428 return nil, object.NewRuntimeError("'tostring' must return a string to 'print'") 429 } 430 431 fmt.Println(s) 432 433 return nil, nil 434 } 435 436 func pcall(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 437 ap := fnutil.NewArgParser(th, args) 438 439 fn, err := ap.ToFunctionOrNil(0) 440 if err != nil { 441 return nil, err 442 } 443 444 rets, err := th.Call(fn, args[1:]...) 445 if err != nil { 446 return []object.Value{object.False, err.Value()}, nil 447 } 448 449 return append([]object.Value{object.True}, rets...), nil 450 } 451 452 func rawequal(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 453 ap := fnutil.NewArgParser(th, args) 454 455 x, err := ap.ToValue(0) 456 if err != nil { 457 return nil, err 458 } 459 460 y, err := ap.ToValue(1) 461 if err != nil { 462 return nil, err 463 } 464 465 return []object.Value{object.Boolean(object.Equal(x, y))}, nil 466 } 467 468 func rawlen(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 469 ap := fnutil.NewArgParser(th, args) 470 471 x, err := ap.ToTypes(0, object.TSTRING, object.TTABLE) 472 if err != nil { 473 return nil, err 474 } 475 476 switch x := x.(type) { 477 case object.String: 478 return []object.Value{object.Integer(len(x))}, nil 479 case object.Table: 480 return []object.Value{object.Integer(x.Len())}, nil 481 } 482 483 return nil, nil 484 } 485 486 func rawget(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 487 ap := fnutil.NewArgParser(th, args) 488 489 t, err := ap.ToTable(0) 490 if err != nil { 491 return nil, err 492 } 493 494 key, err := ap.ToValue(1) 495 if err != nil { 496 return nil, err 497 } 498 499 return []object.Value{t.Get(key)}, nil 500 } 501 502 func rawset(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 503 ap := fnutil.NewArgParser(th, args) 504 505 t, err := ap.ToTable(0) 506 if err != nil { 507 return nil, err 508 } 509 510 key, err := ap.ToValue(1) 511 if err != nil { 512 return nil, err 513 } 514 515 if key == nil { 516 return nil, errors.NilIndexError() 517 } 518 519 if object.IsNaN(key) { 520 return nil, errors.NaNIndexError() 521 } 522 523 val, err := ap.ToValue(2) 524 if err != nil { 525 return nil, err 526 } 527 528 t.Set(key, val) 529 530 return []object.Value{t}, nil 531 } 532 533 func _select(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 534 ap := fnutil.NewArgParser(th, args) 535 536 i, err := ap.ToGoInt(0) 537 if err != nil { 538 if s, e := ap.ToGoString(0); e == nil { 539 if s == "#" { 540 return []object.Value{object.Integer(len(args) - 1)}, nil 541 } 542 } 543 return nil, err 544 } 545 546 switch { 547 case i < 0: 548 if len(args) < -i || int64(i) == limits.MinInt { 549 return nil, ap.ArgError(0, "index out of range") 550 } 551 552 return args[len(args)+i:], nil 553 case i == 0: 554 return nil, ap.ArgError(0, "index out of range") 555 } 556 557 if len(args) < i { 558 return nil, nil 559 } 560 561 return args[i:], nil 562 } 563 564 // setmetatable(table, metatable) -> table 565 func setmetatable(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 566 ap := fnutil.NewArgParser(th, args) 567 568 t, err := ap.ToTable(0) 569 if err != nil { 570 return nil, err 571 } 572 573 mt, err := ap.ToTypes(1, object.TNIL, object.TTABLE) 574 if err != nil { 575 return nil, err 576 } 577 578 if old := th.GetMetatable(t); old != nil { 579 if old.Get(object.TM_METATABLE) != nil { 580 return nil, object.NewRuntimeError("cannot change a protected metatable") 581 } 582 } 583 584 switch mt := mt.(type) { 585 case nil: 586 t.SetMetatable(nil) 587 case object.Table: 588 t.SetMetatable(mt) 589 default: 590 panic("unreachable") 591 } 592 593 return []object.Value{t}, nil 594 } 595 596 func tonumber(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 597 ap := fnutil.NewArgParser(th, args) 598 599 switch len(args) { 600 case 0, 1: 601 val, err := ap.ToValue(0) 602 if err != nil { 603 return nil, err 604 } 605 606 if i, ok := object.ToInteger(val); ok { 607 return []object.Value{i}, nil 608 } 609 610 if n, ok := object.ToNumber(val); ok { 611 if n == 0 { // for ErrRange 612 return []object.Value{object.Integer(0)}, nil 613 } 614 return []object.Value{n}, nil 615 } 616 617 return []object.Value{nil}, nil 618 } 619 620 base, err := ap.OptGoInt(1, 10) 621 if err != nil { 622 return nil, err 623 } 624 625 if base < 2 || base > 36 { 626 return nil, ap.ArgError(1, "base out of range") 627 } 628 629 s, err := ap.ToGoString(0) 630 if err != nil { 631 return nil, err 632 } 633 634 if i, err := strconv.ParseInt(strings.TrimSpace(s), base, 64); err == nil { 635 return []object.Value{object.Integer(i)}, nil 636 } 637 638 return []object.Value{nil}, nil 639 } 640 641 func tostring(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 642 ap := fnutil.NewArgParser(th, args) 643 644 val, err := ap.ToValue(0) 645 if err != nil { 646 return nil, err 647 } 648 649 if mt := th.GetMetatable(val); mt != nil { 650 if tm := mt.Get(object.TM_TOSTRING); tm != nil { 651 return th.Call(tm, val) 652 } 653 } 654 655 return []object.Value{object.String(object.Repr(val))}, nil 656 } 657 658 func _type(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 659 ap := fnutil.NewArgParser(th, args) 660 661 val, err := ap.ToValue(0) 662 if err != nil { 663 return nil, err 664 } 665 666 return []object.Value{object.String(object.ToType(val).String())}, nil 667 } 668 669 func xpcall(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 670 ap := fnutil.NewArgParser(th, args) 671 672 f, err := ap.ToFunction(0) 673 if err != nil { 674 return nil, err 675 } 676 677 msgh, err := ap.ToFunction(1) 678 if err != nil { 679 return nil, err 680 } 681 682 rets, err := th.Call(f, args[2:]...) 683 if err != nil { 684 rets, err = th.Call(msgh, err.Value()) 685 if err != nil { 686 err.RawValue = object.String("error in error handling") 687 688 return []object.Value{object.False, err.Value()}, nil 689 } 690 return append([]object.Value{object.False}, rets...), nil 691 } 692 693 return append([]object.Value{object.True}, rets...), nil 694 } 695 696 func Open(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 697 g := th.Globals() 698 699 g.Set(object.String("_G"), g) 700 g.Set(object.String("_VERSION"), object.String(version.LUA_NAME)) 701 g.Set(object.String("assert"), object.GoFunction(assert)) 702 g.Set(object.String("collectgarbage"), object.GoFunction(collectgarbage)) 703 g.Set(object.String("dofile"), object.GoFunction(dofile)) 704 g.Set(object.String("error"), object.GoFunction(_error)) 705 g.Set(object.String("getmetatable"), object.GoFunction(getmetatable)) 706 g.Set(object.String("ipairs"), object.GoFunction(ipairs)) 707 g.Set(object.String("loadfile"), object.GoFunction(loadfile)) 708 g.Set(object.String("load"), object.GoFunction(load)) 709 g.Set(object.String("next"), object.GoFunction(next)) 710 g.Set(object.String("pairs"), object.GoFunction(pairs)) 711 g.Set(object.String("pcall"), object.GoFunction(pcall)) 712 g.Set(object.String("print"), object.GoFunction(_print)) 713 g.Set(object.String("rawequal"), object.GoFunction(rawequal)) 714 g.Set(object.String("rawlen"), object.GoFunction(rawlen)) 715 g.Set(object.String("rawget"), object.GoFunction(rawget)) 716 g.Set(object.String("rawset"), object.GoFunction(rawset)) 717 g.Set(object.String("select"), object.GoFunction(_select)) 718 g.Set(object.String("setmetatable"), object.GoFunction(setmetatable)) 719 g.Set(object.String("tonumber"), object.GoFunction(tonumber)) 720 g.Set(object.String("tostring"), object.GoFunction(tostring)) 721 g.Set(object.String("type"), object.GoFunction(_type)) 722 g.Set(object.String("xpcall"), object.GoFunction(xpcall)) 723 724 return []object.Value{g}, nil 725 }