// minimizeAnyOf tries to find common children of given node of AnyOf pattern // it searches for common children from left and from right // if any common children are found – then it returns new optimized ast tree // else it returns nil func minimizeTreeAnyOf(tree *ast.Node) *ast.Node { if !areOfSameKind(tree.Children, ast.KindPattern) { return nil } commonLeft, commonRight := commonChildren(tree.Children) commonLeftCount, commonRightCount := len(commonLeft), len(commonRight) if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts return nil } var result []*ast.Node if commonLeftCount > 0 { result = append(result, ast.NewNode(ast.KindPattern, nil, commonLeft...)) } var anyOf []*ast.Node for _, child := range tree.Children { reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount] var node *ast.Node if len(reuse) == 0 { // this pattern is completely reduced by commonLeft and commonRight patterns // so it become nothing node = ast.NewNode(ast.KindNothing, nil) } else { node = ast.NewNode(ast.KindPattern, nil, reuse...) } anyOf = appendIfUnique(anyOf, node) } switch { case len(anyOf) == 1 && anyOf[0].Kind != ast.KindNothing: result = append(result, anyOf[0]) case len(anyOf) > 1: result = append(result, ast.NewNode(ast.KindAnyOf, nil, anyOf...)) } if commonRightCount > 0 { result = append(result, ast.NewNode(ast.KindPattern, nil, commonRight...)) } return ast.NewNode(ast.KindPattern, nil, result...) }
func TestCompiler(t *testing.T) { for id, test := range []struct { ast *ast.Node result match.Matcher sep []rune }{ { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ), result: match.NewText("abc"), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ), sep: separators, result: match.NewAny(separators), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ), result: match.NewSuper(), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindSuper, nil), ), result: match.NewSuper(), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindSingle, nil), ), sep: separators, result: match.NewSingle(separators), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindRange, ast.Range{ Lo: 'a', Hi: 'z', Not: true, }), ), result: match.NewRange('a', 'z', true), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindList, ast.List{ Chars: "abc", Not: true, }), ), result: match.NewList([]rune{'a', 'b', 'c'}, true), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil), ), sep: separators, result: match.EveryOf{Matchers: match.Matchers{ match.NewMin(3), match.NewContains(string(separators), true), }}, }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil), ), result: match.NewMin(3), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindSingle, nil), ), sep: separators, result: match.NewBTree( match.NewRow( 4, match.Matchers{ match.NewText("abc"), match.NewSingle(separators), }..., ), match.NewAny(separators), nil, ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"/"}), ast.NewNode(ast.KindAnyOf, nil, ast.NewNode(ast.KindText, ast.Text{"z"}), ast.NewNode(ast.KindText, ast.Text{"ab"}), ), ast.NewNode(ast.KindSuper, nil), ), sep: separators, result: match.NewBTree( match.NewText("/"), nil, match.NewBTree( match.NewAnyOf(match.NewText("z"), match.NewText("ab")), nil, match.NewSuper(), ), ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindSuper, nil), ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindSingle, nil), ), sep: separators, result: match.NewBTree( match.NewRow( 5, match.Matchers{ match.NewSingle(separators), match.NewText("abc"), match.NewSingle(separators), }..., ), match.NewSuper(), nil, ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindText, ast.Text{"abc"}), ), result: match.NewSuffix("abc"), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindAny, nil), ), result: match.NewPrefix("abc"), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindText, ast.Text{"def"}), ), result: match.NewPrefixSuffix("abc", "def"), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil), ), result: match.NewContains("abc", false), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil), ), sep: separators, result: match.NewBTree( match.NewText("abc"), match.NewAny(separators), match.NewAny(separators), ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindSuper, nil), ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindSuper, nil), ast.NewNode(ast.KindSingle, nil), ), result: match.NewBTree( match.NewText("abc"), match.NewMin(1), match.NewMin(1), ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ), result: match.NewText("abc"), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAnyOf, nil, ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAnyOf, nil, ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ), ), ), ), ), result: match.NewText("abc"), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAnyOf, nil, ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindSingle, nil), ), ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindList, ast.List{Chars: "def"}), ), ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ), ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ), ), ), result: match.NewBTree( match.NewText("abc"), nil, match.AnyOf{Matchers: match.Matchers{ match.NewSingle(nil), match.NewList([]rune{'d', 'e', 'f'}, false), match.NewNothing(), }}, ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindRange, ast.Range{Lo: 'a', Hi: 'z'}), ast.NewNode(ast.KindRange, ast.Range{Lo: 'a', Hi: 'x', Not: true}), ast.NewNode(ast.KindAny, nil), ), result: match.NewBTree( match.NewRow( 2, match.Matchers{ match.NewRange('a', 'z', false), match.NewRange('a', 'x', true), }..., ), nil, match.NewSuper(), ), }, { ast: ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindAnyOf, nil, ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindList, ast.List{Chars: "abc"}), ast.NewNode(ast.KindText, ast.Text{"ghi"}), ), ast.NewNode(ast.KindPattern, nil, ast.NewNode(ast.KindText, ast.Text{"abc"}), ast.NewNode(ast.KindList, ast.List{Chars: "def"}), ast.NewNode(ast.KindText, ast.Text{"ghi"}), ), ), ), result: match.NewRow( 7, match.Matchers{ match.NewText("abc"), match.AnyOf{Matchers: match.Matchers{ match.NewList([]rune{'a', 'b', 'c'}, false), match.NewList([]rune{'d', 'e', 'f'}, false), }}, match.NewText("ghi"), }..., ), }, } { m, err := Compile(test.ast, test.sep) if err != nil { t.Errorf("compilation error: %s", err) continue } if !reflect.DeepEqual(m, test.result) { t.Errorf("[%d] Compile():\nexp: %#v\nact: %#v\n\ngraphviz:\nexp:\n%s\nact:\n%s\n", id, test.result, m, debug.Graphviz("", test.result.(match.Matcher)), debug.Graphviz("", m.(match.Matcher))) continue } } }
func TestCommonChildren(t *testing.T) { for i, test := range []struct { nodes []*ast.Node left []*ast.Node right []*ast.Node }{ { nodes: []*ast.Node{ ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"z"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ), }, }, { nodes: []*ast.Node{ ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"z"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ), ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ), }, left: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"a"}), }, right: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"c"}), }, }, { nodes: []*ast.Node{ ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ast.NewNode(ast.KindText, ast.Text{"d"}), ), ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ast.NewNode(ast.KindText, ast.Text{"d"}), ), }, left: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), }, right: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"c"}), ast.NewNode(ast.KindText, ast.Text{"d"}), }, }, { nodes: []*ast.Node{ ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ), ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), ast.NewNode(ast.KindText, ast.Text{"b"}), ast.NewNode(ast.KindText, ast.Text{"c"}), ), }, left: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"b"}), }, right: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"c"}), }, }, { nodes: []*ast.Node{ ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"d"}), ), ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"d"}), ), ast.NewNode(ast.KindNothing, nil, ast.NewNode(ast.KindText, ast.Text{"a"}), ast.NewNode(ast.KindText, ast.Text{"e"}), ), }, left: []*ast.Node{ ast.NewNode(ast.KindText, ast.Text{"a"}), }, right: []*ast.Node{}, }, } { left, right := commonChildren(test.nodes) if !nodesEqual(left, test.left) { t.Errorf("[%d] left, right := commonChildren(); left = %v; want %v", i, left, test.left) } if !nodesEqual(right, test.right) { t.Errorf("[%d] left, right := commonChildren(); right = %v; want %v", i, right, test.right) } } }