This commit is contained in:
Sergey Kamardin 2018-11-24 21:47:12 +03:00
parent 2b9d056d0d
commit abc7140723
7 changed files with 268 additions and 234 deletions

View File

@ -5,190 +5,11 @@ import (
"testing" "testing"
"github.com/gobwas/glob/match" "github.com/gobwas/glob/match"
"github.com/gobwas/glob/match/debug"
"github.com/gobwas/glob/syntax/ast" "github.com/gobwas/glob/syntax/ast"
) )
var separators = []rune{'.'} var separators = []rune{'.'}
func TestGlueMatchers(t *testing.T) {
for id, test := range []struct {
in []match.Matcher
exp match.Matcher
}{
{
[]match.Matcher{
match.NewSuper(),
match.NewSingle(nil),
},
match.NewMin(1),
},
{
[]match.Matcher{
match.NewAny(separators),
match.NewSingle(separators),
},
match.EveryOf{match.Matchers{
match.NewMin(1),
match.NewContains(string(separators), true),
}},
},
{
[]match.Matcher{
match.NewSingle(nil),
match.NewSingle(nil),
match.NewSingle(nil),
},
match.EveryOf{match.Matchers{
match.NewMin(3),
match.NewMax(3),
}},
},
{
[]match.Matcher{
match.NewList([]rune{'a'}, true),
match.NewAny([]rune{'a'}),
},
match.EveryOf{match.Matchers{
match.NewMin(1),
match.NewContains("a", true),
}},
},
} {
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 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 TestConvertMatchers(t *testing.T) {
for id, test := range []struct {
in, exp []match.Matcher
}{
{
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
match.NewAny(nil),
},
[]match.Matcher{
match.NewRow(
4,
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(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.NewAny(nil),
match.NewSingle(nil),
match.NewSingle(nil),
match.NewAny(nil),
},
[]match.Matcher{
match.NewRow(
3,
match.Matchers{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
}...,
),
match.NewMin(3),
},
},
} {
act := minimizeMatchers(test.in)
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected convert matchers 2 result:\nact: %#v\nexp: %#v", id, act, test.exp)
continue
}
}
}
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
for id, test := range []struct { for id, test := range []struct {
ast *ast.Node ast *ast.Node
@ -254,10 +75,10 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil),
), ),
sep: separators, sep: separators,
result: match.EveryOf{Matchers: match.Matchers{ result: match.NewEveryOf([]match.Matcher{
match.NewMin(3), match.NewMin(3),
match.NewContains(string(separators), true), match.NewAny(separators),
}}, }),
}, },
{ {
ast: ast.NewNode(ast.KindPattern, nil, ast: ast.NewNode(ast.KindPattern, nil,
@ -275,14 +96,11 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil),
), ),
sep: separators, sep: separators,
result: match.NewBTree( result: match.NewTree(
match.NewRow( match.NewRow([]match.MatchIndexSizer{
4, match.NewText("abc"),
match.Matchers{ match.NewSingle(separators),
match.NewText("abc"), }),
match.NewSingle(separators),
}...,
),
match.NewAny(separators), match.NewAny(separators),
nil, nil,
), ),
@ -297,11 +115,14 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindSuper, nil), ast.NewNode(ast.KindSuper, nil),
), ),
sep: separators, sep: separators,
result: match.NewBTree( result: match.NewTree(
match.NewText("/"), match.NewText("/"),
nil, nil,
match.NewBTree( match.NewTree(
match.NewAnyOf(match.NewText("z"), match.NewText("ab")), match.MustIndexedAnyOf(
match.NewText("z"),
match.NewText("ab"),
),
nil, nil,
match.NewSuper(), match.NewSuper(),
), ),
@ -315,15 +136,12 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil),
), ),
sep: separators, sep: separators,
result: match.NewBTree( result: match.NewTree(
match.NewRow( match.NewRow([]match.MatchIndexSizer{
5, match.NewSingle(separators),
match.Matchers{ match.NewText("abc"),
match.NewSingle(separators), match.NewSingle(separators),
match.NewText("abc"), }),
match.NewSingle(separators),
}...,
),
match.NewSuper(), match.NewSuper(),
nil, nil,
), ),
@ -359,7 +177,7 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil),
ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil),
), ),
result: match.NewContains("abc", false), result: match.NewContains("abc"),
}, },
{ {
ast: ast.NewNode(ast.KindPattern, nil, ast: ast.NewNode(ast.KindPattern, nil,
@ -371,13 +189,14 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil),
), ),
sep: separators, sep: separators,
result: match.NewBTree( result: match.NewTree(
match.NewText("abc"), match.NewText("abc"),
match.NewAny(separators), match.NewAny(separators),
match.NewAny(separators), match.NewAny(separators),
), ),
}, },
{ {
// TODO: THIS!
ast: ast.NewNode(ast.KindPattern, nil, ast: ast.NewNode(ast.KindPattern, nil,
ast.NewNode(ast.KindSuper, nil), ast.NewNode(ast.KindSuper, nil),
ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil),
@ -385,7 +204,7 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindSuper, nil), ast.NewNode(ast.KindSuper, nil),
ast.NewNode(ast.KindSingle, nil), ast.NewNode(ast.KindSingle, nil),
), ),
result: match.NewBTree( result: match.NewTree(
match.NewText("abc"), match.NewText("abc"),
match.NewMin(1), match.NewMin(1),
match.NewMin(1), match.NewMin(1),
@ -430,14 +249,14 @@ func TestCompiler(t *testing.T) {
), ),
), ),
), ),
result: match.NewBTree( result: match.NewTree(
match.NewText("abc"), match.NewText("abc"),
nil, nil,
match.AnyOf{Matchers: match.Matchers{ match.NewAnyOf(
match.NewSingle(nil), match.NewSingle(nil),
match.NewList([]rune{'d', 'e', 'f'}, false), match.NewList([]rune{'d', 'e', 'f'}, false),
match.NewNothing(), match.NewNothing(),
}}, ),
), ),
}, },
{ {
@ -446,14 +265,11 @@ func TestCompiler(t *testing.T) {
ast.NewNode(ast.KindRange, ast.Range{Lo: 'a', Hi: 'x', Not: true}), ast.NewNode(ast.KindRange, ast.Range{Lo: 'a', Hi: 'x', Not: true}),
ast.NewNode(ast.KindAny, nil), ast.NewNode(ast.KindAny, nil),
), ),
result: match.NewBTree( result: match.NewTree(
match.NewRow( match.NewRow([]match.MatchIndexSizer{
2, match.NewRange('a', 'z', false),
match.Matchers{ match.NewRange('a', 'x', true),
match.NewRange('a', 'z', false), }),
match.NewRange('a', 'x', true),
}...,
),
nil, nil,
match.NewSuper(), match.NewSuper(),
), ),
@ -473,17 +289,14 @@ func TestCompiler(t *testing.T) {
), ),
), ),
), ),
result: match.NewRow( result: match.NewRow([]match.MatchIndexSizer{
7, match.NewText("abc"),
match.Matchers{ match.MustIndexedSizedAnyOf(
match.NewText("abc"), match.NewList([]rune{'a', 'b', 'c'}, false),
match.AnyOf{Matchers: match.Matchers{ match.NewList([]rune{'d', 'e', 'f'}, false),
match.NewList([]rune{'a', 'b', 'c'}, false), ),
match.NewList([]rune{'d', 'e', 'f'}, false), match.NewText("ghi"),
}}, }),
match.NewText("ghi"),
}...,
),
}, },
} { } {
m, err := Compile(test.ast, test.sep) m, err := Compile(test.ast, test.sep)
@ -493,7 +306,7 @@ func TestCompiler(t *testing.T) {
} }
if !reflect.DeepEqual(m, test.result) { 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))) t.Errorf("[%d] Compile():\nexp: %#v\nact: %#v\n\ngraphviz:\nexp:\n%s\nact:\n%s\n", id, test.result, m, match.Graphviz("", test.result.(match.Matcher)), match.Graphviz("", m.(match.Matcher)))
continue continue
} }
} }

