Refactor visitor

This commit is contained in:
Anton Medvedev
2019-06-25 11:48:43 +03:00
parent 080a6b3c5f
commit 1fac0d05f2
9 changed files with 174 additions and 268 deletions
+1 -1
View File
@@ -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
// | ........^
```
-45
View File
@@ -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
View File
@@ -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
View File
@@ -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
-5
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+1
View File
@@ -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
View File
@@ -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)
}
}