github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/arith.go (about) 1 package runtime 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 ) 8 9 // Unm returns (z, true) where z is the value representing -x if x is a number, 10 // else (NilValue, false). 11 func Unm(x Value) (Value, bool) { 12 switch x.iface.(type) { 13 case int64: 14 return IntValue(-x.AsInt()), true 15 case float64: 16 return FloatValue(-x.AsFloat()), true 17 default: 18 return NilValue, false 19 } 20 } 21 22 // Add returns (z, true) where z is the value representing x+y if x and y are 23 // numbers, else (NilValue, false). 24 func Add(x, y Value) (Value, bool) { 25 switch x.iface.(type) { 26 case int64: 27 switch y.iface.(type) { 28 case int64: 29 return IntValue(x.AsInt() + y.AsInt()), true 30 case float64: 31 return FloatValue(float64(x.AsInt()) + y.AsFloat()), true 32 } 33 case float64: 34 switch y.iface.(type) { 35 case int64: 36 return FloatValue(x.AsFloat() + float64(y.AsInt())), true 37 case float64: 38 return FloatValue(x.AsFloat() + y.AsFloat()), true 39 } 40 } 41 return NilValue, false 42 } 43 44 // Sub returns (z, true) where z is the value representing x-y if x and y are 45 // numbers, else (NilValue, false). 46 func Sub(x, y Value) (Value, bool) { 47 switch x.iface.(type) { 48 case int64: 49 switch y.iface.(type) { 50 case int64: 51 return IntValue(x.AsInt() - y.AsInt()), true 52 case float64: 53 return FloatValue(float64(x.AsInt()) - y.AsFloat()), true 54 } 55 case float64: 56 switch y.iface.(type) { 57 case int64: 58 return FloatValue(x.AsFloat() - float64(y.AsInt())), true 59 case float64: 60 return FloatValue(x.AsFloat() - y.AsFloat()), true 61 } 62 } 63 return NilValue, false 64 } 65 66 // Mul returns (z, true) where z is the value representing x*y if x and y are 67 // numbers, else (NilValue, false). 68 func Mul(x, y Value) (Value, bool) { 69 switch x.iface.(type) { 70 case int64: 71 switch y.iface.(type) { 72 case int64: 73 return IntValue(x.AsInt() * y.AsInt()), true 74 case float64: 75 return FloatValue(float64(x.AsInt()) * y.AsFloat()), true 76 } 77 case float64: 78 switch y.iface.(type) { 79 case int64: 80 return FloatValue(x.AsFloat() * float64(y.AsInt())), true 81 case float64: 82 return FloatValue(x.AsFloat() * y.AsFloat()), true 83 } 84 } 85 return NilValue, false 86 } 87 88 // Div returns (z, true) where z is the (float) value representing x/y if x and 89 // y are numbers, else (NilValue, false). 90 func Div(x, y Value) (Value, bool) { 91 switch x.iface.(type) { 92 case int64: 93 switch y.iface.(type) { 94 case int64: 95 return FloatValue(float64(x.AsInt()) / float64(y.AsInt())), true 96 case float64: 97 return FloatValue(float64(x.AsInt()) / y.AsFloat()), true 98 } 99 case float64: 100 switch y.iface.(type) { 101 case int64: 102 return FloatValue(x.AsFloat() / float64(y.AsInt())), true 103 case float64: 104 return FloatValue(x.AsFloat() / y.AsFloat()), true 105 } 106 } 107 return NilValue, false 108 } 109 110 func floordivInt(x, y int64) int64 { 111 r := x % y 112 q := x / y 113 if r != 0 && (r < 0) != (y < 0) { 114 q-- 115 } 116 return q 117 } 118 119 func floordivFloat(x, y float64) float64 { 120 return math.Floor(x / y) 121 } 122 123 // Div returns (z, true, nil) where z is the (integer) value representing x//y 124 // if x and y are numbers and y != 0, if y == 0 it returns (NilValue, true, 125 // div_by_zero_error), else (NilValue, false, nil) if x or y is not a number. 126 func Idiv(x Value, y Value) (Value, bool, error) { 127 switch x.iface.(type) { 128 case int64: 129 switch y.iface.(type) { 130 case int64: 131 ny := y.AsInt() 132 if ny == 0 { 133 return NilValue, true, errors.New("attempt to divide by zero") 134 } 135 return IntValue(floordivInt(x.AsInt(), ny)), true, nil 136 case float64: 137 return FloatValue(floordivFloat(float64(x.AsInt()), y.AsFloat())), true, nil 138 } 139 case float64: 140 switch y.iface.(type) { 141 case int64: 142 return FloatValue(floordivFloat(x.AsFloat(), float64(y.AsInt()))), true, nil 143 case float64: 144 return FloatValue(floordivFloat(x.AsFloat(), y.AsFloat())), true, nil 145 } 146 } 147 return NilValue, false, nil 148 } 149 150 func modInt(x, y int64) int64 { 151 r := x % y 152 if r != 0 && (r < 0) != (y < 0) { 153 r += y 154 } 155 return r 156 } 157 158 func modFloat(x, y float64) float64 { 159 r := math.Mod(x, y) 160 if r != 0 && (r < 0) != (y < 0) { 161 r += y 162 } 163 return r 164 } 165 166 // Mod returns (z, true, nil) where z is the (integer or float) value 167 // representing x%y if x and y are numbers and y != 0, if y == 0 it returns 168 // (NilValue, true, mod_by_zero_error), else (NilValue, false, nil) if x or y is 169 // not a number. 170 func Mod(x Value, y Value) (Value, bool, error) { 171 switch x.iface.(type) { 172 case int64: 173 switch y.iface.(type) { 174 case int64: 175 ny := y.AsInt() 176 if ny == 0 { 177 return NilValue, true, errors.New("attempt to perform 'n%0'") 178 } 179 return IntValue(modInt(x.AsInt(), ny)), true, nil 180 case float64: 181 return FloatValue(modFloat(float64(x.AsInt()), y.AsFloat())), true, nil 182 } 183 case float64: 184 switch y.iface.(type) { 185 case int64: 186 return FloatValue(modFloat(x.AsFloat(), float64(y.AsInt()))), true, nil 187 case float64: 188 return FloatValue(modFloat(x.AsFloat(), y.AsFloat())), true, nil 189 } 190 } 191 return NilValue, false, nil 192 } 193 194 func powFloat(x, y float64) float64 { 195 return math.Pow(x, y) 196 } 197 198 // Pow returns (z, true) where z is the (float) value representing x^y if x and 199 // y are numbers, else (NilValue, false). 200 func Pow(x, y Value) (Value, bool) { 201 var fx, fy float64 202 switch x.iface.(type) { 203 case int64: 204 fx = float64(x.AsInt()) 205 case float64: 206 fx = x.AsFloat() 207 default: 208 return NilValue, false 209 } 210 switch y.iface.(type) { 211 case int64: 212 fy = float64(y.AsInt()) 213 case float64: 214 fy = y.AsFloat() 215 default: 216 return NilValue, false 217 } 218 return FloatValue(powFloat(fx, fy)), true 219 } 220 221 func binaryArithFallback(t *Thread, op string, x, y Value) (Value, error) { 222 res, err, ok := metabin(t, op, x, y) 223 if ok { 224 return res, err 225 } 226 return NilValue, BinaryArithmeticError(op[2:], x, y) 227 } 228 229 // BinaryArithmeticError returns an error describing the problem with trying to 230 // perform x op y. 231 func BinaryArithmeticError(op string, x, y Value) error { 232 var wrongVal Value 233 switch { 234 case numberType(y) != NaN: 235 wrongVal = x 236 case numberType(x) != NaN: 237 wrongVal = y 238 default: 239 return fmt.Errorf("attempt to %s a '%s' with a '%s'", op, x.CustomTypeName(), y.CustomTypeName()) 240 } 241 return fmt.Errorf("attempt to perform arithmetic on a %s value", wrongVal.CustomTypeName()) 242 } 243 244 func unaryArithFallback(t *Thread, op string, x Value) (Value, error) { 245 res, err, ok := metaun(t, op, x) 246 if ok { 247 return res, err 248 } 249 return NilValue, UnaryArithmeticError(op[2:], x) 250 } 251 252 // UnaryArithmeticError returns an error describing the problem with trying to 253 // perform the unary operation op(x). 254 func UnaryArithmeticError(op string, x Value) error { 255 return fmt.Errorf("attempt to %s a '%s'", op, x.CustomTypeName()) 256 }