mirror of https://github.com/gobwas/glob.git
Fixes, cleanup
This commit is contained in:
parent
d2a191e0f0
commit
55776ffb29
230
compiler.go
230
compiler.go
|
@ -3,6 +3,7 @@ package glob
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gobwas/glob/match"
|
"github.com/gobwas/glob/match"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
func optimize(matcher match.Matcher) match.Matcher {
|
func optimize(matcher match.Matcher) match.Matcher {
|
||||||
|
@ -13,6 +14,13 @@ func optimize(matcher match.Matcher) match.Matcher {
|
||||||
return match.Super{}
|
return match.Super{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case match.AnyOf:
|
||||||
|
if len(m.Matchers) == 1 {
|
||||||
|
return m.Matchers[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
case match.BTree:
|
case match.BTree:
|
||||||
m.Left = optimize(m.Left)
|
m.Left = optimize(m.Left)
|
||||||
m.Right = optimize(m.Right)
|
m.Right = optimize(m.Right)
|
||||||
|
@ -235,6 +243,124 @@ func minimizeMatchers(matchers []match.Matcher) []match.Matcher {
|
||||||
return minimizeMatchers(next)
|
return minimizeMatchers(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func minimizeAnyOf(children []node) node {
|
||||||
|
var nodes [][]node
|
||||||
|
var min int
|
||||||
|
var idx int
|
||||||
|
for i, desc := range children {
|
||||||
|
pat, ok := desc.(*nodePattern)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := pat.children()
|
||||||
|
ln := len(n)
|
||||||
|
if len(nodes) == 0 || (ln < min) {
|
||||||
|
min = ln
|
||||||
|
idx = i
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = append(nodes, pat.children())
|
||||||
|
}
|
||||||
|
|
||||||
|
minNodes := nodes[idx]
|
||||||
|
if idx+1 < len(nodes) {
|
||||||
|
nodes = append(nodes[:idx], nodes[idx+1:]...)
|
||||||
|
} else {
|
||||||
|
nodes = nodes[:idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
var commonLeft []node
|
||||||
|
var commonLeftCount int
|
||||||
|
for i, n := range minNodes {
|
||||||
|
has := true
|
||||||
|
for _, t := range nodes {
|
||||||
|
if !reflect.DeepEqual(n, t[i]) {
|
||||||
|
has = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has {
|
||||||
|
commonLeft = append(commonLeft, n)
|
||||||
|
commonLeftCount++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var commonRight []node
|
||||||
|
var commonRightCount int
|
||||||
|
for i := min - 1; i > commonLeftCount-1; i-- {
|
||||||
|
n := minNodes[i]
|
||||||
|
has := true
|
||||||
|
for _, t := range nodes {
|
||||||
|
if !reflect.DeepEqual(n, t[len(t)-(min-i)]) {
|
||||||
|
has = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has {
|
||||||
|
commonRight = append(commonRight, n)
|
||||||
|
commonRightCount++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if commonLeftCount == 0 && commonRightCount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = append(nodes, minNodes)
|
||||||
|
nodes[len(nodes)-1], nodes[idx] = nodes[idx], nodes[len(nodes)-1]
|
||||||
|
|
||||||
|
var result []node
|
||||||
|
if commonLeftCount > 0 {
|
||||||
|
result = append(result, &nodePattern{nodeImpl: nodeImpl{desc: commonLeft}})
|
||||||
|
}
|
||||||
|
|
||||||
|
var anyOf []node
|
||||||
|
for _, n := range nodes {
|
||||||
|
if commonLeftCount+commonRightCount == len(n) {
|
||||||
|
anyOf = append(anyOf, nil)
|
||||||
|
} else {
|
||||||
|
anyOf = append(anyOf, &nodePattern{nodeImpl: nodeImpl{desc: n[commonLeftCount : len(n)-commonRightCount]}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anyOf = uniqueNodes(anyOf)
|
||||||
|
if len(anyOf) == 1 {
|
||||||
|
if anyOf[0] != nil {
|
||||||
|
result = append(result, &nodePattern{nodeImpl: nodeImpl{desc: anyOf}})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = append(result, &nodeAnyOf{nodeImpl: nodeImpl{desc: anyOf}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if commonRightCount > 0 {
|
||||||
|
result = append(result, &nodePattern{nodeImpl: nodeImpl{desc: commonRight}})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &nodePattern{nodeImpl: nodeImpl{desc: result}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniqueNodes(nodes []node) (result []node) {
|
||||||
|
head:
|
||||||
|
for _, n := range nodes {
|
||||||
|
for _, e := range result {
|
||||||
|
if reflect.DeepEqual(e, n) {
|
||||||
|
continue head
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
|
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
|
||||||
if len(matchers) == 0 {
|
if len(matchers) == 0 {
|
||||||
return nil, fmt.Errorf("compile error: need at least one matcher")
|
return nil, fmt.Errorf("compile error: need at least one matcher")
|
||||||
|
@ -287,12 +413,87 @@ func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
|
||||||
return match.NewBTree(val, l, r), nil
|
return match.NewBTree(val, l, r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func do(node node, s string) (m match.Matcher, err error) {
|
//func complexity(m match.Matcher) int {
|
||||||
switch n := node.(type) {
|
// var matchers []match.Matcher
|
||||||
|
// var k int
|
||||||
|
//
|
||||||
|
// switch matcher := m.(type) {
|
||||||
|
//
|
||||||
|
// case match.Nothing:
|
||||||
|
// return 0
|
||||||
|
//
|
||||||
|
// case match.Max, match.Range, match.Suffix, match.Text:
|
||||||
|
// return 1
|
||||||
|
//
|
||||||
|
// case match.PrefixSuffix, match.Single, match.Row:
|
||||||
|
// return 2
|
||||||
|
//
|
||||||
|
// case match.Any, match.Contains, match.List, match.Min, match.Prefix, match.Super:
|
||||||
|
// return 4
|
||||||
|
//
|
||||||
|
// case match.BTree:
|
||||||
|
// matchers = append(matchers, matcher.Value)
|
||||||
|
// if matcher.Left != nil {
|
||||||
|
// matchers = append(matchers, matcher.Left)
|
||||||
|
// }
|
||||||
|
// if matcher.Right != nil {
|
||||||
|
// matchers = append(matchers, matcher.Right)
|
||||||
|
// }
|
||||||
|
// k = 1
|
||||||
|
//
|
||||||
|
// case match.AnyOf:
|
||||||
|
// matchers = matcher.Matchers
|
||||||
|
// k = 1
|
||||||
|
// case match.EveryOf:
|
||||||
|
// matchers = matcher.Matchers
|
||||||
|
// k = 1
|
||||||
|
//
|
||||||
|
// default:
|
||||||
|
// return 0
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var sum int
|
||||||
|
// for _, m := range matchers {
|
||||||
|
// sum += complexity(m)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return sum * k
|
||||||
|
//}
|
||||||
|
|
||||||
|
func doAnyOf(n *nodeAnyOf, s string) (match.Matcher, error) {
|
||||||
|
var matchers []match.Matcher
|
||||||
|
for _, desc := range n.children() {
|
||||||
|
if desc == nil {
|
||||||
|
matchers = append(matchers, match.Nothing{})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := do(desc, s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
matchers = append(matchers, optimize(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
return match.AnyOf{matchers}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func do(leaf node, s string) (m match.Matcher, err error) {
|
||||||
|
switch n := leaf.(type) {
|
||||||
|
|
||||||
|
case *nodeAnyOf:
|
||||||
|
// todo this could be faster on pattern_alternatives_combine_lite
|
||||||
|
if n := minimizeAnyOf(n.children()); n != nil {
|
||||||
|
return do(n, s)
|
||||||
|
}
|
||||||
|
|
||||||
case *nodePattern, *nodeAnyOf:
|
|
||||||
var matchers []match.Matcher
|
var matchers []match.Matcher
|
||||||
for _, desc := range node.children() {
|
for _, desc := range n.children() {
|
||||||
|
if desc == nil {
|
||||||
|
matchers = append(matchers, match.Nothing{})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
m, err := do(desc, s)
|
m, err := do(desc, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -300,13 +501,26 @@ func do(node node, s string) (m match.Matcher, err error) {
|
||||||
matchers = append(matchers, optimize(m))
|
matchers = append(matchers, optimize(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := node.(*nodeAnyOf); ok {
|
return match.AnyOf{matchers}, nil
|
||||||
m = match.AnyOf{matchers}
|
|
||||||
} else {
|
case *nodePattern:
|
||||||
m, err = compileMatchers(minimizeMatchers(matchers))
|
nodes := leaf.children()
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return match.Nothing{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchers []match.Matcher
|
||||||
|
for _, desc := range nodes {
|
||||||
|
m, err := do(desc, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
matchers = append(matchers, optimize(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err = compileMatchers(minimizeMatchers(matchers))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case *nodeList:
|
case *nodeList:
|
||||||
|
|
188
compiler_test.go
188
compiler_test.go
|
@ -320,18 +320,39 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ast: pattern(anyOf(&nodeText{text: "abc"})),
|
ast: pattern(anyOf(&nodeText{text: "abc"})),
|
||||||
result: match.AnyOf{match.Matchers{
|
result: match.NewText("abc"),
|
||||||
match.NewText("abc"),
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ast: pattern(anyOf(pattern(anyOf(pattern(&nodeText{text: "abc"}))))),
|
ast: pattern(anyOf(pattern(anyOf(pattern(&nodeText{text: "abc"}))))),
|
||||||
result: match.AnyOf{match.Matchers{
|
result: match.NewText("abc"),
|
||||||
match.AnyOf{match.Matchers{
|
},
|
||||||
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(
|
ast: pattern(
|
||||||
|
@ -351,7 +372,32 @@ func TestCompiler(t *testing.T) {
|
||||||
match.Super{},
|
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(
|
// ast: pattern(
|
||||||
// anyOf(&nodeText{text: "a"}, &nodeText{text: "b"}),
|
// anyOf(&nodeText{text: "a"}, &nodeText{text: "b"}),
|
||||||
// anyOf(&nodeText{text: "c"}, &nodeText{text: "d"}),
|
// anyOf(&nodeText{text: "c"}, &nodeText{text: "d"}),
|
||||||
|
@ -376,3 +422,125 @@ func TestCompiler(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const complexityString = "abcd"
|
||||||
|
|
||||||
|
//func BenchmarkComplexityAny(b *testing.B) {
|
||||||
|
// m := match.Any{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityContains(b *testing.B) {
|
||||||
|
// m := match.Contains{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityList(b *testing.B) {
|
||||||
|
// m := match.List{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityMax(b *testing.B) {
|
||||||
|
// m := match.Max{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityMin(b *testing.B) {
|
||||||
|
// m := match.Min{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityNothing(b *testing.B) {
|
||||||
|
// m := match.Nothing{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityPrefix(b *testing.B) {
|
||||||
|
// m := match.Prefix{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityPrefixSuffix(b *testing.B) {
|
||||||
|
// m := match.PrefixSuffix{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityRange(b *testing.B) {
|
||||||
|
// m := match.Range{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityRow(b *testing.B) {
|
||||||
|
// m := match.Row{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexitySingle(b *testing.B) {
|
||||||
|
// m := match.Single{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexitySuffix(b *testing.B) {
|
||||||
|
// m := match.Suffix{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexitySuper(b *testing.B) {
|
||||||
|
// m := match.Super{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityText(b *testing.B) {
|
||||||
|
// m := match.Text{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityAnyOf(b *testing.B) {
|
||||||
|
// m := match.AnyOf{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityBTree(b *testing.B) {
|
||||||
|
// m := match.NewBTree(match.NewText("abc"), match.NewText("d"), match.NewText("e"))
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//func BenchmarkComplexityEveryOf(b *testing.B) {
|
||||||
|
// m := match.EveryOf{}
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// _ = m.Match(complexityString)
|
||||||
|
// _, _ = m.Index(complexityString)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
133
glob_test.go
133
glob_test.go
|
@ -5,7 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gobwas/glob/match"
|
"github.com/gobwas/glob/match"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,10 +22,20 @@ const (
|
||||||
pattern_alternatives = "{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}"
|
pattern_alternatives = "{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}"
|
||||||
fixture_alternatives = "http://yahoo.com"
|
fixture_alternatives = "http://yahoo.com"
|
||||||
|
|
||||||
|
pattern_alternatives_suffix = "{https://*gobwas.com,http://exclude.gobwas.com}"
|
||||||
|
fixture_alternatives_suffix_first = "https://safe.gobwas.com"
|
||||||
|
fixture_alternatives_suffix_second = "http://exclude.gobwas.com"
|
||||||
|
|
||||||
pattern_prefix = "abc*"
|
pattern_prefix = "abc*"
|
||||||
pattern_suffix = "*def"
|
pattern_suffix = "*def"
|
||||||
pattern_prefix_suffix = "ab*ef"
|
pattern_prefix_suffix = "ab*ef"
|
||||||
fixture_prefix_suffix = "abcdef"
|
fixture_prefix_suffix = "abcdef"
|
||||||
|
|
||||||
|
pattern_alternatives_combine_lite = "{abc*def,abc?def,abc[zte]def}"
|
||||||
|
fixture_alternatives_combine_lite = "abczdef"
|
||||||
|
|
||||||
|
pattern_alternatives_combine_hard = "{abc*[a-c]def,abc?[d-g]def,abc[zte]?def}"
|
||||||
|
fixture_alternatives_combine_hard = "abczqdef"
|
||||||
)
|
)
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
|
@ -39,63 +49,66 @@ func glob(s bool, p, m string, d ...string) test {
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(pattern string, m match.Matcher) string {
|
func draw(pattern string, m match.Matcher) string {
|
||||||
if tree, ok := m.(match.BTree); ok {
|
return fmt.Sprintf(`digraph G {graph[label="%s"];%s}`, pattern, graphviz(m, fmt.Sprintf("%x", rand.Int63())))
|
||||||
return fmt.Sprintf(`digraph G {graph[label="%s"];%s}`, pattern, graphviz(tree, fmt.Sprintf("%x", rand.Int63())))
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func graphviz(tree match.BTree, id string) string {
|
func graphviz(m match.Matcher, id string) string {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, tree.Value.String())
|
switch matcher := m.(type) {
|
||||||
for _, m := range []match.Matcher{tree.Left, tree.Right} {
|
case match.BTree:
|
||||||
switch n := m.(type) {
|
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, matcher.Value.String())
|
||||||
case nil:
|
for _, m := range []match.Matcher{matcher.Left, matcher.Right} {
|
||||||
rnd := rand.Int63()
|
switch n := m.(type) {
|
||||||
fmt.Fprintf(buf, `"%x"[label="<nil>"];`, rnd)
|
case nil:
|
||||||
// fmt.Fprintf(buf, `"%s"->"%x"[label="len = 0"];`, id, rnd)
|
rnd := rand.Int63()
|
||||||
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
fmt.Fprintf(buf, `"%x"[label="<nil>"];`, rnd)
|
||||||
|
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
||||||
|
|
||||||
case match.BTree:
|
default:
|
||||||
sub := fmt.Sprintf("%x", rand.Int63())
|
sub := fmt.Sprintf("%x", rand.Int63())
|
||||||
// fmt.Fprintf(buf, `"%s"->"%s"[label="len=%d"];`, id, sub, n.Len())
|
fmt.Fprintf(buf, `"%s"->"%s";`, id, sub)
|
||||||
fmt.Fprintf(buf, `"%s"->"%s";`, id, sub)
|
fmt.Fprintf(buf, graphviz(n, sub))
|
||||||
fmt.Fprintf(buf, graphviz(n, sub))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
case match.AnyOf:
|
||||||
|
fmt.Fprintf(buf, `"%s"[label="AnyOf"];`, id)
|
||||||
|
for _, m := range matcher.Matchers {
|
||||||
rnd := rand.Int63()
|
rnd := rand.Int63()
|
||||||
fmt.Fprintf(buf, `"%x"[label="%s"];`, rnd, m.String())
|
fmt.Fprintf(buf, graphviz(m, fmt.Sprintf("%x", rnd)))
|
||||||
// fmt.Fprintf(buf, `"%s"->"%x"[label="len = %d"];`, id, rnd, m.Len())
|
|
||||||
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case match.EveryOf:
|
||||||
|
fmt.Fprintf(buf, `"%s"[label="EveryOf"];`, id)
|
||||||
|
for _, m := range matcher.Matchers {
|
||||||
|
rnd := rand.Int63()
|
||||||
|
fmt.Fprintf(buf, graphviz(m, fmt.Sprintf("%x", rnd)))
|
||||||
|
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, m.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompilePattern(t *testing.T) {
|
func DrawPatterns(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
pattern string
|
pattern string
|
||||||
sep string
|
sep string
|
||||||
exp match.Matcher
|
|
||||||
}{
|
}{
|
||||||
// {
|
|
||||||
// pattern: "left*??B*abcd*[!b]??*abc*right",
|
|
||||||
// exp: match.Raw{"t"},
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pattern: "abc*??def",
|
|
||||||
// exp: match.Raw{"t"},
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
pattern: "{abc[abc]ghi,abc[def]ghi}",
|
pattern: pattern_alternatives_suffix,
|
||||||
exp: match.NewBTree(
|
sep: separators,
|
||||||
match.AnyOf{match.Matchers{match.List{"abc", false}, match.List{"qwe", false}}},
|
},
|
||||||
match.NewText("abc"),
|
{
|
||||||
match.NewText("ghi"),
|
pattern: pattern_alternatives_combine_lite,
|
||||||
),
|
},
|
||||||
|
{
|
||||||
|
pattern: pattern_alternatives_combine_hard,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
glob, err := Compile(test.pattern, test.sep)
|
glob, err := Compile(test.pattern, test.sep)
|
||||||
|
@ -105,10 +118,12 @@ func TestCompilePattern(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher := glob.(match.Matcher)
|
matcher := glob.(match.Matcher)
|
||||||
if !reflect.DeepEqual(test.exp, matcher) {
|
fmt.Println(test.pattern)
|
||||||
t.Errorf("#%d unexpected compilation:\nexp: %s\nact: %s", id, test.exp, draw(test.pattern, matcher))
|
fmt.Println(strings.Repeat("=", len(test.pattern)))
|
||||||
continue
|
fmt.Println(draw(test.pattern, matcher))
|
||||||
}
|
fmt.Println()
|
||||||
|
fmt.Println(matcher.String())
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +223,10 @@ func TestGlob(t *testing.T) {
|
||||||
glob(true, pattern_plain, fixture_plain),
|
glob(true, pattern_plain, fixture_plain),
|
||||||
glob(true, pattern_multiple, fixture_multiple),
|
glob(true, pattern_multiple, fixture_multiple),
|
||||||
glob(true, pattern_alternatives, fixture_alternatives),
|
glob(true, pattern_alternatives, fixture_alternatives),
|
||||||
|
glob(true, pattern_alternatives_suffix, fixture_alternatives_suffix_first),
|
||||||
|
glob(true, pattern_alternatives_suffix, fixture_alternatives_suffix_second),
|
||||||
|
glob(true, pattern_alternatives_combine_hard, fixture_alternatives_combine_hard),
|
||||||
|
glob(true, pattern_alternatives_combine_lite, fixture_alternatives_combine_lite),
|
||||||
glob(true, pattern_prefix, fixture_prefix_suffix),
|
glob(true, pattern_prefix, fixture_prefix_suffix),
|
||||||
glob(true, pattern_suffix, fixture_prefix_suffix),
|
glob(true, pattern_suffix, fixture_prefix_suffix),
|
||||||
glob(true, pattern_prefix_suffix, fixture_prefix_suffix),
|
glob(true, pattern_prefix_suffix, fixture_prefix_suffix),
|
||||||
|
@ -255,6 +274,34 @@ func BenchmarkAlternatives(b *testing.B) {
|
||||||
_ = m.Match(fixture_alternatives)
|
_ = m.Match(fixture_alternatives)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func BenchmarkAlternativesSuffixFirst(b *testing.B) {
|
||||||
|
m, _ := Compile(pattern_alternatives_suffix)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = m.Match(fixture_alternatives_suffix_first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkAlternativesSuffixSecond(b *testing.B) {
|
||||||
|
m, _ := Compile(pattern_alternatives_suffix)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = m.Match(fixture_alternatives_suffix_second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkAlternativesCombineLite(b *testing.B) {
|
||||||
|
m, _ := Compile(pattern_alternatives_combine_lite)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = m.Match(fixture_alternatives_combine_lite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkAlternativesCombineHard(b *testing.B) {
|
||||||
|
m, _ := Compile(pattern_alternatives_combine_hard)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = m.Match(fixture_alternatives_combine_hard)
|
||||||
|
}
|
||||||
|
}
|
||||||
func BenchmarkPlain(b *testing.B) {
|
func BenchmarkPlain(b *testing.B) {
|
||||||
m, _ := Compile(pattern_plain)
|
m, _ := Compile(pattern_plain)
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ func TestAnyOfIndex(t *testing.T) {
|
||||||
{
|
{
|
||||||
Matchers{
|
Matchers{
|
||||||
Any{},
|
Any{},
|
||||||
Text{"b"},
|
NewText("b"),
|
||||||
Text{"c"},
|
NewText("c"),
|
||||||
},
|
},
|
||||||
"abc",
|
"abc",
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -11,28 +11,30 @@ func TestBTree(t *testing.T) {
|
||||||
exp bool
|
exp bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
BTree{Value: Text{"abc"}, Left: Super{}, Right: Super{}},
|
NewBTree(NewText("abc"), Super{}, Super{}),
|
||||||
"abc",
|
"abc",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
BTree{Value: Text{"a"}, Left: Single{}, Right: Single{}},
|
NewBTree(NewText("a"), Single{}, Single{}),
|
||||||
"aaa",
|
"aaa",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
BTree{Value: Text{"b"}, Left: Single{}},
|
NewBTree(NewText("b"), Single{}, nil),
|
||||||
"bbb",
|
"bbb",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
BTree{
|
NewBTree(
|
||||||
Left: BTree{
|
NewText("c"),
|
||||||
Left: Super{},
|
NewBTree(
|
||||||
Value: Single{},
|
Single{},
|
||||||
},
|
Super{},
|
||||||
Value: Text{"c"},
|
nil,
|
||||||
},
|
),
|
||||||
|
nil,
|
||||||
|
),
|
||||||
"abc",
|
"abc",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,8 +15,8 @@ func TestEveryOfIndex(t *testing.T) {
|
||||||
{
|
{
|
||||||
Matchers{
|
Matchers{
|
||||||
Any{},
|
Any{},
|
||||||
Text{"b"},
|
NewText("b"),
|
||||||
Text{"c"},
|
NewText("c"),
|
||||||
},
|
},
|
||||||
"abc",
|
"abc",
|
||||||
-1,
|
-1,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const lenOne = 1
|
const lenOne = 1
|
||||||
|
const lenZero = 0
|
||||||
const lenNo = -1
|
const lenNo = -1
|
||||||
|
|
||||||
type Matcher interface {
|
type Matcher interface {
|
||||||
|
|
|
@ -21,12 +21,7 @@ func (self Max) Match(s string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Max) Index(s string) (int, []int) {
|
func (self Max) Index(s string) (index int, segments []int) {
|
||||||
if !self.Match(s) {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
segments := make([]int, 0, self.Limit+1)
|
|
||||||
segments = append(segments, 0)
|
segments = append(segments, 0)
|
||||||
var count int
|
var count int
|
||||||
for i, r := range s {
|
for i, r := range s {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package match
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Nothing struct{}
|
||||||
|
|
||||||
|
func (self Nothing) Match(s string) bool {
|
||||||
|
return len(s) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Nothing) Index(s string) (int, []int) {
|
||||||
|
return 0, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Nothing) Len() int {
|
||||||
|
return lenZero
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Nothing) String() string {
|
||||||
|
return fmt.Sprintf("<nothing>")
|
||||||
|
}
|
|
@ -15,25 +15,32 @@ func (self PrefixSuffix) Index(s string) (int, []int) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var segments []int
|
var resp []int
|
||||||
for sub := s[prefixIdx:]; ; {
|
suffixLen := len(self.Suffix)
|
||||||
suffixIdx := strings.LastIndex(sub, self.Suffix)
|
|
||||||
if suffixIdx == -1 {
|
if suffixLen > 0 {
|
||||||
break
|
var segments []int
|
||||||
|
for sub := s[prefixIdx:]; ; {
|
||||||
|
suffixIdx := strings.LastIndex(sub, self.Suffix)
|
||||||
|
if suffixIdx == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
segments = append(segments, suffixIdx+suffixLen)
|
||||||
|
sub = sub[:suffixIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
segments = append(segments, suffixIdx+len(self.Suffix))
|
segLen := len(segments)
|
||||||
sub = s[:suffixIdx]
|
if segLen == 0 {
|
||||||
}
|
return -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
segLen := len(segments)
|
resp = make([]int, segLen)
|
||||||
if segLen == 0 {
|
for i, s := range segments {
|
||||||
return -1, nil
|
resp[segLen-i-1] = s
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
resp := make([]int, segLen)
|
resp = append(resp, len(s)-prefixIdx)
|
||||||
for i, s := range segments {
|
|
||||||
resp[segLen-i-1] = s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefixIdx, resp
|
return prefixIdx, resp
|
||||||
|
|
|
@ -12,7 +12,12 @@ type Single struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Single) Match(s string) bool {
|
func (self Single) Match(s string) bool {
|
||||||
return strings.IndexAny(s, self.Separators) == -1
|
r, w := utf8.DecodeRuneInString(s)
|
||||||
|
if len(s) > w {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.IndexRune(self.Separators, r) == -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Single) Len() int {
|
func (self Single) Len() int {
|
||||||
|
|
Loading…
Reference in New Issue