github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/internal/arith/call.go (about)

     1  package arith
     2  
     3  import (
     4  	"github.com/hirochachacha/plua/internal/errors"
     5  	"github.com/hirochachacha/plua/object"
     6  )
     7  
     8  func CallLen(th object.Thread, x object.Value) (object.Value, *object.RuntimeError) {
     9  	switch x := x.(type) {
    10  	case object.String:
    11  		return object.Integer(len(x)), nil
    12  	case object.Table:
    13  		tm := gettm(x.Metatable(), object.TM_LEN)
    14  		if tm != nil {
    15  			return calltm(th, tm, x)
    16  		}
    17  		return object.Integer(x.Len()), nil
    18  	default:
    19  		return calluntm(th, x, object.TM_LEN)
    20  	}
    21  }
    22  
    23  func CallUnm(th object.Thread, x object.Value) (object.Value, *object.RuntimeError) {
    24  	if unm := Unm(x); unm != nil {
    25  		return unm, nil
    26  	}
    27  	return calluntm(th, x, object.TM_UNM)
    28  }
    29  
    30  func CallBnot(th object.Thread, x object.Value) (object.Value, *object.RuntimeError) {
    31  	if unm := Bnot(x); unm != nil {
    32  		return unm, nil
    33  	}
    34  	return calluntm(th, x, object.TM_BNOT)
    35  }
    36  
    37  func CallEqual(th object.Thread, not bool, x, y object.Value) (bool, *object.RuntimeError) {
    38  	// fast path for avoiding assertI2I2
    39  	eq := object.Equal(x, y)
    40  	if eq {
    41  		return true != not, nil
    42  	}
    43  
    44  	switch x := x.(type) {
    45  	case object.Table:
    46  		// tm := gettm(x.Metatable(), object.TM_EQ)
    47  		// if tm != nil {
    48  		// return callcmptm(th, not, tm, x, y)
    49  		// }
    50  
    51  		// tm = gettmbyobj(th, y, object.TM_EQ)
    52  		// if tm != nil {
    53  		// return callcmptm(th, not, tm, x, y)
    54  		// }
    55  
    56  		// return false != not, nil
    57  
    58  		if y, ok := y.(object.Table); ok {
    59  			tm := gettm(x.Metatable(), object.TM_EQ)
    60  			if tm == nil {
    61  				tm = gettm(y.Metatable(), object.TM_EQ)
    62  				if tm == nil {
    63  					return false != not, nil
    64  				}
    65  			}
    66  
    67  			return callcmptm(th, not, tm, x, y)
    68  		}
    69  
    70  		return false != not, nil
    71  	case *object.Userdata:
    72  		// tm := gettm(x.Metatable, object.TM_EQ)
    73  		// if tm != nil {
    74  		// return callcmptm(th, not, tm, x, y)
    75  		// }
    76  
    77  		// tm = gettmbyobj(th, y, object.TM_EQ)
    78  		// if tm != nil {
    79  		// return callcmptm(th, not, tm, x, y)
    80  		// }
    81  
    82  		// return false != not, nil
    83  
    84  		if y, ok := y.(*object.Userdata); ok {
    85  			tm := gettm(x.Metatable, object.TM_EQ)
    86  			if tm == nil {
    87  				tm = gettm(y.Metatable, object.TM_EQ)
    88  				if tm == nil {
    89  					return false != not, nil
    90  				}
    91  			}
    92  
    93  			return callcmptm(th, not, tm, x, y)
    94  		}
    95  
    96  		return false != not, nil
    97  	default:
    98  		return eq != not, nil
    99  	}
   100  }
   101  
   102  func CallLessThan(th object.Thread, not bool, x, y object.Value) (bool, *object.RuntimeError) {
   103  	if b := LessThan(x, y); b != nil {
   104  		return b != object.Boolean(not), nil
   105  	}
   106  	return callordertm(th, not, x, y, object.TM_LT)
   107  }
   108  
   109  func CallLessThanOrEqualTo(th object.Thread, not bool, x, y object.Value) (bool, *object.RuntimeError) {
   110  	if b := LessThanOrEqualTo(x, y); b != nil {
   111  		return b != object.Boolean(not), nil
   112  	}
   113  	return callordertm(th, not, x, y, object.TM_LE)
   114  }
   115  
   116  func CallAdd(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   117  	if sum := Add(x, y); sum != nil {
   118  		return sum, nil
   119  	}
   120  	return callbintm(th, x, y, object.TM_ADD)
   121  }
   122  
   123  func CallSub(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   124  	if sum := Sub(x, y); sum != nil {
   125  		return sum, nil
   126  	}
   127  	return callbintm(th, x, y, object.TM_SUB)
   128  }
   129  
   130  func CallMul(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   131  	if prod := Mul(x, y); prod != nil {
   132  		return prod, nil
   133  	}
   134  	return callbintm(th, x, y, object.TM_MUL)
   135  }
   136  
   137  func CallDiv(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   138  	if quo := Div(x, y); quo != nil {
   139  		return quo, nil
   140  	}
   141  	return callbintm(th, x, y, object.TM_DIV)
   142  }
   143  
   144  func CallIdiv(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   145  	quo, ok := Idiv(x, y)
   146  	if !ok {
   147  		return nil, object.NewRuntimeError("attempt to divide by zero")
   148  	}
   149  	if quo != nil {
   150  		return quo, nil
   151  	}
   152  	return callbintm(th, x, y, object.TM_IDIV)
   153  }
   154  
   155  func CallMod(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   156  	rem, ok := Mod(x, y)
   157  	if !ok {
   158  		return nil, object.NewRuntimeError("attempt to perform 'n%0'")
   159  	}
   160  	if rem != nil {
   161  		return rem, nil
   162  	}
   163  	return callbintm(th, x, y, object.TM_MOD)
   164  }
   165  
   166  func CallPow(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   167  	if prod := Pow(x, y); prod != nil {
   168  		return prod, nil
   169  	}
   170  	return callbintm(th, x, y, object.TM_POW)
   171  }
   172  
   173  func CallBand(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   174  	if band := Band(x, y); band != nil {
   175  		return band, nil
   176  	}
   177  	return callbintm(th, x, y, object.TM_BAND)
   178  }
   179  
   180  func CallBor(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   181  	if bor := Bor(x, y); bor != nil {
   182  		return bor, nil
   183  	}
   184  	return callbintm(th, x, y, object.TM_BOR)
   185  }
   186  
   187  func CallBxor(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   188  	if bxor := Bxor(x, y); bxor != nil {
   189  		return bxor, nil
   190  	}
   191  	return callbintm(th, x, y, object.TM_BXOR)
   192  }
   193  
   194  func CallShl(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   195  	if shl := Shl(x, y); shl != nil {
   196  		return shl, nil
   197  	}
   198  	return callbintm(th, x, y, object.TM_SHL)
   199  }
   200  
   201  func CallShr(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   202  	if shr := Shr(x, y); shr != nil {
   203  		return shr, nil
   204  	}
   205  	return callbintm(th, x, y, object.TM_SHR)
   206  }
   207  
   208  func CallConcat(th object.Thread, x, y object.Value) (object.Value, *object.RuntimeError) {
   209  	if con := Concat(x, y); con != nil {
   210  		return con, nil
   211  	}
   212  	tm := gettmbyobj(th, x, object.TM_CONCAT)
   213  	if tm == nil {
   214  		tm = gettmbyobj(th, y, object.TM_CONCAT)
   215  		if tm == nil {
   216  			return nil, errors.BinaryError(th, object.TM_CONCAT, x, y)
   217  		}
   218  	}
   219  	return calltm(th, tm, x, y)
   220  }
   221  
   222  func gettm(mt object.Table, tag object.Value) object.Value {
   223  	if mt == nil {
   224  		return nil
   225  	}
   226  	return mt.Get(tag)
   227  }
   228  
   229  func gettmbyobj(th object.Thread, x object.Value, tag object.Value) object.Value {
   230  	mt := th.GetMetatable(x)
   231  	if mt == nil {
   232  		return nil
   233  	}
   234  	return gettm(mt, tag)
   235  }
   236  
   237  func calltm(th object.Thread, tm object.Value, args ...object.Value) (object.Value, *object.RuntimeError) {
   238  	rets, err := th.Call(tm, args...)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	if len(rets) == 0 {
   243  		return nil, nil
   244  	}
   245  	return rets[0], nil
   246  }
   247  
   248  func callcmptm(th object.Thread, not bool, tm object.Value, x, y object.Value) (bool, *object.RuntimeError) {
   249  	rets, err := th.Call(tm, x, y)
   250  	if err != nil {
   251  		return false, err
   252  	}
   253  
   254  	var ret object.Value
   255  
   256  	if len(rets) != 0 {
   257  		ret = rets[0]
   258  	}
   259  
   260  	return object.ToGoBool(ret) != not, nil
   261  }
   262  
   263  func calluntm(th object.Thread, x object.Value, tag object.Value) (object.Value, *object.RuntimeError) {
   264  	tm := gettmbyobj(th, x, tag)
   265  	if tm == nil {
   266  		return nil, errors.UnaryError(th, tag, x)
   267  	}
   268  	return calltm(th, tm, x)
   269  }
   270  
   271  func callbintm(th object.Thread, x, y object.Value, tag object.Value) (object.Value, *object.RuntimeError) {
   272  	tm := gettmbyobj(th, x, tag)
   273  	if tm == nil {
   274  		tm = gettmbyobj(th, y, tag)
   275  		if tm == nil {
   276  			return nil, errors.BinaryError(th, tag, x, y)
   277  		}
   278  	}
   279  	return calltm(th, tm, x, y)
   280  }
   281  
   282  func callordertm(th object.Thread, not bool, x, y object.Value, tag object.Value) (bool, *object.RuntimeError) {
   283  	tm := gettmbyobj(th, x, tag)
   284  	if tm == nil {
   285  		tm = gettmbyobj(th, y, tag)
   286  		if tm == nil {
   287  			switch tag {
   288  			case object.TM_LT:
   289  				tm = gettmbyobj(th, x, object.TM_LE)
   290  				if tm == nil {
   291  					tm = gettmbyobj(th, y, object.TM_LE)
   292  					if tm == nil {
   293  						return false, errors.CompareError(th, x, y)
   294  					}
   295  				}
   296  
   297  				x, y = y, x
   298  
   299  				not = !not
   300  			case object.TM_LE:
   301  				tm = gettmbyobj(th, x, object.TM_LT)
   302  				if tm == nil {
   303  					tm = gettmbyobj(th, y, object.TM_LT)
   304  					if tm == nil {
   305  						return false, errors.CompareError(th, x, y)
   306  					}
   307  				}
   308  
   309  				x, y = y, x
   310  
   311  				not = !not
   312  			default:
   313  				panic("unreachable")
   314  			}
   315  		}
   316  	}
   317  
   318  	return callcmptm(th, not, tm, x, y)
   319  }