Tune, new feature test

This commit is contained in:
s.kamardin 2016-01-14 21:32:02 +03:00
parent c65eb46cf3
commit d2a191e0f0
13 changed files with 142 additions and 117 deletions

3
.gitignore vendored
View File

@ -3,3 +3,6 @@ glob.iml
*.cpu *.cpu
*.mem *.mem
*.test *.test
*.dot
*.png
*.svg

View File

@ -3,7 +3,7 @@ sudo: false
language: go language: go
go: go:
- 1.5.1 - 1.5.3
script: script:
- go test -v ./... - go test -v ./...

View File

@ -17,7 +17,7 @@ func optimize(matcher match.Matcher) match.Matcher {
m.Left = optimize(m.Left) m.Left = optimize(m.Left)
m.Right = optimize(m.Right) m.Right = optimize(m.Right)
r, ok := m.Value.(match.Raw) r, ok := m.Value.(match.Text)
if !ok { if !ok {
return m return m
} }
@ -26,7 +26,7 @@ func optimize(matcher match.Matcher) match.Matcher {
rightNil := m.Right == nil rightNil := m.Right == nil
if leftNil && rightNil { if leftNil && rightNil {
return match.NewRaw(r.Str) return match.NewText(r.Str)
} }
_, leftSuper := m.Left.(match.Super) _, leftSuper := m.Left.(match.Super)
@ -325,7 +325,7 @@ func do(node node, s string) (m match.Matcher, err error) {
m = match.Single{s} m = match.Single{s}
case *nodeText: case *nodeText:
m = match.NewRaw(n.text) m = match.NewText(n.text)
default: default:
return nil, fmt.Errorf("could not compile tree: unknown node type") return nil, fmt.Errorf("could not compile tree: unknown node type")
@ -424,7 +424,7 @@ func do2(node node, s string) ([]match.Matcher, error) {
result = append(result, match.Single{s}) result = append(result, match.Single{s})
case *nodeText: case *nodeText:
result = append(result, match.NewRaw(n.text)) result = append(result, match.NewText(n.text))
default: default:
return nil, fmt.Errorf("could not compile tree: unknown node type") return nil, fmt.Errorf("could not compile tree: unknown node type")

View File

@ -74,10 +74,10 @@ func TestCompileMatchers(t *testing.T) {
[]match.Matcher{ []match.Matcher{
match.Super{}, match.Super{},
match.Single{separators}, match.Single{separators},
match.Raw{"c", 1}, match.NewText("c"),
}, },
match.NewBTree( match.NewBTree(
match.Raw{"c", 1}, match.NewText("c"),
match.NewBTree( match.NewBTree(
match.Single{separators}, match.Single{separators},
match.Super{}, match.Super{},
@ -89,11 +89,11 @@ func TestCompileMatchers(t *testing.T) {
{ {
[]match.Matcher{ []match.Matcher{
match.Any{}, match.Any{},
match.Raw{"c", 1}, match.NewText("c"),
match.Any{}, match.Any{},
}, },
match.NewBTree( match.NewBTree(
match.Raw{"c", 1}, match.NewText("c"),
match.Any{}, match.Any{},
match.Any{}, match.Any{},
), ),
@ -102,17 +102,17 @@ func TestCompileMatchers(t *testing.T) {
[]match.Matcher{ []match.Matcher{
match.Range{'a', 'c', true}, match.Range{'a', 'c', true},
match.List{"zte", false}, match.List{"zte", false},
match.Raw{"c", 1}, match.NewText("c"),
match.Single{}, match.Single{},
}, },
match.Row{ match.Row{
Matchers: match.Matchers{ Matchers: match.Matchers{
match.Range{'a', 'c', true}, match.Range{'a', 'c', true},
match.List{"zte", false}, match.List{"zte", false},
match.Raw{"c", 1}, match.NewText("c"),
match.Single{}, match.Single{},
}, },
Length: 4, RunesLength: 4,
}, },
}, },
} { } {
@ -137,7 +137,7 @@ func TestConvertMatchers(t *testing.T) {
[]match.Matcher{ []match.Matcher{
match.Range{'a', 'c', true}, match.Range{'a', 'c', true},
match.List{"zte", false}, match.List{"zte", false},
match.Raw{"c", 1}, match.NewText("c"),
match.Single{}, match.Single{},
match.Any{}, match.Any{},
}, },
@ -146,10 +146,10 @@ func TestConvertMatchers(t *testing.T) {
Matchers: match.Matchers{ Matchers: match.Matchers{
match.Range{'a', 'c', true}, match.Range{'a', 'c', true},
match.List{"zte", false}, match.List{"zte", false},
match.Raw{"c", 1}, match.NewText("c"),
match.Single{}, match.Single{},
}, },
Length: 4, RunesLength: 4,
}, },
match.Any{}, match.Any{},
}, },
@ -158,7 +158,7 @@ func TestConvertMatchers(t *testing.T) {
[]match.Matcher{ []match.Matcher{
match.Range{'a', 'c', true}, match.Range{'a', 'c', true},
match.List{"zte", false}, match.List{"zte", false},
match.Raw{"c", 1}, match.NewText("c"),
match.Single{}, match.Single{},
match.Any{}, match.Any{},
match.Single{}, match.Single{},
@ -170,9 +170,9 @@ func TestConvertMatchers(t *testing.T) {
Matchers: match.Matchers{ Matchers: match.Matchers{
match.Range{'a', 'c', true}, match.Range{'a', 'c', true},
match.List{"zte", false}, match.List{"zte", false},
match.Raw{"c", 1}, match.NewText("c"),
}, },
Length: 3, RunesLength: 3,
}, },
match.Min{3}, match.Min{3},
}, },
@ -208,7 +208,7 @@ func TestCompiler(t *testing.T) {
}{ }{
{ {
ast: pattern(&nodeText{text: "abc"}), ast: pattern(&nodeText{text: "abc"}),
result: match.Raw{"abc", 3}, result: match.NewText("abc"),
}, },
{ {
ast: pattern(&nodeAny{}), ast: pattern(&nodeAny{}),
@ -261,10 +261,10 @@ func TestCompiler(t *testing.T) {
result: match.NewBTree( result: match.NewBTree(
match.Row{ match.Row{
Matchers: match.Matchers{ Matchers: match.Matchers{
match.Raw{"abc", 3}, match.NewText("abc"),
match.Single{separators}, match.Single{separators},
}, },
Length: 4, RunesLength: 4,
}, },
match.Any{separators}, match.Any{separators},
nil, nil,
@ -277,10 +277,10 @@ func TestCompiler(t *testing.T) {
match.Row{ match.Row{
Matchers: match.Matchers{ Matchers: match.Matchers{
match.Single{separators}, match.Single{separators},
match.Raw{"abc", 3}, match.NewText("abc"),
match.Single{separators}, match.Single{separators},
}, },
Length: 5, RunesLength: 5,
}, },
match.Super{}, match.Super{},
nil, nil,
@ -306,7 +306,7 @@ func TestCompiler(t *testing.T) {
ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}), ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}),
sep: separators, sep: separators,
result: match.NewBTree( result: match.NewBTree(
match.Raw{"abc", 3}, match.NewText("abc"),
match.Any{separators}, match.Any{separators},
match.Any{separators}, match.Any{separators},
), ),
@ -314,7 +314,7 @@ func TestCompiler(t *testing.T) {
{ {
ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSuper{}, &nodeSingle{}), ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSuper{}, &nodeSingle{}),
result: match.NewBTree( result: match.NewBTree(
match.Raw{"abc", 3}, match.NewText("abc"),
match.Min{1}, match.Min{1},
match.Min{1}, match.Min{1},
), ),
@ -322,14 +322,14 @@ func TestCompiler(t *testing.T) {
{ {
ast: pattern(anyOf(&nodeText{text: "abc"})), ast: pattern(anyOf(&nodeText{text: "abc"})),
result: match.AnyOf{match.Matchers{ result: match.AnyOf{match.Matchers{
match.Raw{"abc", 3}, 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.AnyOf{match.Matchers{
match.AnyOf{match.Matchers{ match.AnyOf{match.Matchers{
match.Raw{"abc", 3}, match.NewText("abc"),
}}, }},
}}, }},
}, },
@ -345,7 +345,7 @@ func TestCompiler(t *testing.T) {
match.Range{Lo: 'a', Hi: 'z'}, match.Range{Lo: 'a', Hi: 'z'},
match.Range{Lo: 'a', Hi: 'x', Not: true}, match.Range{Lo: 'a', Hi: 'x', Not: true},
}, },
Length: 2, RunesLength: 2,
}, },
nil, nil,
match.Super{}, match.Super{},

View File

@ -19,7 +19,7 @@ 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.*}" pattern_alternatives = "{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}"
fixture_alternatives = "http://yahoo.com" fixture_alternatives = "http://yahoo.com"
pattern_prefix = "abc*" pattern_prefix = "abc*"
@ -89,6 +89,14 @@ func TestCompilePattern(t *testing.T) {
// pattern: "abc*??def", // pattern: "abc*??def",
// exp: match.Raw{"t"}, // exp: match.Raw{"t"},
// }, // },
{
pattern: "{abc[abc]ghi,abc[def]ghi}",
exp: match.NewBTree(
match.AnyOf{match.Matchers{match.List{"abc", false}, match.List{"qwe", false}}},
match.NewText("abc"),
match.NewText("ghi"),
),
},
} { } {
glob, err := Compile(test.pattern, test.sep) glob, err := Compile(test.pattern, test.sep)
if err != nil { if err != nil {

View File

@ -15,8 +15,8 @@ func TestAnyOfIndex(t *testing.T) {
{ {
Matchers{ Matchers{
Any{}, Any{},
Raw{"b"}, Text{"b"},
Raw{"c"}, Text{"c"},
}, },
"abc", "abc",
0, 0,

View File

@ -6,9 +6,13 @@ import (
) )
type BTree struct { type BTree struct {
Value, Left, Right Matcher Value Matcher
VLen, LLen, RLen int Left Matcher
Length int Right Matcher
ValueLengthRunes int
LeftLengthRunes int
RightLengthRunes int
LengthRunes int
} }
func NewBTree(Value, Left, Right Matcher) (tree BTree) { func NewBTree(Value, Left, Right Matcher) (tree BTree) {
@ -17,33 +21,33 @@ func NewBTree(Value, Left, Right Matcher) (tree BTree) {
tree.Right = Right tree.Right = Right
lenOk := true lenOk := true
if tree.VLen = Value.Len(); tree.VLen == -1 { if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 {
lenOk = false lenOk = false
} }
if Left != nil { if Left != nil {
if tree.LLen = Left.Len(); tree.LLen == -1 { if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 {
lenOk = false lenOk = false
} }
} }
if Right != nil { if Right != nil {
if tree.RLen = Right.Len(); tree.RLen == -1 { if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 {
lenOk = false lenOk = false
} }
} }
if lenOk { if lenOk {
tree.Length = tree.LLen + tree.VLen + tree.RLen tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes
} else { } else {
tree.Length = -1 tree.LengthRunes = -1
} }
return tree return tree
} }
func (self BTree) Len() int { func (self BTree) Len() int {
return self.Length return self.LengthRunes
} }
// todo? // todo?
@ -54,27 +58,33 @@ func (self BTree) Index(s string) (int, []int) {
func (self BTree) Match(s string) bool { func (self BTree) Match(s string) bool {
inputLen := len(s) inputLen := len(s)
if self.Length != -1 && self.Length > inputLen { // self.Length, self.RLen and self.LLen are values meaning the length of runes for each part
// here we manipulating byte length for better optimizations
// but these checks still works, cause minLen of 1-rune string is 1 byte.
if self.LengthRunes != -1 && self.LengthRunes > inputLen {
return false return false
} }
// try to cut unnecessary parts
// by knowledge of length of right and left part
var offset, limit int var offset, limit int
if self.LLen >= 0 { if self.LeftLengthRunes >= 0 {
offset = self.LLen offset = self.LeftLengthRunes
} }
if self.RLen >= 0 { if self.RightLengthRunes >= 0 {
limit = inputLen - self.RLen limit = inputLen - self.RightLengthRunes
} else { } else {
limit = inputLen limit = inputLen
} }
for offset < limit { for offset < limit {
// search for matching part in substring
index, segments := self.Value.Index(s[offset:limit]) index, segments := self.Value.Index(s[offset:limit])
if index == -1 { if index == -1 {
return false return false
} }
l := string(s[:offset+index]) l := s[:offset+index]
var left bool var left bool
if self.Left != nil { if self.Left != nil {
left = self.Left.Match(l) left = self.Left.Match(l)
@ -86,12 +96,7 @@ func (self BTree) Match(s string) bool {
for i := len(segments) - 1; i >= 0; i-- { for i := len(segments) - 1; i >= 0; i-- {
length := segments[i] length := segments[i]
if self.RLen >= 0 && inputLen-(offset+index+length) != self.RLen {
continue
}
var right bool var right bool
var r string var r string
// if there is no string for the right branch // if there is no string for the right branch
if inputLen <= offset+index+length { if inputLen <= offset+index+length {

View File

@ -11,17 +11,17 @@ func TestBTree(t *testing.T) {
exp bool exp bool
}{ }{
{ {
BTree{Value: Raw{"abc"}, Left: Super{}, Right: Super{}}, BTree{Value: Text{"abc"}, Left: Super{}, Right: Super{}},
"abc", "abc",
true, true,
}, },
{ {
BTree{Value: Raw{"a"}, Left: Single{}, Right: Single{}}, BTree{Value: Text{"a"}, Left: Single{}, Right: Single{}},
"aaa", "aaa",
true, true,
}, },
{ {
BTree{Value: Raw{"b"}, Left: Single{}}, BTree{Value: Text{"b"}, Left: Single{}},
"bbb", "bbb",
false, false,
}, },
@ -31,7 +31,7 @@ func TestBTree(t *testing.T) {
Left: Super{}, Left: Super{},
Value: Single{}, Value: Single{},
}, },
Value: Raw{"c"}, Value: Text{"c"},
}, },
"abc", "abc",
true, true,

View File

@ -15,8 +15,8 @@ func TestEveryOfIndex(t *testing.T) {
{ {
Matchers{ Matchers{
Any{}, Any{},
Raw{"b"}, Text{"b"},
Raw{"c"}, Text{"c"},
}, },
"abc", "abc",
-1, -1,

View File

@ -12,6 +12,13 @@ type List struct {
} }
func (self List) Match(s string) bool { func (self List) Match(s string) bool {
// if s 100% have two symbols
// _, w := utf8.DecodeRuneInString(s)
// if len(s) > w {
if len(s) > 4 {
return false
}
inList := strings.Index(self.List, s) != -1 inList := strings.Index(self.List, s) != -1
return inList == !self.Not return inList == !self.Not
} }

View File

@ -1,43 +0,0 @@
package match
import (
"fmt"
"strings"
"unicode/utf8"
)
// raw represents raw string to match
type Raw struct {
Str string
Length int
}
func NewRaw(s string) Raw {
return Raw{
Str: s,
Length: utf8.RuneCountInString(s),
}
}
func (self Raw) Match(s string) bool {
return self.Str == s
}
func (self Raw) Len() int {
return self.Length
}
func (self Raw) Index(s string) (index int, segments []int) {
index = strings.Index(s, self.Str)
if index == -1 {
return
}
segments = []int{self.Length}
return
}
func (self Raw) String() string {
return fmt.Sprintf("<raw:%s>", self.Str)
}

View File

@ -7,7 +7,7 @@ import (
type Row struct { type Row struct {
Matchers Matchers Matchers Matchers
Length int RunesLength int
} }
func (self Row) matchAll(s string) bool { func (self Row) matchAll(s string) bool {
@ -25,7 +25,7 @@ func (self Row) matchAll(s string) bool {
} }
func (self Row) Match(s string) bool { func (self Row) Match(s string) bool {
if utf8.RuneCountInString(s) < self.Length { if utf8.RuneCountInString(s) < self.RunesLength {
return false return false
} }
@ -33,23 +33,23 @@ func (self Row) Match(s string) bool {
} }
func (self Row) Len() (l int) { func (self Row) Len() (l int) {
return self.Length return self.RunesLength
} }
func (self Row) Index(s string) (int, []int) { func (self Row) Index(s string) (int, []int) {
l := utf8.RuneCountInString(s) l := utf8.RuneCountInString(s)
if l < self.Length { if l < self.RunesLength {
return -1, nil return -1, nil
} }
for i := range s { for i := range s {
sub := s[i:] sub := s[i:]
if self.matchAll(sub) { if self.matchAll(sub) {
return i, []int{self.Length} return i, []int{self.RunesLength}
} }
l -= 1 l -= 1
if l < self.Length { if l < self.RunesLength {
return -1, nil return -1, nil
} }
} }
@ -58,5 +58,5 @@ func (self Row) Index(s string) (int, []int) {
} }
func (self Row) String() string { func (self Row) String() string {
return fmt.Sprintf("<row_%d:[%s]>", self.Length, self.Matchers) return fmt.Sprintf("<row_%d:[%s]>", self.RunesLength, self.Matchers)
} }

45
match/text.go Normal file
View File

@ -0,0 +1,45 @@
package match
import (
"fmt"
"strings"
"unicode/utf8"
)
// raw represents raw string to match
type Text struct {
Str string
RunesLength int
BytesLength int
}
func NewText(s string) Text {
return Text{
Str: s,
RunesLength: utf8.RuneCountInString(s),
BytesLength: len(s),
}
}
func (self Text) Match(s string) bool {
return self.Str == s
}
func (self Text) Len() int {
return self.RunesLength
}
func (self Text) Index(s string) (index int, segments []int) {
index = strings.Index(s, self.Str)
if index == -1 {
return
}
segments = []int{self.BytesLength}
return
}
func (self Text) String() string {
return fmt.Sprintf("<text:%s>", self.Str)
}