github.com/expr-lang/expr@v1.16.9/expr.go (about) 1 package expr 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "time" 8 9 "github.com/expr-lang/expr/ast" 10 "github.com/expr-lang/expr/builtin" 11 "github.com/expr-lang/expr/checker" 12 "github.com/expr-lang/expr/compiler" 13 "github.com/expr-lang/expr/conf" 14 "github.com/expr-lang/expr/file" 15 "github.com/expr-lang/expr/optimizer" 16 "github.com/expr-lang/expr/patcher" 17 "github.com/expr-lang/expr/vm" 18 ) 19 20 // Option for configuring config. 21 type Option func(c *conf.Config) 22 23 // Env specifies expected input of env for type checks. 24 // If struct is passed, all fields will be treated as variables, 25 // as well as all fields of embedded structs and struct itself. 26 // If map is passed, all items will be treated as variables. 27 // Methods defined on this type will be available as functions. 28 func Env(env any) Option { 29 return func(c *conf.Config) { 30 c.WithEnv(env) 31 } 32 } 33 34 // AllowUndefinedVariables allows to use undefined variables inside expressions. 35 // This can be used with expr.Env option to partially define a few variables. 36 func AllowUndefinedVariables() Option { 37 return func(c *conf.Config) { 38 c.Strict = false 39 } 40 } 41 42 // Operator allows to replace a binary operator with a function. 43 func Operator(operator string, fn ...string) Option { 44 return func(c *conf.Config) { 45 p := &patcher.OperatorOverloading{ 46 Operator: operator, 47 Overloads: fn, 48 Types: c.Types, 49 Functions: c.Functions, 50 } 51 c.Visitors = append(c.Visitors, p) 52 } 53 } 54 55 // ConstExpr defines func expression as constant. If all argument to this function is constants, 56 // then it can be replaced by result of this func call on compile step. 57 func ConstExpr(fn string) Option { 58 return func(c *conf.Config) { 59 c.ConstExpr(fn) 60 } 61 } 62 63 // AsAny tells the compiler to expect any result. 64 func AsAny() Option { 65 return func(c *conf.Config) { 66 c.ExpectAny = true 67 } 68 } 69 70 // AsKind tells the compiler to expect kind of the result. 71 func AsKind(kind reflect.Kind) Option { 72 return func(c *conf.Config) { 73 c.Expect = kind 74 c.ExpectAny = true 75 } 76 } 77 78 // AsBool tells the compiler to expect a boolean result. 79 func AsBool() Option { 80 return func(c *conf.Config) { 81 c.Expect = reflect.Bool 82 c.ExpectAny = true 83 } 84 } 85 86 // AsInt tells the compiler to expect an int result. 87 func AsInt() Option { 88 return func(c *conf.Config) { 89 c.Expect = reflect.Int 90 c.ExpectAny = true 91 } 92 } 93 94 // AsInt64 tells the compiler to expect an int64 result. 95 func AsInt64() Option { 96 return func(c *conf.Config) { 97 c.Expect = reflect.Int64 98 c.ExpectAny = true 99 } 100 } 101 102 // AsFloat64 tells the compiler to expect a float64 result. 103 func AsFloat64() Option { 104 return func(c *conf.Config) { 105 c.Expect = reflect.Float64 106 c.ExpectAny = true 107 } 108 } 109 110 // WarnOnAny tells the compiler to warn if expression return any type. 111 func WarnOnAny() Option { 112 return func(c *conf.Config) { 113 if c.Expect == reflect.Invalid { 114 panic("WarnOnAny() works only with combination with AsInt(), AsBool(), etc. options") 115 } 116 c.ExpectAny = false 117 } 118 } 119 120 // Optimize turns optimizations on or off. 121 func Optimize(b bool) Option { 122 return func(c *conf.Config) { 123 c.Optimize = b 124 } 125 } 126 127 // Patch adds visitor to list of visitors what will be applied before compiling AST to bytecode. 128 func Patch(visitor ast.Visitor) Option { 129 return func(c *conf.Config) { 130 c.Visitors = append(c.Visitors, visitor) 131 } 132 } 133 134 // Function adds function to list of functions what will be available in expressions. 135 func Function(name string, fn func(params ...any) (any, error), types ...any) Option { 136 return func(c *conf.Config) { 137 ts := make([]reflect.Type, len(types)) 138 for i, t := range types { 139 t := reflect.TypeOf(t) 140 if t.Kind() == reflect.Ptr { 141 t = t.Elem() 142 } 143 if t.Kind() != reflect.Func { 144 panic(fmt.Sprintf("expr: type of %s is not a function", name)) 145 } 146 ts[i] = t 147 } 148 c.Functions[name] = &builtin.Function{ 149 Name: name, 150 Func: fn, 151 Types: ts, 152 } 153 } 154 } 155 156 // DisableAllBuiltins disables all builtins. 157 func DisableAllBuiltins() Option { 158 return func(c *conf.Config) { 159 for name := range c.Builtins { 160 c.Disabled[name] = true 161 } 162 } 163 } 164 165 // DisableBuiltin disables builtin function. 166 func DisableBuiltin(name string) Option { 167 return func(c *conf.Config) { 168 c.Disabled[name] = true 169 } 170 } 171 172 // EnableBuiltin enables builtin function. 173 func EnableBuiltin(name string) Option { 174 return func(c *conf.Config) { 175 delete(c.Disabled, name) 176 } 177 } 178 179 // WithContext passes context to all functions calls with a context.Context argument. 180 func WithContext(name string) Option { 181 return Patch(patcher.WithContext{ 182 Name: name, 183 }) 184 } 185 186 // Timezone sets default timezone for date() and now() builtin functions. 187 func Timezone(name string) Option { 188 tz, err := time.LoadLocation(name) 189 if err != nil { 190 panic(err) 191 } 192 return Patch(patcher.WithTimezone{ 193 Location: tz, 194 }) 195 } 196 197 // Compile parses and compiles given input expression to bytecode program. 198 func Compile(input string, ops ...Option) (*vm.Program, error) { 199 config := conf.CreateNew() 200 for _, op := range ops { 201 op(config) 202 } 203 for name := range config.Disabled { 204 delete(config.Builtins, name) 205 } 206 config.Check() 207 208 tree, err := checker.ParseCheck(input, config) 209 if err != nil { 210 return nil, err 211 } 212 213 if config.Optimize { 214 err = optimizer.Optimize(&tree.Node, config) 215 if err != nil { 216 var fileError *file.Error 217 if errors.As(err, &fileError) { 218 return nil, fileError.Bind(tree.Source) 219 } 220 return nil, err 221 } 222 } 223 224 program, err := compiler.Compile(tree, config) 225 if err != nil { 226 return nil, err 227 } 228 229 return program, nil 230 } 231 232 // Run evaluates given bytecode program. 233 func Run(program *vm.Program, env any) (any, error) { 234 return vm.Run(program, env) 235 } 236 237 // Eval parses, compiles and runs given input. 238 func Eval(input string, env any) (any, error) { 239 if _, ok := env.(Option); ok { 240 return nil, fmt.Errorf("misused expr.Eval: second argument (env) should be passed without expr.Env") 241 } 242 243 program, err := Compile(input) 244 if err != nil { 245 return nil, err 246 } 247 248 output, err := Run(program, env) 249 if err != nil { 250 return nil, err 251 } 252 253 return output, nil 254 }