View File

@ -12,11 +12,35 @@ type AnyOf struct {
func NewAnyOf(ms ...Matcher) Matcher { func NewAnyOf(ms ...Matcher) Matcher {
a := AnyOf{ms, minLen(ms)} a := AnyOf{ms, minLen(ms)}
if mis, ok := MatchIndexers(ms); ok { if mis, ok := MatchIndexers(ms); ok {
return IndexedAnyOf{a, mis} x := IndexedAnyOf{a, mis}
if msz, ok := MatchIndexSizers(ms); ok {
sz := -1
for _, m := range msz {
n := m.RunesCount()
if sz == -1 {
sz = n
} else if sz != n {
sz = -1
break
}
}
if sz != -1 {
return IndexedSizedAnyOf{x, sz}
}
}
return x
} }
return a return a
} }
func MustIndexedAnyOf(ms ...Matcher) MatchIndexer {
return NewAnyOf(ms...).(MatchIndexer)
}
func MustIndexedSizedAnyOf(ms ...Matcher) MatchIndexSizer {
return NewAnyOf(ms...).(MatchIndexSizer)
}
func (a AnyOf) Match(s string) bool { func (a AnyOf) Match(s string) bool {
for _, m := range a.ms { for _, m := range a.ms {
if m.Match(s) { if m.Match(s) {
@ -72,3 +96,12 @@ func (a IndexedAnyOf) Index(s string) (int, []int) {
func (a IndexedAnyOf) String() string { func (a IndexedAnyOf) String() string {
return fmt.Sprintf("<indexed_any_of:[%s]>", a.ms) return fmt.Sprintf("<indexed_any_of:[%s]>", a.ms)
} }
type IndexedSizedAnyOf struct {
IndexedAnyOf
runes int
}
func (a IndexedSizedAnyOf) RunesCount() int {
return a.runes
}

View File

@ -14,6 +14,10 @@ func NewContains(needle string) Contains {
return Contains{needle, false} return Contains{needle, false}
} }
func NewNotContains(needle string) Contains {
return Contains{needle, true}
}
func (c Contains) Match(s string) bool { func (c Contains) Match(s string) bool {
return strings.Contains(s, c.s) != c.not return strings.Contains(s, c.s) != c.not
} }

View File

@ -30,6 +30,10 @@ func (l List) MinLen() int {
return 1 return 1
} }
func (l List) RunesCount() int {
return 1
}
func (l List) Index(s string) (int, []int) { func (l List) Index(s string) (int, []int) {
for i, r := range s { for i, r := range s {
if l.not == (runes.IndexRune(l.rs, r) == -1) { if l.not == (runes.IndexRune(l.rs, r) == -1) {

View File

@ -42,15 +42,28 @@ type Container interface {
func MatchIndexers(ms []Matcher) ([]MatchIndexer, bool) { func MatchIndexers(ms []Matcher) ([]MatchIndexer, bool) {
for _, m := range ms { for _, m := range ms {
if _, ok := m.(Indexer); !ok { if _, ok := m.(MatchIndexer); !ok {
return nil, false return nil, false
} }
} }
mis := make([]MatchIndexer, len(ms)) r := make([]MatchIndexer, len(ms))
for i := range mis { for i := range r {
mis[i] = ms[i].(MatchIndexer) r[i] = ms[i].(MatchIndexer)
} }
return mis, true return r, true
}
func MatchIndexSizers(ms []Matcher) ([]MatchIndexSizer, bool) {
for _, m := range ms {
if _, ok := m.(MatchIndexSizer); !ok {
return nil, false
}
}
r := make([]MatchIndexSizer, len(ms))
for i := range r {
r[i] = ms[i].(MatchIndexSizer)
}
return r, true
} }
type Matchers []Matcher type Matchers []Matcher

163
match/optimize_test.go Normal file
View File

@ -0,0 +1,163 @@
package match
import (
"reflect"
"testing"
"github.com/gobwas/glob/match"
)
func TestCompile(t *testing.T) {
for id, test := range []struct {
in []Matcher
exp Matcher
}{
{
[]Matcher{
NewSuper(),
NewSingle(nil),
},
NewMin(1),
},
{
[]Matcher{
NewAny(separators),
NewSingle(separators),
},
NewEveryOf([]Matcher{
NewMin(1),
NewContains(string(separators)),
}),
},
{
[]Matcher{
NewSingle(nil),
NewSingle(nil),
NewSingle(nil),
},
NewEveryOf([]Matcher{
NewMin(3),
NewMax(3),
}),
},
{
[]Matcher{
NewList([]rune{'a'}, true),
NewAny([]rune{'a'}),
},
NewEveryOf([]Matcher{
NewMin(1),
NewContains("a"),
}),
},
{
[]Matcher{
NewSuper(),
NewSingle(separators),
NewText("c"),
},
NewTree(
NewText("c"),
NewBTree(
NewSingle(separators),
NewSuper(),
nil,
),
nil,
),
},
{
[]Matcher{
NewAny(nil),
NewText("c"),
NewAny(nil),
},
NewTree(
NewText("c"),
NewAny(nil),
NewAny(nil),
),
},
{
[]Matcher{
NewRange('a', 'c', true),
NewList([]rune{'z', 't', 'e'}, false),
NewText("c"),
NewSingle(nil),
},
NewRow([]MatchIndexSizer{
NewRange('a', 'c', true),
NewList([]rune{'z', 't', 'e'}, false),
NewText("c"),
NewSingle(nil),
}),
},
} {
act, err := Compile(test.in)
if err != nil {
t.Errorf("#%d compile matchers error: %s", id, err)
continue
}
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected compile matchers result:\nact: %#v;\nexp: %#v", id, act, test.exp)
continue
}
}
}
func TestMinimize(t *testing.T) {
for id, test := range []struct {
in, exp []match.Matcher
}{
{
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
match.NewAny(nil),
},
[]match.Matcher{
match.NewRow(
4,
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(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.NewAny(nil),
match.NewSingle(nil),
match.NewSingle(nil),
match.NewAny(nil),
},
[]match.Matcher{
match.NewRow(
3,
match.Matchers{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
}...,
),
match.NewMin(3),
},
},
} {
act := minimizeMatchers(test.in)
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected convert matchers 2 result:\nact: %#v\nexp: %#v", id, act, test.exp)
continue
}
}
}

View File

@ -18,6 +18,10 @@ func (self Range) MinLen() int {
return 1 return 1
} }
func (self Range) RunesCount() int {
return 1
}
func (self Range) Match(s string) bool { func (self Range) Match(s string) bool {
r, w := utf8.DecodeRuneInString(s) r, w := utf8.DecodeRuneInString(s)
if len(s) > w { if len(s) > w {