mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Refactor visitor
This commit is contained in:
@@ -31,7 +31,7 @@ product.Stock < 15
|
||||
* Static typing ([example](https://godoc.org/github.com/antonmedv/expr#example-Env)).
|
||||
```go
|
||||
out, err := expr.Eval("'hello' + 10")
|
||||
// err: invalid operation + (mismatched types string and int64)
|
||||
// err: invalid operation + (mismatched types string and int)
|
||||
// | 'hello' + 10
|
||||
// | ........^
|
||||
```
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package ast
|
||||
|
||||
type BaseVisitor struct{}
|
||||
|
||||
func (BaseVisitor) NilNode(node *NilNode) {}
|
||||
|
||||
func (BaseVisitor) IdentifierNode(node *IdentifierNode) {}
|
||||
|
||||
func (BaseVisitor) IntegerNode(node *IntegerNode) {}
|
||||
|
||||
func (BaseVisitor) FloatNode(node *FloatNode) {}
|
||||
|
||||
func (BaseVisitor) BoolNode(node *BoolNode) {}
|
||||
|
||||
func (BaseVisitor) StringNode(node *StringNode) {}
|
||||
|
||||
func (BaseVisitor) UnaryNode(node *UnaryNode) {}
|
||||
|
||||
func (BaseVisitor) BinaryNode(node *BinaryNode) {}
|
||||
|
||||
func (BaseVisitor) MatchesNode(node *MatchesNode) {}
|
||||
|
||||
func (BaseVisitor) PropertyNode(node *PropertyNode) {}
|
||||
|
||||
func (BaseVisitor) IndexNode(node *IndexNode) {}
|
||||
|
||||
func (BaseVisitor) MethodNode(node *MethodNode) {}
|
||||
|
||||
func (BaseVisitor) FunctionNode(node *FunctionNode) {}
|
||||
|
||||
func (BaseVisitor) BuiltinNode(node *BuiltinNode) {}
|
||||
|
||||
func (BaseVisitor) ClosureNode(node *ClosureNode) {}
|
||||
|
||||
func (BaseVisitor) PointerNode(node *PointerNode) {}
|
||||
|
||||
func (BaseVisitor) ConditionalNode(node *ConditionalNode) {}
|
||||
|
||||
func (BaseVisitor) ArrayNode(node *ArrayNode) {}
|
||||
|
||||
func (BaseVisitor) MapNode(node *MapNode) {}
|
||||
|
||||
func (BaseVisitor) PairNode(node *PairNode) {}
|
||||
|
||||
func (BaseVisitor) Node(node *Node) {}
|
||||
+23
-42
@@ -3,27 +3,8 @@ package ast
|
||||
import "fmt"
|
||||
|
||||
type Visitor interface {
|
||||
NilNode(node *NilNode)
|
||||
IdentifierNode(node *IdentifierNode)
|
||||
IntegerNode(node *IntegerNode)
|
||||
FloatNode(node *FloatNode)
|
||||
BoolNode(node *BoolNode)
|
||||
StringNode(node *StringNode)
|
||||
UnaryNode(node *UnaryNode)
|
||||
BinaryNode(node *BinaryNode)
|
||||
MatchesNode(node *MatchesNode)
|
||||
PropertyNode(node *PropertyNode)
|
||||
IndexNode(node *IndexNode)
|
||||
MethodNode(node *MethodNode)
|
||||
FunctionNode(node *FunctionNode)
|
||||
BuiltinNode(node *BuiltinNode)
|
||||
ClosureNode(node *ClosureNode)
|
||||
PointerNode(node *PointerNode)
|
||||
ConditionalNode(node *ConditionalNode)
|
||||
ArrayNode(node *ArrayNode)
|
||||
MapNode(node *MapNode)
|
||||
PairNode(node *PairNode)
|
||||
Node(node *Node)
|
||||
Enter(node *Node)
|
||||
Exit(node *Node)
|
||||
}
|
||||
|
||||
type walker struct {
|
||||
@@ -38,79 +19,79 @@ func Walk(node *Node, visitor Visitor) {
|
||||
}
|
||||
|
||||
func (w *walker) walk(node *Node) {
|
||||
w.visitor.Node(node)
|
||||
w.visitor.Enter(node)
|
||||
|
||||
switch n := (*node).(type) {
|
||||
case *NilNode:
|
||||
w.visitor.NilNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *IdentifierNode:
|
||||
w.visitor.IdentifierNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *IntegerNode:
|
||||
w.visitor.IntegerNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *FloatNode:
|
||||
w.visitor.FloatNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *BoolNode:
|
||||
w.visitor.BoolNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *StringNode:
|
||||
w.visitor.StringNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *UnaryNode:
|
||||
w.walk(&n.Node)
|
||||
w.visitor.UnaryNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *BinaryNode:
|
||||
w.walk(&n.Left)
|
||||
w.walk(&n.Right)
|
||||
w.visitor.BinaryNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *MatchesNode:
|
||||
w.walk(&n.Left)
|
||||
w.walk(&n.Right)
|
||||
w.visitor.MatchesNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *PropertyNode:
|
||||
w.walk(&n.Node)
|
||||
w.visitor.PropertyNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *IndexNode:
|
||||
w.walk(&n.Node)
|
||||
w.walk(&n.Index)
|
||||
w.visitor.IndexNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *MethodNode:
|
||||
w.walk(&n.Node)
|
||||
for _, arg := range n.Arguments {
|
||||
w.walk(&arg)
|
||||
}
|
||||
w.visitor.MethodNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *FunctionNode:
|
||||
for _, arg := range n.Arguments {
|
||||
w.walk(&arg)
|
||||
}
|
||||
w.visitor.FunctionNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *BuiltinNode:
|
||||
for _, arg := range n.Arguments {
|
||||
w.walk(&arg)
|
||||
}
|
||||
w.visitor.BuiltinNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *ClosureNode:
|
||||
w.walk(&n.Node)
|
||||
w.visitor.ClosureNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *PointerNode:
|
||||
w.visitor.PointerNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *ConditionalNode:
|
||||
w.walk(&n.Cond)
|
||||
w.walk(&n.Exp1)
|
||||
w.walk(&n.Exp2)
|
||||
w.visitor.ConditionalNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *ArrayNode:
|
||||
for _, node := range n.Nodes {
|
||||
w.walk(&node)
|
||||
}
|
||||
w.visitor.ArrayNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *MapNode:
|
||||
var pair Node
|
||||
for _, pair = range n.Pairs {
|
||||
w.walk(&pair)
|
||||
}
|
||||
w.visitor.MapNode(n)
|
||||
w.visitor.Exit(node)
|
||||
case *PairNode:
|
||||
w.walk(&n.Value)
|
||||
w.visitor.PairNode(n)
|
||||
w.visitor.Exit(node)
|
||||
default:
|
||||
panic(fmt.Sprintf("undefined node type (%T)", node))
|
||||
}
|
||||
|
||||
+8
-7
@@ -7,12 +7,14 @@ import (
|
||||
)
|
||||
|
||||
type visitor struct {
|
||||
ast.BaseVisitor
|
||||
identifiers []string
|
||||
}
|
||||
|
||||
func (v *visitor) IdentifierNode(node *ast.IdentifierNode) {
|
||||
v.identifiers = append(v.identifiers, node.Value)
|
||||
func (v *visitor) Enter(node *ast.Node) {}
|
||||
func (v *visitor) Exit(node *ast.Node) {
|
||||
if n, ok := (*node).(*ast.IdentifierNode); ok {
|
||||
v.identifiers = append(v.identifiers, n.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalk(t *testing.T) {
|
||||
@@ -28,15 +30,14 @@ func TestWalk(t *testing.T) {
|
||||
assert.Equal(t, []string{"foo", "bar"}, visitor.identifiers)
|
||||
}
|
||||
|
||||
type patcher struct {
|
||||
ast.BaseVisitor
|
||||
}
|
||||
type patcher struct{}
|
||||
|
||||
func (p *patcher) Node(node *ast.Node) {
|
||||
func (p *patcher) Enter(node *ast.Node) {
|
||||
if _, ok := (*node).(*ast.IdentifierNode); ok {
|
||||
*node = &ast.NilNode{}
|
||||
}
|
||||
}
|
||||
func (p *patcher) Exit(node *ast.Node) {}
|
||||
|
||||
func TestWalk_patch(t *testing.T) {
|
||||
var node ast.Node
|
||||
|
||||
@@ -31,11 +31,6 @@ func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
|
||||
|
||||
t = v.visit(tree.Node)
|
||||
|
||||
// TODO: Move patch into visitor.
|
||||
if v.operators != nil {
|
||||
patchOperators(tree, config)
|
||||
}
|
||||
|
||||
if v.expect != reflect.Invalid {
|
||||
switch v.expect {
|
||||
case reflect.Int64, reflect.Float64:
|
||||
|
||||
+3
-3
@@ -7,12 +7,12 @@ import (
|
||||
)
|
||||
|
||||
type operatorPatcher struct {
|
||||
ast.BaseVisitor
|
||||
ops map[string][]string
|
||||
types conf.TypesTable
|
||||
}
|
||||
|
||||
func (p *operatorPatcher) Node(node *ast.Node) {
|
||||
func (p *operatorPatcher) Enter(node *ast.Node) {}
|
||||
func (p *operatorPatcher) Exit(node *ast.Node) {
|
||||
binaryNode, ok := (*node).(*ast.BinaryNode)
|
||||
if !ok {
|
||||
return
|
||||
@@ -40,7 +40,7 @@ func (p *operatorPatcher) Node(node *ast.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func patchOperators(tree *parser.Tree, config *conf.Config) {
|
||||
func PatchOperators(tree *parser.Tree, config *conf.Config) {
|
||||
if len(config.Operators) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
+115
-130
@@ -24,7 +24,6 @@ func dotAst(node Node) {
|
||||
}
|
||||
|
||||
type visitor struct {
|
||||
BaseVisitor
|
||||
nodes string
|
||||
links string
|
||||
index int
|
||||
@@ -47,145 +46,131 @@ func (v *visitor) link(node int) {
|
||||
v.links += fmt.Sprintf(" n%v -> n%v\n", v.index, node)
|
||||
}
|
||||
|
||||
func (v *visitor) NilNode(node *NilNode) {
|
||||
v.push("nil")
|
||||
}
|
||||
func (v *visitor) Enter(node *Node) {}
|
||||
func (v *visitor) Exit(ref *Node) {
|
||||
switch node := (*ref).(type) {
|
||||
|
||||
func (v *visitor) IdentifierNode(node *IdentifierNode) {
|
||||
v.push(node.Value)
|
||||
}
|
||||
case *NilNode:
|
||||
v.push("nil")
|
||||
|
||||
func (v *visitor) IntegerNode(node *IntegerNode) {
|
||||
v.push(fmt.Sprintf("%v", node.Value))
|
||||
}
|
||||
case *IdentifierNode:
|
||||
v.push(node.Value)
|
||||
|
||||
func (v *visitor) FloatNode(node *FloatNode) {
|
||||
v.push(fmt.Sprintf("%v", node.Value))
|
||||
}
|
||||
case *IntegerNode:
|
||||
v.push(fmt.Sprintf("%v", node.Value))
|
||||
|
||||
func (v *visitor) BoolNode(node *BoolNode) {
|
||||
v.push(fmt.Sprintf("%v", node.Value))
|
||||
}
|
||||
case *FloatNode:
|
||||
v.push(fmt.Sprintf("%v", node.Value))
|
||||
|
||||
func (v *visitor) StringNode(node *StringNode) {
|
||||
v.push(fmt.Sprintf("%q", node.Value))
|
||||
}
|
||||
case *BoolNode:
|
||||
v.push(fmt.Sprintf("%v", node.Value))
|
||||
|
||||
func (v *visitor) UnaryNode(node *UnaryNode) {
|
||||
n := v.pop()
|
||||
v.push("-")
|
||||
v.link(n)
|
||||
}
|
||||
case *StringNode:
|
||||
v.push(fmt.Sprintf("%q", node.Value))
|
||||
|
||||
func (v *visitor) BinaryNode(node *BinaryNode) {
|
||||
b := v.pop()
|
||||
a := v.pop()
|
||||
v.push(node.Operator)
|
||||
v.link(a)
|
||||
v.link(b)
|
||||
}
|
||||
case *UnaryNode:
|
||||
n := v.pop()
|
||||
v.push("-")
|
||||
v.link(n)
|
||||
|
||||
func (v *visitor) MatchesNode(node *MatchesNode) {
|
||||
b := v.pop()
|
||||
a := v.pop()
|
||||
v.push("matches")
|
||||
v.link(a)
|
||||
v.link(b)
|
||||
}
|
||||
case *BinaryNode:
|
||||
b := v.pop()
|
||||
a := v.pop()
|
||||
v.push(node.Operator)
|
||||
v.link(a)
|
||||
v.link(b)
|
||||
|
||||
func (v *visitor) PropertyNode(node *PropertyNode) {
|
||||
a := v.pop()
|
||||
v.push(fmt.Sprintf(".%v", node.Property))
|
||||
v.link(a)
|
||||
}
|
||||
case *MatchesNode:
|
||||
b := v.pop()
|
||||
a := v.pop()
|
||||
v.push("matches")
|
||||
v.link(a)
|
||||
v.link(b)
|
||||
|
||||
func (v *visitor) IndexNode(node *IndexNode) {
|
||||
b := v.pop()
|
||||
a := v.pop()
|
||||
v.push("[...]")
|
||||
v.link(a)
|
||||
v.link(b)
|
||||
}
|
||||
case *PropertyNode:
|
||||
a := v.pop()
|
||||
v.push(fmt.Sprintf(".%v", node.Property))
|
||||
v.link(a)
|
||||
|
||||
func (v *visitor) MethodNode(node *MethodNode) {
|
||||
args := make([]int, 0)
|
||||
for range node.Arguments {
|
||||
args = append(args, v.pop())
|
||||
}
|
||||
a := v.pop()
|
||||
v.push(fmt.Sprintf(".%v(...)", node.Method))
|
||||
v.link(a)
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
v.link(args[i])
|
||||
case *IndexNode:
|
||||
b := v.pop()
|
||||
a := v.pop()
|
||||
v.push("[...]")
|
||||
v.link(a)
|
||||
v.link(b)
|
||||
|
||||
case *MethodNode:
|
||||
args := make([]int, 0)
|
||||
for range node.Arguments {
|
||||
args = append(args, v.pop())
|
||||
}
|
||||
a := v.pop()
|
||||
v.push(fmt.Sprintf(".%v(...)", node.Method))
|
||||
v.link(a)
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
v.link(args[i])
|
||||
}
|
||||
|
||||
case *FunctionNode:
|
||||
args := make([]int, 0)
|
||||
for range node.Arguments {
|
||||
args = append(args, v.pop())
|
||||
}
|
||||
v.push(fmt.Sprintf("%v(...)", node.Name))
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
v.link(args[i])
|
||||
}
|
||||
|
||||
case *BuiltinNode:
|
||||
args := make([]int, 0)
|
||||
for range node.Arguments {
|
||||
args = append(args, v.pop())
|
||||
}
|
||||
v.push(fmt.Sprintf("%v(...)", node.Name))
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
v.link(args[i])
|
||||
}
|
||||
|
||||
case *ClosureNode:
|
||||
a := v.pop()
|
||||
v.push("func {...}")
|
||||
v.link(a)
|
||||
|
||||
case *PointerNode:
|
||||
v.push("#")
|
||||
|
||||
case *ConditionalNode:
|
||||
e2 := v.pop()
|
||||
e1 := v.pop()
|
||||
c := v.pop()
|
||||
v.push("? :")
|
||||
v.link(c)
|
||||
v.link(e1)
|
||||
v.link(e2)
|
||||
|
||||
case *ArrayNode:
|
||||
n := make([]int, 0)
|
||||
for range node.Nodes {
|
||||
n = append(n, v.pop())
|
||||
}
|
||||
v.push("[...]")
|
||||
for i := len(n) - 1; i >= 0; i-- {
|
||||
v.link(n[i])
|
||||
}
|
||||
|
||||
case *MapNode:
|
||||
n := make([]int, 0)
|
||||
for range node.Pairs {
|
||||
n = append(n, v.pop())
|
||||
}
|
||||
v.push("{...}")
|
||||
for i := len(n) - 1; i >= 0; i-- {
|
||||
v.link(n[i])
|
||||
}
|
||||
|
||||
case *PairNode:
|
||||
a := v.pop()
|
||||
v.push(fmt.Sprintf("%q:", node.Key.(*StringNode).Value))
|
||||
v.link(a)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *visitor) FunctionNode(node *FunctionNode) {
|
||||
args := make([]int, 0)
|
||||
for range node.Arguments {
|
||||
args = append(args, v.pop())
|
||||
}
|
||||
v.push(fmt.Sprintf("%v(...)", node.Name))
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
v.link(args[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (v *visitor) BuiltinNode(node *BuiltinNode) {
|
||||
args := make([]int, 0)
|
||||
for range node.Arguments {
|
||||
args = append(args, v.pop())
|
||||
}
|
||||
v.push(fmt.Sprintf("%v(...)", node.Name))
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
v.link(args[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (v *visitor) ClosureNode(node *ClosureNode) {
|
||||
a := v.pop()
|
||||
v.push("func {...}")
|
||||
v.link(a)
|
||||
}
|
||||
|
||||
func (v *visitor) PointerNode(node *PointerNode) {
|
||||
v.push("#")
|
||||
}
|
||||
|
||||
func (v *visitor) ConditionalNode(node *ConditionalNode) {
|
||||
e2 := v.pop()
|
||||
e1 := v.pop()
|
||||
c := v.pop()
|
||||
v.push("? :")
|
||||
v.link(c)
|
||||
v.link(e1)
|
||||
v.link(e2)
|
||||
}
|
||||
|
||||
func (v *visitor) ArrayNode(node *ArrayNode) {
|
||||
n := make([]int, 0)
|
||||
for range node.Nodes {
|
||||
n = append(n, v.pop())
|
||||
}
|
||||
v.push("[...]")
|
||||
for i := len(n) - 1; i >= 0; i-- {
|
||||
v.link(n[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (v *visitor) MapNode(node *MapNode) {
|
||||
n := make([]int, 0)
|
||||
for range node.Pairs {
|
||||
n = append(n, v.pop())
|
||||
}
|
||||
v.push("{...}")
|
||||
for i := len(n) - 1; i >= 0; i-- {
|
||||
v.link(n[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (v *visitor) PairNode(node *PairNode) {
|
||||
a := v.pop()
|
||||
v.push(fmt.Sprintf("%q:", node.Key.(*StringNode).Value))
|
||||
v.link(a)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ func Compile(input string, ops ...conf.Option) (*vm.Program, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
checker.PatchOperators(tree, config)
|
||||
}
|
||||
|
||||
program, err := compiler.Compile(tree, config)
|
||||
|
||||
+23
-35
@@ -291,66 +291,54 @@ func TestExpr(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
code string
|
||||
want interface{}
|
||||
}{
|
||||
{
|
||||
name: "+ operator",
|
||||
code: "1 + 1",
|
||||
want: 2,
|
||||
`1 + 1`,
|
||||
2,
|
||||
},
|
||||
{
|
||||
name: "associativity",
|
||||
code: "(One * Two) * Three == One * (Two * Three)",
|
||||
want: true,
|
||||
`(One * Two) * Three == One * (Two * Three)`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
name: "indexing",
|
||||
code: "IntArray[0]",
|
||||
want: 1,
|
||||
`IntArray[0]`,
|
||||
1,
|
||||
},
|
||||
{
|
||||
name: "helpers",
|
||||
code: "Sum(IntArray)",
|
||||
want: 6,
|
||||
`Sum(IntArray)`,
|
||||
6,
|
||||
},
|
||||
{
|
||||
name: "binary with indexing",
|
||||
code: "IntArray[0] < IntArray[1]",
|
||||
want: true,
|
||||
`IntArray[0] < IntArray[1]`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
name: "helpers with indexing",
|
||||
code: "Sum(MultiDimArray[0])",
|
||||
want: 6,
|
||||
`Sum(MultiDimArray[0])`,
|
||||
6,
|
||||
},
|
||||
{
|
||||
name: "helpers with indexing in binary operations",
|
||||
code: "Sum(MultiDimArray[0]) + Sum(MultiDimArray[1])",
|
||||
want: 12,
|
||||
`Sum(MultiDimArray[0]) + Sum(MultiDimArray[1])`,
|
||||
12,
|
||||
},
|
||||
{
|
||||
name: "binary operations in function call arguments",
|
||||
code: "Inc(IntArray[0] + IntArray[1])",
|
||||
want: 4,
|
||||
`Inc(IntArray[0] + IntArray[1])`,
|
||||
4,
|
||||
},
|
||||
{
|
||||
name: "binary operations with indexing",
|
||||
code: "IntArray[0] + IntArray[1]",
|
||||
want: 3,
|
||||
`IntArray[0] + IntArray[1]`,
|
||||
3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
program, err := expr.Compile(tt.code, expr.Env(mockEnv{}))
|
||||
require.NoError(t, err, "Compile() error")
|
||||
program, err := expr.Compile(tt.code, expr.Env(mockEnv{}))
|
||||
require.NoError(t, err, tt.code)
|
||||
|
||||
got, err := expr.Run(program, request)
|
||||
require.NoError(t, err, "Run() error")
|
||||
got, err := expr.Run(program, request)
|
||||
require.NoError(t, err, tt.code)
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
assert.Equal(t, tt.want, got, tt.code)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user