github.com/avicd/go-utilx@v0.1.0/evalx/eval.go (about) 1 package evalx 2 3 import ( 4 "errors" 5 "fmt" 6 "github.com/avicd/go-utilx/conv" 7 "github.com/avicd/go-utilx/refx" 8 "github.com/avicd/go-utilx/tokx" 9 "go/ast" 10 "go/parser" 11 "go/token" 12 "reflect" 13 "strings" 14 ) 15 16 func Eval(text string, cts ...Context) (any, error) { 17 text = strings.TrimSpace(text) 18 if text == "" { 19 return nil, errors.New("empty expression") 20 } 21 expr := tokx.DoubleQuota(text) 22 var stack *Stack 23 if len(cts) > 0 && cts[0] != nil { 24 stack = StackOf(cts[0]) 25 } else { 26 stack = StackOf(NewScope()) 27 } 28 var astExpr ast.Expr 29 if ae, ok := stack.Ctx.CacheOf(expr); ok { 30 astExpr = ae 31 } else { 32 var err error 33 astExpr, err = parser.ParseExpr(expr) 34 if err != nil { 35 return nil, err 36 } else { 37 stack.Ctx.Cache(expr, astExpr) 38 } 39 } 40 val := evalExpr(astExpr, stack) 41 if stack.Error != nil { 42 return nil, stack.Error 43 } 44 return val, nil 45 } 46 47 func evalExpr(buf ast.Expr, stack *Stack) any { 48 defer func() { 49 rc := recover() 50 if rc != nil { 51 stack.Error = refx.AsError(rc) 52 } 53 }() 54 switch expr := buf.(type) { 55 case *ast.SelectorExpr: 56 return evalSelector(expr, stack) 57 case *ast.Ident: 58 return evalIdent(expr, stack, false) 59 case *ast.BasicLit: 60 return evalBasicLit(expr) 61 case *ast.UnaryExpr: 62 return evalUnary(expr, stack) 63 case *ast.ParenExpr: 64 return evalExpr(expr.X, stack) 65 case *ast.IndexExpr: 66 return evalIndex(expr, stack) 67 case *ast.CallExpr: 68 return evalCall(expr, stack) 69 case *ast.BinaryExpr: 70 return evalBinary(expr, stack) 71 } 72 return nil 73 } 74 75 func evalSelector(expr *ast.SelectorExpr, stack *Stack) any { 76 target := stack.PopTarget() 77 var parent any 78 switch exprX := expr.X.(type) { 79 case *ast.SelectorExpr: 80 return evalSelector(exprX, stack) 81 case *ast.Ident: 82 parent = evalIdent(exprX, stack, true) 83 default: 84 parent = evalExpr(expr.X, stack) 85 } 86 if parent != nil { 87 if target == METHOD { 88 if method, ok := refx.MethodOf(parent, expr.Sel.Name); ok { 89 return method 90 } 91 } 92 if val, ok := refx.PropOf(parent, expr.Sel.Name); ok { 93 if target == METHOD { 94 if refx.IsFunc(val) { 95 return val 96 } 97 } else { 98 return val 99 } 100 } 101 } 102 return nil 103 } 104 105 func evalIndex(expr *ast.IndexExpr, stack *Stack) any { 106 target := stack.PopTarget() 107 obj := evalExpr(expr.X, stack) 108 index := evalExpr(expr.Index, stack) 109 if target == METHOD { 110 if method, ok := refx.MethodOf(obj, index); ok { 111 return method 112 } 113 } 114 if val, ok := refx.PropOf(obj, index); ok { 115 if target == METHOD { 116 if refx.IsFunc(val) { 117 return val 118 } 119 } else { 120 return val 121 } 122 } 123 return nil 124 } 125 126 func evalIdent(expr *ast.Ident, stack *Stack, sel bool) any { 127 target := stack.PopTarget() 128 if !sel { 129 switch expr.Name { 130 case "nil", "null": 131 return nil 132 case "true": 133 return true 134 case "false": 135 return false 136 } 137 } 138 if target == METHOD { 139 if method, ok := stack.Ctx.MethodOf(expr.Name); ok { 140 return method 141 } 142 } 143 if val, ok := stack.Ctx.ValueOf(expr.Name); ok { 144 if target == METHOD { 145 if refx.IsFunc(val) { 146 return val 147 } 148 } else { 149 return val 150 } 151 } 152 return nil 153 } 154 155 func evalUnary(expr *ast.UnaryExpr, stack *Stack) any { 156 ret := evalExpr(expr.X, stack) 157 switch expr.Op { 158 case token.NOT: 159 return !refx.AsBool(ret) 160 case token.SUB: 161 if refx.IsInteger(ret) { 162 return -refx.AsInt64(ret) 163 } else if refx.IsUInteger(ret) { 164 return -refx.AsUint64(ret) 165 } else if refx.IsFloat(ret) { 166 return -refx.AsFloat64(ret) 167 } else { 168 panic(fmt.Errorf("evalx: invalid operator '%s' on %v", expr.Op, expr.X)) 169 } 170 case token.ADD: 171 if refx.IsNumber(ret) { 172 panic(fmt.Errorf("evalx: invalid operator '%s' on %v", expr.Op, expr.X)) 173 } 174 } 175 return ret 176 } 177 178 func evalCall(expr *ast.CallExpr, stack *Stack) any { 179 stack.PushTarget(METHOD) 180 val := evalExpr(expr.Fun, stack) 181 if val == nil { 182 panic(fmt.Errorf("evalx: invalid function")) 183 return nil 184 } 185 method := refx.ValueOf(val) 186 var args []reflect.Value 187 for _, buf := range expr.Args { 188 argVal := evalExpr(buf, stack) 189 args = append(args, refx.ValueOf(argVal)) 190 } 191 values := method.Call(args) 192 if len(values) > 0 { 193 return values[0].Interface() 194 } 195 return nil 196 } 197 198 func evalBinary(expr *ast.BinaryExpr, stack *Stack) any { 199 left := evalExpr(expr.X, stack) 200 if expr.Op == token.LOR && refx.AsBool(left) { 201 return true 202 } 203 var right any 204 if expr.Op == token.ADD { 205 if refx.IsString(left) { 206 right = evalString(expr.Y, stack) 207 } else { 208 right = evalExpr(expr.Y, stack) 209 if refx.IsString(right) { 210 left = evalString(expr.X, stack) 211 } 212 } 213 } else { 214 right = evalExpr(expr.Y, stack) 215 } 216 stack.X = left 217 stack.Y = right 218 var ret any 219 switch expr.Op { 220 case token.LOR: 221 ret = refx.AsBool(right) 222 case token.LAND: 223 ret = refx.AsBool(left) && refx.AsBool(right) 224 case token.EQL, token.NEQ, token.GTR, token.LSS, token.GEQ, token.LEQ: 225 ret = evalCmpBool(expr, left, right) 226 case token.ADD, token.SUB, token.MUL, token.QUO, token.REM: 227 ret = evalArithmetic(expr, stack) 228 case token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT: 229 ret = evalBitOpr(expr, stack) 230 } 231 return ret 232 } 233 234 func evalBasicLit(expr *ast.BasicLit) any { 235 switch expr.Kind { 236 case token.STRING: 237 return strings.Trim(expr.Value, "\"") 238 case token.CHAR: 239 return strings.Trim(expr.Value, "'") 240 case token.INT: 241 return conv.ParseInt(expr.Value) 242 case token.FLOAT: 243 return conv.ParseFloat(expr.Value) 244 default: 245 return nil 246 } 247 } 248 249 func evalString(input ast.Expr, stack *Stack) string { 250 if expr, ok := input.(*ast.BinaryExpr); ok { 251 if expr.Op == token.ADD { 252 left := evalString(expr.X, stack) 253 right := evalString(expr.Y, stack) 254 return left + right 255 } 256 } 257 return refx.AsString(evalExpr(input, stack)) 258 } 259 260 func evalCmpBool(expr *ast.BinaryExpr, x, y any) bool { 261 var ret bool 262 cmp := refx.Cmp(x, y) 263 switch expr.Op { 264 case token.EQL: 265 ret = cmp == refx.CmpEq 266 case token.NEQ: 267 ret = cmp != refx.CmpEq 268 case token.GTR: 269 ret = cmp == refx.CmpGtr 270 case token.LSS: 271 ret = cmp == refx.CmpLss 272 case token.GEQ: 273 ret = cmp == refx.CmpGtr || cmp == refx.CmpEq 274 case token.LEQ: 275 ret = cmp == refx.CmpLss || cmp == refx.CmpEq 276 } 277 return ret 278 } 279 280 func evalArithmetic(expr *ast.BinaryExpr, stack *Stack) any { 281 var ret any 282 x := stack.X 283 y := stack.Y 284 if expr.Op == token.ADD && (refx.IsString(x) || refx.IsString(y)) { 285 return refx.AsString(x) + refx.AsString(y) 286 } 287 if refx.IsNumber(x) && refx.IsNumber(y) { 288 if refx.IsGeneralInt(x) && refx.IsGeneralInt(y) { 289 a := refx.AsInt64(x) 290 b := refx.AsInt64(y) 291 switch expr.Op { 292 case token.ADD: 293 ret = a + b 294 case token.SUB: 295 ret = a - b 296 case token.MUL: 297 ret = a * b 298 case token.QUO: 299 ret = a / b 300 case token.REM: 301 ret = a % b 302 } 303 } else { 304 a := refx.AsFloat64(x) 305 b := refx.AsFloat64(y) 306 switch expr.Op { 307 case token.ADD: 308 ret = a + b 309 case token.SUB: 310 ret = a - b 311 case token.MUL: 312 ret = a * b 313 case token.QUO: 314 ret = a / b 315 case token.REM: 316 panic(fmt.Errorf("evalx: invalid operator '%s' on float", expr.Op)) 317 } 318 } 319 } 320 return ret 321 } 322 323 func evalBitOpr(expr *ast.BinaryExpr, stack *Stack) any { 324 var val uint64 325 x := stack.X 326 y := stack.Y 327 if refx.IsGeneralInt(x) && refx.IsGeneralInt(y) { 328 a := refx.AsUint64(x) 329 b := refx.AsUint64(y) 330 switch expr.Op { 331 case token.AND: 332 val = a & b 333 case token.OR: 334 val = a | b 335 case token.XOR: 336 val = a ^ b 337 case token.SHL: 338 val = a << b 339 case token.SHR: 340 val = a >> b 341 case token.AND_NOT: 342 val = a &^ b 343 } 344 } else { 345 panic(fmt.Errorf("evalx: bit operator work only on integer")) 346 } 347 return val 348 }