diff --git a/vendor/github.com/antonmedv/expr/.gitignore b/vendor/github.com/antonmedv/expr/.gitignore new file mode 100644 index 0000000..39b3c48 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/.gitignore @@ -0,0 +1,7 @@ +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out diff --git a/vendor/github.com/antonmedv/expr/.travis.yml b/vendor/github.com/antonmedv/expr/.travis.yml new file mode 100644 index 0000000..745b115 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - 1.13.x diff --git a/vendor/github.com/antonmedv/expr/LICENSE b/vendor/github.com/antonmedv/expr/LICENSE new file mode 100644 index 0000000..7d058f8 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Anton Medvedev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/antonmedv/expr/README.md b/vendor/github.com/antonmedv/expr/README.md new file mode 100644 index 0000000..00a644f --- /dev/null +++ b/vendor/github.com/antonmedv/expr/README.md @@ -0,0 +1,163 @@ +# Expr +[![Build Status](https://travis-ci.org/antonmedv/expr.svg?branch=master)](https://travis-ci.org/antonmedv/expr) +[![Go Report Card](https://goreportcard.com/badge/github.com/antonmedv/expr)](https://goreportcard.com/report/github.com/antonmedv/expr) +[![GoDoc](https://godoc.org/github.com/antonmedv/expr?status.svg)](https://godoc.org/github.com/antonmedv/expr) + +expr logo + +**Expr** package provides an engine that can compile and evaluate expressions. +An expression is a one-liner that returns a value (mostly, but not limited to, booleans). +It is designed for simplicity, speed and safety. + +The purpose of the package is to allow users to use expressions inside configuration for more complex logic. +It is a perfect candidate for the foundation of a _business rule engine_. +The idea is to let configure things in a dynamic way without recompile of a program: + +```coffeescript +# Get the special price if +user.Group in ["good_customers", "collaborator"] + +# Promote article to the homepage when +len(article.Comments) > 100 and article.Category not in ["misc"] + +# Send an alert when +product.Stock < 15 +``` + +## Features + +* Seamless integration with Go (no need to redefine types) +* Static typing ([example](https://godoc.org/github.com/antonmedv/expr#example-Env)). + ```go + out, err := expr.Compile(`name + age`) + // err: invalid operation + (mismatched types string and int) + // | name + age + // | .....^ + ``` +* User-friendly error messages. +* Reasonable set of basic operators. +* Builtins `all`, `none`, `any`, `one`, `filter`, `map`. + ```coffeescript + all(Tweets, {.Size <= 280}) + ``` +* Fast ([benchmarks](https://github.com/antonmedv/golang-expression-evaluation-comparison#readme)): uses bytecode virtual machine and optimizing compiler. + +## Install + +``` +go get github.com/antonmedv/expr +``` + +## Documentation + +* See [Getting Started](docs/Getting-Started.md) page for developer documentation. +* See [Language Definition](docs/Language-Definition.md) page to learn the syntax. + +## Expr Code Editor + + + Expr Code Editor + + +Also, I have an embeddable code editor written in JavaScript which allows editing expressions with syntax highlighting and autocomplete based on your types declaration. + +[Learn more →](https://antonmedv.github.io/expr/) + +## Examples + +[Play Online](https://play.golang.org/p/z7T8ytJ1T1d) + +```go +package main + +import ( + "fmt" + "github.com/antonmedv/expr" +) + +func main() { + env := map[string]interface{}{ + "greet": "Hello, %v!", + "names": []string{"world", "you"}, + "sprintf": fmt.Sprintf, + } + + code := `sprintf(greet, names[0])` + + program, err := expr.Compile(code, expr.Env(env)) + if err != nil { + panic(err) + } + + output, err := expr.Run(program, env) + if err != nil { + panic(err) + } + + fmt.Println(output) +} +``` + +[Play Online](https://play.golang.org/p/4S4brsIvU4i) + +```go +package main + +import ( + "fmt" + "github.com/antonmedv/expr" +) + +type Tweet struct { + Len int +} + +type Env struct { + Tweets []Tweet +} + +func main() { + code := `all(Tweets, {.Len <= 240})` + + program, err := expr.Compile(code, expr.Env(Env{})) + if err != nil { + panic(err) + } + + env := Env{ + Tweets: []Tweet{{42}, {98}, {69}}, + } + output, err := expr.Run(program, env) + if err != nil { + panic(err) + } + + fmt.Println(output) +} +``` + +## Contributing + +**Expr** consist of a few packages for parsing source code to AST, type checking AST, compiling to bytecode and VM for running bytecode program. + +Also expr provides powerful tool [exe](cmd/exe) for debugging. It has interactive terminal debugger for our bytecode virtual machine. + +

+ debugger +

