func compileMatchers(matchers []match.Matcher) (match.Matcher, error) { if len(matchers) == 0 { return nil, fmt.Errorf("compile error: need at least one matcher") } if len(matchers) == 1 { return matchers[0], nil } if m := glueMatchers(matchers); m != nil { return m, nil } idx := -1 maxLen := -1 var val match.Matcher for i, matcher := range matchers { if l := matcher.Len(); l != -1 && l >= maxLen { maxLen = l idx = i val = matcher } } if val == nil { // not found matcher with static length r, err := compileMatchers(matchers[1:]) if err != nil { return nil, err } return match.NewBTree(matchers[0], nil, r), nil } left := matchers[:idx] var right []match.Matcher if len(matchers) > idx+1 { right = matchers[idx+1:] } var l, r match.Matcher var err error if len(left) > 0 { l, err = compileMatchers(left) if err != nil { return nil, err } } if len(right) > 0 { r, err = compileMatchers(right) if err != nil { return nil, err } } return match.NewBTree(val, l, r), nil }
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) { if len(matchers) == 0 { return nil, fmt.Errorf("compile error: need at least one matcher") } if len(matchers) == 1 { return matchers[0], nil } if m := glueMatchers(matchers); m != nil { return m, nil } var ( val match.Matcher idx int ) maxLen := -1 for i, matcher := range matchers { l := matcher.Len() if l >= maxLen { maxLen = l idx = i val = matcher } } left := matchers[:idx] var right []match.Matcher if len(matchers) > idx+1 { right = matchers[idx+1:] } var l, r match.Matcher var err error if len(left) > 0 { l, err = compileMatchers(left) if err != nil { return nil, err } } if len(right) > 0 { r, err = compileMatchers(right) if err != nil { return nil, err } } return match.NewBTree(val, l, r), nil }
func TestCompileMatchers(t *testing.T) { for id, test := range []struct { in []match.Matcher exp match.Matcher }{ { []match.Matcher{ match.NewSuper(), match.NewSingle(separators), match.NewText("c"), }, match.NewBTree( match.NewText("c"), match.NewBTree( match.NewSingle(separators), match.NewSuper(), nil, ), nil, ), }, { []match.Matcher{ match.NewAny(nil), match.NewText("c"), match.NewAny(nil), }, match.NewBTree( match.NewText("c"), match.NewAny(nil), match.NewAny(nil), ), }, { []match.Matcher{ match.NewRange('a', 'c', true), match.NewList([]rune{'z', 't', 'e'}, false), match.NewText("c"), match.NewSingle(nil), }, match.NewRow( 4, match.Matchers{ match.NewRange('a', 'c', true), match.NewList([]rune{'z', 't', 'e'}, false), match.NewText("c"), match.NewSingle(nil), }..., ), }, } { act, err := compileMatchers(test.in) if err != nil { t.Errorf("#%d convert matchers error: %s", id, err) continue } if !reflect.DeepEqual(act, test.exp) { t.Errorf("#%d unexpected convert matchers result:\nact: %#v\nexp: %#v", id, act, test.exp) continue } } }
func TestCompiler(t *testing.T) { for id, test := range []struct { ast *nodePattern result Glob sep []rune }{ { ast: pattern(&nodeText{text: "abc"}), result: match.NewText("abc"), }, { ast: pattern(&nodeAny{}), sep: separators, result: match.NewAny(separators), }, { ast: pattern(&nodeAny{}), result: match.NewSuper(), }, { ast: pattern(&nodeSuper{}), result: match.NewSuper(), }, { ast: pattern(&nodeSingle{}), sep: separators, result: match.NewSingle(separators), }, { ast: pattern(&nodeRange{ lo: 'a', hi: 'z', not: true, }), result: match.NewRange('a', 'z', true), }, { ast: pattern(&nodeList{ chars: "abc", not: true, }), result: match.NewList([]rune{'a', 'b', 'c'}, true), }, { ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}), sep: separators, result: match.EveryOf{Matchers: match.Matchers{ match.NewMin(3), match.NewContains(string(separators), true), }}, }, { ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}), result: match.NewMin(3), }, { ast: pattern(&nodeAny{}, &nodeText{text: "abc"}, &nodeSingle{}), sep: separators, result: match.NewBTree( match.NewRow( 4, match.Matchers{ match.NewText("abc"), match.NewSingle(separators), }..., ), match.NewAny(separators), nil, ), }, { ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSingle{}), sep: separators, result: match.NewBTree( match.NewRow( 5, match.Matchers{ match.NewSingle(separators), match.NewText("abc"), match.NewSingle(separators), }..., ), match.NewSuper(), nil, ), }, { ast: pattern(&nodeAny{}, &nodeText{text: "abc"}), result: match.NewSuffix("abc"), }, { ast: pattern(&nodeText{text: "abc"}, &nodeAny{}), result: match.NewPrefix("abc"), }, { ast: pattern(&nodeText{text: "abc"}, &nodeAny{}, &nodeText{text: "def"}), result: match.NewPrefixSuffix("abc", "def"), }, { ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}), result: match.NewContains("abc", false), }, { ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}), sep: separators, result: match.NewBTree( match.NewText("abc"), match.NewAny(separators), match.NewAny(separators), ), }, { ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSuper{}, &nodeSingle{}), result: match.NewBTree( match.NewText("abc"), match.NewMin(1), match.NewMin(1), ), }, { ast: pattern(anyOf(&nodeText{text: "abc"})), result: match.NewText("abc"), }, { ast: pattern(anyOf(pattern(anyOf(pattern(&nodeText{text: "abc"}))))), result: match.NewText("abc"), }, { ast: pattern(anyOf( pattern( &nodeText{text: "abc"}, &nodeSingle{}, ), pattern( &nodeText{text: "abc"}, &nodeList{chars: "def"}, ), pattern( &nodeText{text: "abc"}, ), pattern( &nodeText{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: pattern( &nodeRange{lo: 'a', hi: 'z'}, &nodeRange{lo: 'a', hi: 'x', not: true}, &nodeAny{}, ), result: match.NewBTree( match.NewRow( 2, match.Matchers{ match.NewRange('a', 'z', false), match.NewRange('a', 'x', true), }..., ), nil, match.NewSuper(), ), }, { ast: pattern(anyOf( pattern( &nodeText{text: "abc"}, &nodeList{chars: "abc"}, &nodeText{text: "ghi"}, ), pattern( &nodeText{text: "abc"}, &nodeList{chars: "def"}, &nodeText{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"), }..., ), }, // { // ast: pattern( // anyOf(&nodeText{text: "a"}, &nodeText{text: "b"}), // anyOf(&nodeText{text: "c"}, &nodeText{text: "d"}), // ), // result: match.AnyOf{Matchers: match.Matchers{ // match.NewRow(Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"c", 1}}), // match.NewRow(Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"d"}}), // match.NewRow(Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"c", 1}}), // match.NewRow(Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"d"}}), // }}, // }, } { 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 results are not equal:\nexp: %#v\nact: %#v", id, test.result, m) continue } } }
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 TestCompileMatchers(t *testing.T) { for id, test := range []struct { in []match.Matcher exp match.Matcher }{ { []match.Matcher{ match.Super{}, match.Single{separators}, match.NewText("c"), }, match.NewBTree( match.NewText("c"), match.NewBTree( match.Single{separators}, match.Super{}, nil, ), nil, ), }, { []match.Matcher{ match.Any{}, match.NewText("c"), match.Any{}, }, match.NewBTree( match.NewText("c"), match.Any{}, match.Any{}, ), }, { []match.Matcher{ match.Range{'a', 'c', true}, match.List{"zte", false}, match.NewText("c"), match.Single{}, }, match.Row{ Matchers: match.Matchers{ match.Range{'a', 'c', true}, match.List{"zte", false}, match.NewText("c"), match.Single{}, }, RunesLength: 4, }, }, } { act, err := compileMatchers(test.in) if err != nil { t.Errorf("#%d convert matchers error: %s", id, err) continue } if !reflect.DeepEqual(act, test.exp) { t.Errorf("#%d unexpected convert matchers result:\nact: %s;\nexp: %s", id, act, test.exp) continue } } }
func TestCompiler(t *testing.T) { for id, test := range []struct { ast *nodePattern result Glob sep string }{ { ast: pattern(&nodeText{text: "abc"}), result: match.NewText("abc"), }, { ast: pattern(&nodeAny{}), sep: separators, result: match.Any{separators}, }, { ast: pattern(&nodeAny{}), result: match.Super{}, }, { ast: pattern(&nodeSuper{}), result: match.Super{}, }, { ast: pattern(&nodeSingle{}), sep: separators, result: match.Single{separators}, }, { ast: pattern(&nodeRange{ lo: 'a', hi: 'z', not: true, }), result: match.Range{'a', 'z', true}, }, { ast: pattern(&nodeList{ chars: "abc", not: true, }), result: match.List{"abc", true}, }, { ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}), sep: separators, result: match.EveryOf{Matchers: match.Matchers{ match.Min{3}, match.Contains{separators, true}, }}, }, { ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}), result: match.Min{3}, }, { ast: pattern(&nodeAny{}, &nodeText{text: "abc"}, &nodeSingle{}), sep: separators, result: match.NewBTree( match.Row{ Matchers: match.Matchers{ match.NewText("abc"), match.Single{separators}, }, RunesLength: 4, }, match.Any{separators}, nil, ), }, { ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSingle{}), sep: separators, result: match.NewBTree( match.Row{ Matchers: match.Matchers{ match.Single{separators}, match.NewText("abc"), match.Single{separators}, }, RunesLength: 5, }, match.Super{}, nil, ), }, { ast: pattern(&nodeAny{}, &nodeText{text: "abc"}), result: match.Suffix{"abc"}, }, { ast: pattern(&nodeText{text: "abc"}, &nodeAny{}), result: match.Prefix{"abc"}, }, { ast: pattern(&nodeText{text: "abc"}, &nodeAny{}, &nodeText{text: "def"}), result: match.PrefixSuffix{"abc", "def"}, }, { ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}), result: match.Contains{"abc", false}, }, { ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}), sep: separators, result: match.NewBTree( match.NewText("abc"), match.Any{separators}, match.Any{separators}, ), }, { ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSuper{}, &nodeSingle{}), result: match.NewBTree( match.NewText("abc"), match.Min{1}, match.Min{1}, ), }, { ast: pattern(anyOf(&nodeText{text: "abc"})), result: match.NewText("abc"), }, { ast: pattern(anyOf(pattern(anyOf(pattern(&nodeText{text: "abc"}))))), result: match.NewText("abc"), }, { ast: pattern(anyOf( pattern( &nodeText{text: "abc"}, &nodeSingle{}, ), pattern( &nodeText{text: "abc"}, &nodeList{chars: "def"}, ), pattern( &nodeText{text: "abc"}, ), pattern( &nodeText{text: "abc"}, ), )), result: match.NewBTree( match.NewText("abc"), nil, match.AnyOf{Matchers: match.Matchers{ match.Single{}, match.List{List: "def"}, match.Nothing{}, }}, ), }, { ast: pattern( &nodeRange{lo: 'a', hi: 'z'}, &nodeRange{lo: 'a', hi: 'x', not: true}, &nodeAny{}, ), result: match.NewBTree( match.Row{ Matchers: match.Matchers{ match.Range{Lo: 'a', Hi: 'z'}, match.Range{Lo: 'a', Hi: 'x', Not: true}, }, RunesLength: 2, }, nil, match.Super{}, ), }, { ast: pattern(anyOf( pattern( &nodeText{text: "abc"}, &nodeList{chars: "abc"}, &nodeText{text: "ghi"}, ), pattern( &nodeText{text: "abc"}, &nodeList{chars: "def"}, &nodeText{text: "ghi"}, ), )), result: match.Row{ RunesLength: 7, Matchers: match.Matchers{ match.NewText("abc"), match.AnyOf{Matchers: match.Matchers{ match.List{List: "abc"}, match.List{List: "def"}, }}, match.NewText("ghi"), }, }, }, // { // ast: pattern( // anyOf(&nodeText{text: "a"}, &nodeText{text: "b"}), // anyOf(&nodeText{text: "c"}, &nodeText{text: "d"}), // ), // result: match.AnyOf{Matchers: match.Matchers{ // match.Row{Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"c", 1}}}, // match.Row{Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"d"}}}, // match.Row{Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"c", 1}}}, // match.Row{Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"d"}}}, // }}, // }, } { prog, err := compile(test.ast, test.sep) if err != nil { t.Errorf("compilation error: %s", err) continue } if !reflect.DeepEqual(prog, test.result) { t.Errorf("#%d results are not equal:\nexp: %s,\nact: %s", id, test.result, prog) continue } } }