github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/object/fnutil/arg_parser.go (about) 1 package fnutil 2 3 import ( 4 "fmt" 5 6 "github.com/hirochachacha/plua/internal/limits" 7 "github.com/hirochachacha/plua/object" 8 ) 9 10 type ArgParser struct { 11 th object.Thread 12 args []object.Value 13 offset int 14 } 15 16 func NewArgParser(th object.Thread, args []object.Value) *ArgParser { 17 return &ArgParser{ 18 th: th, 19 args: args, 20 } 21 } 22 23 func (ap *ArgParser) Args() []object.Value { 24 return ap.args[ap.offset:] 25 } 26 27 func (ap *ArgParser) Get(n int) (object.Value, bool) { 28 n = n + ap.offset 29 30 if len(ap.args) <= n { 31 return nil, false 32 } 33 34 return ap.args[n], true 35 } 36 37 func (ap *ArgParser) Set(n int, val object.Value) bool { 38 n = n + ap.offset 39 40 if len(ap.args) <= n { 41 return false 42 } 43 44 ap.args[n] = val 45 46 return true 47 } 48 49 func (ap *ArgParser) GetThread() object.Thread { 50 if len(ap.args) > ap.offset { 51 if th, ok := ap.args[ap.offset].(object.Thread); ok { 52 ap.offset++ 53 54 return th 55 } 56 } 57 58 return ap.th 59 } 60 61 func (ap *ArgParser) ToValue(n int) (object.Value, *object.RuntimeError) { 62 arg, ok := ap.Get(n) 63 if !ok { 64 return nil, ap.ArgError(n, "value expected") 65 } 66 67 return arg, nil 68 } 69 70 func (ap *ArgParser) ToUserdata(n int) (object.Value, *object.RuntimeError) { 71 arg, ok := ap.Get(n) 72 if !ok { 73 return nil, ap.ArgError(n, "userdata expected, got no value") 74 } 75 76 switch ud := arg.(type) { 77 case object.LightUserdata: 78 return ud, nil 79 case *object.Userdata: 80 return ud, nil 81 } 82 83 return nil, ap.TypeError(n, "userdata") 84 } 85 86 func (ap *ArgParser) ToFunction(n int) (object.Value, *object.RuntimeError) { 87 arg, ok := ap.Get(n) 88 if !ok { 89 return nil, ap.ArgError(n, "function expected, got no value") 90 } 91 92 if typ := object.ToType(arg); typ != object.TFUNCTION { 93 return nil, ap.TypeError(n, "function") 94 } 95 96 return arg, nil 97 } 98 99 func (ap *ArgParser) ToFunctionOrNil(n int) (object.Value, *object.RuntimeError) { 100 arg, ok := ap.Get(n) 101 if !ok { 102 return nil, ap.ArgError(n, "function expected, got no value") 103 } 104 105 if typ := object.ToType(arg); typ != object.TFUNCTION && typ != object.TNIL { 106 return nil, ap.TypeError(n, "function or nil") 107 } 108 109 return arg, nil 110 } 111 112 func (ap *ArgParser) ToTypes(n int, types ...object.Type) (object.Value, *object.RuntimeError) { 113 val, ok := ap.Get(n) 114 if !ok { 115 typess := "" 116 for _, typ := range types[:len(types)-1] { 117 typess += typ.String() + " or " 118 } 119 typess += types[len(types)-1].String() 120 121 return nil, ap.ArgError(n, typess+" expected, got no value") 122 } 123 124 { 125 for _, typ := range types { 126 switch val.(type) { 127 case nil: 128 if typ == object.TNIL { 129 goto Found 130 } 131 case object.Integer: 132 if typ == object.TNUMINT || typ == object.TNUMBER { 133 goto Found 134 } 135 case object.Number: 136 if typ == object.TNUMBER { 137 goto Found 138 } 139 case object.String: 140 if typ == object.TSTRING { 141 goto Found 142 } 143 case object.Boolean: 144 if typ == object.TBOOLEAN { 145 goto Found 146 } 147 case object.LightUserdata: 148 if typ == object.TUSERDATA { 149 goto Found 150 } 151 case object.GoFunction: 152 if typ == object.TFUNCTION { 153 goto Found 154 } 155 case *object.Userdata: 156 if typ == object.TUSERDATA { 157 goto Found 158 } 159 case object.Table: 160 if typ == object.TTABLE { 161 goto Found 162 } 163 case object.Closure: 164 if typ == object.TFUNCTION { 165 goto Found 166 } 167 case object.Thread: 168 if typ == object.TTHREAD { 169 goto Found 170 } 171 } 172 } 173 174 typess := "" 175 for _, typ := range types[:len(types)-1] { 176 typess += typ.String() + " or " 177 } 178 typess += types[len(types)-1].String() 179 180 return nil, ap.TypeError(n, typess) 181 } 182 Found: 183 return val, nil 184 } 185 186 func (ap *ArgParser) ToInteger(n int) (object.Integer, *object.RuntimeError) { 187 arg, ok := ap.Get(n) 188 if !ok { 189 return 0, ap.ArgError(n, "integer expected, got no value") 190 } 191 192 i, ok := object.ToInteger(arg) 193 if !ok { 194 return 0, ap.TypeError(n, "integer") 195 } 196 197 return i, nil 198 } 199 200 func (ap *ArgParser) ToNumber(n int) (object.Number, *object.RuntimeError) { 201 arg, ok := ap.Get(n) 202 if !ok { 203 return 0, ap.ArgError(n, "number expected, got no value") 204 } 205 206 f, ok := object.ToNumber(arg) 207 if !ok { 208 return 0, ap.TypeError(n, "number") 209 } 210 211 return f, nil 212 } 213 214 func (ap *ArgParser) ToString(n int) (object.String, *object.RuntimeError) { 215 arg, ok := ap.Get(n) 216 if !ok { 217 return "", ap.ArgError(n, "string expected, got no value") 218 } 219 220 s, ok := object.ToString(arg) 221 if !ok { 222 return "", ap.TypeError(n, "string") 223 } 224 225 return s, nil 226 } 227 228 func (ap *ArgParser) ToBoolean(n int) (object.Boolean, *object.RuntimeError) { 229 arg, ok := ap.Get(n) 230 if !ok { 231 return object.False, ap.ArgError(n, "boolean expected, got no value") 232 } 233 234 return object.ToBoolean(arg), nil 235 } 236 237 func (ap *ArgParser) ToLightUserdata(n int) (object.LightUserdata, *object.RuntimeError) { 238 arg, ok := ap.Get(n) 239 if !ok { 240 return object.LightUserdata{}, ap.ArgError(n, "light userdata expected, got no value") 241 } 242 243 lud, ok := arg.(object.LightUserdata) 244 if !ok { 245 return object.LightUserdata{}, ap.TypeError(n, "light userdata") 246 } 247 248 return lud, nil 249 } 250 251 func (ap *ArgParser) ToGoFunction(n int) (object.GoFunction, *object.RuntimeError) { 252 arg, ok := ap.Get(n) 253 if !ok { 254 return nil, ap.ArgError(n, "go function expected, got no value") 255 } 256 257 fn, ok := arg.(object.GoFunction) 258 if !ok { 259 return nil, ap.TypeError(n, "go function") 260 } 261 262 return fn, nil 263 } 264 265 func (ap *ArgParser) ToTable(n int) (object.Table, *object.RuntimeError) { 266 arg, ok := ap.Get(n) 267 if !ok { 268 return nil, ap.ArgError(n, "table expected, got no value") 269 } 270 271 t, ok := arg.(object.Table) 272 if !ok { 273 return nil, ap.TypeError(n, "table") 274 } 275 276 return t, nil 277 } 278 279 func (ap *ArgParser) ToFullUserdata(n int) (*object.Userdata, *object.RuntimeError) { 280 arg, ok := ap.Get(n) 281 if !ok { 282 return nil, ap.ArgError(n, "full userdata expected, got no value") 283 } 284 285 ud, ok := arg.(*object.Userdata) 286 if !ok { 287 return nil, ap.TypeError(n, "full userdata") 288 } 289 290 return ud, nil 291 } 292 293 func (ap *ArgParser) ToClosure(n int) (object.Closure, *object.RuntimeError) { 294 arg, ok := ap.Get(n) 295 if !ok { 296 return nil, ap.ArgError(n, "lua function expected, got no value") 297 } 298 299 cl, ok := arg.(object.Closure) 300 if !ok { 301 return nil, ap.TypeError(n, "lua function") 302 } 303 304 return cl, nil 305 } 306 307 func (ap *ArgParser) ToThread(n int) (object.Thread, *object.RuntimeError) { 308 arg, ok := ap.Get(n) 309 if !ok { 310 return nil, ap.ArgError(n, "thread expected, got no value") 311 } 312 313 th, ok := arg.(object.Thread) 314 if !ok { 315 return nil, ap.TypeError(n, "thread") 316 } 317 318 return th, nil 319 } 320 321 func (ap *ArgParser) ToGoInt(n int) (int, *object.RuntimeError) { 322 arg, ok := ap.Get(n) 323 if !ok { 324 return 0, ap.ArgError(n, "integer expected, got no value") 325 } 326 327 i, ok := object.ToGoInt64(arg) 328 if !ok { 329 if _, ok := object.ToNumber(arg); ok { 330 return 0, ap.ArgError(n, "number has no integer representation") 331 } 332 return 0, ap.TypeError(n, "integer") 333 } 334 335 if i < limits.MinInt || i > limits.MaxInt { 336 return 0, ap.ArgError(n, "integer overflow") 337 } 338 339 return int(i), nil 340 } 341 342 func (ap *ArgParser) ToGoInt64(n int) (int64, *object.RuntimeError) { 343 arg, ok := ap.Get(n) 344 if !ok { 345 return 0, ap.ArgError(n, "integer expected, got no value") 346 } 347 348 i, ok := object.ToGoInt64(arg) 349 if !ok { 350 if _, ok := object.ToNumber(arg); ok { 351 return 0, ap.ArgError(n, "number has no integer representation") 352 } 353 return 0, ap.TypeError(n, "integer") 354 } 355 356 return i, nil 357 } 358 359 func (ap *ArgParser) ToGoFloat64(n int) (float64, *object.RuntimeError) { 360 arg, ok := ap.Get(n) 361 if !ok { 362 return 0, ap.ArgError(n, "number expected, got no value") 363 } 364 365 f, ok := object.ToGoFloat64(arg) 366 if !ok { 367 return 0, ap.TypeError(n, "number") 368 } 369 370 return f, nil 371 } 372 373 func (ap *ArgParser) ToGoString(n int) (string, *object.RuntimeError) { 374 arg, ok := ap.Get(n) 375 if !ok { 376 return "", ap.ArgError(n, "string expected, got no value") 377 } 378 379 s, ok := object.ToGoString(arg) 380 if !ok { 381 return "", ap.TypeError(n, "string") 382 } 383 384 return s, nil 385 } 386 387 func (ap *ArgParser) ToGoBool(n int) (bool, *object.RuntimeError) { 388 arg, ok := ap.Get(n) 389 if !ok { 390 return false, ap.ArgError(n, "boolean expected, got no value") 391 } 392 393 return object.ToGoBool(arg), nil 394 } 395 396 func (ap *ArgParser) OptGoInt64(n int, i int64) (int64, *object.RuntimeError) { 397 arg, ok := ap.Get(n) 398 if !ok || arg == nil { 399 return i, nil 400 } 401 402 i64, ok := object.ToGoInt64(arg) 403 if !ok { 404 return 0, ap.TypeError(n, "integer") 405 } 406 407 return i64, nil 408 } 409 410 func (ap *ArgParser) OptGoInt(n int, i int) (int, *object.RuntimeError) { 411 arg, ok := ap.Get(n) 412 if !ok || arg == nil { 413 return i, nil 414 } 415 416 i64, ok := object.ToGoInt64(arg) 417 if !ok { 418 return 0, ap.TypeError(n, "integer") 419 } 420 421 if i64 < limits.MinInt || i64 > limits.MaxInt { 422 return i, nil 423 } 424 425 return int(i64), nil 426 } 427 428 func (ap *ArgParser) OptGoFloat64(n int, f float64) (float64, *object.RuntimeError) { 429 arg, ok := ap.Get(n) 430 if !ok || arg == nil { 431 return f, nil 432 } 433 434 f64, ok := object.ToGoFloat64(arg) 435 if !ok { 436 return 0, ap.TypeError(n, "number") 437 } 438 439 return f64, nil 440 } 441 442 func (ap *ArgParser) OptGoString(n int, s string) (string, *object.RuntimeError) { 443 arg, ok := ap.Get(n) 444 if !ok || arg == nil { 445 return s, nil 446 } 447 448 gs, ok := object.ToGoString(arg) 449 if !ok { 450 return "", ap.TypeError(n, "string") 451 } 452 453 return gs, nil 454 } 455 456 func (ap *ArgParser) OptGoBool(n int, b bool) bool { 457 arg, ok := ap.Get(n) 458 if !ok || arg == nil { 459 return b 460 } 461 462 return object.ToGoBool(arg) 463 } 464 465 func (ap *ArgParser) ArgError(n int, extramsg string) *object.RuntimeError { 466 n = n + ap.offset 467 468 n++ 469 470 d := ap.th.GetInfo(0, "n") 471 if d == nil { 472 return object.NewRuntimeError(fmt.Sprintf("bad argument #%d (%s)", n, extramsg)) 473 } 474 475 if d.NameWhat == "method" { 476 n-- 477 if n == 0 { 478 return object.NewRuntimeError(fmt.Sprintf("calling '%s' on bad self (%s)", d.Name, extramsg)) 479 } 480 } 481 482 if d.Name == "" { 483 d.Name = ap.getFuncName(d.Func) 484 } 485 486 return object.NewRuntimeError(fmt.Sprintf("bad argument #%d to '%s' (%s)", n, d.Name, extramsg)) 487 } 488 489 func (ap *ArgParser) TypeError(n int, tname string) *object.RuntimeError { 490 arg, ok := ap.Get(n) 491 if !ok { 492 return ap.ArgError(n, fmt.Sprintf("%s expected, got no value", tname)) 493 } 494 return ap.ArgError(n, fmt.Sprintf("%s expected, got %s", tname, typeName(ap.th, arg))) 495 } 496 497 func (ap *ArgParser) OptionError(n int, opt string) *object.RuntimeError { 498 return ap.ArgError(n, fmt.Sprintf("invalid option '%s'", opt)) 499 } 500 501 func (ap *ArgParser) getFuncName(fn object.Value) string { 502 loaded := ap.th.Loaded() 503 504 var key object.Value 505 var val object.Value 506 for { 507 key, val, _ = loaded.Next(key) 508 if val == nil { 509 break 510 } 511 512 if modname, ok := key.(object.String); ok { 513 if module, ok := val.(object.Table); ok { 514 var mkey object.Value 515 var mval object.Value 516 for { 517 mkey, mval, _ = module.Next(mkey) 518 if mval == nil { 519 break 520 } 521 522 if fname, ok := mkey.(object.String); ok { 523 if object.Equal(mval, fn) { 524 if modname == "_G" { 525 return string(fname) 526 } 527 return string(modname) + "." + string(fname) 528 } 529 } 530 } 531 } 532 } 533 } 534 535 return "?" 536 } 537 538 func typeName(th object.Thread, arg object.Value) string { 539 if mt := th.GetMetatable(arg); mt != nil { 540 if name := mt.Get(object.TM_NAME); name != nil { 541 if name, ok := name.(object.String); ok { 542 return string(name) 543 } 544 if _, ok := arg.(object.LightUserdata); ok { 545 return "light userdata" 546 } 547 return object.ToType(arg).String() 548 } 549 } 550 if _, ok := arg.(object.LightUserdata); ok { 551 return "light userdata" 552 } 553 return object.ToType(arg).String() 554 }