+ + +## Who is using Expr? + +* Aviasales [Aviasales](https://aviasales.ru) are actively using Expr for different parts of the search engine. +* Argo [Argo Rollouts](https://argoproj.github.io/argo-rollouts/) - Progressive Delivery for Kubernetes. +* Argo [Argo Workflows](https://argoproj.github.io/argo/) - The workflow engine for KubernetesOverview. +* CrowdSec [Crowdsec](https://crowdsec.net/) - A security automation tool. +* [Mystery Minds](https://www.mysteryminds.com/en/) uses Expr to allow easy yet powerful customization of its matching algorithm. +* [qiniu](https://www.qiniu.com/) qiniu cloud use Expr in trade systems. + +[Add your company too](https://github.com/antonmedv/expr/edit/master/README.md) + +## License + +[MIT](LICENSE) diff --git a/vendor/github.com/antonmedv/expr/ast/node.go b/vendor/github.com/antonmedv/expr/ast/node.go new file mode 100644 index 0000000..4b2b5c2 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/ast/node.go @@ -0,0 +1,171 @@ +package ast + +import ( + "reflect" + "regexp" + + "github.com/antonmedv/expr/file" +) + +// Node represents items of abstract syntax tree. +type Node interface { + Location() file.Location + SetLocation(file.Location) + Type() reflect.Type + SetType(reflect.Type) +} + +func Patch(node *Node, newNode Node) { + newNode.SetType((*node).Type()) + newNode.SetLocation((*node).Location()) + *node = newNode +} + +type base struct { + loc file.Location + nodeType reflect.Type +} + +func (n *base) Location() file.Location { + return n.loc +} + +func (n *base) SetLocation(loc file.Location) { + n.loc = loc +} + +func (n *base) Type() reflect.Type { + return n.nodeType +} + +func (n *base) SetType(t reflect.Type) { + n.nodeType = t +} + +type NilNode struct { + base +} + +type IdentifierNode struct { + base + Value string + NilSafe bool +} + +type IntegerNode struct { + base + Value int +} + +type FloatNode struct { + base + Value float64 +} + +type BoolNode struct { + base + Value bool +} + +type StringNode struct { + base + Value string +} + +type ConstantNode struct { + base + Value interface{} +} + +type UnaryNode struct { + base + Operator string + Node Node +} + +type BinaryNode struct { + base + Operator string + Left Node + Right Node +} + +type MatchesNode struct { + base + Regexp *regexp.Regexp + Left Node + Right Node +} + +type PropertyNode struct { + base + Node Node + Property string + NilSafe bool +} + +type IndexNode struct { + base + Node Node + Index Node +} + +type SliceNode struct { + base + Node Node + From Node + To Node +} + +type MethodNode struct { + base + Node Node + Method string + Arguments []Node + NilSafe bool +} + +type FunctionNode struct { + base + Name string + Arguments []Node + Fast bool +} + +type BuiltinNode struct { + base + Name string + Arguments []Node +} + +type ClosureNode struct { + base + Node Node +} + +type PointerNode struct { + base +} + +type ConditionalNode struct { + base + Cond Node + Exp1 Node + Exp2 Node +} + +type ArrayNode struct { + base + Nodes []Node +} + +type MapNode struct { + base + Pairs []Node +} + +type PairNode struct { + base + Key Node + Value Node +} diff --git a/vendor/github.com/antonmedv/expr/ast/print.go b/vendor/github.com/antonmedv/expr/ast/print.go new file mode 100644 index 0000000..285984b --- /dev/null +++ b/vendor/github.com/antonmedv/expr/ast/print.go @@ -0,0 +1,59 @@ +package ast + +import ( + "fmt" + "reflect" + "regexp" +) + +func Dump(node Node) string { + return dump(reflect.ValueOf(node), "") +} + +func dump(v reflect.Value, ident string) string { + if !v.IsValid() { + return "nil" + } + t := v.Type() + switch t.Kind() { + case reflect.Struct: + out := t.Name() + "{\n" + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if isPrivate(f.Name) { + continue + } + s := v.Field(i) + out += fmt.Sprintf("%v%v: %v,\n", ident+"\t", f.Name, dump(s, ident+"\t")) + } + return out + ident + "}" + case reflect.Slice: + if v.Len() == 0 { + return "[]" + } + out := "[\n" + for i := 0; i < v.Len(); i++ { + s := v.Index(i) + out += fmt.Sprintf("%v%v,", ident+"\t", dump(s, ident+"\t")) + if i+1 < v.Len() { + out += "\n" + } + } + return out + "\n" + ident + "]" + case reflect.Ptr: + return dump(v.Elem(), ident) + case reflect.Interface: + return dump(reflect.ValueOf(v.Interface()), ident) + + case reflect.String: + return fmt.Sprintf("%q", v) + default: + return fmt.Sprintf("%v", v) + } +} + +var isCapital = regexp.MustCompile("^[A-Z]") + +func isPrivate(s string) bool { + return !isCapital.Match([]byte(s)) +} diff --git a/vendor/github.com/antonmedv/expr/ast/visitor.go b/vendor/github.com/antonmedv/expr/ast/visitor.go new file mode 100644 index 0000000..a3e270e --- /dev/null +++ b/vendor/github.com/antonmedv/expr/ast/visitor.go @@ -0,0 +1,108 @@ +package ast + +import "fmt" + +type Visitor interface { + Enter(node *Node) + Exit(node *Node) +} + +type walker struct { + visitor Visitor +} + +func Walk(node *Node, visitor Visitor) { + w := walker{ + visitor: visitor, + } + w.walk(node) +} + +func (w *walker) walk(node *Node) { + w.visitor.Enter(node) + + switch n := (*node).(type) { + case *NilNode: + w.visitor.Exit(node) + case *IdentifierNode: + w.visitor.Exit(node) + case *IntegerNode: + w.visitor.Exit(node) + case *FloatNode: + w.visitor.Exit(node) + case *BoolNode: + w.visitor.Exit(node) + case *StringNode: + w.visitor.Exit(node) + case *ConstantNode: + w.visitor.Exit(node) + case *UnaryNode: + w.walk(&n.Node) + w.visitor.Exit(node) + case *BinaryNode: + w.walk(&n.Left) + w.walk(&n.Right) + w.visitor.Exit(node) + case *MatchesNode: + w.walk(&n.Left) + w.walk(&n.Right) + w.visitor.Exit(node) + case *PropertyNode: + w.walk(&n.Node) + w.visitor.Exit(node) + case *IndexNode: + w.walk(&n.Node) + w.walk(&n.Index) + w.visitor.Exit(node) + case *SliceNode: + if n.From != nil { + w.walk(&n.From) + } + if n.To != nil { + w.walk(&n.To) + } + w.visitor.Exit(node) + case *MethodNode: + w.walk(&n.Node) + for i := range n.Arguments { + w.walk(&n.Arguments[i]) + } + w.visitor.Exit(node) + case *FunctionNode: + for i := range n.Arguments { + w.walk(&n.Arguments[i]) + } + w.visitor.Exit(node) + case *BuiltinNode: + for i := range n.Arguments { + w.walk(&n.Arguments[i]) + } + w.visitor.Exit(node) + case *ClosureNode: + w.walk(&n.Node) + w.visitor.Exit(node) + case *PointerNode: + w.visitor.Exit(node) + case *ConditionalNode: + w.walk(&n.Cond) + w.walk(&n.Exp1) + w.walk(&n.Exp2) + w.visitor.Exit(node) + case *ArrayNode: + for i := range n.Nodes { + w.walk(&n.Nodes[i]) + } + w.visitor.Exit(node) + case *MapNode: + for i := range n.Pairs { + w.walk(&n.Pairs[i]) + } + w.visitor.Exit(node) + case *PairNode: + w.walk(&n.Key) + w.walk(&n.Value) + w.visitor.Exit(node) + default: + panic(fmt.Sprintf("undefined node type (%T)", node)) + } +} diff --git a/vendor/github.com/antonmedv/expr/checker/checker.go b/vendor/github.com/antonmedv/expr/checker/checker.go new file mode 100644 index 0000000..282031a --- /dev/null +++ b/vendor/github.com/antonmedv/expr/checker/checker.go @@ -0,0 +1,615 @@ +package checker + +import ( + "fmt" + "reflect" + + "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/conf" + "github.com/antonmedv/expr/file" + "github.com/antonmedv/expr/parser" +) + +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +func Check(tree *parser.Tree, config *conf.Config) (reflect.Type, error) { + v := &visitor{ + collections: make([]reflect.Type, 0), + } + if config != nil { + v.types = config.Types + v.operators = config.Operators + v.expect = config.Expect + v.strict = config.Strict + v.defaultType = config.DefaultType + } + + t := v.visit(tree.Node) + + if v.expect != reflect.Invalid { + switch v.expect { + case reflect.Int64, reflect.Float64: + if !isNumber(t) { + return nil, fmt.Errorf("expected %v, but got %v", v.expect, t) + } + default: + if t.Kind() != v.expect { + return nil, fmt.Errorf("expected %v, but got %v", v.expect, t) + } + } + } + + if v.err != nil { + return t, v.err.Bind(tree.Source) + } + + return t, nil +} + +type visitor struct { + types conf.TypesTable + operators conf.OperatorsTable + expect reflect.Kind + collections []reflect.Type + strict bool + defaultType reflect.Type + err *file.Error +} + +func (v *visitor) visit(node ast.Node) reflect.Type { + var t reflect.Type + switch n := node.(type) { + case *ast.NilNode: + t = v.NilNode(n) + case *ast.IdentifierNode: + t = v.IdentifierNode(n) + case *ast.IntegerNode: + t = v.IntegerNode(n) + case *ast.FloatNode: + t = v.FloatNode(n) + case *ast.BoolNode: + t = v.BoolNode(n) + case *ast.StringNode: + t = v.StringNode(n) + case *ast.ConstantNode: + t = v.ConstantNode(n) + case *ast.UnaryNode: + t = v.UnaryNode(n) + case *ast.BinaryNode: + t = v.BinaryNode(n) + case *ast.MatchesNode: + t = v.MatchesNode(n) + case *ast.PropertyNode: + t = v.PropertyNode(n) + case *ast.IndexNode: + t = v.IndexNode(n) + case *ast.SliceNode: + t = v.SliceNode(n) + case *ast.MethodNode: + t = v.MethodNode(n) + case *ast.FunctionNode: + t = v.FunctionNode(n) + case *ast.BuiltinNode: + t = v.BuiltinNode(n) + case *ast.ClosureNode: + t = v.ClosureNode(n) + case *ast.PointerNode: + t = v.PointerNode(n) + case *ast.ConditionalNode: + t = v.ConditionalNode(n) + case *ast.ArrayNode: + t = v.ArrayNode(n) + case *ast.MapNode: + t = v.MapNode(n) + case *ast.PairNode: + t = v.PairNode(n) + default: + panic(fmt.Sprintf("undefined node type (%T)", node)) + } + node.SetType(t) + return t +} + +func (v *visitor) error(node ast.Node, format string, args ...interface{}) reflect.Type { + if v.err == nil { // show first error + v.err = &file.Error{ + Location: node.Location(), + Message: fmt.Sprintf(format, args...), + } + } + return interfaceType // interface represent undefined type +} + +func (v *visitor) NilNode(*ast.NilNode) reflect.Type { + return nilType +} + +func (v *visitor) IdentifierNode(node *ast.IdentifierNode) reflect.Type { + if v.types == nil { + return interfaceType + } + if t, ok := v.types[node.Value]; ok { + if t.Ambiguous { + return v.error(node, "ambiguous identifier %v", node.Value) + } + return t.Type + } + if !v.strict { + if v.defaultType != nil { + return v.defaultType + } + return interfaceType + } + if !node.NilSafe { + return v.error(node, "unknown name %v", node.Value) + } + return nilType +} + +func (v *visitor) IntegerNode(*ast.IntegerNode) reflect.Type { + return integerType +} + +func (v *visitor) FloatNode(*ast.FloatNode) reflect.Type { + return floatType +} + +func (v *visitor) BoolNode(*ast.BoolNode) reflect.Type { + return boolType +} + +func (v *visitor) StringNode(*ast.StringNode) reflect.Type { + return stringType +} + +func (v *visitor) ConstantNode(node *ast.ConstantNode) reflect.Type { + return reflect.TypeOf(node.Value) +} + +func (v *visitor) UnaryNode(node *ast.UnaryNode) reflect.Type { + t := v.visit(node.Node) + + switch node.Operator { + + case "!", "not": + if isBool(t) { + return boolType + } + + case "+", "-": + if isNumber(t) { + return t + } + + default: + return v.error(node, "unknown operator (%v)", node.Operator) + } + + return v.error(node, `invalid operation: %v (mismatched type %v)`, node.Operator, t) +} + +func (v *visitor) BinaryNode(node *ast.BinaryNode) reflect.Type { + l := v.visit(node.Left) + r := v.visit(node.Right) + + // check operator overloading + if fns, ok := v.operators[node.Operator]; ok { + t, _, ok := conf.FindSuitableOperatorOverload(fns, v.types, l, r) + if ok { + return t + } + } + + switch node.Operator { + case "==", "!=": + if isNumber(l) && isNumber(r) { + return boolType + } + if isComparable(l, r) { + return boolType + } + + case "or", "||", "and", "&&": + if isBool(l) && isBool(r) { + return boolType + } + + case "in", "not in": + if isString(l) && isStruct(r) { + return boolType + } + if isMap(r) { + return boolType + } + if isArray(r) { + return boolType + } + + case "<", ">", ">=", "<=": + if isNumber(l) && isNumber(r) { + return boolType + } + if isString(l) && isString(r) { + return boolType + } + + case "/", "-", "*": + if isNumber(l) && isNumber(r) { + return combined(l, r) + } + + case "**": + if isNumber(l) && isNumber(r) { + return floatType + } + + case "%": + if isInteger(l) && isInteger(r) { + return combined(l, r) + } + + case "+": + if isNumber(l) && isNumber(r) { + return combined(l, r) + } + if isString(l) && isString(r) { + return stringType + } + + case "contains", "startsWith", "endsWith": + if isString(l) && isString(r) { + return boolType + } + + case "..": + if isInteger(l) && isInteger(r) { + return reflect.SliceOf(integerType) + } + + default: + return v.error(node, "unknown operator (%v)", node.Operator) + + } + + return v.error(node, `invalid operation: %v (mismatched types %v and %v)`, node.Operator, l, r) +} + +func (v *visitor) MatchesNode(node *ast.MatchesNode) reflect.Type { + l := v.visit(node.Left) + r := v.visit(node.Right) + + if isString(l) && isString(r) { + return boolType + } + + return v.error(node, `invalid operation: matches (mismatched types %v and %v)`, l, r) +} + +func (v *visitor) PropertyNode(node *ast.PropertyNode) reflect.Type { + t := v.visit(node.Node) + if t, ok := fieldType(t, node.Property); ok { + return t + } + if !node.NilSafe { + return v.error(node, "type %v has no field %v", t, node.Property) + } + return nil +} + +func (v *visitor) IndexNode(node *ast.IndexNode) reflect.Type { + t := v.visit(node.Node) + i := v.visit(node.Index) + + if t, ok := indexType(t); ok { + if !isInteger(i) && !isString(i) { + return v.error(node, "invalid operation: cannot use %v as index to %v", i, t) + } + return t + } + + return v.error(node, "invalid operation: type %v does not support indexing", t) +} + +func (v *visitor) SliceNode(node *ast.SliceNode) reflect.Type { + t := v.visit(node.Node) + + _, isIndex := indexType(t) + + if isIndex || isString(t) { + if node.From != nil { + from := v.visit(node.From) + if !isInteger(from) { + return v.error(node.From, "invalid operation: non-integer slice index %v", from) + } + } + if node.To != nil { + to := v.visit(node.To) + if !isInteger(to) { + return v.error(node.To, "invalid operation: non-integer slice index %v", to) + } + } + return t + } + + return v.error(node, "invalid operation: cannot slice %v", t) +} + +func (v *visitor) FunctionNode(node *ast.FunctionNode) reflect.Type { + if f, ok := v.types[node.Name]; ok { + if fn, ok := isFuncType(f.Type); ok { + + inputParamsCount := 1 // for functions + if f.Method { + inputParamsCount = 2 // for methods + } + + if !isInterface(fn) && + fn.IsVariadic() && + fn.NumIn() == inputParamsCount && + ((fn.NumOut() == 1 && // Function with one return value + fn.Out(0).Kind() == reflect.Interface) || + (fn.NumOut() == 2 && // Function with one return value and an error + fn.Out(0).Kind() == reflect.Interface && + fn.Out(1) == errorType)) { + rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods + if rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface { + node.Fast = true + } + } + + return v.checkFunc(fn, f.Method, node, node.Name, node.Arguments) + } + } + if !v.strict { + if v.defaultType != nil { + return v.defaultType + } + return interfaceType + } + return v.error(node, "unknown func %v", node.Name) +} + +func (v *visitor) MethodNode(node *ast.MethodNode) reflect.Type { + t := v.visit(node.Node) + if f, method, ok := methodType(t, node.Method); ok { + if fn, ok := isFuncType(f); ok { + return v.checkFunc(fn, method, node, node.Method, node.Arguments) + } + } + if !node.NilSafe { + return v.error(node, "type %v has no method %v", t, node.Method) + } + return nil +} + +// checkFunc checks func arguments and returns "return type" of func or method. +func (v *visitor) checkFunc(fn reflect.Type, method bool, node ast.Node, name string, arguments []ast.Node) reflect.Type { + if isInterface(fn) { + return interfaceType + } + + if fn.NumOut() == 0 { + return v.error(node, "func %v doesn't return value", name) + } + if numOut := fn.NumOut(); numOut > 2 { + return v.error(node, "func %v returns more then two values", name) + } + + numIn := fn.NumIn() + + // If func is method on an env, first argument should be a receiver, + // and actual arguments less then numIn by one. + if method { + numIn-- + } + + if fn.IsVariadic() { + if len(arguments) < numIn-1 { + return v.error(node, "not enough arguments to call %v", name) + } + } else { + if len(arguments) > numIn { + return v.error(node, "too many arguments to call %v", name) + } + if len(arguments) < numIn { + return v.error(node, "not enough arguments to call %v", name) + } + } + + offset := 0 + + // Skip first argument in case of the receiver. + if method { + offset = 1 + } + + for i, arg := range arguments { + t := v.visit(arg) + + var in reflect.Type + if fn.IsVariadic() && i >= numIn-1 { + // For variadic arguments fn(xs ...int), go replaces type of xs (int) with ([]int). + // As we compare arguments one by one, we need underling type. + in = fn.In(fn.NumIn() - 1) + in, _ = indexType(in) + } else { + in = fn.In(i + offset) + } + + if isIntegerOrArithmeticOperation(arg) { + t = in + setTypeForIntegers(arg, t) + } + + if t == nil { + continue + } + + if !t.AssignableTo(in) && t.Kind() != reflect.Interface { + return v.error(arg, "cannot use %v as argument (type %v) to call %v ", t, in, name) + } + } + + return fn.Out(0) +} + +func (v *visitor) BuiltinNode(node *ast.BuiltinNode) reflect.Type { + switch node.Name { + + case "len": + param := v.visit(node.Arguments[0]) + if isArray(param) || isMap(param) || isString(param) { + return integerType + } + return v.error(node, "invalid argument for len (type %v)", param) + + case "all", "none", "any", "one": + collection := v.visit(node.Arguments[0]) + if !isArray(collection) { + return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection) + } + + v.collections = append(v.collections, collection) + closure := v.visit(node.Arguments[1]) + v.collections = v.collections[:len(v.collections)-1] + + if isFunc(closure) && + closure.NumOut() == 1 && + closure.NumIn() == 1 && isInterface(closure.In(0)) { + + if !isBool(closure.Out(0)) { + return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String()) + } + return boolType + } + return v.error(node.Arguments[1], "closure should has one input and one output param") + + case "filter": + collection := v.visit(node.Arguments[0]) + if !isArray(collection) { + return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection) + } + + v.collections = append(v.collections, collection) + closure := v.visit(node.Arguments[1]) + v.collections = v.collections[:len(v.collections)-1] + + if isFunc(closure) && + closure.NumOut() == 1 && + closure.NumIn() == 1 && isInterface(closure.In(0)) { + + if !isBool(closure.Out(0)) { + return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String()) + } + if isInterface(collection) { + return arrayType + } + return reflect.SliceOf(collection.Elem()) + } + return v.error(node.Arguments[1], "closure should has one input and one output param") + + case "map": + collection := v.visit(node.Arguments[0]) + if !isArray(collection) { + return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection) + } + + v.collections = append(v.collections, collection) + closure := v.visit(node.Arguments[1]) + v.collections = v.collections[:len(v.collections)-1] + + if isFunc(closure) && + closure.NumOut() == 1 && + closure.NumIn() == 1 && isInterface(closure.In(0)) { + + return reflect.SliceOf(closure.Out(0)) + } + return v.error(node.Arguments[1], "closure should has one input and one output param") + + case "count": + collection := v.visit(node.Arguments[0]) + if !isArray(collection) { + return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection) + } + + v.collections = append(v.collections, collection) + closure := v.visit(node.Arguments[1]) + v.collections = v.collections[:len(v.collections)-1] + + if isFunc(closure) && + closure.NumOut() == 1 && + closure.NumIn() == 1 && isInterface(closure.In(0)) { + if !isBool(closure.Out(0)) { + return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String()) + } + + return integerType + } + return v.error(node.Arguments[1], "closure should has one input and one output param") + + default: + return v.error(node, "unknown builtin %v", node.Name) + } +} + +func (v *visitor) ClosureNode(node *ast.ClosureNode) reflect.Type { + t := v.visit(node.Node) + return reflect.FuncOf([]reflect.Type{interfaceType}, []reflect.Type{t}, false) +} + +func (v *visitor) PointerNode(node *ast.PointerNode) reflect.Type { + if len(v.collections) == 0 { + return v.error(node, "cannot use pointer accessor outside closure") + } + + collection := v.collections[len(v.collections)-1] + + if t, ok := indexType(collection); ok { + return t + } + return v.error(node, "cannot use %v as array", collection) +} + +func (v *visitor) ConditionalNode(node *ast.ConditionalNode) reflect.Type { + c := v.visit(node.Cond) + if !isBool(c) { + return v.error(node.Cond, "non-bool expression (type %v) used as condition", c) + } + + t1 := v.visit(node.Exp1) + t2 := v.visit(node.Exp2) + + if t1 == nil && t2 != nil { + return t2 + } + if t1 != nil && t2 == nil { + return t1 + } + if t1 == nil && t2 == nil { + return nilType + } + if t1.AssignableTo(t2) { + return t1 + } + return interfaceType +} + +func (v *visitor) ArrayNode(node *ast.ArrayNode) reflect.Type { + for _, node := range node.Nodes { + _ = v.visit(node) + } + return arrayType +} + +func (v *visitor) MapNode(node *ast.MapNode) reflect.Type { + for _, pair := range node.Pairs { + v.visit(pair) + } + return mapType +} + +func (v *visitor) PairNode(node *ast.PairNode) reflect.Type { + v.visit(node.Key) + v.visit(node.Value) + return nilType +} diff --git a/vendor/github.com/antonmedv/expr/checker/types.go b/vendor/github.com/antonmedv/expr/checker/types.go new file mode 100644 index 0000000..756ed8f --- /dev/null +++ b/vendor/github.com/antonmedv/expr/checker/types.go @@ -0,0 +1,349 @@ +package checker + +import ( + "reflect" + + "github.com/antonmedv/expr/ast" +) + +var ( + nilType = reflect.TypeOf(nil) + boolType = reflect.TypeOf(true) + integerType = reflect.TypeOf(int(0)) + floatType = reflect.TypeOf(float64(0)) + stringType = reflect.TypeOf("") + arrayType = reflect.TypeOf([]interface{}{}) + mapType = reflect.TypeOf(map[string]interface{}{}) + interfaceType = reflect.TypeOf(new(interface{})).Elem() +) + +func typeWeight(t reflect.Type) int { + switch t.Kind() { + case reflect.Uint: + return 1 + case reflect.Uint8: + return 2 + case reflect.Uint16: + return 3 + case reflect.Uint32: + return 4 + case reflect.Uint64: + return 5 + case reflect.Int: + return 6 + case reflect.Int8: + return 7 + case reflect.Int16: + return 8 + case reflect.Int32: + return 9 + case reflect.Int64: + return 10 + case reflect.Float32: + return 11 + case reflect.Float64: + return 12 + default: + return 0 + } +} + +func combined(a, b reflect.Type) reflect.Type { + if typeWeight(a) > typeWeight(b) { + return a + } else { + return b + } +} + +func dereference(t reflect.Type) reflect.Type { + if t == nil { + return nil + } + if t.Kind() == reflect.Ptr { + t = dereference(t.Elem()) + } + return t +} + +func isComparable(l, r reflect.Type) bool { + l = dereference(l) + r = dereference(r) + + if l == nil || r == nil { // It is possible to compare with nil. + return true + } + if l.Kind() == r.Kind() { + return true + } + if isInterface(l) || isInterface(r) { + return true + } + return false +} + +func isInterface(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Interface: + return true + } + } + return false +} + +func isInteger(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + fallthrough + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.Interface: + return true + } + } + return false +} + +func isFloat(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Float32, reflect.Float64: + return true + case reflect.Interface: + return true + } + } + return false +} + +func isNumber(t reflect.Type) bool { + return isInteger(t) || isFloat(t) +} + +func isBool(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Bool: + return true + case reflect.Interface: + return true + } + } + return false +} + +func isString(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.String: + return true + case reflect.Interface: + return true + } + } + return false +} + +func isArray(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Slice, reflect.Array: + return true + case reflect.Interface: + return true + } + } + return false +} + +func isMap(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Map: + return true + case reflect.Interface: + return true + } + } + return false +} + +func isStruct(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Struct: + return true + } + } + return false +} + +func isFunc(t reflect.Type) bool { + t = dereference(t) + if t != nil { + switch t.Kind() { + case reflect.Func: + return true + } + } + return false +} + +func fieldType(ntype reflect.Type, name string) (reflect.Type, bool) { + ntype = dereference(ntype) + if ntype != nil { + switch ntype.Kind() { + case reflect.Interface: + return interfaceType, true + case reflect.Struct: + // First check all struct's fields. + for i := 0; i < ntype.NumField(); i++ { + f := ntype.Field(i) + if f.Name == name { + return f.Type, true + } + } + + // Second check fields of embedded structs. + for i := 0; i < ntype.NumField(); i++ { + f := ntype.Field(i) + if f.Anonymous { + if t, ok := fieldType(f.Type, name); ok { + return t, true + } + } + } + case reflect.Map: + return ntype.Elem(), true + } + } + + return nil, false +} + +func methodType(t reflect.Type, name string) (reflect.Type, bool, bool) { + if t != nil { + // First, check methods defined on type itself, + // independent of which type it is. + if m, ok := t.MethodByName(name); ok { + if t.Kind() == reflect.Interface { + // In case of interface type method will not have a receiver, + // and to prevent checker decreasing numbers of in arguments + // return method type as not method (second argument is false). + return m.Type, false, true + } else { + return m.Type, true, true + } + } + + d := t + if t.Kind() == reflect.Ptr { + d = t.Elem() + } + + switch d.Kind() { + case reflect.Interface: + return interfaceType, false, true + case reflect.Struct: + // First, check all struct's fields. + for i := 0; i < d.NumField(); i++ { + f := d.Field(i) + if !f.Anonymous && f.Name == name { + return f.Type, false, true + } + } + + // Second, check fields of embedded structs. + for i := 0; i < d.NumField(); i++ { + f := d.Field(i) + if f.Anonymous { + if t, method, ok := methodType(f.Type, name); ok { + return t, method, true + } + } + } + + case reflect.Map: + return d.Elem(), false, true + } + } + return nil, false, false +} + +func indexType(ntype reflect.Type) (reflect.Type, bool) { + ntype = dereference(ntype) + if ntype == nil { + return nil, false + } + + switch ntype.Kind() { + case reflect.Interface: + return interfaceType, true + case reflect.Map, reflect.Array, reflect.Slice: + return ntype.Elem(), true + } + + return nil, false +} + +func isFuncType(ntype reflect.Type) (reflect.Type, bool) { + ntype = dereference(ntype) + if ntype == nil { + return nil, false + } + + switch ntype.Kind() { + case reflect.Interface: + return interfaceType, true + case reflect.Func: + return ntype, true + } + + return nil, false +} + +func isIntegerOrArithmeticOperation(node ast.Node) bool { + switch n := node.(type) { + case *ast.IntegerNode: + return true + case *ast.UnaryNode: + switch n.Operator { + case "+", "-": + return true + } + case *ast.BinaryNode: + switch n.Operator { + case "+", "/", "-", "*": + return true + } + } + return false +} + +func setTypeForIntegers(node ast.Node, t reflect.Type) { + switch n := node.(type) { + case *ast.IntegerNode: + n.SetType(t) + case *ast.UnaryNode: + switch n.Operator { + case "+", "-": + setTypeForIntegers(n.Node, t) + } + case *ast.BinaryNode: + switch n.Operator { + case "+", "/", "-", "*": + setTypeForIntegers(n.Left, t) + setTypeForIntegers(n.Right, t) + } + } +} diff --git a/vendor/github.com/antonmedv/expr/compiler/compiler.go b/vendor/github.com/antonmedv/expr/compiler/compiler.go new file mode 100644 index 0000000..36ac92f --- /dev/null +++ b/vendor/github.com/antonmedv/expr/compiler/compiler.go @@ -0,0 +1,673 @@ +package compiler + +import ( + "encoding/binary" + "fmt" + "math" + "reflect" + + "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/conf" + "github.com/antonmedv/expr/file" + "github.com/antonmedv/expr/parser" + . "github.com/antonmedv/expr/vm" +) + +func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + } + }() + + c := &compiler{ + index: make(map[interface{}]uint16), + locations: make(map[int]file.Location), + } + + if config != nil { + c.mapEnv = config.MapEnv + c.cast = config.Expect + } + + c.compile(tree.Node) + + switch c.cast { + case reflect.Int64: + c.emit(OpCast, encode(0)...) + case reflect.Float64: + c.emit(OpCast, encode(1)...) + } + + program = &Program{ + Source: tree.Source, + Locations: c.locations, + Constants: c.constants, + Bytecode: c.bytecode, + } + return +} + +type compiler struct { + locations map[int]file.Location + constants []interface{} + bytecode []byte + index map[interface{}]uint16 + mapEnv bool + cast reflect.Kind + nodes []ast.Node +} + +func (c *compiler) emit(op byte, b ...byte) int { + c.bytecode = append(c.bytecode, op) + current := len(c.bytecode) + c.bytecode = append(c.bytecode, b...) + + var loc file.Location + if len(c.nodes) > 0 { + loc = c.nodes[len(c.nodes)-1].Location() + } + c.locations[current-1] = loc + + return current +} + +func (c *compiler) emitPush(value interface{}) int { + return c.emit(OpPush, c.makeConstant(value)...) +} + +func (c *compiler) makeConstant(i interface{}) []byte { + hashable := true + switch reflect.TypeOf(i).Kind() { + case reflect.Slice, reflect.Map: + hashable = false + } + + if hashable { + if p, ok := c.index[i]; ok { + return encode(p) + } + } + + c.constants = append(c.constants, i) + if len(c.constants) > math.MaxUint16 { + panic("exceeded constants max space limit") + } + + p := uint16(len(c.constants) - 1) + if hashable { + c.index[i] = p + } + return encode(p) +} + +func (c *compiler) placeholder() []byte { + return []byte{0xFF, 0xFF} +} + +func (c *compiler) patchJump(placeholder int) { + offset := len(c.bytecode) - 2 - placeholder + b := encode(uint16(offset)) + c.bytecode[placeholder] = b[0] + c.bytecode[placeholder+1] = b[1] +} + +func (c *compiler) calcBackwardJump(to int) []byte { + return encode(uint16(len(c.bytecode) + 1 + 2 - to)) +} + +func (c *compiler) compile(node ast.Node) { + c.nodes = append(c.nodes, node) + defer func() { + c.nodes = c.nodes[:len(c.nodes)-1] + }() + + switch n := node.(type) { + case *ast.NilNode: + c.NilNode(n) + case *ast.IdentifierNode: + c.IdentifierNode(n) + case *ast.IntegerNode: + c.IntegerNode(n) + case *ast.FloatNode: + c.FloatNode(n) + case *ast.BoolNode: + c.BoolNode(n) + case *ast.StringNode: + c.StringNode(n) + case *ast.ConstantNode: + c.ConstantNode(n) + case *ast.UnaryNode: + c.UnaryNode(n) + case *ast.BinaryNode: + c.BinaryNode(n) + case *ast.MatchesNode: + c.MatchesNode(n) + case *ast.PropertyNode: + c.PropertyNode(n) + case *ast.IndexNode: + c.IndexNode(n) + case *ast.SliceNode: + c.SliceNode(n) + case *ast.MethodNode: + c.MethodNode(n) + case *ast.FunctionNode: + c.FunctionNode(n) + case *ast.BuiltinNode: + c.BuiltinNode(n) + case *ast.ClosureNode: + c.ClosureNode(n) + case *ast.PointerNode: + c.PointerNode(n) + case *ast.ConditionalNode: + c.ConditionalNode(n) + case *ast.ArrayNode: + c.ArrayNode(n) + case *ast.MapNode: + c.MapNode(n) + case *ast.PairNode: + c.PairNode(n) + default: + panic(fmt.Sprintf("undefined node type (%T)", node)) + } +} + +func (c *compiler) NilNode(node *ast.NilNode) { + c.emit(OpNil) +} + +func (c *compiler) IdentifierNode(node *ast.IdentifierNode) { + v := c.makeConstant(node.Value) + if c.mapEnv { + c.emit(OpFetchMap, v...) + } else if node.NilSafe { + c.emit(OpFetchNilSafe, v...) + } else { + c.emit(OpFetch, v...) + } +} + +func (c *compiler) IntegerNode(node *ast.IntegerNode) { + t := node.Type() + if t == nil { + c.emitPush(node.Value) + return + } + + switch t.Kind() { + case reflect.Float32: + c.emitPush(float32(node.Value)) + case reflect.Float64: + c.emitPush(float64(node.Value)) + + case reflect.Int: + c.emitPush(int(node.Value)) + case reflect.Int8: + c.emitPush(int8(node.Value)) + case reflect.Int16: + c.emitPush(int16(node.Value)) + case reflect.Int32: + c.emitPush(int32(node.Value)) + case reflect.Int64: + c.emitPush(int64(node.Value)) + + case reflect.Uint: + c.emitPush(uint(node.Value)) + case reflect.Uint8: + c.emitPush(uint8(node.Value)) + case reflect.Uint16: + c.emitPush(uint16(node.Value)) + case reflect.Uint32: + c.emitPush(uint32(node.Value)) + case reflect.Uint64: + c.emitPush(uint64(node.Value)) + + default: + c.emitPush(node.Value) + } +} + +func (c *compiler) FloatNode(node *ast.FloatNode) { + c.emitPush(node.Value) +} + +func (c *compiler) BoolNode(node *ast.BoolNode) { + if node.Value { + c.emit(OpTrue) + } else { + c.emit(OpFalse) + } +} + +func (c *compiler) StringNode(node *ast.StringNode) { + c.emitPush(node.Value) +} + +func (c *compiler) ConstantNode(node *ast.ConstantNode) { + c.emitPush(node.Value) +} + +func (c *compiler) UnaryNode(node *ast.UnaryNode) { + c.compile(node.Node) + + switch node.Operator { + + case "!", "not": + c.emit(OpNot) + + case "+": + // Do nothing + + case "-": + c.emit(OpNegate) + + default: + panic(fmt.Sprintf("unknown operator (%v)", node.Operator)) + } +} + +func (c *compiler) BinaryNode(node *ast.BinaryNode) { + l := kind(node.Left) + r := kind(node.Right) + + switch node.Operator { + case "==": + c.compile(node.Left) + c.compile(node.Right) + + if l == r && l == reflect.Int { + c.emit(OpEqualInt) + } else if l == r && l == reflect.String { + c.emit(OpEqualString) + } else { + c.emit(OpEqual) + } + + case "!=": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpEqual) + c.emit(OpNot) + + case "or", "||": + c.compile(node.Left) + end := c.emit(OpJumpIfTrue, c.placeholder()...) + c.emit(OpPop) + c.compile(node.Right) + c.patchJump(end) + + case "and", "&&": + c.compile(node.Left) + end := c.emit(OpJumpIfFalse, c.placeholder()...) + c.emit(OpPop) + c.compile(node.Right) + c.patchJump(end) + + case "in": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpIn) + + case "not in": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpIn) + c.emit(OpNot) + + case "<": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpLess) + + case ">": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpMore) + + case "<=": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpLessOrEqual) + + case ">=": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpMoreOrEqual) + + case "+": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpAdd) + + case "-": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpSubtract) + + case "*": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpMultiply) + + case "/": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpDivide) + + case "%": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpModulo) + + case "**": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpExponent) + + case "contains": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpContains) + + case "startsWith": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpStartsWith) + + case "endsWith": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpEndsWith) + + case "..": + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpRange) + + default: + panic(fmt.Sprintf("unknown operator (%v)", node.Operator)) + + } +} + +func (c *compiler) MatchesNode(node *ast.MatchesNode) { + if node.Regexp != nil { + c.compile(node.Left) + c.emit(OpMatchesConst, c.makeConstant(node.Regexp)...) + return + } + c.compile(node.Left) + c.compile(node.Right) + c.emit(OpMatches) +} + +func (c *compiler) PropertyNode(node *ast.PropertyNode) { + c.compile(node.Node) + if !node.NilSafe { + c.emit(OpProperty, c.makeConstant(node.Property)...) + } else { + c.emit(OpPropertyNilSafe, c.makeConstant(node.Property)...) + } +} + +func (c *compiler) IndexNode(node *ast.IndexNode) { + c.compile(node.Node) + c.compile(node.Index) + c.emit(OpIndex) +} + +func (c *compiler) SliceNode(node *ast.SliceNode) { + c.compile(node.Node) + if node.To != nil { + c.compile(node.To) + } else { + c.emit(OpLen) + } + if node.From != nil { + c.compile(node.From) + } else { + c.emitPush(0) + } + c.emit(OpSlice) +} + +func (c *compiler) MethodNode(node *ast.MethodNode) { + c.compile(node.Node) + for _, arg := range node.Arguments { + c.compile(arg) + } + if !node.NilSafe { + c.emit(OpMethod, c.makeConstant(Call{Name: node.Method, Size: len(node.Arguments)})...) + } else { + c.emit(OpMethodNilSafe, c.makeConstant(Call{Name: node.Method, Size: len(node.Arguments)})...) + } +} + +func (c *compiler) FunctionNode(node *ast.FunctionNode) { + for _, arg := range node.Arguments { + c.compile(arg) + } + op := OpCall + if node.Fast { + op = OpCallFast + } + c.emit(op, c.makeConstant(Call{Name: node.Name, Size: len(node.Arguments)})...) +} + +func (c *compiler) BuiltinNode(node *ast.BuiltinNode) { + switch node.Name { + case "len": + c.compile(node.Arguments[0]) + c.emit(OpLen) + c.emit(OpRot) + c.emit(OpPop) + + case "all": + c.compile(node.Arguments[0]) + c.emit(OpBegin) + var loopBreak int + c.emitLoop(func() { + c.compile(node.Arguments[1]) + loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...) + c.emit(OpPop) + }) + c.emit(OpTrue) + c.patchJump(loopBreak) + c.emit(OpEnd) + + case "none": + c.compile(node.Arguments[0]) + c.emit(OpBegin) + var loopBreak int + c.emitLoop(func() { + c.compile(node.Arguments[1]) + c.emit(OpNot) + loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...) + c.emit(OpPop) + }) + c.emit(OpTrue) + c.patchJump(loopBreak) + c.emit(OpEnd) + + case "any": + c.compile(node.Arguments[0]) + c.emit(OpBegin) + var loopBreak int + c.emitLoop(func() { + c.compile(node.Arguments[1]) + loopBreak = c.emit(OpJumpIfTrue, c.placeholder()...) + c.emit(OpPop) + }) + c.emit(OpFalse) + c.patchJump(loopBreak) + c.emit(OpEnd) + + case "one": + count := c.makeConstant("count") + c.compile(node.Arguments[0]) + c.emit(OpBegin) + c.emitPush(0) + c.emit(OpStore, count...) + c.emitLoop(func() { + c.compile(node.Arguments[1]) + c.emitCond(func() { + c.emit(OpInc, count...) + }) + }) + c.emit(OpLoad, count...) + c.emitPush(1) + c.emit(OpEqual) + c.emit(OpEnd) + + case "filter": + count := c.makeConstant("count") + c.compile(node.Arguments[0]) + c.emit(OpBegin) + c.emitPush(0) + c.emit(OpStore, count...) + c.emitLoop(func() { + c.compile(node.Arguments[1]) + c.emitCond(func() { + c.emit(OpInc, count...) + + c.emit(OpLoad, c.makeConstant("array")...) + c.emit(OpLoad, c.makeConstant("i")...) + c.emit(OpIndex) + }) + }) + c.emit(OpLoad, count...) + c.emit(OpEnd) + c.emit(OpArray) + + case "map": + c.compile(node.Arguments[0]) + c.emit(OpBegin) + size := c.emitLoop(func() { + c.compile(node.Arguments[1]) + }) + c.emit(OpLoad, size...) + c.emit(OpEnd) + c.emit(OpArray) + + case "count": + count := c.makeConstant("count") + c.compile(node.Arguments[0]) + c.emit(OpBegin) + c.emitPush(0) + c.emit(OpStore, count...) + c.emitLoop(func() { + c.compile(node.Arguments[1]) + c.emitCond(func() { + c.emit(OpInc, count...) + }) + }) + c.emit(OpLoad, count...) + c.emit(OpEnd) + + default: + panic(fmt.Sprintf("unknown builtin %v", node.Name)) + } +} + +func (c *compiler) emitCond(body func()) { + noop := c.emit(OpJumpIfFalse, c.placeholder()...) + c.emit(OpPop) + + body() + + jmp := c.emit(OpJump, c.placeholder()...) + c.patchJump(noop) + c.emit(OpPop) + c.patchJump(jmp) +} + +func (c *compiler) emitLoop(body func()) []byte { + i := c.makeConstant("i") + size := c.makeConstant("size") + array := c.makeConstant("array") + + c.emit(OpLen) + c.emit(OpStore, size...) + c.emit(OpStore, array...) + c.emitPush(0) + c.emit(OpStore, i...) + + cond := len(c.bytecode) + c.emit(OpLoad, i...) + c.emit(OpLoad, size...) + c.emit(OpLess) + end := c.emit(OpJumpIfFalse, c.placeholder()...) + c.emit(OpPop) + + body() + + c.emit(OpInc, i...) + c.emit(OpJumpBackward, c.calcBackwardJump(cond)...) + + c.patchJump(end) + c.emit(OpPop) + + return size +} + +func (c *compiler) ClosureNode(node *ast.ClosureNode) { + c.compile(node.Node) +} + +func (c *compiler) PointerNode(node *ast.PointerNode) { + c.emit(OpLoad, c.makeConstant("array")...) + c.emit(OpLoad, c.makeConstant("i")...) + c.emit(OpIndex) +} + +func (c *compiler) ConditionalNode(node *ast.ConditionalNode) { + c.compile(node.Cond) + otherwise := c.emit(OpJumpIfFalse, c.placeholder()...) + + c.emit(OpPop) + c.compile(node.Exp1) + end := c.emit(OpJump, c.placeholder()...) + + c.patchJump(otherwise) + c.emit(OpPop) + c.compile(node.Exp2) + + c.patchJump(end) +} + +func (c *compiler) ArrayNode(node *ast.ArrayNode) { + for _, node := range node.Nodes { + c.compile(node) + } + + c.emitPush(len(node.Nodes)) + c.emit(OpArray) +} + +func (c *compiler) MapNode(node *ast.MapNode) { + for _, pair := range node.Pairs { + c.compile(pair) + } + + c.emitPush(len(node.Pairs)) + c.emit(OpMap) +} + +func (c *compiler) PairNode(node *ast.PairNode) { + c.compile(node.Key) + c.compile(node.Value) +} + +func encode(i uint16) []byte { + b := make([]byte, 2) + binary.LittleEndian.PutUint16(b, i) + return b +} + +func kind(node ast.Node) reflect.Kind { + t := node.Type() + if t == nil { + return reflect.Invalid + } + return t.Kind() +} diff --git a/vendor/github.com/antonmedv/expr/compiler/patcher.go b/vendor/github.com/antonmedv/expr/compiler/patcher.go new file mode 100644 index 0000000..2491dec --- /dev/null +++ b/vendor/github.com/antonmedv/expr/compiler/patcher.go @@ -0,0 +1,44 @@ +package compiler + +import ( + "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/conf" +) + +type operatorPatcher struct { + ops map[string][]string + types conf.TypesTable +} + +func (p *operatorPatcher) Enter(node *ast.Node) {} +func (p *operatorPatcher) Exit(node *ast.Node) { + binaryNode, ok := (*node).(*ast.BinaryNode) + if !ok { + return + } + + fns, ok := p.ops[binaryNode.Operator] + if !ok { + return + } + + leftType := binaryNode.Left.Type() + rightType := binaryNode.Right.Type() + + _, fn, ok := conf.FindSuitableOperatorOverload(fns, p.types, leftType, rightType) + if ok { + newNode := &ast.FunctionNode{ + Name: fn, + Arguments: []ast.Node{binaryNode.Left, binaryNode.Right}, + } + ast.Patch(node, newNode) + } +} + +func PatchOperators(node *ast.Node, config *conf.Config) { + if len(config.Operators) == 0 { + return + } + patcher := &operatorPatcher{ops: config.Operators, types: config.Types} + ast.Walk(node, patcher) +} diff --git a/vendor/github.com/antonmedv/expr/conf/config.go b/vendor/github.com/antonmedv/expr/conf/config.go new file mode 100644 index 0000000..7ba07fe --- /dev/null +++ b/vendor/github.com/antonmedv/expr/conf/config.go @@ -0,0 +1,89 @@ +package conf + +import ( + "fmt" + "reflect" + + "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/vm" +) + +type Config struct { + Env interface{} + MapEnv bool + Types TypesTable + Operators OperatorsTable + Expect reflect.Kind + Optimize bool + Strict bool + DefaultType reflect.Type + ConstExprFns map[string]reflect.Value + Visitors []ast.Visitor + err error +} + +func New(env interface{}) *Config { + var mapEnv bool + var mapValueType reflect.Type + if _, ok := env.(map[string]interface{}); ok { + mapEnv = true + } else { + if reflect.ValueOf(env).Kind() == reflect.Map { + mapValueType = reflect.TypeOf(env).Elem() + } + } + + return &Config{ + Env: env, + MapEnv: mapEnv, + Types: CreateTypesTable(env), + Optimize: true, + Strict: true, + DefaultType: mapValueType, + ConstExprFns: make(map[string]reflect.Value), + } +} + +// Check validates the compiler configuration. +func (c *Config) Check() error { + // Check that all functions that define operator overloading + // exist in environment and have correct signatures. + for op, fns := range c.Operators { + for _, fn := range fns { + fnType, ok := c.Types[fn] + if !ok || fnType.Type.Kind() != reflect.Func { + return fmt.Errorf("function %s for %s operator does not exist in environment", fn, op) + } + requiredNumIn := 2 + if fnType.Method { + requiredNumIn = 3 // As first argument of method is receiver. + } + if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != 1 { + return fmt.Errorf("function %s for %s operator does not have a correct signature", fn, op) + } + } + } + + // Check that all ConstExprFns are functions. + for name, fn := range c.ConstExprFns { + if fn.Kind() != reflect.Func { + return fmt.Errorf("const expression %q must be a function", name) + } + } + + return c.err +} + +func (c *Config) ConstExpr(name string) { + if c.Env == nil { + c.Error(fmt.Errorf("no environment for const expression: %v", name)) + return + } + c.ConstExprFns[name] = vm.FetchFn(c.Env, name) +} + +func (c *Config) Error(err error) { + if c.err == nil { + c.err = err + } +} diff --git a/vendor/github.com/antonmedv/expr/conf/operators_table.go b/vendor/github.com/antonmedv/expr/conf/operators_table.go new file mode 100644 index 0000000..0ceb844 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/conf/operators_table.go @@ -0,0 +1,26 @@ +package conf + +import "reflect" + +// OperatorsTable maps binary operators to corresponding list of functions. +// Functions should be provided in the environment to allow operator overloading. +type OperatorsTable map[string][]string + +func FindSuitableOperatorOverload(fns []string, types TypesTable, l, r reflect.Type) (reflect.Type, string, bool) { + for _, fn := range fns { + fnType := types[fn] + firstInIndex := 0 + if fnType.Method { + firstInIndex = 1 // As first argument to method is receiver. + } + firstArgType := fnType.Type.In(firstInIndex) + secondArgType := fnType.Type.In(firstInIndex + 1) + + firstArgumentFit := l == firstArgType || (firstArgType.Kind() == reflect.Interface && (l == nil || l.Implements(firstArgType))) + secondArgumentFit := r == secondArgType || (secondArgType.Kind() == reflect.Interface && (r == nil || r.Implements(secondArgType))) + if firstArgumentFit && secondArgumentFit { + return fnType.Type.Out(0), fn, true + } + } + return nil, "", false +} diff --git a/vendor/github.com/antonmedv/expr/conf/types_table.go b/vendor/github.com/antonmedv/expr/conf/types_table.go new file mode 100644 index 0000000..d5539da --- /dev/null +++ b/vendor/github.com/antonmedv/expr/conf/types_table.go @@ -0,0 +1,100 @@ +package conf + +import "reflect" + +type Tag struct { + Type reflect.Type + Method bool + Ambiguous bool +} + +type TypesTable map[string]Tag + +// CreateTypesTable creates types table for type checks during parsing. +// If struct is passed, all fields will be treated as variables, +// as well as all fields of embedded structs and struct itself. +// +// If map is passed, all items will be treated as variables +// (key as name, value as type). +func CreateTypesTable(i interface{}) TypesTable { + if i == nil { + return nil + } + + types := make(TypesTable) + v := reflect.ValueOf(i) + t := reflect.TypeOf(i) + + d := t + if t.Kind() == reflect.Ptr { + d = t.Elem() + } + + switch d.Kind() { + case reflect.Struct: + types = FieldsFromStruct(d) + + // Methods of struct should be gathered from original struct with pointer, + // as methods maybe declared on pointer receiver. Also this method retrieves + // all embedded structs methods as well, no need to recursion. + for i := 0; i < t.NumMethod(); i++ { + m := t.Method(i) + types[m.Name] = Tag{Type: m.Type, Method: true} + } + + case reflect.Map: + for _, key := range v.MapKeys() { + value := v.MapIndex(key) + if key.Kind() == reflect.String && value.IsValid() && value.CanInterface() { + types[key.String()] = Tag{Type: reflect.TypeOf(value.Interface())} + } + } + + // A map may have method too. + for i := 0; i < t.NumMethod(); i++ { + m := t.Method(i) + types[m.Name] = Tag{Type: m.Type, Method: true} + } + } + + return types +} + +func FieldsFromStruct(t reflect.Type) TypesTable { + types := make(TypesTable) + t = dereference(t) + if t == nil { + return types + } + + switch t.Kind() { + case reflect.Struct: + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + + if f.Anonymous { + for name, typ := range FieldsFromStruct(f.Type) { + if _, ok := types[name]; ok { + types[name] = Tag{Ambiguous: true} + } else { + types[name] = typ + } + } + } + + types[f.Name] = Tag{Type: f.Type} + } + } + + return types +} + +func dereference(t reflect.Type) reflect.Type { + if t == nil { + return nil + } + if t.Kind() == reflect.Ptr { + t = dereference(t.Elem()) + } + return t +} diff --git a/vendor/github.com/antonmedv/expr/expr.go b/vendor/github.com/antonmedv/expr/expr.go new file mode 100644 index 0000000..05c54ad --- /dev/null +++ b/vendor/github.com/antonmedv/expr/expr.go @@ -0,0 +1,187 @@ +package expr + +import ( + "fmt" + "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/file" + "reflect" + + "github.com/antonmedv/expr/checker" + "github.com/antonmedv/expr/compiler" + "github.com/antonmedv/expr/conf" + "github.com/antonmedv/expr/optimizer" + "github.com/antonmedv/expr/parser" + "github.com/antonmedv/expr/vm" +) + +// Option for configuring config. +type Option func(c *conf.Config) + +// Eval parses, compiles and runs given input. +func Eval(input string, env interface{}) (interface{}, error) { + if _, ok := env.(Option); ok { + return nil, fmt.Errorf("misused expr.Eval: second argument (env) should be passed without expr.Env") + } + + tree, err := parser.Parse(input) + if err != nil { + return nil, err + } + + program, err := compiler.Compile(tree, nil) + if err != nil { + return nil, err + } + + output, err := vm.Run(program, env) + if err != nil { + return nil, err + } + + return output, nil +} + +// Env specifies expected input of env for type checks. +// If struct is passed, all fields will be treated as variables, +// as well as all fields of embedded structs and struct itself. +// If map is passed, all items will be treated as variables. +// Methods defined on this type will be available as functions. +func Env(env interface{}) Option { + return func(c *conf.Config) { + if _, ok := env.(map[string]interface{}); ok { + c.MapEnv = true + } else { + if reflect.ValueOf(env).Kind() == reflect.Map { + c.DefaultType = reflect.TypeOf(env).Elem() + } + } + c.Strict = true + c.Types = conf.CreateTypesTable(env) + c.Env = env + } +} + +// AllowUndefinedVariables allows to use undefined variables inside expressions. +// This can be used with expr.Env option to partially define a few variables. +// Note what this option is only works in map environment are used, otherwise +// runtime.fetch will panic as there is no way to get missing field zero value. +func AllowUndefinedVariables() Option { + return func(c *conf.Config) { + c.Strict = false + } +} + +// Operator allows to override binary operator with function. +func Operator(operator string, fn ...string) Option { + return func(c *conf.Config) { + c.Operators[operator] = append(c.Operators[operator], fn...) + } +} + +// ConstExpr defines func expression as constant. If all argument to this function is constants, +// then it can be replaced by result of this func call on compile step. +func ConstExpr(fn string) Option { + return func(c *conf.Config) { + c.ConstExpr(fn) + } +} + +// AsBool tells the compiler to expect boolean result. +func AsBool() Option { + return func(c *conf.Config) { + c.Expect = reflect.Bool + } +} + +// AsInt64 tells the compiler to expect int64 result. +func AsInt64() Option { + return func(c *conf.Config) { + c.Expect = reflect.Int64 + } +} + +// AsFloat64 tells the compiler to expect float64 result. +func AsFloat64() Option { + return func(c *conf.Config) { + c.Expect = reflect.Float64 + } +} + +// Optimize turns optimizations on or off. +func Optimize(b bool) Option { + return func(c *conf.Config) { + c.Optimize = b + } +} + +// Patch adds visitor to list of visitors what will be applied before compiling AST to bytecode. +func Patch(visitor ast.Visitor) Option { + return func(c *conf.Config) { + c.Visitors = append(c.Visitors, visitor) + } +} + +// Compile parses and compiles given input expression to bytecode program. +func Compile(input string, ops ...Option) (*vm.Program, error) { + config := &conf.Config{ + Operators: make(map[string][]string), + ConstExprFns: make(map[string]reflect.Value), + Optimize: true, + } + + for _, op := range ops { + op(config) + } + + if err := config.Check(); err != nil { + return nil, err + } + + tree, err := parser.Parse(input) + if err != nil { + return nil, err + } + + _, err = checker.Check(tree, config) + + // If we have a patch to apply, it may fix out error and + // second type check is needed. Otherwise it is an error. + if err != nil && len(config.Visitors) == 0 { + return nil, err + } + + // Patch operators before Optimize, as we may also mark it as ConstExpr. + compiler.PatchOperators(&tree.Node, config) + + if len(config.Visitors) >= 0 { + for _, v := range config.Visitors { + ast.Walk(&tree.Node, v) + } + _, err = checker.Check(tree, config) + if err != nil { + return nil, err + } + } + + if config.Optimize { + err = optimizer.Optimize(&tree.Node, config) + if err != nil { + if fileError, ok := err.(*file.Error); ok { + return nil, fileError.Bind(tree.Source) + } + return nil, err + } + } + + program, err := compiler.Compile(tree, config) + if err != nil { + return nil, err + } + + return program, nil +} + +// Run evaluates given bytecode program. +func Run(program *vm.Program, env interface{}) (interface{}, error) { + return vm.Run(program, env) +} diff --git a/vendor/github.com/antonmedv/expr/file/error.go b/vendor/github.com/antonmedv/expr/file/error.go new file mode 100644 index 0000000..b7af3e6 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/file/error.go @@ -0,0 +1,58 @@ +package file + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +type Error struct { + Location + Message string + Snippet string +} + +func (e *Error) Error() string { + return e.format() +} + +func (e *Error) Bind(source *Source) *Error { + if snippet, found := source.Snippet(e.Location.Line); found { + snippet := strings.Replace(snippet, "\t", " ", -1) + srcLine := "\n | " + snippet + var bytes = []byte(snippet) + var indLine = "\n | " + for i := 0; i < e.Location.Column && len(bytes) > 0; i++ { + _, sz := utf8.DecodeRune(bytes) + bytes = bytes[sz:] + if sz > 1 { + goto noind + } else { + indLine += "." + } + } + if _, sz := utf8.DecodeRune(bytes); sz > 1 { + goto noind + } else { + indLine += "^" + } + srcLine += indLine + + noind: + e.Snippet = srcLine + } + return e +} + +func (e *Error) format() string { + if e.Location.Empty() { + return e.Message + } + return fmt.Sprintf( + "%s (%d:%d)%s", + e.Message, + e.Line, + e.Column+1, // add one to the 0-based column for display + e.Snippet, + ) +} diff --git a/vendor/github.com/antonmedv/expr/file/location.go b/vendor/github.com/antonmedv/expr/file/location.go new file mode 100644 index 0000000..a92e27f --- /dev/null +++ b/vendor/github.com/antonmedv/expr/file/location.go @@ -0,0 +1,10 @@ +package file + +type Location struct { + Line int // The 1-based line of the location. + Column int // The 0-based column number of the location. +} + +func (l Location) Empty() bool { + return l.Column == 0 && l.Line == 0 +} diff --git a/vendor/github.com/antonmedv/expr/file/source.go b/vendor/github.com/antonmedv/expr/file/source.go new file mode 100644 index 0000000..185d156 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/file/source.go @@ -0,0 +1,95 @@ +package file + +import ( + "encoding/json" + "strings" + "unicode/utf8" +) + +type Source struct { + contents []rune + lineOffsets []int32 +} + +func NewSource(contents string) *Source { + s := &Source{ + contents: []rune(contents), + } + s.updateOffsets() + return s +} + +func (s *Source) MarshalJSON() ([]byte, error) { + return json.Marshal(s.contents) +} + +func (s *Source) UnmarshalJSON(b []byte) error { + contents := make([]rune, 0) + err := json.Unmarshal(b, &contents) + if err != nil { + return err + } + + s.contents = contents + s.updateOffsets() + return nil +} + +func (s *Source) Content() string { + return string(s.contents) +} + +func (s *Source) Snippet(line int) (string, bool) { + charStart, found := s.findLineOffset(line) + if !found || len(s.contents) == 0 { + return "", false + } + charEnd, found := s.findLineOffset(line + 1) + if found { + return string(s.contents[charStart : charEnd-1]), true + } + return string(s.contents[charStart:]), true +} + +// updateOffsets compute line offsets up front as they are referred to frequently. +func (s *Source) updateOffsets() { + lines := strings.Split(string(s.contents), "\n") + offsets := make([]int32, len(lines)) + var offset int32 + for i, line := range lines { + offset = offset + int32(utf8.RuneCountInString(line)) + 1 + offsets[int32(i)] = offset + } + s.lineOffsets = offsets +} + +// findLineOffset returns the offset where the (1-indexed) line begins, +// or false if line doesn't exist. +func (s *Source) findLineOffset(line int) (int32, bool) { + if line == 1 { + return 0, true + } else if line > 1 && line <= len(s.lineOffsets) { + offset := s.lineOffsets[line-2] + return offset, true + } + return -1, false +} + +// findLine finds the line that contains the given character offset and +// returns the line number and offset of the beginning of that line. +// Note that the last line is treated as if it contains all offsets +// beyond the end of the actual source. +func (s *Source) findLine(characterOffset int32) (int32, int32) { + var line int32 = 1 + for _, lineOffset := range s.lineOffsets { + if lineOffset > characterOffset { + break + } else { + line++ + } + } + if line == 1 { + return line, 0 + } + return line, s.lineOffsets[line-2] +} diff --git a/vendor/github.com/antonmedv/expr/go.mod b/vendor/github.com/antonmedv/expr/go.mod new file mode 100644 index 0000000..5548360 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/go.mod @@ -0,0 +1,10 @@ +module github.com/antonmedv/expr + +go 1.13 + +require ( + github.com/gdamore/tcell v1.3.0 + github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498 + github.com/sanity-io/litter v1.2.0 + github.com/stretchr/testify v1.5.1 +) diff --git a/vendor/github.com/antonmedv/expr/go.sum b/vendor/github.com/antonmedv/expr/go.sum new file mode 100644 index 0000000..af0ea8e --- /dev/null +++ b/vendor/github.com/antonmedv/expr/go.sum @@ -0,0 +1,38 @@ +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM= +github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= +github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= +github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= +github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498 h1:4CFNy7/q7P06AsIONZzuWy7jcdqEmYQvOZ9FAFZdbls= +github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/sanity-io/litter v1.2.0 h1:DGJO0bxH/+C2EukzOSBmAlxmkhVMGqzvcx/rvySYw9M= +github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/antonmedv/expr/optimizer/const_expr.go b/vendor/github.com/antonmedv/expr/optimizer/const_expr.go new file mode 100644 index 0000000..85fcc33 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/optimizer/const_expr.go @@ -0,0 +1,77 @@ +package optimizer + +import ( + "fmt" + . "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/file" + "reflect" + "strings" +) + +type constExpr struct { + applied bool + err error + fns map[string]reflect.Value +} + +func (*constExpr) Enter(*Node) {} +func (c *constExpr) Exit(node *Node) { + defer func() { + if r := recover(); r != nil { + msg := fmt.Sprintf("%v", r) + // Make message more actual, it's a runtime error, but at compile step. + msg = strings.Replace(msg, "runtime error:", "compile error:", 1) + c.err = &file.Error{ + Location: (*node).Location(), + Message: msg, + } + } + }() + + patch := func(newNode Node) { + c.applied = true + Patch(node, newNode) + } + + switch n := (*node).(type) { + case *FunctionNode: + fn, ok := c.fns[n.Name] + if ok { + in := make([]reflect.Value, len(n.Arguments)) + for i := 0; i < len(n.Arguments); i++ { + arg := n.Arguments[i] + var param interface{} + + switch a := arg.(type) { + case *NilNode: + param = nil + case *IntegerNode: + param = a.Value + case *FloatNode: + param = a.Value + case *BoolNode: + param = a.Value + case *StringNode: + param = a.Value + case *ConstantNode: + param = a.Value + + default: + return // Const expr optimization not applicable. + } + + if param == nil && reflect.TypeOf(param) == nil { + // In case of nil value and nil type use this hack, + // otherwise reflect.Call will panic on zero value. + in[i] = reflect.ValueOf(¶m).Elem() + } else { + in[i] = reflect.ValueOf(param) + } + } + + out := fn.Call(in) + constNode := &ConstantNode{Value: out[0].Interface()} + patch(constNode) + } + } +} diff --git a/vendor/github.com/antonmedv/expr/optimizer/const_range.go b/vendor/github.com/antonmedv/expr/optimizer/const_range.go new file mode 100644 index 0000000..5205aa1 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/optimizer/const_range.go @@ -0,0 +1,41 @@ +package optimizer + +import ( + . "github.com/antonmedv/expr/ast" +) + +type constRange struct{} + +func (*constRange) Enter(*Node) {} +func (*constRange) Exit(node *Node) { + switch n := (*node).(type) { + case *BinaryNode: + if n.Operator == ".." { + if min, ok := n.Left.(*IntegerNode); ok { + if max, ok := n.Right.(*IntegerNode); ok { + size := max.Value - min.Value + 1 + // In case the max < min, patch empty slice + // as max must be greater than equal to min. + if size < 1 { + Patch(node, &ConstantNode{ + Value: make([]int, 0), + }) + return + } + // In this case array is too big. Skip generation, + // and wait for memory budget detection on runtime. + if size > 1e6 { + return + } + value := make([]int, size) + for i := range value { + value[i] = min.Value + i + } + Patch(node, &ConstantNode{ + Value: value, + }) + } + } + } + } +} diff --git a/vendor/github.com/antonmedv/expr/optimizer/fold.go b/vendor/github.com/antonmedv/expr/optimizer/fold.go new file mode 100644 index 0000000..6669125 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/optimizer/fold.go @@ -0,0 +1,133 @@ +package optimizer + +import ( + "math" + "reflect" + + . "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/file" +) + +type fold struct { + applied bool + err *file.Error +} + +func (*fold) Enter(*Node) {} +func (fold *fold) Exit(node *Node) { + patch := func(newNode Node) { + fold.applied = true + Patch(node, newNode) + } + // for IntegerNode the type may have been changed from int->float + // preserve this information by setting the type after the Patch + patchWithType := func(newNode Node, leafType reflect.Type) { + patch(newNode) + newNode.SetType(leafType) + } + + switch n := (*node).(type) { + case *UnaryNode: + switch n.Operator { + case "-": + if i, ok := n.Node.(*IntegerNode); ok { + patchWithType(&IntegerNode{Value: -i.Value}, n.Node.Type()) + } + case "+": + if i, ok := n.Node.(*IntegerNode); ok { + patchWithType(&IntegerNode{Value: i.Value}, n.Node.Type()) + } + } + + case *BinaryNode: + switch n.Operator { + case "+": + if a, ok := n.Left.(*IntegerNode); ok { + if b, ok := n.Right.(*IntegerNode); ok { + patchWithType(&IntegerNode{Value: a.Value + b.Value}, a.Type()) + } + } + if a, ok := n.Left.(*StringNode); ok { + if b, ok := n.Right.(*StringNode); ok { + patch(&StringNode{Value: a.Value + b.Value}) + } + } + case "-": + if a, ok := n.Left.(*IntegerNode); ok { + if b, ok := n.Right.(*IntegerNode); ok { + patchWithType(&IntegerNode{Value: a.Value - b.Value}, a.Type()) + } + } + case "*": + if a, ok := n.Left.(*IntegerNode); ok { + if b, ok := n.Right.(*IntegerNode); ok { + patchWithType(&IntegerNode{Value: a.Value * b.Value}, a.Type()) + } + } + case "/": + if a, ok := n.Left.(*IntegerNode); ok { + if b, ok := n.Right.(*IntegerNode); ok { + if b.Value == 0 { + fold.err = &file.Error{ + Location: (*node).Location(), + Message: "integer divide by zero", + } + return + } + patchWithType(&IntegerNode{Value: a.Value / b.Value}, a.Type()) + } + } + case "%": + if a, ok := n.Left.(*IntegerNode); ok { + if b, ok := n.Right.(*IntegerNode); ok { + if b.Value == 0 { + fold.err = &file.Error{ + Location: (*node).Location(), + Message: "integer divide by zero", + } + return + } + patch(&IntegerNode{Value: a.Value % b.Value}) + } + } + case "**": + if a, ok := n.Left.(*IntegerNode); ok { + if b, ok := n.Right.(*IntegerNode); ok { + patch(&FloatNode{Value: math.Pow(float64(a.Value), float64(b.Value))}) + } + } + } + + case *ArrayNode: + if len(n.Nodes) > 0 { + + for _, a := range n.Nodes { + if _, ok := a.(*IntegerNode); !ok { + goto string + } + } + { + value := make([]int, len(n.Nodes)) + for i, a := range n.Nodes { + value[i] = a.(*IntegerNode).Value + } + patch(&ConstantNode{Value: value}) + } + + string: + for _, a := range n.Nodes { + if _, ok := a.(*StringNode); !ok { + return + } + } + { + value := make([]string, len(n.Nodes)) + for i, a := range n.Nodes { + value[i] = a.(*StringNode).Value + } + patch(&ConstantNode{Value: value}) + } + + } + } +} diff --git a/vendor/github.com/antonmedv/expr/optimizer/in_array.go b/vendor/github.com/antonmedv/expr/optimizer/in_array.go new file mode 100644 index 0000000..8156faa --- /dev/null +++ b/vendor/github.com/antonmedv/expr/optimizer/in_array.go @@ -0,0 +1,65 @@ +package optimizer + +import ( + "reflect" + + . "github.com/antonmedv/expr/ast" +) + +type inArray struct{} + +func (*inArray) Enter(*Node) {} +func (*inArray) Exit(node *Node) { + switch n := (*node).(type) { + case *BinaryNode: + if n.Operator == "in" || n.Operator == "not in" { + if array, ok := n.Right.(*ArrayNode); ok { + if len(array.Nodes) > 0 { + t := n.Left.Type() + if t == nil || t.Kind() != reflect.Int { + // This optimization can be only performed if left side is int type, + // as runtime.in func uses reflect.Map.MapIndex and keys of map must, + // be same as checked value type. + goto string + } + + for _, a := range array.Nodes { + if _, ok := a.(*IntegerNode); !ok { + goto string + } + } + { + value := make(map[int]struct{}) + for _, a := range array.Nodes { + value[a.(*IntegerNode).Value] = struct{}{} + } + Patch(node, &BinaryNode{ + Operator: n.Operator, + Left: n.Left, + Right: &ConstantNode{Value: value}, + }) + } + + string: + for _, a := range array.Nodes { + if _, ok := a.(*StringNode); !ok { + return + } + } + { + value := make(map[string]struct{}) + for _, a := range array.Nodes { + value[a.(*StringNode).Value] = struct{}{} + } + Patch(node, &BinaryNode{ + Operator: n.Operator, + Left: n.Left, + Right: &ConstantNode{Value: value}, + }) + } + + } + } + } + } +} diff --git a/vendor/github.com/antonmedv/expr/optimizer/in_range.go b/vendor/github.com/antonmedv/expr/optimizer/in_range.go new file mode 100644 index 0000000..177c919 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/optimizer/in_range.go @@ -0,0 +1,41 @@ +package optimizer + +import ( + . "github.com/antonmedv/expr/ast" +) + +type inRange struct{} + +func (*inRange) Enter(*Node) {} +func (*inRange) Exit(node *Node) { + switch n := (*node).(type) { + case *BinaryNode: + if n.Operator == "in" || n.Operator == "not in" { + if rng, ok := n.Right.(*BinaryNode); ok && rng.Operator == ".." { + if from, ok := rng.Left.(*IntegerNode); ok { + if to, ok := rng.Right.(*IntegerNode); ok { + Patch(node, &BinaryNode{ + Operator: "and", + Left: &BinaryNode{ + Operator: ">=", + Left: n.Left, + Right: from, + }, + Right: &BinaryNode{ + Operator: "<=", + Left: n.Left, + Right: to, + }, + }) + if n.Operator == "not in" { + Patch(node, &UnaryNode{ + Operator: "not", + Node: *node, + }) + } + } + } + } + } + } +} diff --git a/vendor/github.com/antonmedv/expr/optimizer/optimizer.go b/vendor/github.com/antonmedv/expr/optimizer/optimizer.go new file mode 100644 index 0000000..738348d --- /dev/null +++ b/vendor/github.com/antonmedv/expr/optimizer/optimizer.go @@ -0,0 +1,37 @@ +package optimizer + +import ( + . "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/conf" +) + +func Optimize(node *Node, config *conf.Config) error { + Walk(node, &inArray{}) + for limit := 1000; limit >= 0; limit-- { + fold := &fold{} + Walk(node, fold) + if fold.err != nil { + return fold.err + } + if !fold.applied { + break + } + } + if config != nil && len(config.ConstExprFns) > 0 { + for limit := 100; limit >= 0; limit-- { + constExpr := &constExpr{ + fns: config.ConstExprFns, + } + Walk(node, constExpr) + if constExpr.err != nil { + return constExpr.err + } + if !constExpr.applied { + break + } + } + } + Walk(node, &inRange{}) + Walk(node, &constRange{}) + return nil +} diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go b/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go new file mode 100644 index 0000000..6e4848a --- /dev/null +++ b/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go @@ -0,0 +1,212 @@ +package lexer + +import ( + "fmt" + "strings" + "unicode/utf8" + + "github.com/antonmedv/expr/file" +) + +func Lex(source *file.Source) ([]Token, error) { + l := &lexer{ + input: source.Content(), + tokens: make([]Token, 0), + } + + l.loc = file.Location{Line: 1, Column: 0} + l.prev = l.loc + l.startLoc = l.loc + + for state := root; state != nil; { + state = state(l) + } + + if l.err != nil { + return nil, l.err.Bind(source) + } + + return l.tokens, nil +} + +type lexer struct { + input string + tokens []Token + start, end int // current position in input + width int // last rune width + startLoc file.Location // start location + prev, loc file.Location // prev location of end location, end location + err *file.Error +} + +const eof rune = -1 + +func (l *lexer) next() rune { + if l.end >= len(l.input) { + l.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(l.input[l.end:]) + l.width = w + l.end += w + + l.prev = l.loc + if r == '\n' { + l.loc.Line++ + l.loc.Column = 0 + } else { + l.loc.Column++ + } + + return r +} + +func (l *lexer) peek() rune { + r := l.next() + l.backup() + return r +} + +func (l *lexer) backup() { + l.end -= l.width + l.loc = l.prev +} + +func (l *lexer) emit(t Kind) { + l.emitValue(t, l.word()) +} + +func (l *lexer) emitValue(t Kind, value string) { + l.tokens = append(l.tokens, Token{ + Location: l.startLoc, + Kind: t, + Value: value, + }) + l.start = l.end + l.startLoc = l.loc +} + +func (l *lexer) emitEOF() { + l.tokens = append(l.tokens, Token{ + Location: l.prev, // Point to previous position for better error messages. + Kind: EOF, + }) + l.start = l.end + l.startLoc = l.loc +} + +func (l *lexer) word() string { + return l.input[l.start:l.end] +} + +func (l *lexer) ignore() { + l.start = l.end + l.startLoc = l.loc +} + +func (l *lexer) accept(valid string) bool { + if strings.ContainsRune(valid, l.next()) { + return true + } + l.backup() + return false +} + +func (l *lexer) acceptRun(valid string) { + for strings.ContainsRune(valid, l.next()) { + } + l.backup() +} + +func (l *lexer) acceptWord(word string) bool { + pos, loc, prev := l.end, l.loc, l.prev + + // Skip spaces (U+0020) if any + r := l.peek() + for ; r == ' '; r = l.peek() { + l.next() + } + + for _, ch := range word { + if l.next() != ch { + l.end, l.loc, l.prev = pos, loc, prev + return false + } + } + if r = l.peek(); r != ' ' && r != eof { + l.end, l.loc, l.prev = pos, loc, prev + return false + } + + return true +} + +func (l *lexer) error(format string, args ...interface{}) stateFn { + if l.err == nil { // show first error + l.err = &file.Error{ + Location: l.loc, + Message: fmt.Sprintf(format, args...), + } + } + return nil +} + +func digitVal(ch rune) int { + switch { + case '0' <= ch && ch <= '9': + return int(ch - '0') + case 'a' <= lower(ch) && lower(ch) <= 'f': + return int(lower(ch) - 'a' + 10) + } + return 16 // larger than any legal digit val +} + +func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter + +func (l *lexer) scanDigits(ch rune, base, n int) rune { + for n > 0 && digitVal(ch) < base { + ch = l.next() + n-- + } + if n > 0 { + l.error("invalid char escape") + } + return ch +} + +func (l *lexer) scanEscape(quote rune) rune { + ch := l.next() // read character after '/' + switch ch { + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: + // nothing to do + ch = l.next() + case '0', '1', '2', '3', '4', '5', '6', '7': + ch = l.scanDigits(ch, 8, 3) + case 'x': + ch = l.scanDigits(l.next(), 16, 2) + case 'u': + ch = l.scanDigits(l.next(), 16, 4) + case 'U': + ch = l.scanDigits(l.next(), 16, 8) + default: + l.error("invalid char escape") + } + return ch +} + +func (l *lexer) scanString(quote rune) (n int) { + ch := l.next() // read character after quote + for ch != quote { + if ch == '\n' || ch == eof { + l.error("literal not terminated") + return + } + if ch == '\\' { + ch = l.scanEscape(quote) + } else { + ch = l.next() + } + n++ + } + return +} diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/state.go b/vendor/github.com/antonmedv/expr/parser/lexer/state.go new file mode 100644 index 0000000..0d4bece --- /dev/null +++ b/vendor/github.com/antonmedv/expr/parser/lexer/state.go @@ -0,0 +1,148 @@ +package lexer + +import ( + "strings" +) + +type stateFn func(*lexer) stateFn + +func root(l *lexer) stateFn { + switch r := l.next(); { + case r == eof: + l.emitEOF() + return nil + case IsSpace(r): + l.ignore() + return root + case r == '\'' || r == '"': + l.scanString(r) + str, err := unescape(l.word()) + if err != nil { + l.error("%v", err) + } + l.emitValue(String, str) + case '0' <= r && r <= '9': + l.backup() + return number + case r == '?': + if l.peek() == '.' { + return nilsafe + } + l.emit(Operator) + case strings.ContainsRune("([{", r): + l.emit(Bracket) + case strings.ContainsRune(")]}", r): + l.emit(Bracket) + case strings.ContainsRune("#,?:%+-/", r): // single rune operator + l.emit(Operator) + case strings.ContainsRune("&|!=*<>", r): // possible double rune operator + l.accept("&|=*") + l.emit(Operator) + case r == '.': + l.backup() + return dot + case IsAlphaNumeric(r): + l.backup() + return identifier + default: + return l.error("unrecognized character: %#U", r) + } + return root +} + +func number(l *lexer) stateFn { + if !l.scanNumber() { + return l.error("bad number syntax: %q", l.word()) + } + l.emit(Number) + return root +} + +func (l *lexer) scanNumber() bool { + digits := "0123456789_" + // Is it hex? + if l.accept("0") { + // Note: Leading 0 does not mean octal in floats. + if l.accept("xX") { + digits = "0123456789abcdefABCDEF_" + } else if l.accept("oO") { + digits = "01234567_" + } else if l.accept("bB") { + digits = "01_" + } + } + l.acceptRun(digits) + loc, prev, end := l.loc, l.prev, l.end + if l.accept(".") { + // Lookup for .. operator: if after dot there is another dot (1..2), it maybe a range operator. + if l.peek() == '.' { + // We can't backup() here, as it would require two backups, + // and backup() func supports only one for now. So, save and + // restore it here. + l.loc, l.prev, l.end = loc, prev, end + return true + } + l.acceptRun(digits) + } + if l.accept("eE") { + l.accept("+-") + l.acceptRun(digits) + } + // Next thing mustn't be alphanumeric. + if IsAlphaNumeric(l.peek()) { + l.next() + return false + } + return true +} + +func dot(l *lexer) stateFn { + l.next() + if l.accept("0123456789") { + l.backup() + return number + } + l.accept(".") + l.emit(Operator) + return root +} + +func nilsafe(l *lexer) stateFn { + l.next() + l.accept("?.") + l.emit(Operator) + return root +} + +func identifier(l *lexer) stateFn { +loop: + for { + switch r := l.next(); { + case IsAlphaNumeric(r): + // absorb + default: + l.backup() + switch l.word() { + case "not": + return not + case "in", "or", "and", "matches", "contains", "startsWith", "endsWith": + l.emit(Operator) + default: + l.emit(Identifier) + } + break loop + } + } + return root +} + +func not(l *lexer) stateFn { + switch l.acceptWord("in") { + case true: + l.emitValue(Operator, "not in") + case false: + l.emitValue(Operator, "not") + } + + return root +} diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/token.go b/vendor/github.com/antonmedv/expr/parser/lexer/token.go new file mode 100644 index 0000000..8917b26 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/parser/lexer/token.go @@ -0,0 +1,47 @@ +package lexer + +import ( + "fmt" + + "github.com/antonmedv/expr/file" +) + +type Kind string + +const ( + Identifier Kind = "Identifier" + Number Kind = "Number" + String Kind = "String" + Operator Kind = "Operator" + Bracket Kind = "Bracket" + EOF Kind = "EOF" +) + +type Token struct { + file.Location + Kind Kind + Value string +} + +func (t Token) String() string { + if t.Value == "" { + return string(t.Kind) + } + return fmt.Sprintf("%s(%#v)", t.Kind, t.Value) +} + +func (t Token) Is(kind Kind, values ...string) bool { + if len(values) == 0 { + return kind == t.Kind + } + + for _, v := range values { + if v == t.Value { + goto found + } + } + return false + +found: + return kind == t.Kind +} diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/utils.go b/vendor/github.com/antonmedv/expr/parser/lexer/utils.go new file mode 100644 index 0000000..72e3cf2 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/parser/lexer/utils.go @@ -0,0 +1,194 @@ +package lexer + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +func IsSpace(r rune) bool { + return unicode.IsSpace(r) +} + +func IsAlphaNumeric(r rune) bool { + return IsAlphabetic(r) || unicode.IsDigit(r) +} + +func IsAlphabetic(r rune) bool { + return r == '_' || r == '$' || unicode.IsLetter(r) +} + +var ( + newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n") +) + +// Unescape takes a quoted string, unquotes, and unescapes it. +func unescape(value string) (string, error) { + // All strings normalize newlines to the \n representation. + value = newlineNormalizer.Replace(value) + n := len(value) + + // Nothing to unescape / decode. + if n < 2 { + return value, fmt.Errorf("unable to unescape string") + } + + // Quoted string of some form, must have same first and last char. + if value[0] != value[n-1] || (value[0] != '"' && value[0] != '\'') { + return value, fmt.Errorf("unable to unescape string") + } + + value = value[1 : n-1] + + // The string contains escape characters. + // The following logic is adapted from `strconv/quote.go` + var runeTmp [utf8.UTFMax]byte + buf := make([]byte, 0, 3*n/2) + for len(value) > 0 { + c, multibyte, rest, err := unescapeChar(value) + if err != nil { + return "", err + } + value = rest + if c < utf8.RuneSelf || !multibyte { + buf = append(buf, byte(c)) + } else { + n := utf8.EncodeRune(runeTmp[:], c) + buf = append(buf, runeTmp[:n]...) + } + } + return string(buf), nil +} + +// unescapeChar takes a string input and returns the following info: +// +// value - the escaped unicode rune at the front of the string. +// multibyte - whether the rune value might require multiple bytes to represent. +// tail - the remainder of the input string. +// err - error value, if the character could not be unescaped. +// +// When multibyte is true the return value may still fit within a single byte, +// but a multibyte conversion is attempted which is more expensive than when the +// value is known to fit within one byte. +func unescapeChar(s string) (value rune, multibyte bool, tail string, err error) { + // 1. Character is not an escape sequence. + switch c := s[0]; { + case c >= utf8.RuneSelf: + r, size := utf8.DecodeRuneInString(s) + return r, true, s[size:], nil + case c != '\\': + return rune(s[0]), false, s[1:], nil + } + + // 2. Last character is the start of an escape sequence. + if len(s) <= 1 { + err = fmt.Errorf("unable to unescape string, found '\\' as last character") + return + } + + c := s[1] + s = s[2:] + // 3. Common escape sequences shared with Google SQL + switch c { + case 'a': + value = '\a' + case 'b': + value = '\b' + case 'f': + value = '\f' + case 'n': + value = '\n' + case 'r': + value = '\r' + case 't': + value = '\t' + case 'v': + value = '\v' + case '\\': + value = '\\' + case '\'': + value = '\'' + case '"': + value = '"' + case '`': + value = '`' + case '?': + value = '?' + + // 4. Unicode escape sequences, reproduced from `strconv/quote.go` + case 'x', 'X', 'u', 'U': + n := 0 + switch c { + case 'x', 'X': + n = 2 + case 'u': + n = 4 + case 'U': + n = 8 + } + var v rune + if len(s) < n { + err = fmt.Errorf("unable to unescape string") + return + } + for j := 0; j < n; j++ { + x, ok := unhex(s[j]) + if !ok { + err = fmt.Errorf("unable to unescape string") + return + } + v = v<<4 | x + } + s = s[n:] + if v > utf8.MaxRune { + err = fmt.Errorf("unable to unescape string") + return + } + value = v + multibyte = true + + // 5. Octal escape sequences, must be three digits \[0-3][0-7][0-7] + case '0', '1', '2', '3': + if len(s) < 2 { + err = fmt.Errorf("unable to unescape octal sequence in string") + return + } + v := rune(c - '0') + for j := 0; j < 2; j++ { + x := s[j] + if x < '0' || x > '7' { + err = fmt.Errorf("unable to unescape octal sequence in string") + return + } + v = v*8 + rune(x-'0') + } + if v > utf8.MaxRune { + err = fmt.Errorf("unable to unescape string") + return + } + value = v + s = s[2:] + multibyte = true + + // Unknown escape sequence. + default: + err = fmt.Errorf("unable to unescape string") + } + + tail = s + return +} + +func unhex(b byte) (rune, bool) { + c := rune(b) + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + return 0, false +} diff --git a/vendor/github.com/antonmedv/expr/parser/parser.go b/vendor/github.com/antonmedv/expr/parser/parser.go new file mode 100644 index 0000000..821de9d --- /dev/null +++ b/vendor/github.com/antonmedv/expr/parser/parser.go @@ -0,0 +1,588 @@ +package parser + +import ( + "fmt" + "regexp" + "strconv" + "strings" + "unicode/utf8" + + . "github.com/antonmedv/expr/ast" + "github.com/antonmedv/expr/file" + . "github.com/antonmedv/expr/parser/lexer" +) + +type associativity int + +const ( + left associativity = iota + 1 + right +) + +type operator struct { + precedence int + associativity associativity +} + +type builtin struct { + arity int +} + +var unaryOperators = map[string]operator{ + "not": {50, left}, + "!": {50, left}, + "-": {500, left}, + "+": {500, left}, +} + +var binaryOperators = map[string]operator{ + "or": {10, left}, + "||": {10, left}, + "and": {15, left}, + "&&": {15, left}, + "==": {20, left}, + "!=": {20, left}, + "<": {20, left}, + ">": {20, left}, + ">=": {20, left}, + "<=": {20, left}, + "not in": {20, left}, + "in": {20, left}, + "matches": {20, left}, + "contains": {20, left}, + "startsWith": {20, left}, + "endsWith": {20, left}, + "..": {25, left}, + "+": {30, left}, + "-": {30, left}, + "*": {60, left}, + "/": {60, left}, + "%": {60, left}, + "**": {70, right}, +} + +var builtins = map[string]builtin{ + "len": {1}, + "all": {2}, + "none": {2}, + "any": {2}, + "one": {2}, + "filter": {2}, + "map": {2}, + "count": {2}, +} + +type parser struct { + tokens []Token + current Token + pos int + err *file.Error + depth int // closure call depth +} + +type Tree struct { + Node Node + Source *file.Source +} + +func Parse(input string) (*Tree, error) { + source := file.NewSource(input) + + tokens, err := Lex(source) + if err != nil { + return nil, err + } + + p := &parser{ + tokens: tokens, + current: tokens[0], + } + + node := p.parseExpression(0) + + if !p.current.Is(EOF) { + p.error("unexpected token %v", p.current) + } + + if p.err != nil { + return nil, p.err.Bind(source) + } + + return &Tree{ + Node: node, + Source: source, + }, nil +} + +func (p *parser) error(format string, args ...interface{}) { + if p.err == nil { // show first error + p.err = &file.Error{ + Location: p.current.Location, + Message: fmt.Sprintf(format, args...), + } + } +} + +func (p *parser) next() { + p.pos++ + if p.pos >= len(p.tokens) { + p.error("unexpected end of expression") + return + } + p.current = p.tokens[p.pos] +} + +func (p *parser) expect(kind Kind, values ...string) { + if p.current.Is(kind, values...) { + p.next() + return + } + p.error("unexpected token %v", p.current) +} + +// parse functions + +func (p *parser) parseExpression(precedence int) Node { + nodeLeft := p.parsePrimary() + + token := p.current + for token.Is(Operator) && p.err == nil { + if op, ok := binaryOperators[token.Value]; ok { + if op.precedence >= precedence { + p.next() + + var nodeRight Node + if op.associativity == left { + nodeRight = p.parseExpression(op.precedence + 1) + } else { + nodeRight = p.parseExpression(op.precedence) + } + + if token.Is(Operator, "matches") { + var r *regexp.Regexp + var err error + + if s, ok := nodeRight.(*StringNode); ok { + r, err = regexp.Compile(s.Value) + if err != nil { + p.error("%v", err) + } + } + nodeLeft = &MatchesNode{ + Regexp: r, + Left: nodeLeft, + Right: nodeRight, + } + nodeLeft.SetLocation(token.Location) + } else { + nodeLeft = &BinaryNode{ + Operator: token.Value, + Left: nodeLeft, + Right: nodeRight, + } + nodeLeft.SetLocation(token.Location) + } + token = p.current + continue + } + } + break + } + + if precedence == 0 { + nodeLeft = p.parseConditionalExpression(nodeLeft) + } + + return nodeLeft +} + +func (p *parser) parsePrimary() Node { + token := p.current + + if token.Is(Operator) { + if op, ok := unaryOperators[token.Value]; ok { + p.next() + expr := p.parseExpression(op.precedence) + node := &UnaryNode{ + Operator: token.Value, + Node: expr, + } + node.SetLocation(token.Location) + return p.parsePostfixExpression(node) + } + } + + if token.Is(Bracket, "(") { + p.next() + expr := p.parseExpression(0) + p.expect(Bracket, ")") // "an opened parenthesis is not properly closed" + return p.parsePostfixExpression(expr) + } + + if p.depth > 0 { + if token.Is(Operator, "#") || token.Is(Operator, ".") { + if token.Is(Operator, "#") { + p.next() + } + node := &PointerNode{} + node.SetLocation(token.Location) + return p.parsePostfixExpression(node) + } + } else { + if token.Is(Operator, "#") || token.Is(Operator, ".") { + p.error("cannot use pointer accessor outside closure") + } + } + + return p.parsePrimaryExpression() +} + +func (p *parser) parseConditionalExpression(node Node) Node { + var expr1, expr2 Node + for p.current.Is(Operator, "?") && p.err == nil { + p.next() + + if !p.current.Is(Operator, ":") { + expr1 = p.parseExpression(0) + p.expect(Operator, ":") + expr2 = p.parseExpression(0) + } else { + p.next() + expr1 = node + expr2 = p.parseExpression(0) + } + + node = &ConditionalNode{ + Cond: node, + Exp1: expr1, + Exp2: expr2, + } + } + return node +} + +func (p *parser) parsePrimaryExpression() Node { + var node Node + token := p.current + + switch token.Kind { + + case Identifier: + p.next() + switch token.Value { + case "true": + node := &BoolNode{Value: true} + node.SetLocation(token.Location) + return node + case "false": + node := &BoolNode{Value: false} + node.SetLocation(token.Location) + return node + case "nil": + node := &NilNode{} + node.SetLocation(token.Location) + return node + default: + node = p.parseIdentifierExpression(token, p.current) + } + + case Number: + p.next() + value := strings.Replace(token.Value, "_", "", -1) + if strings.ContainsAny(value, ".eE") { + number, err := strconv.ParseFloat(value, 64) + if err != nil { + p.error("invalid float literal: %v", err) + } + node := &FloatNode{Value: number} + node.SetLocation(token.Location) + return node + } else if strings.Contains(value, "x") { + number, err := strconv.ParseInt(value, 0, 64) + if err != nil { + p.error("invalid hex literal: %v", err) + } + node := &IntegerNode{Value: int(number)} + node.SetLocation(token.Location) + return node + } else { + number, err := strconv.ParseInt(value, 10, 64) + if err != nil { + p.error("invalid integer literal: %v", err) + } + node := &IntegerNode{Value: int(number)} + node.SetLocation(token.Location) + return node + } + + case String: + p.next() + node := &StringNode{Value: token.Value} + node.SetLocation(token.Location) + return node + + default: + if token.Is(Bracket, "[") { + node = p.parseArrayExpression(token) + } else if token.Is(Bracket, "{") { + node = p.parseMapExpression(token) + } else { + p.error("unexpected token %v", token) + } + } + + return p.parsePostfixExpression(node) +} + +func (p *parser) parseIdentifierExpression(token, next Token) Node { + var node Node + if p.current.Is(Bracket, "(") { + var arguments []Node + + if b, ok := builtins[token.Value]; ok { + p.expect(Bracket, "(") + // TODO: Add builtins signatures. + if b.arity == 1 { + arguments = make([]Node, 1) + arguments[0] = p.parseExpression(0) + } else if b.arity == 2 { + arguments = make([]Node, 2) + arguments[0] = p.parseExpression(0) + p.expect(Operator, ",") + arguments[1] = p.parseClosure() + } + p.expect(Bracket, ")") + + node = &BuiltinNode{ + Name: token.Value, + Arguments: arguments, + } + node.SetLocation(token.Location) + } else { + arguments = p.parseArguments() + node = &FunctionNode{ + Name: token.Value, + Arguments: arguments, + } + node.SetLocation(token.Location) + } + } else { + var nilsafe bool + if next.Value == "?." { + nilsafe = true + } + node = &IdentifierNode{Value: token.Value, NilSafe: nilsafe} + node.SetLocation(token.Location) + } + return node +} + +func (p *parser) parseClosure() Node { + token := p.current + p.expect(Bracket, "{") + + p.depth++ + node := p.parseExpression(0) + p.depth-- + + p.expect(Bracket, "}") + closure := &ClosureNode{ + Node: node, + } + closure.SetLocation(token.Location) + return closure +} + +func (p *parser) parseArrayExpression(token Token) Node { + nodes := make([]Node, 0) + + p.expect(Bracket, "[") + for !p.current.Is(Bracket, "]") && p.err == nil { + if len(nodes) > 0 { + p.expect(Operator, ",") + if p.current.Is(Bracket, "]") { + goto end + } + } + node := p.parseExpression(0) + nodes = append(nodes, node) + } +end: + p.expect(Bracket, "]") + + node := &ArrayNode{Nodes: nodes} + node.SetLocation(token.Location) + return node +} + +func (p *parser) parseMapExpression(token Token) Node { + p.expect(Bracket, "{") + + nodes := make([]Node, 0) + for !p.current.Is(Bracket, "}") && p.err == nil { + if len(nodes) > 0 { + p.expect(Operator, ",") + if p.current.Is(Bracket, "}") { + goto end + } + if p.current.Is(Operator, ",") { + p.error("unexpected token %v", p.current) + } + } + + var key Node + // a map key can be: + // * a number + // * a string + // * a identifier, which is equivalent to a string + // * an expression, which must be enclosed in parentheses -- (1 + 2) + if p.current.Is(Number) || p.current.Is(String) || p.current.Is(Identifier) { + key = &StringNode{Value: p.current.Value} + key.SetLocation(token.Location) + p.next() + } else if p.current.Is(Bracket, "(") { + key = p.parseExpression(0) + } else { + p.error("a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token %v)", p.current) + } + + p.expect(Operator, ":") + + node := p.parseExpression(0) + pair := &PairNode{Key: key, Value: node} + pair.SetLocation(token.Location) + nodes = append(nodes, pair) + } + +end: + p.expect(Bracket, "}") + + node := &MapNode{Pairs: nodes} + node.SetLocation(token.Location) + return node +} + +func (p *parser) parsePostfixExpression(node Node) Node { + token := p.current + var nilsafe bool + for (token.Is(Operator) || token.Is(Bracket)) && p.err == nil { + if token.Value == "." || token.Value == "?." { + if token.Value == "?." { + nilsafe = true + } + p.next() + + token = p.current + p.next() + + if token.Kind != Identifier && + // Operators like "not" and "matches" are valid methods or property names. + (token.Kind != Operator || !isValidIdentifier(token.Value)) { + p.error("expected name") + } + + if p.current.Is(Bracket, "(") { + arguments := p.parseArguments() + node = &MethodNode{ + Node: node, + Method: token.Value, + Arguments: arguments, + NilSafe: nilsafe, + } + node.SetLocation(token.Location) + } else { + node = &PropertyNode{ + Node: node, + Property: token.Value, + NilSafe: nilsafe, + } + node.SetLocation(token.Location) + } + + } else if token.Value == "[" { + p.next() + var from, to Node + + if p.current.Is(Operator, ":") { // slice without from [:1] + p.next() + + if !p.current.Is(Bracket, "]") { // slice without from and to [:] + to = p.parseExpression(0) + } + + node = &SliceNode{ + Node: node, + To: to, + } + node.SetLocation(token.Location) + p.expect(Bracket, "]") + + } else { + + from = p.parseExpression(0) + + if p.current.Is(Operator, ":") { + p.next() + + if !p.current.Is(Bracket, "]") { // slice without to [1:] + to = p.parseExpression(0) + } + + node = &SliceNode{ + Node: node, + From: from, + To: to, + } + node.SetLocation(token.Location) + p.expect(Bracket, "]") + + } else { + // Slice operator [:] was not found, it should by just index node. + + node = &IndexNode{ + Node: node, + Index: from, + } + node.SetLocation(token.Location) + p.expect(Bracket, "]") + } + } + } else { + break + } + + token = p.current + } + return node +} + +func isValidIdentifier(str string) bool { + if len(str) == 0 { + return false + } + h, w := utf8.DecodeRuneInString(str) + if !IsAlphabetic(h) { + return false + } + for _, r := range str[w:] { + if !IsAlphaNumeric(r) { + return false + } + } + return true +} + +func (p *parser) parseArguments() []Node { + p.expect(Bracket, "(") + nodes := make([]Node, 0) + for !p.current.Is(Bracket, ")") && p.err == nil { + if len(nodes) > 0 { + p.expect(Operator, ",") + } + node := p.parseExpression(0) + nodes = append(nodes, node) + } + p.expect(Bracket, ")") + + return nodes +} diff --git a/vendor/github.com/antonmedv/expr/vm/helpers.go b/vendor/github.com/antonmedv/expr/vm/helpers.go new file mode 100644 index 0000000..775b0e7 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/vm/helpers.go @@ -0,0 +1,3247 @@ +// Code generated by vm/generate/main.go. DO NOT EDIT. + +package vm + +import ( + "fmt" + "reflect" +) + +func equal(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x == y + case uint8: + return uint8(x) == y + case uint16: + return uint16(x) == y + case uint32: + return uint32(x) == y + case uint64: + return uint64(x) == y + case int: + return int(x) == y + case int8: + return int8(x) == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case uint8: + switch y := b.(type) { + case uint: + return x == uint8(y) + case uint8: + return x == y + case uint16: + return uint16(x) == y + case uint32: + return uint32(x) == y + case uint64: + return uint64(x) == y + case int: + return int(x) == y + case int8: + return int8(x) == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case uint16: + switch y := b.(type) { + case uint: + return x == uint16(y) + case uint8: + return x == uint16(y) + case uint16: + return x == y + case uint32: + return uint32(x) == y + case uint64: + return uint64(x) == y + case int: + return int(x) == y + case int8: + return int8(x) == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case uint32: + switch y := b.(type) { + case uint: + return x == uint32(y) + case uint8: + return x == uint32(y) + case uint16: + return x == uint32(y) + case uint32: + return x == y + case uint64: + return uint64(x) == y + case int: + return int(x) == y + case int8: + return int8(x) == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case uint64: + switch y := b.(type) { + case uint: + return x == uint64(y) + case uint8: + return x == uint64(y) + case uint16: + return x == uint64(y) + case uint32: + return x == uint64(y) + case uint64: + return x == y + case int: + return int(x) == y + case int8: + return int8(x) == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case int: + switch y := b.(type) { + case uint: + return x == int(y) + case uint8: + return x == int(y) + case uint16: + return x == int(y) + case uint32: + return x == int(y) + case uint64: + return x == int(y) + case int: + return x == y + case int8: + return int8(x) == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case int8: + switch y := b.(type) { + case uint: + return x == int8(y) + case uint8: + return x == int8(y) + case uint16: + return x == int8(y) + case uint32: + return x == int8(y) + case uint64: + return x == int8(y) + case int: + return x == int8(y) + case int8: + return x == y + case int16: + return int16(x) == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case int16: + switch y := b.(type) { + case uint: + return x == int16(y) + case uint8: + return x == int16(y) + case uint16: + return x == int16(y) + case uint32: + return x == int16(y) + case uint64: + return x == int16(y) + case int: + return x == int16(y) + case int8: + return x == int16(y) + case int16: + return x == y + case int32: + return int32(x) == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case int32: + switch y := b.(type) { + case uint: + return x == int32(y) + case uint8: + return x == int32(y) + case uint16: + return x == int32(y) + case uint32: + return x == int32(y) + case uint64: + return x == int32(y) + case int: + return x == int32(y) + case int8: + return x == int32(y) + case int16: + return x == int32(y) + case int32: + return x == y + case int64: + return int64(x) == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case int64: + switch y := b.(type) { + case uint: + return x == int64(y) + case uint8: + return x == int64(y) + case uint16: + return x == int64(y) + case uint32: + return x == int64(y) + case uint64: + return x == int64(y) + case int: + return x == int64(y) + case int8: + return x == int64(y) + case int16: + return x == int64(y) + case int32: + return x == int64(y) + case int64: + return x == y + case float32: + return float32(x) == y + case float64: + return float64(x) == y + } + case float32: + switch y := b.(type) { + case uint: + return x == float32(y) + case uint8: + return x == float32(y) + case uint16: + return x == float32(y) + case uint32: + return x == float32(y) + case uint64: + return x == float32(y) + case int: + return x == float32(y) + case int8: + return x == float32(y) + case int16: + return x == float32(y) + case int32: + return x == float32(y) + case int64: + return x == float32(y) + case float32: + return x == y + case float64: + return float64(x) == y + } + case float64: + switch y := b.(type) { + case uint: + return x == float64(y) + case uint8: + return x == float64(y) + case uint16: + return x == float64(y) + case uint32: + return x == float64(y) + case uint64: + return x == float64(y) + case int: + return x == float64(y) + case int8: + return x == float64(y) + case int16: + return x == float64(y) + case int32: + return x == float64(y) + case int64: + return x == float64(y) + case float32: + return x == float64(y) + case float64: + return x == y + } + case string: + switch y := b.(type) { + case string: + return x == y + } + } + if isNil(a) && isNil(b) { + return true + } + return reflect.DeepEqual(a, b) +} + +func less(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x < y + case uint8: + return uint8(x) < y + case uint16: + return uint16(x) < y + case uint32: + return uint32(x) < y + case uint64: + return uint64(x) < y + case int: + return int(x) < y + case int8: + return int8(x) < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case uint8: + switch y := b.(type) { + case uint: + return x < uint8(y) + case uint8: + return x < y + case uint16: + return uint16(x) < y + case uint32: + return uint32(x) < y + case uint64: + return uint64(x) < y + case int: + return int(x) < y + case int8: + return int8(x) < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case uint16: + switch y := b.(type) { + case uint: + return x < uint16(y) + case uint8: + return x < uint16(y) + case uint16: + return x < y + case uint32: + return uint32(x) < y + case uint64: + return uint64(x) < y + case int: + return int(x) < y + case int8: + return int8(x) < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case uint32: + switch y := b.(type) { + case uint: + return x < uint32(y) + case uint8: + return x < uint32(y) + case uint16: + return x < uint32(y) + case uint32: + return x < y + case uint64: + return uint64(x) < y + case int: + return int(x) < y + case int8: + return int8(x) < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case uint64: + switch y := b.(type) { + case uint: + return x < uint64(y) + case uint8: + return x < uint64(y) + case uint16: + return x < uint64(y) + case uint32: + return x < uint64(y) + case uint64: + return x < y + case int: + return int(x) < y + case int8: + return int8(x) < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case int: + switch y := b.(type) { + case uint: + return x < int(y) + case uint8: + return x < int(y) + case uint16: + return x < int(y) + case uint32: + return x < int(y) + case uint64: + return x < int(y) + case int: + return x < y + case int8: + return int8(x) < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case int8: + switch y := b.(type) { + case uint: + return x < int8(y) + case uint8: + return x < int8(y) + case uint16: + return x < int8(y) + case uint32: + return x < int8(y) + case uint64: + return x < int8(y) + case int: + return x < int8(y) + case int8: + return x < y + case int16: + return int16(x) < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case int16: + switch y := b.(type) { + case uint: + return x < int16(y) + case uint8: + return x < int16(y) + case uint16: + return x < int16(y) + case uint32: + return x < int16(y) + case uint64: + return x < int16(y) + case int: + return x < int16(y) + case int8: + return x < int16(y) + case int16: + return x < y + case int32: + return int32(x) < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case int32: + switch y := b.(type) { + case uint: + return x < int32(y) + case uint8: + return x < int32(y) + case uint16: + return x < int32(y) + case uint32: + return x < int32(y) + case uint64: + return x < int32(y) + case int: + return x < int32(y) + case int8: + return x < int32(y) + case int16: + return x < int32(y) + case int32: + return x < y + case int64: + return int64(x) < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case int64: + switch y := b.(type) { + case uint: + return x < int64(y) + case uint8: + return x < int64(y) + case uint16: + return x < int64(y) + case uint32: + return x < int64(y) + case uint64: + return x < int64(y) + case int: + return x < int64(y) + case int8: + return x < int64(y) + case int16: + return x < int64(y) + case int32: + return x < int64(y) + case int64: + return x < y + case float32: + return float32(x) < y + case float64: + return float64(x) < y + } + case float32: + switch y := b.(type) { + case uint: + return x < float32(y) + case uint8: + return x < float32(y) + case uint16: + return x < float32(y) + case uint32: + return x < float32(y) + case uint64: + return x < float32(y) + case int: + return x < float32(y) + case int8: + return x < float32(y) + case int16: + return x < float32(y) + case int32: + return x < float32(y) + case int64: + return x < float32(y) + case float32: + return x < y + case float64: + return float64(x) < y + } + case float64: + switch y := b.(type) { + case uint: + return x < float64(y) + case uint8: + return x < float64(y) + case uint16: + return x < float64(y) + case uint32: + return x < float64(y) + case uint64: + return x < float64(y) + case int: + return x < float64(y) + case int8: + return x < float64(y) + case int16: + return x < float64(y) + case int32: + return x < float64(y) + case int64: + return x < float64(y) + case float32: + return x < float64(y) + case float64: + return x < y + } + case string: + switch y := b.(type) { + case string: + return x < y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "<", b)) +} + +func more(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x > y + case uint8: + return uint8(x) > y + case uint16: + return uint16(x) > y + case uint32: + return uint32(x) > y + case uint64: + return uint64(x) > y + case int: + return int(x) > y + case int8: + return int8(x) > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case uint8: + switch y := b.(type) { + case uint: + return x > uint8(y) + case uint8: + return x > y + case uint16: + return uint16(x) > y + case uint32: + return uint32(x) > y + case uint64: + return uint64(x) > y + case int: + return int(x) > y + case int8: + return int8(x) > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case uint16: + switch y := b.(type) { + case uint: + return x > uint16(y) + case uint8: + return x > uint16(y) + case uint16: + return x > y + case uint32: + return uint32(x) > y + case uint64: + return uint64(x) > y + case int: + return int(x) > y + case int8: + return int8(x) > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case uint32: + switch y := b.(type) { + case uint: + return x > uint32(y) + case uint8: + return x > uint32(y) + case uint16: + return x > uint32(y) + case uint32: + return x > y + case uint64: + return uint64(x) > y + case int: + return int(x) > y + case int8: + return int8(x) > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case uint64: + switch y := b.(type) { + case uint: + return x > uint64(y) + case uint8: + return x > uint64(y) + case uint16: + return x > uint64(y) + case uint32: + return x > uint64(y) + case uint64: + return x > y + case int: + return int(x) > y + case int8: + return int8(x) > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case int: + switch y := b.(type) { + case uint: + return x > int(y) + case uint8: + return x > int(y) + case uint16: + return x > int(y) + case uint32: + return x > int(y) + case uint64: + return x > int(y) + case int: + return x > y + case int8: + return int8(x) > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case int8: + switch y := b.(type) { + case uint: + return x > int8(y) + case uint8: + return x > int8(y) + case uint16: + return x > int8(y) + case uint32: + return x > int8(y) + case uint64: + return x > int8(y) + case int: + return x > int8(y) + case int8: + return x > y + case int16: + return int16(x) > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case int16: + switch y := b.(type) { + case uint: + return x > int16(y) + case uint8: + return x > int16(y) + case uint16: + return x > int16(y) + case uint32: + return x > int16(y) + case uint64: + return x > int16(y) + case int: + return x > int16(y) + case int8: + return x > int16(y) + case int16: + return x > y + case int32: + return int32(x) > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case int32: + switch y := b.(type) { + case uint: + return x > int32(y) + case uint8: + return x > int32(y) + case uint16: + return x > int32(y) + case uint32: + return x > int32(y) + case uint64: + return x > int32(y) + case int: + return x > int32(y) + case int8: + return x > int32(y) + case int16: + return x > int32(y) + case int32: + return x > y + case int64: + return int64(x) > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case int64: + switch y := b.(type) { + case uint: + return x > int64(y) + case uint8: + return x > int64(y) + case uint16: + return x > int64(y) + case uint32: + return x > int64(y) + case uint64: + return x > int64(y) + case int: + return x > int64(y) + case int8: + return x > int64(y) + case int16: + return x > int64(y) + case int32: + return x > int64(y) + case int64: + return x > y + case float32: + return float32(x) > y + case float64: + return float64(x) > y + } + case float32: + switch y := b.(type) { + case uint: + return x > float32(y) + case uint8: + return x > float32(y) + case uint16: + return x > float32(y) + case uint32: + return x > float32(y) + case uint64: + return x > float32(y) + case int: + return x > float32(y) + case int8: + return x > float32(y) + case int16: + return x > float32(y) + case int32: + return x > float32(y) + case int64: + return x > float32(y) + case float32: + return x > y + case float64: + return float64(x) > y + } + case float64: + switch y := b.(type) { + case uint: + return x > float64(y) + case uint8: + return x > float64(y) + case uint16: + return x > float64(y) + case uint32: + return x > float64(y) + case uint64: + return x > float64(y) + case int: + return x > float64(y) + case int8: + return x > float64(y) + case int16: + return x > float64(y) + case int32: + return x > float64(y) + case int64: + return x > float64(y) + case float32: + return x > float64(y) + case float64: + return x > y + } + case string: + switch y := b.(type) { + case string: + return x > y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, ">", b)) +} + +func lessOrEqual(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x <= y + case uint8: + return uint8(x) <= y + case uint16: + return uint16(x) <= y + case uint32: + return uint32(x) <= y + case uint64: + return uint64(x) <= y + case int: + return int(x) <= y + case int8: + return int8(x) <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case uint8: + switch y := b.(type) { + case uint: + return x <= uint8(y) + case uint8: + return x <= y + case uint16: + return uint16(x) <= y + case uint32: + return uint32(x) <= y + case uint64: + return uint64(x) <= y + case int: + return int(x) <= y + case int8: + return int8(x) <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case uint16: + switch y := b.(type) { + case uint: + return x <= uint16(y) + case uint8: + return x <= uint16(y) + case uint16: + return x <= y + case uint32: + return uint32(x) <= y + case uint64: + return uint64(x) <= y + case int: + return int(x) <= y + case int8: + return int8(x) <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case uint32: + switch y := b.(type) { + case uint: + return x <= uint32(y) + case uint8: + return x <= uint32(y) + case uint16: + return x <= uint32(y) + case uint32: + return x <= y + case uint64: + return uint64(x) <= y + case int: + return int(x) <= y + case int8: + return int8(x) <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case uint64: + switch y := b.(type) { + case uint: + return x <= uint64(y) + case uint8: + return x <= uint64(y) + case uint16: + return x <= uint64(y) + case uint32: + return x <= uint64(y) + case uint64: + return x <= y + case int: + return int(x) <= y + case int8: + return int8(x) <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case int: + switch y := b.(type) { + case uint: + return x <= int(y) + case uint8: + return x <= int(y) + case uint16: + return x <= int(y) + case uint32: + return x <= int(y) + case uint64: + return x <= int(y) + case int: + return x <= y + case int8: + return int8(x) <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case int8: + switch y := b.(type) { + case uint: + return x <= int8(y) + case uint8: + return x <= int8(y) + case uint16: + return x <= int8(y) + case uint32: + return x <= int8(y) + case uint64: + return x <= int8(y) + case int: + return x <= int8(y) + case int8: + return x <= y + case int16: + return int16(x) <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case int16: + switch y := b.(type) { + case uint: + return x <= int16(y) + case uint8: + return x <= int16(y) + case uint16: + return x <= int16(y) + case uint32: + return x <= int16(y) + case uint64: + return x <= int16(y) + case int: + return x <= int16(y) + case int8: + return x <= int16(y) + case int16: + return x <= y + case int32: + return int32(x) <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case int32: + switch y := b.(type) { + case uint: + return x <= int32(y) + case uint8: + return x <= int32(y) + case uint16: + return x <= int32(y) + case uint32: + return x <= int32(y) + case uint64: + return x <= int32(y) + case int: + return x <= int32(y) + case int8: + return x <= int32(y) + case int16: + return x <= int32(y) + case int32: + return x <= y + case int64: + return int64(x) <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case int64: + switch y := b.(type) { + case uint: + return x <= int64(y) + case uint8: + return x <= int64(y) + case uint16: + return x <= int64(y) + case uint32: + return x <= int64(y) + case uint64: + return x <= int64(y) + case int: + return x <= int64(y) + case int8: + return x <= int64(y) + case int16: + return x <= int64(y) + case int32: + return x <= int64(y) + case int64: + return x <= y + case float32: + return float32(x) <= y + case float64: + return float64(x) <= y + } + case float32: + switch y := b.(type) { + case uint: + return x <= float32(y) + case uint8: + return x <= float32(y) + case uint16: + return x <= float32(y) + case uint32: + return x <= float32(y) + case uint64: + return x <= float32(y) + case int: + return x <= float32(y) + case int8: + return x <= float32(y) + case int16: + return x <= float32(y) + case int32: + return x <= float32(y) + case int64: + return x <= float32(y) + case float32: + return x <= y + case float64: + return float64(x) <= y + } + case float64: + switch y := b.(type) { + case uint: + return x <= float64(y) + case uint8: + return x <= float64(y) + case uint16: + return x <= float64(y) + case uint32: + return x <= float64(y) + case uint64: + return x <= float64(y) + case int: + return x <= float64(y) + case int8: + return x <= float64(y) + case int16: + return x <= float64(y) + case int32: + return x <= float64(y) + case int64: + return x <= float64(y) + case float32: + return x <= float64(y) + case float64: + return x <= y + } + case string: + switch y := b.(type) { + case string: + return x <= y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "<=", b)) +} + +func moreOrEqual(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x >= y + case uint8: + return uint8(x) >= y + case uint16: + return uint16(x) >= y + case uint32: + return uint32(x) >= y + case uint64: + return uint64(x) >= y + case int: + return int(x) >= y + case int8: + return int8(x) >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case uint8: + switch y := b.(type) { + case uint: + return x >= uint8(y) + case uint8: + return x >= y + case uint16: + return uint16(x) >= y + case uint32: + return uint32(x) >= y + case uint64: + return uint64(x) >= y + case int: + return int(x) >= y + case int8: + return int8(x) >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case uint16: + switch y := b.(type) { + case uint: + return x >= uint16(y) + case uint8: + return x >= uint16(y) + case uint16: + return x >= y + case uint32: + return uint32(x) >= y + case uint64: + return uint64(x) >= y + case int: + return int(x) >= y + case int8: + return int8(x) >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case uint32: + switch y := b.(type) { + case uint: + return x >= uint32(y) + case uint8: + return x >= uint32(y) + case uint16: + return x >= uint32(y) + case uint32: + return x >= y + case uint64: + return uint64(x) >= y + case int: + return int(x) >= y + case int8: + return int8(x) >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case uint64: + switch y := b.(type) { + case uint: + return x >= uint64(y) + case uint8: + return x >= uint64(y) + case uint16: + return x >= uint64(y) + case uint32: + return x >= uint64(y) + case uint64: + return x >= y + case int: + return int(x) >= y + case int8: + return int8(x) >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case int: + switch y := b.(type) { + case uint: + return x >= int(y) + case uint8: + return x >= int(y) + case uint16: + return x >= int(y) + case uint32: + return x >= int(y) + case uint64: + return x >= int(y) + case int: + return x >= y + case int8: + return int8(x) >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case int8: + switch y := b.(type) { + case uint: + return x >= int8(y) + case uint8: + return x >= int8(y) + case uint16: + return x >= int8(y) + case uint32: + return x >= int8(y) + case uint64: + return x >= int8(y) + case int: + return x >= int8(y) + case int8: + return x >= y + case int16: + return int16(x) >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case int16: + switch y := b.(type) { + case uint: + return x >= int16(y) + case uint8: + return x >= int16(y) + case uint16: + return x >= int16(y) + case uint32: + return x >= int16(y) + case uint64: + return x >= int16(y) + case int: + return x >= int16(y) + case int8: + return x >= int16(y) + case int16: + return x >= y + case int32: + return int32(x) >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case int32: + switch y := b.(type) { + case uint: + return x >= int32(y) + case uint8: + return x >= int32(y) + case uint16: + return x >= int32(y) + case uint32: + return x >= int32(y) + case uint64: + return x >= int32(y) + case int: + return x >= int32(y) + case int8: + return x >= int32(y) + case int16: + return x >= int32(y) + case int32: + return x >= y + case int64: + return int64(x) >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case int64: + switch y := b.(type) { + case uint: + return x >= int64(y) + case uint8: + return x >= int64(y) + case uint16: + return x >= int64(y) + case uint32: + return x >= int64(y) + case uint64: + return x >= int64(y) + case int: + return x >= int64(y) + case int8: + return x >= int64(y) + case int16: + return x >= int64(y) + case int32: + return x >= int64(y) + case int64: + return x >= y + case float32: + return float32(x) >= y + case float64: + return float64(x) >= y + } + case float32: + switch y := b.(type) { + case uint: + return x >= float32(y) + case uint8: + return x >= float32(y) + case uint16: + return x >= float32(y) + case uint32: + return x >= float32(y) + case uint64: + return x >= float32(y) + case int: + return x >= float32(y) + case int8: + return x >= float32(y) + case int16: + return x >= float32(y) + case int32: + return x >= float32(y) + case int64: + return x >= float32(y) + case float32: + return x >= y + case float64: + return float64(x) >= y + } + case float64: + switch y := b.(type) { + case uint: + return x >= float64(y) + case uint8: + return x >= float64(y) + case uint16: + return x >= float64(y) + case uint32: + return x >= float64(y) + case uint64: + return x >= float64(y) + case int: + return x >= float64(y) + case int8: + return x >= float64(y) + case int16: + return x >= float64(y) + case int32: + return x >= float64(y) + case int64: + return x >= float64(y) + case float32: + return x >= float64(y) + case float64: + return x >= y + } + case string: + switch y := b.(type) { + case string: + return x >= y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, ">=", b)) +} + +func add(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x + y + case uint8: + return uint8(x) + y + case uint16: + return uint16(x) + y + case uint32: + return uint32(x) + y + case uint64: + return uint64(x) + y + case int: + return int(x) + y + case int8: + return int8(x) + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case uint8: + switch y := b.(type) { + case uint: + return x + uint8(y) + case uint8: + return x + y + case uint16: + return uint16(x) + y + case uint32: + return uint32(x) + y + case uint64: + return uint64(x) + y + case int: + return int(x) + y + case int8: + return int8(x) + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case uint16: + switch y := b.(type) { + case uint: + return x + uint16(y) + case uint8: + return x + uint16(y) + case uint16: + return x + y + case uint32: + return uint32(x) + y + case uint64: + return uint64(x) + y + case int: + return int(x) + y + case int8: + return int8(x) + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case uint32: + switch y := b.(type) { + case uint: + return x + uint32(y) + case uint8: + return x + uint32(y) + case uint16: + return x + uint32(y) + case uint32: + return x + y + case uint64: + return uint64(x) + y + case int: + return int(x) + y + case int8: + return int8(x) + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case uint64: + switch y := b.(type) { + case uint: + return x + uint64(y) + case uint8: + return x + uint64(y) + case uint16: + return x + uint64(y) + case uint32: + return x + uint64(y) + case uint64: + return x + y + case int: + return int(x) + y + case int8: + return int8(x) + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case int: + switch y := b.(type) { + case uint: + return x + int(y) + case uint8: + return x + int(y) + case uint16: + return x + int(y) + case uint32: + return x + int(y) + case uint64: + return x + int(y) + case int: + return x + y + case int8: + return int8(x) + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case int8: + switch y := b.(type) { + case uint: + return x + int8(y) + case uint8: + return x + int8(y) + case uint16: + return x + int8(y) + case uint32: + return x + int8(y) + case uint64: + return x + int8(y) + case int: + return x + int8(y) + case int8: + return x + y + case int16: + return int16(x) + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case int16: + switch y := b.(type) { + case uint: + return x + int16(y) + case uint8: + return x + int16(y) + case uint16: + return x + int16(y) + case uint32: + return x + int16(y) + case uint64: + return x + int16(y) + case int: + return x + int16(y) + case int8: + return x + int16(y) + case int16: + return x + y + case int32: + return int32(x) + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case int32: + switch y := b.(type) { + case uint: + return x + int32(y) + case uint8: + return x + int32(y) + case uint16: + return x + int32(y) + case uint32: + return x + int32(y) + case uint64: + return x + int32(y) + case int: + return x + int32(y) + case int8: + return x + int32(y) + case int16: + return x + int32(y) + case int32: + return x + y + case int64: + return int64(x) + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case int64: + switch y := b.(type) { + case uint: + return x + int64(y) + case uint8: + return x + int64(y) + case uint16: + return x + int64(y) + case uint32: + return x + int64(y) + case uint64: + return x + int64(y) + case int: + return x + int64(y) + case int8: + return x + int64(y) + case int16: + return x + int64(y) + case int32: + return x + int64(y) + case int64: + return x + y + case float32: + return float32(x) + y + case float64: + return float64(x) + y + } + case float32: + switch y := b.(type) { + case uint: + return x + float32(y) + case uint8: + return x + float32(y) + case uint16: + return x + float32(y) + case uint32: + return x + float32(y) + case uint64: + return x + float32(y) + case int: + return x + float32(y) + case int8: + return x + float32(y) + case int16: + return x + float32(y) + case int32: + return x + float32(y) + case int64: + return x + float32(y) + case float32: + return x + y + case float64: + return float64(x) + y + } + case float64: + switch y := b.(type) { + case uint: + return x + float64(y) + case uint8: + return x + float64(y) + case uint16: + return x + float64(y) + case uint32: + return x + float64(y) + case uint64: + return x + float64(y) + case int: + return x + float64(y) + case int8: + return x + float64(y) + case int16: + return x + float64(y) + case int32: + return x + float64(y) + case int64: + return x + float64(y) + case float32: + return x + float64(y) + case float64: + return x + y + } + case string: + switch y := b.(type) { + case string: + return x + y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "+", b)) +} + +func subtract(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x - y + case uint8: + return uint8(x) - y + case uint16: + return uint16(x) - y + case uint32: + return uint32(x) - y + case uint64: + return uint64(x) - y + case int: + return int(x) - y + case int8: + return int8(x) - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case uint8: + switch y := b.(type) { + case uint: + return x - uint8(y) + case uint8: + return x - y + case uint16: + return uint16(x) - y + case uint32: + return uint32(x) - y + case uint64: + return uint64(x) - y + case int: + return int(x) - y + case int8: + return int8(x) - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case uint16: + switch y := b.(type) { + case uint: + return x - uint16(y) + case uint8: + return x - uint16(y) + case uint16: + return x - y + case uint32: + return uint32(x) - y + case uint64: + return uint64(x) - y + case int: + return int(x) - y + case int8: + return int8(x) - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case uint32: + switch y := b.(type) { + case uint: + return x - uint32(y) + case uint8: + return x - uint32(y) + case uint16: + return x - uint32(y) + case uint32: + return x - y + case uint64: + return uint64(x) - y + case int: + return int(x) - y + case int8: + return int8(x) - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case uint64: + switch y := b.(type) { + case uint: + return x - uint64(y) + case uint8: + return x - uint64(y) + case uint16: + return x - uint64(y) + case uint32: + return x - uint64(y) + case uint64: + return x - y + case int: + return int(x) - y + case int8: + return int8(x) - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case int: + switch y := b.(type) { + case uint: + return x - int(y) + case uint8: + return x - int(y) + case uint16: + return x - int(y) + case uint32: + return x - int(y) + case uint64: + return x - int(y) + case int: + return x - y + case int8: + return int8(x) - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case int8: + switch y := b.(type) { + case uint: + return x - int8(y) + case uint8: + return x - int8(y) + case uint16: + return x - int8(y) + case uint32: + return x - int8(y) + case uint64: + return x - int8(y) + case int: + return x - int8(y) + case int8: + return x - y + case int16: + return int16(x) - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case int16: + switch y := b.(type) { + case uint: + return x - int16(y) + case uint8: + return x - int16(y) + case uint16: + return x - int16(y) + case uint32: + return x - int16(y) + case uint64: + return x - int16(y) + case int: + return x - int16(y) + case int8: + return x - int16(y) + case int16: + return x - y + case int32: + return int32(x) - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case int32: + switch y := b.(type) { + case uint: + return x - int32(y) + case uint8: + return x - int32(y) + case uint16: + return x - int32(y) + case uint32: + return x - int32(y) + case uint64: + return x - int32(y) + case int: + return x - int32(y) + case int8: + return x - int32(y) + case int16: + return x - int32(y) + case int32: + return x - y + case int64: + return int64(x) - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case int64: + switch y := b.(type) { + case uint: + return x - int64(y) + case uint8: + return x - int64(y) + case uint16: + return x - int64(y) + case uint32: + return x - int64(y) + case uint64: + return x - int64(y) + case int: + return x - int64(y) + case int8: + return x - int64(y) + case int16: + return x - int64(y) + case int32: + return x - int64(y) + case int64: + return x - y + case float32: + return float32(x) - y + case float64: + return float64(x) - y + } + case float32: + switch y := b.(type) { + case uint: + return x - float32(y) + case uint8: + return x - float32(y) + case uint16: + return x - float32(y) + case uint32: + return x - float32(y) + case uint64: + return x - float32(y) + case int: + return x - float32(y) + case int8: + return x - float32(y) + case int16: + return x - float32(y) + case int32: + return x - float32(y) + case int64: + return x - float32(y) + case float32: + return x - y + case float64: + return float64(x) - y + } + case float64: + switch y := b.(type) { + case uint: + return x - float64(y) + case uint8: + return x - float64(y) + case uint16: + return x - float64(y) + case uint32: + return x - float64(y) + case uint64: + return x - float64(y) + case int: + return x - float64(y) + case int8: + return x - float64(y) + case int16: + return x - float64(y) + case int32: + return x - float64(y) + case int64: + return x - float64(y) + case float32: + return x - float64(y) + case float64: + return x - y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "-", b)) +} + +func multiply(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x * y + case uint8: + return uint8(x) * y + case uint16: + return uint16(x) * y + case uint32: + return uint32(x) * y + case uint64: + return uint64(x) * y + case int: + return int(x) * y + case int8: + return int8(x) * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case uint8: + switch y := b.(type) { + case uint: + return x * uint8(y) + case uint8: + return x * y + case uint16: + return uint16(x) * y + case uint32: + return uint32(x) * y + case uint64: + return uint64(x) * y + case int: + return int(x) * y + case int8: + return int8(x) * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case uint16: + switch y := b.(type) { + case uint: + return x * uint16(y) + case uint8: + return x * uint16(y) + case uint16: + return x * y + case uint32: + return uint32(x) * y + case uint64: + return uint64(x) * y + case int: + return int(x) * y + case int8: + return int8(x) * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case uint32: + switch y := b.(type) { + case uint: + return x * uint32(y) + case uint8: + return x * uint32(y) + case uint16: + return x * uint32(y) + case uint32: + return x * y + case uint64: + return uint64(x) * y + case int: + return int(x) * y + case int8: + return int8(x) * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case uint64: + switch y := b.(type) { + case uint: + return x * uint64(y) + case uint8: + return x * uint64(y) + case uint16: + return x * uint64(y) + case uint32: + return x * uint64(y) + case uint64: + return x * y + case int: + return int(x) * y + case int8: + return int8(x) * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case int: + switch y := b.(type) { + case uint: + return x * int(y) + case uint8: + return x * int(y) + case uint16: + return x * int(y) + case uint32: + return x * int(y) + case uint64: + return x * int(y) + case int: + return x * y + case int8: + return int8(x) * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case int8: + switch y := b.(type) { + case uint: + return x * int8(y) + case uint8: + return x * int8(y) + case uint16: + return x * int8(y) + case uint32: + return x * int8(y) + case uint64: + return x * int8(y) + case int: + return x * int8(y) + case int8: + return x * y + case int16: + return int16(x) * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case int16: + switch y := b.(type) { + case uint: + return x * int16(y) + case uint8: + return x * int16(y) + case uint16: + return x * int16(y) + case uint32: + return x * int16(y) + case uint64: + return x * int16(y) + case int: + return x * int16(y) + case int8: + return x * int16(y) + case int16: + return x * y + case int32: + return int32(x) * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case int32: + switch y := b.(type) { + case uint: + return x * int32(y) + case uint8: + return x * int32(y) + case uint16: + return x * int32(y) + case uint32: + return x * int32(y) + case uint64: + return x * int32(y) + case int: + return x * int32(y) + case int8: + return x * int32(y) + case int16: + return x * int32(y) + case int32: + return x * y + case int64: + return int64(x) * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case int64: + switch y := b.(type) { + case uint: + return x * int64(y) + case uint8: + return x * int64(y) + case uint16: + return x * int64(y) + case uint32: + return x * int64(y) + case uint64: + return x * int64(y) + case int: + return x * int64(y) + case int8: + return x * int64(y) + case int16: + return x * int64(y) + case int32: + return x * int64(y) + case int64: + return x * y + case float32: + return float32(x) * y + case float64: + return float64(x) * y + } + case float32: + switch y := b.(type) { + case uint: + return x * float32(y) + case uint8: + return x * float32(y) + case uint16: + return x * float32(y) + case uint32: + return x * float32(y) + case uint64: + return x * float32(y) + case int: + return x * float32(y) + case int8: + return x * float32(y) + case int16: + return x * float32(y) + case int32: + return x * float32(y) + case int64: + return x * float32(y) + case float32: + return x * y + case float64: + return float64(x) * y + } + case float64: + switch y := b.(type) { + case uint: + return x * float64(y) + case uint8: + return x * float64(y) + case uint16: + return x * float64(y) + case uint32: + return x * float64(y) + case uint64: + return x * float64(y) + case int: + return x * float64(y) + case int8: + return x * float64(y) + case int16: + return x * float64(y) + case int32: + return x * float64(y) + case int64: + return x * float64(y) + case float32: + return x * float64(y) + case float64: + return x * y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "*", b)) +} + +func divide(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x / y + case uint8: + return uint8(x) / y + case uint16: + return uint16(x) / y + case uint32: + return uint32(x) / y + case uint64: + return uint64(x) / y + case int: + return int(x) / y + case int8: + return int8(x) / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case uint8: + switch y := b.(type) { + case uint: + return x / uint8(y) + case uint8: + return x / y + case uint16: + return uint16(x) / y + case uint32: + return uint32(x) / y + case uint64: + return uint64(x) / y + case int: + return int(x) / y + case int8: + return int8(x) / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case uint16: + switch y := b.(type) { + case uint: + return x / uint16(y) + case uint8: + return x / uint16(y) + case uint16: + return x / y + case uint32: + return uint32(x) / y + case uint64: + return uint64(x) / y + case int: + return int(x) / y + case int8: + return int8(x) / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case uint32: + switch y := b.(type) { + case uint: + return x / uint32(y) + case uint8: + return x / uint32(y) + case uint16: + return x / uint32(y) + case uint32: + return x / y + case uint64: + return uint64(x) / y + case int: + return int(x) / y + case int8: + return int8(x) / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case uint64: + switch y := b.(type) { + case uint: + return x / uint64(y) + case uint8: + return x / uint64(y) + case uint16: + return x / uint64(y) + case uint32: + return x / uint64(y) + case uint64: + return x / y + case int: + return int(x) / y + case int8: + return int8(x) / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case int: + switch y := b.(type) { + case uint: + return x / int(y) + case uint8: + return x / int(y) + case uint16: + return x / int(y) + case uint32: + return x / int(y) + case uint64: + return x / int(y) + case int: + return x / y + case int8: + return int8(x) / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case int8: + switch y := b.(type) { + case uint: + return x / int8(y) + case uint8: + return x / int8(y) + case uint16: + return x / int8(y) + case uint32: + return x / int8(y) + case uint64: + return x / int8(y) + case int: + return x / int8(y) + case int8: + return x / y + case int16: + return int16(x) / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case int16: + switch y := b.(type) { + case uint: + return x / int16(y) + case uint8: + return x / int16(y) + case uint16: + return x / int16(y) + case uint32: + return x / int16(y) + case uint64: + return x / int16(y) + case int: + return x / int16(y) + case int8: + return x / int16(y) + case int16: + return x / y + case int32: + return int32(x) / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case int32: + switch y := b.(type) { + case uint: + return x / int32(y) + case uint8: + return x / int32(y) + case uint16: + return x / int32(y) + case uint32: + return x / int32(y) + case uint64: + return x / int32(y) + case int: + return x / int32(y) + case int8: + return x / int32(y) + case int16: + return x / int32(y) + case int32: + return x / y + case int64: + return int64(x) / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case int64: + switch y := b.(type) { + case uint: + return x / int64(y) + case uint8: + return x / int64(y) + case uint16: + return x / int64(y) + case uint32: + return x / int64(y) + case uint64: + return x / int64(y) + case int: + return x / int64(y) + case int8: + return x / int64(y) + case int16: + return x / int64(y) + case int32: + return x / int64(y) + case int64: + return x / y + case float32: + return float32(x) / y + case float64: + return float64(x) / y + } + case float32: + switch y := b.(type) { + case uint: + return x / float32(y) + case uint8: + return x / float32(y) + case uint16: + return x / float32(y) + case uint32: + return x / float32(y) + case uint64: + return x / float32(y) + case int: + return x / float32(y) + case int8: + return x / float32(y) + case int16: + return x / float32(y) + case int32: + return x / float32(y) + case int64: + return x / float32(y) + case float32: + return x / y + case float64: + return float64(x) / y + } + case float64: + switch y := b.(type) { + case uint: + return x / float64(y) + case uint8: + return x / float64(y) + case uint16: + return x / float64(y) + case uint32: + return x / float64(y) + case uint64: + return x / float64(y) + case int: + return x / float64(y) + case int8: + return x / float64(y) + case int16: + return x / float64(y) + case int32: + return x / float64(y) + case int64: + return x / float64(y) + case float32: + return x / float64(y) + case float64: + return x / y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "/", b)) +} + +func modulo(a, b interface{}) interface{} { + switch x := a.(type) { + case uint: + switch y := b.(type) { + case uint: + return x % y + case uint8: + return uint8(x) % y + case uint16: + return uint16(x) % y + case uint32: + return uint32(x) % y + case uint64: + return uint64(x) % y + case int: + return int(x) % y + case int8: + return int8(x) % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case uint8: + switch y := b.(type) { + case uint: + return x % uint8(y) + case uint8: + return x % y + case uint16: + return uint16(x) % y + case uint32: + return uint32(x) % y + case uint64: + return uint64(x) % y + case int: + return int(x) % y + case int8: + return int8(x) % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case uint16: + switch y := b.(type) { + case uint: + return x % uint16(y) + case uint8: + return x % uint16(y) + case uint16: + return x % y + case uint32: + return uint32(x) % y + case uint64: + return uint64(x) % y + case int: + return int(x) % y + case int8: + return int8(x) % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case uint32: + switch y := b.(type) { + case uint: + return x % uint32(y) + case uint8: + return x % uint32(y) + case uint16: + return x % uint32(y) + case uint32: + return x % y + case uint64: + return uint64(x) % y + case int: + return int(x) % y + case int8: + return int8(x) % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case uint64: + switch y := b.(type) { + case uint: + return x % uint64(y) + case uint8: + return x % uint64(y) + case uint16: + return x % uint64(y) + case uint32: + return x % uint64(y) + case uint64: + return x % y + case int: + return int(x) % y + case int8: + return int8(x) % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case int: + switch y := b.(type) { + case uint: + return x % int(y) + case uint8: + return x % int(y) + case uint16: + return x % int(y) + case uint32: + return x % int(y) + case uint64: + return x % int(y) + case int: + return x % y + case int8: + return int8(x) % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case int8: + switch y := b.(type) { + case uint: + return x % int8(y) + case uint8: + return x % int8(y) + case uint16: + return x % int8(y) + case uint32: + return x % int8(y) + case uint64: + return x % int8(y) + case int: + return x % int8(y) + case int8: + return x % y + case int16: + return int16(x) % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case int16: + switch y := b.(type) { + case uint: + return x % int16(y) + case uint8: + return x % int16(y) + case uint16: + return x % int16(y) + case uint32: + return x % int16(y) + case uint64: + return x % int16(y) + case int: + return x % int16(y) + case int8: + return x % int16(y) + case int16: + return x % y + case int32: + return int32(x) % y + case int64: + return int64(x) % y + } + case int32: + switch y := b.(type) { + case uint: + return x % int32(y) + case uint8: + return x % int32(y) + case uint16: + return x % int32(y) + case uint32: + return x % int32(y) + case uint64: + return x % int32(y) + case int: + return x % int32(y) + case int8: + return x % int32(y) + case int16: + return x % int32(y) + case int32: + return x % y + case int64: + return int64(x) % y + } + case int64: + switch y := b.(type) { + case uint: + return x % int64(y) + case uint8: + return x % int64(y) + case uint16: + return x % int64(y) + case uint32: + return x % int64(y) + case uint64: + return x % int64(y) + case int: + return x % int64(y) + case int8: + return x % int64(y) + case int16: + return x % int64(y) + case int32: + return x % int64(y) + case int64: + return x % y + } + } + panic(fmt.Sprintf("invalid operation: %T %v %T", a, "%", b)) +} diff --git a/vendor/github.com/antonmedv/expr/vm/opcodes.go b/vendor/github.com/antonmedv/expr/vm/opcodes.go new file mode 100644 index 0000000..7f2dd37 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/vm/opcodes.go @@ -0,0 +1,56 @@ +package vm + +const ( + OpPush byte = iota + OpPop + OpRot + OpFetch + OpFetchNilSafe + OpFetchMap + OpTrue + OpFalse + OpNil + OpNegate + OpNot + OpEqual + OpEqualInt + OpEqualString + OpJump + OpJumpIfTrue + OpJumpIfFalse + OpJumpBackward + OpIn + OpLess + OpMore + OpLessOrEqual + OpMoreOrEqual + OpAdd + OpSubtract + OpMultiply + OpDivide + OpModulo + OpExponent + OpRange + OpMatches + OpMatchesConst + OpContains + OpStartsWith + OpEndsWith + OpIndex + OpSlice + OpProperty + OpPropertyNilSafe + OpCall + OpCallFast + OpMethod + OpMethodNilSafe + OpArray + OpMap + OpLen + OpCast + OpStore + OpLoad + OpInc + OpBegin + OpEnd // This opcode must be at the end of this list. +) diff --git a/vendor/github.com/antonmedv/expr/vm/program.go b/vendor/github.com/antonmedv/expr/vm/program.go new file mode 100644 index 0000000..5a41f8a --- /dev/null +++ b/vendor/github.com/antonmedv/expr/vm/program.go @@ -0,0 +1,225 @@ +package vm + +import ( + "encoding/binary" + "fmt" + "regexp" + + "github.com/antonmedv/expr/file" +) + +type Program struct { + Source *file.Source + Locations map[int]file.Location + Constants []interface{} + Bytecode []byte +} + +func (program *Program) Disassemble() string { + out := "" + ip := 0 + for ip < len(program.Bytecode) { + pp := ip + op := program.Bytecode[ip] + ip++ + + readArg := func() uint16 { + if ip+1 >= len(program.Bytecode) { + return 0 + } + + i := binary.LittleEndian.Uint16([]byte{program.Bytecode[ip], program.Bytecode[ip+1]}) + ip += 2 + return i + } + + code := func(label string) { + out += fmt.Sprintf("%v\t%v\n", pp, label) + } + jump := func(label string) { + a := readArg() + out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, a, ip+int(a)) + } + back := func(label string) { + a := readArg() + out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, a, ip-int(a)) + } + argument := func(label string) { + a := readArg() + out += fmt.Sprintf("%v\t%v\t%v\n", pp, label, a) + } + constant := func(label string) { + a := readArg() + var c interface{} + if int(a) < len(program.Constants) { + c = program.Constants[a] + } + if r, ok := c.(*regexp.Regexp); ok { + c = r.String() + } + out += fmt.Sprintf("%v\t%v\t%v\t%#v\n", pp, label, a, c) + } + + switch op { + case OpPush: + constant("OpPush") + + case OpPop: + code("OpPop") + + case OpRot: + code("OpRot") + + case OpFetch: + constant("OpFetch") + + case OpFetchNilSafe: + constant("OpFetchNilSafe") + + case OpFetchMap: + constant("OpFetchMap") + + case OpTrue: + code("OpTrue") + + case OpFalse: + code("OpFalse") + + case OpNil: + code("OpNil") + + case OpNegate: + code("OpNegate") + + case OpNot: + code("OpNot") + + case OpEqual: + code("OpEqual") + + case OpEqualInt: + code("OpEqualInt") + + case OpEqualString: + code("OpEqualString") + + case OpJump: + jump("OpJump") + + case OpJumpIfTrue: + jump("OpJumpIfTrue") + + case OpJumpIfFalse: + jump("OpJumpIfFalse") + + case OpJumpBackward: + back("OpJumpBackward") + + case OpIn: + code("OpIn") + + case OpLess: + code("OpLess") + + case OpMore: + code("OpMore") + + case OpLessOrEqual: + code("OpLessOrEqual") + + case OpMoreOrEqual: + code("OpMoreOrEqual") + + case OpAdd: + code("OpAdd") + + case OpSubtract: + code("OpSubtract") + + case OpMultiply: + code("OpMultiply") + + case OpDivide: + code("OpDivide") + + case OpModulo: + code("OpModulo") + + case OpExponent: + code("OpExponent") + + case OpRange: + code("OpRange") + + case OpMatches: + code("OpMatches") + + case OpMatchesConst: + constant("OpMatchesConst") + + case OpContains: + code("OpContains") + + case OpStartsWith: + code("OpStartsWith") + + case OpEndsWith: + code("OpEndsWith") + + case OpIndex: + code("OpIndex") + + case OpSlice: + code("OpSlice") + + case OpProperty: + constant("OpProperty") + + case OpPropertyNilSafe: + constant("OpPropertyNilSafe") + + case OpCall: + constant("OpCall") + + case OpCallFast: + constant("OpCallFast") + + case OpMethod: + constant("OpMethod") + + case OpMethodNilSafe: + constant("OpMethodNilSafe") + + case OpArray: + code("OpArray") + + case OpMap: + code("OpMap") + + case OpLen: + code("OpLen") + + case OpCast: + argument("OpCast") + + case OpStore: + constant("OpStore") + + case OpLoad: + constant("OpLoad") + + case OpInc: + constant("OpInc") + + case OpBegin: + code("OpBegin") + + case OpEnd: + code("OpEnd") + + default: + out += fmt.Sprintf("%v\t%#x\n", pp, op) + } + } + return out +} diff --git a/vendor/github.com/antonmedv/expr/vm/runtime.go b/vendor/github.com/antonmedv/expr/vm/runtime.go new file mode 100644 index 0000000..2e00913 --- /dev/null +++ b/vendor/github.com/antonmedv/expr/vm/runtime.go @@ -0,0 +1,370 @@ +package vm + +//go:generate go run ./generate + +import ( + "fmt" + "math" + "reflect" +) + +type Call struct { + Name string + Size int +} + +type Scope map[string]interface{} + +type Fetcher interface { + Fetch(interface{}) interface{} +} + +func fetch(from, i interface{}, nilsafe bool) interface{} { + if fetcher, ok := from.(Fetcher); ok { + value := fetcher.Fetch(i) + if value != nil { + return value + } + if !nilsafe { + panic(fmt.Sprintf("cannot fetch %v from %T", i, from)) + } + return nil + } + + v := reflect.ValueOf(from) + kind := v.Kind() + + // Structures can be access through a pointer or through a value, when they + // are accessed through a pointer we don't want to copy them to a value. + if kind == reflect.Ptr && reflect.Indirect(v).Kind() == reflect.Struct { + v = reflect.Indirect(v) + kind = v.Kind() + } + + switch kind { + + case reflect.Array, reflect.Slice, reflect.String: + value := v.Index(toInt(i)) + if value.IsValid() && value.CanInterface() { + return value.Interface() + } + + case reflect.Map: + value := v.MapIndex(reflect.ValueOf(i)) + if value.IsValid() { + if value.CanInterface() { + return value.Interface() + } + } else { + elem := reflect.TypeOf(from).Elem() + return reflect.Zero(elem).Interface() + } + + case reflect.Struct: + value := v.FieldByName(reflect.ValueOf(i).String()) + if value.IsValid() && value.CanInterface() { + return value.Interface() + } + } + if !nilsafe { + panic(fmt.Sprintf("cannot fetch %v from %T", i, from)) + } + return nil +} + +func slice(array, from, to interface{}) interface{} { + v := reflect.ValueOf(array) + + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.String: + length := v.Len() + a, b := toInt(from), toInt(to) + + if b > length { + b = length + } + if a > b { + a = b + } + + value := v.Slice(a, b) + if value.IsValid() && value.CanInterface() { + return value.Interface() + } + + case reflect.Ptr: + value := v.Elem() + if value.IsValid() && value.CanInterface() { + return slice(value.Interface(), from, to) + } + + } + panic(fmt.Sprintf("cannot slice %v", from)) +} + +func FetchFn(from interface{}, name string) reflect.Value { + v := reflect.ValueOf(from) + + // Methods can be defined on any type. + if v.NumMethod() > 0 { + method := v.MethodByName(name) + if method.IsValid() { + return method + } + } + + d := v + if v.Kind() == reflect.Ptr { + d = v.Elem() + } + + switch d.Kind() { + case reflect.Map: + value := d.MapIndex(reflect.ValueOf(name)) + if value.IsValid() && value.CanInterface() { + return value.Elem() + } + case reflect.Struct: + // If struct has not method, maybe it has func field. + // To access this field we need dereference value. + value := d.FieldByName(name) + if value.IsValid() { + return value + } + } + panic(fmt.Sprintf(`cannot get "%v" from %T`, name, from)) +} + +func FetchFnNil(from interface{}, name string) reflect.Value { + if v := reflect.ValueOf(from); !v.IsValid() { + return v + } + return FetchFn(from, name) +} + +func in(needle interface{}, array interface{}) bool { + if array == nil { + return false + } + v := reflect.ValueOf(array) + + switch v.Kind() { + + case reflect.Array, reflect.Slice: + for i := 0; i < v.Len(); i++ { + value := v.Index(i) + if value.IsValid() && value.CanInterface() { + if equal(value.Interface(), needle).(bool) { + return true + } + } + } + return false + + case reflect.Map: + n := reflect.ValueOf(needle) + if !n.IsValid() { + panic(fmt.Sprintf("cannot use %T as index to %T", needle, array)) + } + value := v.MapIndex(n) + if value.IsValid() { + return true + } + return false + + case reflect.Struct: + n := reflect.ValueOf(needle) + if !n.IsValid() || n.Kind() != reflect.String { + panic(fmt.Sprintf("cannot use %T as field name of %T", needle, array)) + } + value := v.FieldByName(n.String()) + if value.IsValid() { + return true + } + return false + + case reflect.Ptr: + value := v.Elem() + if value.IsValid() && value.CanInterface() { + return in(needle, value.Interface()) + } + return false + } + + panic(fmt.Sprintf(`operator "in"" not defined on %T`, array)) +} + +func length(a interface{}) int { + v := reflect.ValueOf(a) + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.String: + return v.Len() + default: + panic(fmt.Sprintf("invalid argument for len (type %T)", a)) + } +} + +func negate(i interface{}) interface{} { + switch v := i.(type) { + case float32: + return -v + case float64: + return -v + + case int: + return -v + case int8: + return -v + case int16: + return -v + case int32: + return -v + case int64: + return -v + + case uint: + return -v + case uint8: + return -v + case uint16: + return -v + case uint32: + return -v + case uint64: + return -v + + default: + panic(fmt.Sprintf("invalid operation: - %T", v)) + } +} + +func exponent(a, b interface{}) float64 { + return math.Pow(toFloat64(a), toFloat64(b)) +} + +func makeRange(min, max int) []int { + size := max - min + 1 + if size <= 0 { + return []int{} + } + rng := make([]int, size) + for i := range rng { + rng[i] = min + i + } + return rng +} + +func toInt(a interface{}) int { + switch x := a.(type) { + case float32: + return int(x) + case float64: + return int(x) + + case int: + return x + case int8: + return int(x) + case int16: + return int(x) + case int32: + return int(x) + case int64: + return int(x) + + case uint: + return int(x) + case uint8: + return int(x) + case uint16: + return int(x) + case uint32: + return int(x) + case uint64: + return int(x) + + default: + panic(fmt.Sprintf("invalid operation: int(%T)", x)) + } +} + +func toInt64(a interface{}) int64 { + switch x := a.(type) { + case float32: + return int64(x) + case float64: + return int64(x) + + case int: + return int64(x) + case int8: + return int64(x) + case int16: + return int64(x) + case int32: + return int64(x) + case int64: + return x + + case uint: + return int64(x) + case uint8: + return int64(x) + case uint16: + return int64(x) + case uint32: + return int64(x) + case uint64: + return int64(x) + + default: + panic(fmt.Sprintf("invalid operation: int64(%T)", x)) + } +} + +func toFloat64(a interface{}) float64 { + switch x := a.(type) { + case float32: + return float64(x) + case float64: + return x + + case int: + return float64(x) + case int8: + return float64(x) + case int16: + return float64(x) + case int32: + return float64(x) + case int64: + return float64(x) + + case uint: + return float64(x) + case uint8: + return float64(x) + case uint16: + return float64(x) + case uint32: + return float64(x) + case uint64: + return float64(x) + + default: + panic(fmt.Sprintf("invalid operation: float64(%T)", x)) + } +} + +func isNil(v interface{}) bool { + if v == nil { + return true + } + r := reflect.ValueOf(v) + switch r.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice: + return r.IsNil() + default: + return false + } +} diff --git a/vendor/github.com/antonmedv/expr/vm/vm.go b/vendor/github.com/antonmedv/expr/vm/vm.go new file mode 100644 index 0000000..6957dfa --- /dev/null +++ b/vendor/github.com/antonmedv/expr/vm/vm.go @@ -0,0 +1,484 @@ +package vm + +import ( + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/antonmedv/expr/file" +) + +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +var ( + MemoryBudget int = 1e6 +) + +func Run(program *Program, env interface{}) (interface{}, error) { + if program == nil { + return nil, fmt.Errorf("program is nil") + } + + vm := VM{} + return vm.Run(program, env) +} + +type VM struct { + stack []interface{} + constants []interface{} + bytecode []byte + ip int + pp int + scopes []Scope + debug bool + step chan struct{} + curr chan int + memory int + limit int +} + +func Debug() *VM { + vm := &VM{ + debug: true, + step: make(chan struct{}, 0), + curr: make(chan int, 0), + } + return vm +} + +func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error) { + defer func() { + if r := recover(); r != nil { + f := &file.Error{ + Location: program.Locations[vm.pp], + Message: fmt.Sprintf("%v", r), + } + err = f.Bind(program.Source) + } + }() + + vm.limit = MemoryBudget + vm.ip = 0 + vm.pp = 0 + + if vm.stack == nil { + vm.stack = make([]interface{}, 0, 2) + } else { + vm.stack = vm.stack[0:0] + } + + if vm.scopes != nil { + vm.scopes = vm.scopes[0:0] + } + + vm.bytecode = program.Bytecode + vm.constants = program.Constants + + for vm.ip < len(vm.bytecode) { + + if vm.debug { + <-vm.step + } + + vm.pp = vm.ip + vm.ip++ + op := vm.bytecode[vm.pp] + + switch op { + + case OpPush: + vm.push(vm.constant()) + + case OpPop: + vm.pop() + + case OpRot: + b := vm.pop() + a := vm.pop() + vm.push(b) + vm.push(a) + + case OpFetch: + vm.push(fetch(env, vm.constant(), false)) + + case OpFetchNilSafe: + vm.push(fetch(env, vm.constant(), true)) + + case OpFetchMap: + vm.push(env.(map[string]interface{})[vm.constant().(string)]) + + case OpTrue: + vm.push(true) + + case OpFalse: + vm.push(false) + + case OpNil: + vm.push(nil) + + case OpNegate: + v := negate(vm.pop()) + vm.push(v) + + case OpNot: + v := vm.pop().(bool) + vm.push(!v) + + case OpEqual: + b := vm.pop() + a := vm.pop() + vm.push(equal(a, b)) + + case OpEqualInt: + b := vm.pop() + a := vm.pop() + vm.push(a.(int) == b.(int)) + + case OpEqualString: + b := vm.pop() + a := vm.pop() + vm.push(a.(string) == b.(string)) + + case OpJump: + offset := vm.arg() + vm.ip += int(offset) + + case OpJumpIfTrue: + offset := vm.arg() + if vm.current().(bool) { + vm.ip += int(offset) + } + + case OpJumpIfFalse: + offset := vm.arg() + if !vm.current().(bool) { + vm.ip += int(offset) + } + + case OpJumpBackward: + offset := vm.arg() + vm.ip -= int(offset) + + case OpIn: + b := vm.pop() + a := vm.pop() + vm.push(in(a, b)) + + case OpLess: + b := vm.pop() + a := vm.pop() + vm.push(less(a, b)) + + case OpMore: + b := vm.pop() + a := vm.pop() + vm.push(more(a, b)) + + case OpLessOrEqual: + b := vm.pop() + a := vm.pop() + vm.push(lessOrEqual(a, b)) + + case OpMoreOrEqual: + b := vm.pop() + a := vm.pop() + vm.push(moreOrEqual(a, b)) + + case OpAdd: + b := vm.pop() + a := vm.pop() + vm.push(add(a, b)) + + case OpSubtract: + b := vm.pop() + a := vm.pop() + vm.push(subtract(a, b)) + + case OpMultiply: + b := vm.pop() + a := vm.pop() + vm.push(multiply(a, b)) + + case OpDivide: + b := vm.pop() + a := vm.pop() + vm.push(divide(a, b)) + + case OpModulo: + b := vm.pop() + a := vm.pop() + vm.push(modulo(a, b)) + + case OpExponent: + b := vm.pop() + a := vm.pop() + vm.push(exponent(a, b)) + + case OpRange: + b := vm.pop() + a := vm.pop() + min := toInt(a) + max := toInt(b) + size := max - min + 1 + if vm.memory+size >= vm.limit { + panic("memory budget exceeded") + } + vm.push(makeRange(min, max)) + vm.memory += size + + case OpMatches: + b := vm.pop() + a := vm.pop() + match, err := regexp.MatchString(b.(string), a.(string)) + if err != nil { + panic(err) + } + + vm.push(match) + + case OpMatchesConst: + a := vm.pop() + r := vm.constant().(*regexp.Regexp) + vm.push(r.MatchString(a.(string))) + + case OpContains: + b := vm.pop() + a := vm.pop() + vm.push(strings.Contains(a.(string), b.(string))) + + case OpStartsWith: + b := vm.pop() + a := vm.pop() + vm.push(strings.HasPrefix(a.(string), b.(string))) + + case OpEndsWith: + b := vm.pop() + a := vm.pop() + vm.push(strings.HasSuffix(a.(string), b.(string))) + + case OpIndex: + b := vm.pop() + a := vm.pop() + vm.push(fetch(a, b, false)) + + case OpSlice: + from := vm.pop() + to := vm.pop() + node := vm.pop() + vm.push(slice(node, from, to)) + + case OpProperty: + a := vm.pop() + b := vm.constant() + vm.push(fetch(a, b, false)) + + case OpPropertyNilSafe: + a := vm.pop() + b := vm.constant() + vm.push(fetch(a, b, true)) + + case OpCall: + call := vm.constant().(Call) + in := make([]reflect.Value, call.Size) + for i := call.Size - 1; i >= 0; i-- { + param := vm.pop() + if param == nil && reflect.TypeOf(param) == nil { + // In case of nil value and nil type use this hack, + // otherwise reflect.Call will panic on zero value. + in[i] = reflect.ValueOf(¶m).Elem() + } else { + in[i] = reflect.ValueOf(param) + } + } + out := FetchFn(env, call.Name).Call(in) + if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() { + return nil, out[1].Interface().(error) + } + vm.push(out[0].Interface()) + + case OpCallFast: + call := vm.constant().(Call) + in := make([]interface{}, call.Size) + for i := call.Size - 1; i >= 0; i-- { + in[i] = vm.pop() + } + fn := FetchFn(env, call.Name).Interface() + if typed, ok := fn.(func(...interface{}) interface{}); ok { + vm.push(typed(in...)) + } else if typed, ok := fn.(func(...interface{}) (interface{}, error)); ok { + res, err := typed(in...) + if err != nil { + return nil, err + } + vm.push(res) + } + + case OpMethod: + call := vm.constants[vm.arg()].(Call) + in := make([]reflect.Value, call.Size) + for i := call.Size - 1; i >= 0; i-- { + param := vm.pop() + if param == nil && reflect.TypeOf(param) == nil { + // In case of nil value and nil type use this hack, + // otherwise reflect.Call will panic on zero value. + in[i] = reflect.ValueOf(¶m).Elem() + } else { + in[i] = reflect.ValueOf(param) + } + } + out := FetchFn(vm.pop(), call.Name).Call(in) + if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() { + return nil, out[1].Interface().(error) + } + vm.push(out[0].Interface()) + + case OpMethodNilSafe: + call := vm.constants[vm.arg()].(Call) + in := make([]reflect.Value, call.Size) + for i := call.Size - 1; i >= 0; i-- { + param := vm.pop() + if param == nil && reflect.TypeOf(param) == nil { + // In case of nil value and nil type use this hack, + // otherwise reflect.Call will panic on zero value. + in[i] = reflect.ValueOf(¶m).Elem() + } else { + in[i] = reflect.ValueOf(param) + } + } + fn := FetchFnNil(vm.pop(), call.Name) + if !fn.IsValid() { + vm.push(nil) + } else { + out := fn.Call(in) + vm.push(out[0].Interface()) + } + + case OpArray: + size := vm.pop().(int) + array := make([]interface{}, size) + for i := size - 1; i >= 0; i-- { + array[i] = vm.pop() + } + vm.push(array) + vm.memory += size + if vm.memory >= vm.limit { + panic("memory budget exceeded") + } + + case OpMap: + size := vm.pop().(int) + m := make(map[string]interface{}) + for i := size - 1; i >= 0; i-- { + value := vm.pop() + key := vm.pop() + m[key.(string)] = value + } + vm.push(m) + vm.memory += size + if vm.memory >= vm.limit { + panic("memory budget exceeded") + } + + case OpLen: + vm.push(length(vm.current())) + + case OpCast: + t := vm.arg() + switch t { + case 0: + vm.push(toInt64(vm.pop())) + case 1: + vm.push(toFloat64(vm.pop())) + } + + case OpStore: + scope := vm.Scope() + key := vm.constant().(string) + value := vm.pop() + scope[key] = value + + case OpLoad: + scope := vm.Scope() + key := vm.constant().(string) + vm.push(scope[key]) + + case OpInc: + scope := vm.Scope() + key := vm.constant().(string) + i := scope[key].(int) + i++ + scope[key] = i + + case OpBegin: + scope := make(Scope) + vm.scopes = append(vm.scopes, scope) + + case OpEnd: + vm.scopes = vm.scopes[:len(vm.scopes)-1] + + default: + panic(fmt.Sprintf("unknown bytecode %#x", op)) + } + + if vm.debug { + vm.curr <- vm.ip + } + } + + if vm.debug { + close(vm.curr) + close(vm.step) + } + + if len(vm.stack) > 0 { + return vm.pop(), nil + } + + return nil, nil +} + +func (vm *VM) push(value interface{}) { + vm.stack = append(vm.stack, value) +} + +func (vm *VM) current() interface{} { + return vm.stack[len(vm.stack)-1] +} + +func (vm *VM) pop() interface{} { + value := vm.stack[len(vm.stack)-1] + vm.stack = vm.stack[:len(vm.stack)-1] + return value +} + +func (vm *VM) arg() uint16 { + b0, b1 := vm.bytecode[vm.ip], vm.bytecode[vm.ip+1] + vm.ip += 2 + return uint16(b0) | uint16(b1)<<8 +} + +func (vm *VM) constant() interface{} { + return vm.constants[vm.arg()] +} + +func (vm *VM) Stack() []interface{} { + return vm.stack +} + +func (vm *VM) Scope() Scope { + if len(vm.scopes) > 0 { + return vm.scopes[len(vm.scopes)-1] + } + return nil +} + +func (vm *VM) Step() { + if vm.ip < len(vm.bytecode) { + vm.step <- struct{}{} + } +} + +func (vm *VM) Position() chan int { + return vm.curr +}