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 }