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  }