This commit is contained in:
gobwas 2016-01-09 02:34:41 +03:00
parent 76b6c27015
commit 66fc4deeeb
22 changed files with 471 additions and 119 deletions

View File

@ -62,6 +62,52 @@ func optimize(matcher match.Matcher) match.Matcher {
} }
func glueMatchers(matchers []match.Matcher) match.Matcher { func glueMatchers(matchers []match.Matcher) match.Matcher {
var (
glued []match.Matcher
winner match.Matcher
)
maxLen := -1
if m := glueAsEvery(matchers); m != nil {
glued = append(glued, m)
return m
}
if m := glueAsRow(matchers); m != nil {
glued = append(glued, m)
return m
}
for _, g := range glued {
if l := g.Len(); l > maxLen {
maxLen = l
winner = g
}
}
return winner
}
func glueAsRow(matchers []match.Matcher) match.Matcher {
switch len(matchers) {
case 0:
return nil
case 1:
return matchers[0]
}
row := match.Row{}
for _, matcher := range matchers {
err := row.Add(matcher)
if err != nil {
return nil
}
}
return row
}
func glueAsEvery(matchers []match.Matcher) match.Matcher {
switch len(matchers) { switch len(matchers) {
case 0: case 0:
return nil return nil
@ -147,7 +193,28 @@ func glueMatchers(matchers []match.Matcher) match.Matcher {
return every return every
} }
func convertMatchers(matchers []match.Matcher) (match.Matcher, error) { func convertMatchers(matchers []match.Matcher, result []match.Matcher) []match.Matcher {
var (
buf []match.Matcher
done match.Matcher
)
for idx, m := range matchers {
buf = append(buf, m)
if g := glueMatchers(buf); g != nil {
done = g
} else {
return convertMatchers(matchers[idx:], append(result, done))
}
}
if done != nil {
return append(result, done)
}
return result
}
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
if m := glueMatchers(matchers); m != nil { if m := glueMatchers(matchers); m != nil {
return m, nil return m, nil
} }
@ -156,14 +223,14 @@ func convertMatchers(matchers []match.Matcher) (match.Matcher, error) {
val match.Primitive val match.Primitive
idx int idx int
) )
maxLen := -1
for i, matcher := range matchers { for i, matcher := range matchers {
if p, ok := matcher.(match.Primitive); ok { if p, ok := matcher.(match.Primitive); ok {
idx = i l := p.Len()
val = p if l >= maxLen {
maxLen = l
if _, ok := matcher.(match.Raw); ok { idx = i
break val = p
} }
} }
} }
@ -181,7 +248,7 @@ func convertMatchers(matchers []match.Matcher) (match.Matcher, error) {
tree := match.BTree{Value: val} tree := match.BTree{Value: val}
if len(left) > 0 { if len(left) > 0 {
l, err := convertMatchers(left) l, err := compileMatchers(left)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -190,7 +257,7 @@ func convertMatchers(matchers []match.Matcher) (match.Matcher, error) {
} }
if len(right) > 0 { if len(right) > 0 {
r, err := convertMatchers(right) r, err := compileMatchers(right)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -217,7 +284,7 @@ func do(node node, s string) (m match.Matcher, err error) {
if _, ok := node.(*nodeAnyOf); ok { if _, ok := node.(*nodeAnyOf); ok {
m = match.AnyOf{matchers} m = match.AnyOf{matchers}
} else { } else {
m, err = convertMatchers(matchers) m, err = compileMatchers(convertMatchers(matchers, nil))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -52,7 +52,7 @@ func TestGlueMatchers(t *testing.T) {
}}, }},
}, },
} { } {
act, err := convertMatchers(test.in) act, err := compileMatchers(test.in)
if err != nil { if err != nil {
t.Errorf("#%d convert matchers error: %s", id, err) t.Errorf("#%d convert matchers error: %s", id, err)
continue continue
@ -65,7 +65,7 @@ func TestGlueMatchers(t *testing.T) {
} }
} }
func TestConvertMatchers(t *testing.T) { func TestCompileMatchers(t *testing.T) {
for id, test := range []struct { for id, test := range []struct {
in []match.Matcher in []match.Matcher
exp match.Matcher exp match.Matcher
@ -96,8 +96,22 @@ func TestConvertMatchers(t *testing.T) {
Right: match.Any{}, Right: match.Any{},
}, },
}, },
{
[]match.Matcher{
match.Range{'a', 'c', true},
match.List{"zte", false},
match.Raw{"c"},
match.Single{},
},
match.Row{Matchers: match.Matchers{
match.Range{'a', 'c', true},
match.List{"zte", false},
match.Raw{"c"},
match.Single{},
}},
},
} { } {
act, err := convertMatchers(test.in) act, err := compileMatchers(test.in)
if err != nil { if err != nil {
t.Errorf("#%d convert matchers error: %s", id, err) t.Errorf("#%d convert matchers error: %s", id, err)
continue continue
@ -110,6 +124,58 @@ func TestConvertMatchers(t *testing.T) {
} }
} }
func TestConvertMatchers2(t *testing.T) {
for id, test := range []struct {
in, exp []match.Matcher
}{
{
[]match.Matcher{
match.Range{'a', 'c', true},
match.List{"zte", false},
match.Raw{"c"},
match.Single{},
match.Any{},
},
[]match.Matcher{
match.Row{Matchers: match.Matchers{
match.Range{'a', 'c', true},
match.List{"zte", false},
match.Raw{"c"},
match.Single{},
}},
match.Any{},
},
},
{
[]match.Matcher{
match.Range{'a', 'c', true},
match.List{"zte", false},
match.Raw{"c"},
match.Single{},
match.Any{},
match.Single{},
match.Single{},
match.Any{},
},
[]match.Matcher{
match.Row{Matchers: match.Matchers{
match.Range{'a', 'c', true},
match.List{"zte", false},
match.Raw{"c"},
match.Single{},
}},
match.Min{2},
},
},
} {
act := convertMatchers(test.in, nil)
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected convert matchers 2 result:\nact: %s;\nexp: %s", id, act, test.exp)
continue
}
}
}
func pattern(nodes ...node) *nodePattern { func pattern(nodes ...node) *nodePattern {
return &nodePattern{ return &nodePattern{
nodeImpl: nodeImpl{ nodeImpl: nodeImpl{
@ -183,21 +249,23 @@ func TestCompiler(t *testing.T) {
ast: pattern(&nodeAny{}, &nodeText{text: "abc"}, &nodeSingle{}), ast: pattern(&nodeAny{}, &nodeText{text: "abc"}, &nodeSingle{}),
sep: separators, sep: separators,
result: match.BTree{ result: match.BTree{
Left: match.Any{separators}, Left: match.Any{separators},
Value: match.Raw{"abc"}, Value: match.Row{Matchers: match.Matchers{
Right: match.Single{separators}, match.Raw{"abc"},
match.Single{separators},
}},
}, },
}, },
{ {
ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSingle{}), ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSingle{}),
sep: separators, sep: separators,
result: match.BTree{ result: match.BTree{
Left: match.BTree{ Left: match.Super{},
Left: match.Super{}, Value: match.Row{Matchers: match.Matchers{
Value: match.Single{separators}, match.Single{separators},
}, match.Raw{"abc"},
Value: match.Raw{"abc"}, match.Single{separators},
Right: match.Single{separators}, }},
}, },
}, },
{ {
@ -243,6 +311,20 @@ func TestCompiler(t *testing.T) {
}}, }},
}}, }},
}, },
{
ast: pattern(
&nodeRange{lo: 'a', hi: 'z'},
&nodeRange{lo: 'a', hi: 'x', not: true},
&nodeAny{},
),
result: match.BTree{
Value: match.Row{Matchers: match.Matchers{
match.Range{Lo: 'a', Hi: 'z'},
match.Range{Lo: 'a', Hi: 'x', Not: true},
}},
Right: match.Super{},
},
},
} { } {
prog, err := compile(test.ast, test.sep) prog, err := compile(test.ast, test.sep)
if err != nil { if err != nil {

View File

@ -16,6 +16,9 @@ const (
pattern_multiple = "https://*.google.*" pattern_multiple = "https://*.google.*"
fixture_multiple = "https://account.google.com" fixture_multiple = "https://account.google.com"
pattern_alternatives = "{https://*.google.*,*yahoo.*}"
fixture_alternatives = "http://yahoo.com"
pattern_prefix = "abc*" pattern_prefix = "abc*"
pattern_suffix = "*def" pattern_suffix = "*def"
pattern_prefix_suffix = "ab*ef" pattern_prefix_suffix = "ab*ef"
@ -39,7 +42,7 @@ func TestCompilePattern(t *testing.T) {
exp match.Matcher exp match.Matcher
}{ }{
// { // {
// pattern: "[!a]*****", // pattern: "{http://*yandex.ru,b}",
// exp: match.Raw{"t"}, // exp: match.Raw{"t"},
// }, // },
} { } {
@ -104,6 +107,8 @@ func TestIndexByteNonEscaped(t *testing.T) {
func TestGlob(t *testing.T) { func TestGlob(t *testing.T) {
for _, test := range []test{ for _, test := range []test{
glob(true, "* ?at * eyes", "my cat has very bright eyes"),
glob(true, "abc", "abc"), glob(true, "abc", "abc"),
glob(true, "a*c", "abc"), glob(true, "a*c", "abc"),
glob(true, "a*c", "a12345c"), glob(true, "a*c", "a12345c"),
@ -119,8 +124,6 @@ func TestGlob(t *testing.T) {
glob(true, `\*`, "*"), glob(true, `\*`, "*"),
glob(true, "**", "a.b.c", "."), glob(true, "**", "a.b.c", "."),
glob(true, "* ?at * eyes", "my cat has very bright eyes"),
glob(false, "?at", "at"), glob(false, "?at", "at"),
glob(false, "?at", "fat", "f"), glob(false, "?at", "fat", "f"),
glob(false, "a.*", "a.b.c", "."), glob(false, "a.*", "a.b.c", "."),
@ -138,15 +141,16 @@ func TestGlob(t *testing.T) {
glob(false, "*no*", "this is a test"), glob(false, "*no*", "this is a test"),
glob(true, "[!a]*", "this is a test3"), glob(true, "[!a]*", "this is a test3"),
// glob(true, "*abc", "abcabc"), glob(true, "*abc", "abcabc"),
glob(true, "**abc", "abcabc"), glob(true, "**abc", "abcabc"),
// glob(true, "???", "abc"), glob(true, "???", "abc"),
// glob(true, "?*?", "abc"), glob(true, "?*?", "abc"),
// glob(true, "?*?", "ac"), glob(true, "?*?", "ac"),
glob(true, pattern_all, fixture_all), glob(true, pattern_all, fixture_all),
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_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),
@ -172,6 +176,8 @@ func BenchmarkParse(b *testing.B) {
func BenchmarkAll(b *testing.B) { func BenchmarkAll(b *testing.B) {
m, _ := Compile(pattern_all) m, _ := Compile(pattern_all)
// fmt.Println("tree all:")
// fmt.Println(m)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_ = m.Match(fixture_all) _ = m.Match(fixture_all)
@ -185,6 +191,13 @@ func BenchmarkMultiple(b *testing.B) {
_ = m.Match(fixture_multiple) _ = m.Match(fixture_multiple)
} }
} }
func BenchmarkAlternatives(b *testing.B) {
m, _ := Compile(pattern_alternatives)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives)
}
}
func BenchmarkPlain(b *testing.B) { func BenchmarkPlain(b *testing.B) {
m, _ := Compile(pattern_plain) m, _ := Compile(pattern_plain)
@ -213,3 +226,11 @@ func BenchmarkPrefixSuffix(b *testing.B) {
_ = m.Match(fixture_prefix_suffix) _ = m.Match(fixture_prefix_suffix)
} }
} }
//BenchmarkParse-8 500000 2235 ns/op
//BenchmarkAll-8 20000000 73.1 ns/op
//BenchmarkMultiple-8 10000000 130 ns/op
//BenchmarkPlain-8 200000000 6.70 ns/op
//BenchmarkPrefix-8 200000000 8.36 ns/op
//BenchmarkSuffix-8 200000000 8.35 ns/op
//BenchmarkPrefixSuffix-8 100000000 13.6 ns/op

View File

@ -13,19 +13,15 @@ func (self Any) Match(s string) bool {
return strings.IndexAny(s, self.Separators) == -1 return strings.IndexAny(s, self.Separators) == -1
} }
func (self Any) Index(s string) (index, min, max int) { func (self Any) Index(s string) (index int, segments []int) {
index = -1 index = -1
for i, r := range s {
for i, r := range []rune(s) {
if strings.IndexRune(self.Separators, r) == -1 { if strings.IndexRune(self.Separators, r) == -1 {
if index == -1 { if index == -1 {
index = i index = i
} }
max++ segments = append(segments, i-index)
continue } else if index != -1 {
}
if index != -1 {
break break
} }
} }
@ -33,6 +29,10 @@ func (self Any) Index(s string) (index, min, max int) {
return return
} }
func (self Any) Len() int {
return -1
}
func (self Any) Kind() Kind { func (self Any) Kind() Kind {
return KindAny return KindAny
} }

View File

@ -8,8 +8,9 @@ type AnyOf struct {
Matchers Matchers Matchers Matchers
} }
func (self *AnyOf) Add(m Matcher) { func (self *AnyOf) Add(m Matcher) error {
self.Matchers = append(self.Matchers, m) self.Matchers = append(self.Matchers, m)
return nil
} }
func (self AnyOf) Match(s string) bool { func (self AnyOf) Match(s string) bool {
@ -22,6 +23,10 @@ func (self AnyOf) Match(s string) bool {
return false return false
} }
func (self AnyOf) Len() int {
return -1
}
func (self AnyOf) Kind() Kind { func (self AnyOf) Kind() Kind {
return KindAnyOf return KindAnyOf
} }

View File

@ -2,6 +2,7 @@ package match
import ( import (
"fmt" "fmt"
"unicode/utf8"
) )
type BTree struct { type BTree struct {
@ -13,51 +14,95 @@ func (self BTree) Kind() Kind {
return KindBTree return KindBTree
} }
func (self BTree) len() (l, v, r int, ok bool) {
v = self.Value.Len()
if self.Left != nil {
l = self.Left.Len()
}
if self.Right != nil {
r = self.Right.Len()
}
ok = l > -1 && v > -1 && r > -1
return
}
func (self BTree) Len() int {
l, v, r, ok := self.len()
if ok {
return l + v + r
}
return -1
}
func (self BTree) Match(s string) bool { func (self BTree) Match(s string) bool {
runes := []rune(s) inputLen := len(s)
inputLen := len(runes)
for offset := 0; offset < inputLen; { lLen, vLen, rLen, ok := self.len()
index, min, max := self.Value.Index(string(runes[offset:])) if ok && lLen+vLen+rLen > inputLen {
return false
}
var offset, limit int
if lLen >= 0 {
offset = lLen
}
if rLen >= 0 {
limit = inputLen - rLen
} else {
limit = inputLen
}
for offset < limit {
index, segments := self.Value.Index(s[offset:limit])
if index == -1 { if index == -1 {
return false return false
} }
for length := min; length <= max; length++ { l := string(s[:offset+index])
var left, right bool var left bool
if self.Left != nil {
left = self.Left.Match(l)
} else {
left = l == ""
}
l := string(runes[:offset+index]) if left {
if self.Left != nil { for i := len(segments) - 1; i >= 0; i-- {
left = self.Left.Match(l) length := segments[i]
} else {
left = l == ""
}
if !left { if rLen >= 0 && inputLen-(offset+index+length) != rLen {
break continue
} }
var r string var right bool
// if there is no string for the right branch
if inputLen <= offset+index+length {
r = ""
} else {
r = string(runes[offset+index+length:])
}
if self.Right != nil { var r string
right = self.Right.Match(r) // if there is no string for the right branch
} else { if inputLen <= offset+index+length {
right = r == "" r = ""
} } else {
r = s[offset+index+length:]
}
if left && right { if self.Right != nil {
return true right = self.Right.Match(r)
} else {
right = r == ""
}
if right {
return true
}
} }
} }
offset += index + 1 _, step := utf8.DecodeRuneInString(s[offset+index:])
offset += index + step
} }
return false return false

View File

@ -14,6 +14,10 @@ func (self Contains) Match(s string) bool {
return strings.Contains(s, self.Needle) != self.Not return strings.Contains(s, self.Needle) != self.Not
} }
func (self Contains) Len() int {
return -1
}
func (self Contains) Kind() Kind { func (self Contains) Kind() Kind {
return KindContains return KindContains
} }

View File

@ -8,8 +8,21 @@ type Every struct {
Matchers Matchers Matchers Matchers
} }
func (self *Every) Add(m Matcher) { func (self *Every) Add(m Matcher) error {
self.Matchers = append(self.Matchers, m) self.Matchers = append(self.Matchers, m)
return nil
}
func (self Every) Len() (l int) {
for _, m := range self.Matchers {
if ml := m.Len(); l > 0 {
l += ml
} else {
return -1
}
}
return
} }
func (self Every) Match(s string) bool { func (self Every) Match(s string) bool {

View File

@ -3,6 +3,7 @@ package match
import ( import (
"fmt" "fmt"
"strings" "strings"
"unicode/utf8"
) )
type List struct { type List struct {
@ -15,7 +16,7 @@ func (self List) Kind() Kind {
} }
func (self List) Match(s string) bool { func (self List) Match(s string) bool {
if len([]rune(s)) != 1 { if utf8.RuneCountInString(s) > 1 {
return false return false
} }
@ -24,14 +25,18 @@ func (self List) Match(s string) bool {
return inList == !self.Not return inList == !self.Not
} }
func (self List) Index(s string) (index, min, max int) { func (self List) Len() int {
for i, r := range []rune(s) { return 1
}
func (self List) Index(s string) (int, []int) {
for i, r := range s {
if self.Not == (strings.IndexRune(self.List, r) == -1) { if self.Not == (strings.IndexRune(self.List, r) == -1) {
return i, 1, 1 return i, []int{utf8.RuneLen(r)}
} }
} }
return -1, 0, 0 return -1, nil
} }
func (self List) String() string { func (self List) String() string {

View File

@ -29,10 +29,12 @@ const (
type Matcher interface { type Matcher interface {
Match(string) bool Match(string) bool
Len() int
} }
type Primitive interface { type Primitive interface {
Index(string) (int, int, int) Matcher
Index(string) (int, []int)
} }
type Matchers []Matcher type Matchers []Matcher

View File

@ -1,13 +1,20 @@
package match package match
import "fmt" import (
"fmt"
"unicode/utf8"
)
type Max struct { type Max struct {
Limit int Limit int
} }
func (self Max) Match(s string) bool { func (self Max) Match(s string) bool {
return len([]rune(s)) <= self.Limit return utf8.RuneCountInString(s) <= self.Limit
}
func (self Max) Len() int {
return -1
} }
func (self Max) Search(s string) (int, int, bool) { func (self Max) Search(s string) (int, int, bool) {

View File

@ -1,13 +1,20 @@
package match package match
import "fmt" import (
"fmt"
"unicode/utf8"
)
type Min struct { type Min struct {
Limit int Limit int
} }
func (self Min) Match(s string) bool { func (self Min) Match(s string) bool {
return len([]rune(s)) >= self.Limit return utf8.RuneCountInString(s) >= self.Limit
}
func (self Min) Len() int {
return -1
} }
func (self Min) Search(s string) (int, int, bool) { func (self Min) Search(s string) (int, int, bool) {

View File

@ -1,11 +1,10 @@
package match package match
import ( import (
"strings"
"fmt" "fmt"
"strings"
) )
type Prefix struct { type Prefix struct {
Prefix string Prefix string
} }
@ -14,6 +13,10 @@ func (self Prefix) Kind() Kind {
return KindPrefix return KindPrefix
} }
func (self Prefix) Len() int {
return -1
}
func (self Prefix) Search(s string) (i int, l int, ok bool) { func (self Prefix) Search(s string) (i int, l int, ok bool) {
if self.Match(s) { if self.Match(s) {
return 0, len(s), true return 0, len(s), true
@ -26,7 +29,6 @@ func (self Prefix) Match(s string) bool {
return strings.HasPrefix(s, self.Prefix) return strings.HasPrefix(s, self.Prefix)
} }
func (self Prefix) String() string { func (self Prefix) String() string {
return fmt.Sprintf("[prefix:%s]", self.Prefix) return fmt.Sprintf("[prefix:%s]", self.Prefix)
} }

View File

@ -13,6 +13,10 @@ func (self PrefixSuffix) Kind() Kind {
return KindPrefixSuffix return KindPrefixSuffix
} }
func (self PrefixSuffix) Len() int {
return -1
}
func (self PrefixSuffix) Search(s string) (i int, l int, ok bool) { func (self PrefixSuffix) Search(s string) (i int, l int, ok bool) {
if self.Match(s) { if self.Match(s) {
return 0, len(s), true return 0, len(s), true

View File

@ -2,6 +2,7 @@ package match
import ( import (
"fmt" "fmt"
"unicode/utf8"
) )
type Range struct { type Range struct {
@ -13,28 +14,31 @@ func (self Range) Kind() Kind {
return KindRange return KindRange
} }
func (self Range) Match(s string) bool { func (self Range) Len() int {
r := []rune(s) return 1
}
if len(r) != 1 { func (self Range) Match(s string) bool {
r, w := utf8.DecodeRuneInString(s)
if len(s) > w {
return false return false
} }
inRange := r[0] >= self.Lo && r[0] <= self.Hi inRange := r >= self.Lo && r <= self.Hi
return inRange == !self.Not return inRange == !self.Not
} }
func (self Range) Index(s string) (index, min, max int) { func (self Range) Index(s string) (int, []int) {
for i, r := range []rune(s) { for i, r := range s {
if self.Not != (r >= self.Lo && r <= self.Hi) { if self.Not != (r >= self.Lo && r <= self.Hi) {
return i, 1, 1 return i, []int{utf8.RuneLen(r)}
} }
} }
return -1, 0, 0 return -1, nil
} }
func (self Range) String() string { func (self Range) String() string {
return fmt.Sprintf("[range_between:%s-%s(%t)]", self.Lo, self.Hi, self.Not) return fmt.Sprintf("[range:%s-%s(%t)]", string(self.Lo), string(self.Hi), self.Not)
} }

View File

@ -14,18 +14,21 @@ func (self Raw) Match(s string) bool {
return self.Str == s return self.Str == s
} }
func (self Raw) Len() int {
return len(self.Str)
}
func (self Raw) Kind() Kind { func (self Raw) Kind() Kind {
return KindRaw return KindRaw
} }
func (self Raw) Index(s string) (index, min, max int) { func (self Raw) Index(s string) (index int, segments []int) {
index = strings.Index(s, self.Str) index = strings.Index(s, self.Str)
if index == -1 { if index == -1 {
return return
} }
min = len(self.Str) segments = []int{len(self.Str)}
max = min
return return
} }

66
match/row.go Normal file
View File

@ -0,0 +1,66 @@
package match
import (
"fmt"
)
type Row struct {
Matchers Matchers
len int
}
func (self *Row) Add(m Matcher) error {
if l := m.Len(); l == -1 {
return fmt.Errorf("matcher should have fixed length")
}
self.Matchers = append(self.Matchers, m)
return nil
}
func (self Row) Match(s string) bool {
if len(s) < self.Len() {
return false
}
var idx int
for _, m := range self.Matchers {
l := m.Len()
if !m.Match(s[idx : idx+l]) {
return false
}
idx += l
}
return true
}
func (self Row) Len() (l int) {
if self.len == 0 {
for _, m := range self.Matchers {
self.len += m.Len()
}
}
return self.len
}
func (self Row) Index(s string) (int, []int) {
for i := range s {
sub := s[i:]
if self.Match(sub) {
return i, []int{self.Len()}
}
}
return -1, nil
}
func (self Row) Kind() Kind {
return KindMin
}
func (self Row) String() string {
return fmt.Sprintf("[row:%s]", self.Matchers)
}

View File

@ -3,6 +3,7 @@ package match
import ( import (
"fmt" "fmt"
"strings" "strings"
"unicode/utf8"
) )
// single represents ? // single represents ?
@ -11,17 +12,21 @@ type Single struct {
} }
func (self Single) Match(s string) bool { func (self Single) Match(s string) bool {
return len([]rune(s)) == 1 && strings.IndexAny(s, self.Separators) == -1 return utf8.RuneCountInString(s) == 1 && strings.IndexAny(s, self.Separators) == -1
} }
func (self Single) Index(s string) (index, min, max int) { func (self Single) Len() int {
for i, c := range []rune(s) { return 1
if strings.IndexRune(self.Separators, c) == -1 { }
return i, 1, 1
func (self Single) Index(s string) (int, []int) {
for i, r := range s {
if strings.IndexRune(self.Separators, r) == -1 {
return i, []int{utf8.RuneLen(r)}
} }
} }
return -1, 0, 0 return -1, nil
} }
func (self Single) Kind() Kind { func (self Single) Kind() Kind {

View File

@ -1,14 +1,10 @@
package match package match
import ( import (
"strings"
"fmt" "fmt"
"strings"
) )
type Suffix struct { type Suffix struct {
Suffix string Suffix string
} }
@ -17,6 +13,10 @@ func (self Suffix) Kind() Kind {
return KindSuffix return KindSuffix
} }
func (self Suffix) Len() int {
return -1
}
func (self Suffix) Search(s string) (i int, l int, ok bool) { func (self Suffix) Search(s string) (i int, l int, ok bool) {
if self.Match(s) { if self.Match(s) {
return 0, len(s), true return 0, len(s), true
@ -32,5 +32,3 @@ func (self Suffix) Match(s string) bool {
func (self Suffix) String() string { func (self Suffix) String() string {
return fmt.Sprintf("[suffix:%s]", self.Suffix) return fmt.Sprintf("[suffix:%s]", self.Suffix)
} }

View File

@ -2,6 +2,7 @@ package match
import ( import (
"fmt" "fmt"
"unicode/utf8"
) )
type Super struct{} type Super struct{}
@ -10,8 +11,19 @@ func (self Super) Match(s string) bool {
return true return true
} }
func (self Super) Index(s string) (index, min, max int) { func (self Super) Len() int {
return 0, 0, len([]rune(s)) return -1
}
func (self Super) Index(s string) (int, []int) {
segments := make([]int, utf8.RuneCountInString(s))
for i := range s {
segments = append(segments, i)
}
segments = append(segments, len(s))
return 0, segments
} }
func (self Super) Kind() Kind { func (self Super) Kind() Kind {

View File

@ -3,6 +3,7 @@ package glob
import ( import (
"errors" "errors"
"fmt" "fmt"
"unicode/utf8"
) )
type node interface { type node interface {
@ -172,23 +173,23 @@ func parserRange(tree *tree, lexer *lexer) (parseFn, error) {
not = true not = true
case item_range_lo: case item_range_lo:
r := []rune(item.s) r, w := utf8.DecodeRuneInString(item.s)
if len(r) != 1 { if len(item.s) > w {
return nil, fmt.Errorf("unexpected length of lo character") return nil, fmt.Errorf("unexpected length of lo character")
} }
lo = r[0] lo = r
case item_range_between: case item_range_between:
// //
case item_range_hi: case item_range_hi:
r := []rune(item.s) r, w := utf8.DecodeRuneInString(item.s)
if len(r) != 1 { if len(item.s) > w {
return nil, fmt.Errorf("unexpected length of hi character") return nil, fmt.Errorf("unexpected length of lo character")
} }
hi = r[0] hi = r
if hi < lo { if hi < lo {
return nil, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo)) return nil, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo))

View File

@ -1,6 +1,5 @@
package glob package glob
import ( import (
"strings" "strings"
) )
@ -18,6 +17,6 @@ func indexByteNonEscaped(source string, needle, escape byte, shift int) int {
return i + shift return i + shift
} }
sh := i+1 sh := i + 1
return indexByteNonEscaped(source[sh:], needle, escape, sh) return indexByteNonEscaped(source[sh:], needle, escape, sh)
} }