github.com/expr-lang/expr@v1.16.9/optimizer/const_expr.go (about) 1 package optimizer 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 . "github.com/expr-lang/expr/ast" 9 "github.com/expr-lang/expr/file" 10 ) 11 12 var errorType = reflect.TypeOf((*error)(nil)).Elem() 13 14 type constExpr struct { 15 applied bool 16 err error 17 fns map[string]reflect.Value 18 } 19 20 func (c *constExpr) Visit(node *Node) { 21 defer func() { 22 if r := recover(); r != nil { 23 msg := fmt.Sprintf("%v", r) 24 // Make message more actual, it's a runtime error, but at compile step. 25 msg = strings.Replace(msg, "runtime error:", "compile error:", 1) 26 c.err = &file.Error{ 27 Location: (*node).Location(), 28 Message: msg, 29 } 30 } 31 }() 32 33 patch := func(newNode Node) { 34 c.applied = true 35 Patch(node, newNode) 36 } 37 38 if call, ok := (*node).(*CallNode); ok { 39 if name, ok := call.Callee.(*IdentifierNode); ok { 40 fn, ok := c.fns[name.Value] 41 if ok { 42 in := make([]reflect.Value, len(call.Arguments)) 43 for i := 0; i < len(call.Arguments); i++ { 44 arg := call.Arguments[i] 45 var param any 46 47 switch a := arg.(type) { 48 case *NilNode: 49 param = nil 50 case *IntegerNode: 51 param = a.Value 52 case *FloatNode: 53 param = a.Value 54 case *BoolNode: 55 param = a.Value 56 case *StringNode: 57 param = a.Value 58 case *ConstantNode: 59 param = a.Value 60 61 default: 62 return // Const expr optimization not applicable. 63 } 64 65 if param == nil && reflect.TypeOf(param) == nil { 66 // In case of nil value and nil type use this hack, 67 // otherwise reflect.Call will panic on zero value. 68 in[i] = reflect.ValueOf(¶m).Elem() 69 } else { 70 in[i] = reflect.ValueOf(param) 71 } 72 } 73 74 out := fn.Call(in) 75 value := out[0].Interface() 76 if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() { 77 c.err = out[1].Interface().(error) 78 return 79 } 80 constNode := &ConstantNode{Value: value} 81 patch(constNode) 82 } 83 } 84 } 85 }