forked from mirror/glob
wip
This commit is contained in:
parent
abc7140723
commit
4a52abd846
|
@ -16,7 +16,6 @@ func Compile(tree *ast.Node, sep []rune) (match.Matcher, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,25 +31,29 @@ func compileNodes(ns []*ast.Node, sep []rune) ([]match.Matcher, error) {
|
||||||
return matchers, nil
|
return matchers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) {
|
func compile(node *ast.Node, sep []rune) (m match.Matcher, err error) {
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
debug.Enter()
|
debug.EnterPrefix("compiler: compiling %s", node)
|
||||||
debug.Logf("compiler: compiling %s", tree)
|
|
||||||
defer func() {
|
defer func() {
|
||||||
debug.Logf("compiler: result %s", m)
|
if err != nil {
|
||||||
debug.Leave()
|
debug.Logf("->! %v", err)
|
||||||
|
} else {
|
||||||
|
debug.Logf("-> %s", m)
|
||||||
|
}
|
||||||
|
debug.LeavePrefix()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go)
|
// todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go)
|
||||||
if n := ast.Minimize(tree); n != nil {
|
if n := ast.Minimize(node); n != nil {
|
||||||
|
debug.Logf("minimized tree -> %s", node, n)
|
||||||
r, err := compile(n, sep)
|
r, err := compile(n, sep)
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Logf("compiler: compile minimized tree failed: %v", err)
|
debug.Logf("compiler: compile minimized tree failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
debug.Logf("compiler: minimized tree")
|
debug.Logf("compiler: minimized tree")
|
||||||
debug.Logf("compiler: \t%s", tree)
|
debug.Logf("compiler: \t%s", node)
|
||||||
debug.Logf("compiler: \t%s", n)
|
debug.Logf("compiler: \t%s", n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,19 +62,19 @@ func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch tree.Kind {
|
switch node.Kind {
|
||||||
case ast.KindAnyOf:
|
case ast.KindAnyOf:
|
||||||
matchers, err := compileNodes(tree.Children, sep)
|
matchers, err := compileNodes(node.Children, sep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return match.NewAnyOf(matchers...), nil
|
return match.NewAnyOf(matchers...), nil
|
||||||
|
|
||||||
case ast.KindPattern:
|
case ast.KindPattern:
|
||||||
if len(tree.Children) == 0 {
|
if len(node.Children) == 0 {
|
||||||
return match.NewNothing(), nil
|
return match.NewNothing(), nil
|
||||||
}
|
}
|
||||||
matchers, err := compileNodes(tree.Children, sep)
|
matchers, err := compileNodes(node.Children, sep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -93,15 +96,15 @@ func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) {
|
||||||
m = match.NewNothing()
|
m = match.NewNothing()
|
||||||
|
|
||||||
case ast.KindList:
|
case ast.KindList:
|
||||||
l := tree.Value.(ast.List)
|
l := node.Value.(ast.List)
|
||||||
m = match.NewList([]rune(l.Chars), l.Not)
|
m = match.NewList([]rune(l.Chars), l.Not)
|
||||||
|
|
||||||
case ast.KindRange:
|
case ast.KindRange:
|
||||||
r := tree.Value.(ast.Range)
|
r := node.Value.(ast.Range)
|
||||||
m = match.NewRange(r.Lo, r.Hi, r.Not)
|
m = match.NewRange(r.Lo, r.Hi, r.Not)
|
||||||
|
|
||||||
case ast.KindText:
|
case ast.KindText:
|
||||||
t := tree.Value.(ast.Text)
|
t := node.Value.(ast.Text)
|
||||||
m = match.NewText(t.Text)
|
m = match.NewText(t.Text)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -11,44 +11,51 @@ import (
|
||||||
var separators = []rune{'.'}
|
var separators = []rune{'.'}
|
||||||
|
|
||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
ast *ast.Node
|
ast *ast.Node
|
||||||
result match.Matcher
|
exp match.Matcher
|
||||||
sep []rune
|
sep []rune
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
// #0
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
||||||
),
|
),
|
||||||
result: match.NewText("abc"),
|
exp: match.NewText("abc"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #1
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewAny(separators),
|
exp: match.NewAny(separators),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #2
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
),
|
),
|
||||||
result: match.NewSuper(),
|
exp: match.NewSuper(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #3
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindSuper, nil),
|
ast.NewNode(ast.KindSuper, nil),
|
||||||
),
|
),
|
||||||
result: match.NewSuper(),
|
exp: match.NewSuper(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #4
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewSingle(separators),
|
exp: match.NewSingle(separators),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #5
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindRange, ast.Range{
|
ast.NewNode(ast.KindRange, ast.Range{
|
||||||
Lo: 'a',
|
Lo: 'a',
|
||||||
|
@ -56,18 +63,20 @@ func TestCompiler(t *testing.T) {
|
||||||
Not: true,
|
Not: true,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
result: match.NewRange('a', 'z', true),
|
exp: match.NewRange('a', 'z', true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #6
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindList, ast.List{
|
ast.NewNode(ast.KindList, ast.List{
|
||||||
Chars: "abc",
|
Chars: "abc",
|
||||||
Not: true,
|
Not: true,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
result: match.NewList([]rune{'a', 'b', 'c'}, true),
|
exp: match.NewList([]rune{'a', 'b', 'c'}, true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #7
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
|
@ -75,28 +84,30 @@ func TestCompiler(t *testing.T) {
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewEveryOf([]match.Matcher{
|
exp: match.NewEveryOf([]match.Matcher{
|
||||||
match.NewMin(3),
|
match.NewMin(3),
|
||||||
match.NewAny(separators),
|
match.NewAny(separators),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #8
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
),
|
),
|
||||||
result: match.NewMin(3),
|
exp: match.NewMin(3),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #9
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewRow([]match.MatchIndexSizer{
|
match.NewRow([]match.MatchIndexSizer{
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
match.NewSingle(separators),
|
match.NewSingle(separators),
|
||||||
|
@ -106,6 +117,7 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #10
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindText, ast.Text{"/"}),
|
ast.NewNode(ast.KindText, ast.Text{"/"}),
|
||||||
ast.NewNode(ast.KindAnyOf, nil,
|
ast.NewNode(ast.KindAnyOf, nil,
|
||||||
|
@ -115,7 +127,7 @@ func TestCompiler(t *testing.T) {
|
||||||
ast.NewNode(ast.KindSuper, nil),
|
ast.NewNode(ast.KindSuper, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewText("/"),
|
match.NewText("/"),
|
||||||
nil,
|
nil,
|
||||||
match.NewTree(
|
match.NewTree(
|
||||||
|
@ -129,6 +141,7 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #11
|
||||||
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),
|
||||||
|
@ -136,7 +149,7 @@ func TestCompiler(t *testing.T) {
|
||||||
ast.NewNode(ast.KindSingle, nil),
|
ast.NewNode(ast.KindSingle, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewRow([]match.MatchIndexSizer{
|
match.NewRow([]match.MatchIndexSizer{
|
||||||
match.NewSingle(separators),
|
match.NewSingle(separators),
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
|
@ -147,28 +160,32 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #12
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
||||||
),
|
),
|
||||||
result: match.NewSuffix("abc"),
|
exp: match.NewSuffix("abc"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #13
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
),
|
),
|
||||||
result: match.NewPrefix("abc"),
|
exp: match.NewPrefix("abc"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #14
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindText, ast.Text{"def"}),
|
ast.NewNode(ast.KindText, ast.Text{"def"}),
|
||||||
),
|
),
|
||||||
result: match.NewPrefixSuffix("abc", "def"),
|
exp: match.NewPrefixSuffix("abc", "def"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #15
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
|
@ -177,9 +194,10 @@ 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"),
|
exp: match.NewContains("abc"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #16
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
|
@ -189,14 +207,15 @@ func TestCompiler(t *testing.T) {
|
||||||
ast.NewNode(ast.KindAny, nil),
|
ast.NewNode(ast.KindAny, nil),
|
||||||
),
|
),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
match.NewAny(separators),
|
match.NewAny(separators),
|
||||||
match.NewAny(separators),
|
match.NewAny(separators),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// TODO: THIS!
|
// #17
|
||||||
|
// pattern: "**?abc**?"
|
||||||
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),
|
||||||
|
@ -204,19 +223,21 @@ 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.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
match.NewMin(1),
|
match.NewMin(1),
|
||||||
match.NewMin(1),
|
match.NewMin(1),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #18
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
ast.NewNode(ast.KindText, ast.Text{"abc"}),
|
||||||
),
|
),
|
||||||
result: match.NewText("abc"),
|
exp: match.NewText("abc"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #19
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAnyOf, nil,
|
ast.NewNode(ast.KindAnyOf, nil,
|
||||||
ast.NewNode(ast.KindPattern, nil,
|
ast.NewNode(ast.KindPattern, nil,
|
||||||
|
@ -228,9 +249,10 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
result: match.NewText("abc"),
|
exp: match.NewText("abc"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #20
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAnyOf, nil,
|
ast.NewNode(ast.KindAnyOf, nil,
|
||||||
ast.NewNode(ast.KindPattern, nil,
|
ast.NewNode(ast.KindPattern, nil,
|
||||||
|
@ -249,7 +271,7 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
result: match.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
nil,
|
nil,
|
||||||
match.NewAnyOf(
|
match.NewAnyOf(
|
||||||
|
@ -260,12 +282,13 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #21
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
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: 'z'}),
|
||||||
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.NewTree(
|
exp: match.NewTree(
|
||||||
match.NewRow([]match.MatchIndexSizer{
|
match.NewRow([]match.MatchIndexSizer{
|
||||||
match.NewRange('a', 'z', false),
|
match.NewRange('a', 'z', false),
|
||||||
match.NewRange('a', 'x', true),
|
match.NewRange('a', 'x', true),
|
||||||
|
@ -275,6 +298,7 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// #22
|
||||||
ast: ast.NewNode(ast.KindPattern, nil,
|
ast: ast.NewNode(ast.KindPattern, nil,
|
||||||
ast.NewNode(ast.KindAnyOf, nil,
|
ast.NewNode(ast.KindAnyOf, nil,
|
||||||
ast.NewNode(ast.KindPattern, nil,
|
ast.NewNode(ast.KindPattern, nil,
|
||||||
|
@ -289,7 +313,7 @@ func TestCompiler(t *testing.T) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
result: match.NewRow([]match.MatchIndexSizer{
|
exp: match.NewRow([]match.MatchIndexSizer{
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
match.MustIndexedSizedAnyOf(
|
match.MustIndexedSizedAnyOf(
|
||||||
match.NewList([]rune{'a', 'b', 'c'}, false),
|
match.NewList([]rune{'a', 'b', 'c'}, false),
|
||||||
|
@ -299,15 +323,19 @@ func TestCompiler(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
m, err := Compile(test.ast, test.sep)
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
act, err := Compile(test.ast, test.sep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("compilation error: %s", err)
|
t.Fatalf("compilation error: %s", err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
if !reflect.DeepEqual(act, test.exp) {
|
||||||
if !reflect.DeepEqual(m, test.result) {
|
t.Errorf(
|
||||||
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)))
|
"Compile():\nact: %#v\nexp: %#v\n\ngraphviz:\n%s\n%s\n",
|
||||||
continue
|
act, test.exp,
|
||||||
|
match.Graphviz("act", act.(match.Matcher)),
|
||||||
|
match.Graphviz("exp", test.exp.(match.Matcher)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,14 @@ package debug
|
||||||
|
|
||||||
const Enabled = false
|
const Enabled = false
|
||||||
|
|
||||||
func Logf(_ string, _ ...interface{}) {}
|
func Logf(string, ...interface{}) {}
|
||||||
func Enter() {}
|
func Enter() {}
|
||||||
func Leave() {}
|
func Leave() {}
|
||||||
|
func EnterPrefix(string, ...interface{}) {}
|
||||||
|
func LeavePrefix() {}
|
||||||
|
func Indexing(n, s string) func(int, []int) {
|
||||||
|
panic("must never be called")
|
||||||
|
}
|
||||||
|
func Matching(n, s string) func(bool) {
|
||||||
|
panic("must never be called")
|
||||||
|
}
|
||||||
|
|
|
@ -6,27 +6,59 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const Enabled = true
|
const Enabled = true
|
||||||
|
|
||||||
var i = new(int32)
|
var (
|
||||||
|
i = 0
|
||||||
|
prefix = map[int]string{}
|
||||||
|
)
|
||||||
|
|
||||||
func Logf(f string, args ...interface{}) {
|
func Logf(f string, args ...interface{}) {
|
||||||
n := int(atomic.LoadInt32(i))
|
if f != "" && prefix[i] != "" {
|
||||||
|
f = ": " + f
|
||||||
|
}
|
||||||
fmt.Fprint(os.Stderr,
|
fmt.Fprint(os.Stderr,
|
||||||
strings.Repeat(" ", n),
|
strings.Repeat(" ", i),
|
||||||
fmt.Sprintf("(%d) ", n),
|
fmt.Sprintf("(%d) ", i),
|
||||||
|
prefix[i],
|
||||||
fmt.Sprintf(f, args...),
|
fmt.Sprintf(f, args...),
|
||||||
"\n",
|
"\n",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Indexing(name, s string) func(int, []int) {
|
||||||
|
EnterPrefix("%s: index: %q", name, s)
|
||||||
|
return func(index int, segments []int) {
|
||||||
|
Logf("-> %d, %v", index, segments)
|
||||||
|
LeavePrefix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Matching(name, s string) func(bool) {
|
||||||
|
EnterPrefix("%s: match %q", name, s)
|
||||||
|
return func(ok bool) {
|
||||||
|
Logf("-> %t", ok)
|
||||||
|
LeavePrefix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnterPrefix(s string, args ...interface{}) {
|
||||||
|
Enter()
|
||||||
|
prefix[i] = fmt.Sprintf(s, args...)
|
||||||
|
Logf("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LeavePrefix() {
|
||||||
|
prefix[i] = ""
|
||||||
|
Leave()
|
||||||
|
}
|
||||||
|
|
||||||
func Enter() {
|
func Enter() {
|
||||||
atomic.AddInt32(i, 1)
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
func Leave() {
|
func Leave() {
|
||||||
atomic.AddInt32(i, -1)
|
i--
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package match
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob/internal/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AnyOf struct {
|
type AnyOf struct {
|
||||||
|
@ -41,7 +43,11 @@ func MustIndexedSizedAnyOf(ms ...Matcher) MatchIndexSizer {
|
||||||
return NewAnyOf(ms...).(MatchIndexSizer)
|
return NewAnyOf(ms...).(MatchIndexSizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AnyOf) Match(s string) bool {
|
func (a AnyOf) Match(s string) (ok bool) {
|
||||||
|
if debug.Enabled {
|
||||||
|
done := debug.Matching("any_of", s)
|
||||||
|
defer func() { done(ok) }()
|
||||||
|
}
|
||||||
for _, m := range a.ms {
|
for _, m := range a.ms {
|
||||||
if m.Match(s) {
|
if m.Match(s) {
|
||||||
return true
|
return true
|
||||||
|
@ -54,8 +60,10 @@ func (a AnyOf) MinLen() (n int) {
|
||||||
return a.min
|
return a.min
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AnyOf) Content() []Matcher {
|
func (a AnyOf) Content(cb func(Matcher)) {
|
||||||
return a.ms
|
for _, m := range a.ms {
|
||||||
|
cb(m)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AnyOf) String() string {
|
func (a AnyOf) String() string {
|
||||||
|
@ -67,10 +75,17 @@ type IndexedAnyOf struct {
|
||||||
ms []MatchIndexer
|
ms []MatchIndexer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a IndexedAnyOf) Index(s string) (int, []int) {
|
func (a IndexedAnyOf) Index(s string) (index int, segments []int) {
|
||||||
index := -1
|
if debug.Enabled {
|
||||||
segments := acquireSegments(len(s))
|
done := debug.Indexing("any_of", s)
|
||||||
|
defer func() { done(index, segments) }()
|
||||||
|
}
|
||||||
|
index = -1
|
||||||
|
segments = acquireSegments(len(s))
|
||||||
for _, m := range a.ms {
|
for _, m := range a.ms {
|
||||||
|
if debug.Enabled {
|
||||||
|
debug.Logf("indexing: any_of: trying %s", m)
|
||||||
|
}
|
||||||
i, seg := m.Index(s)
|
i, seg := m.Index(s)
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -41,7 +41,8 @@ func TestIndexedAnyOf(t *testing.T) {
|
||||||
[]int{1},
|
[]int{1},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
a := NewAnyOf(test.matchers...).(IndexedAnyOf)
|
t.Run("", func(t *testing.T) {
|
||||||
|
a := NewAnyOf(test.matchers...).(Indexer)
|
||||||
index, segments := a.Index(test.fixture)
|
index, segments := a.Index(test.fixture)
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
|
@ -49,5 +50,6 @@ func TestIndexedAnyOf(t *testing.T) {
|
||||||
if !reflect.DeepEqual(segments, test.segments) {
|
if !reflect.DeepEqual(segments, test.segments) {
|
||||||
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
|
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,20 +54,12 @@ func graphviz(m Matcher, id string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
case Container:
|
case Container:
|
||||||
fmt.Fprintf(buf, `"%s"[label="*AnyOf"];`, id)
|
fmt.Fprintf(buf, `"%s"[label="Container(%T)"];`, id, m)
|
||||||
for _, m := range v.Content() {
|
v.Content(func(m Matcher) {
|
||||||
rnd := rand.Int63()
|
rnd := rand.Int63()
|
||||||
fmt.Fprintf(buf, graphviz(m, fmt.Sprintf("%x", rnd)))
|
fmt.Fprintf(buf, graphviz(m, fmt.Sprintf("%x", rnd)))
|
||||||
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
||||||
}
|
})
|
||||||
|
|
||||||
case EveryOf:
|
|
||||||
fmt.Fprintf(buf, `"%s"[label="EveryOf"];`, id)
|
|
||||||
for _, m := range v.ms {
|
|
||||||
rnd := rand.Int63()
|
|
||||||
fmt.Fprintf(buf, graphviz(m, fmt.Sprintf("%x", rnd)))
|
|
||||||
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, m)
|
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, m)
|
||||||
|
|
|
@ -10,7 +10,10 @@ type EveryOf struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEveryOf(ms []Matcher) Matcher {
|
func NewEveryOf(ms []Matcher) Matcher {
|
||||||
e := EveryOf{ms, minLen(ms)}
|
e := EveryOf{
|
||||||
|
ms: ms,
|
||||||
|
min: maxLen(ms),
|
||||||
|
}
|
||||||
if mis, ok := MatchIndexers(ms); ok {
|
if mis, ok := MatchIndexers(ms); ok {
|
||||||
return IndexedEveryOf{e, mis}
|
return IndexedEveryOf{e, mis}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +33,12 @@ func (e EveryOf) Match(s string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e EveryOf) Content(cb func(Matcher)) {
|
||||||
|
for _, m := range e.ms {
|
||||||
|
cb(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (e EveryOf) String() string {
|
func (e EveryOf) String() string {
|
||||||
return fmt.Sprintf("<every_of:[%s]>", e.ms)
|
return fmt.Sprintf("<every_of:[%s]>", e.ms)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ type MatchIndexSizer interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Container interface {
|
type Container interface {
|
||||||
Content() []Matcher
|
Content(func(Matcher))
|
||||||
}
|
}
|
||||||
|
|
||||||
func MatchIndexers(ms []Matcher) ([]MatchIndexer, bool) {
|
func MatchIndexers(ms []Matcher) ([]MatchIndexer, bool) {
|
||||||
|
|
|
@ -3,23 +3,27 @@ package match
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob/internal/debug"
|
||||||
"github.com/gobwas/glob/util/runes"
|
"github.com/gobwas/glob/util/runes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Optimize(m Matcher) Matcher {
|
func Optimize(m Matcher) (opt Matcher) {
|
||||||
|
if debug.Enabled {
|
||||||
|
defer func() {
|
||||||
|
a := fmt.Sprintf("%s", m)
|
||||||
|
b := fmt.Sprintf("%s", opt)
|
||||||
|
if a != b {
|
||||||
|
debug.EnterPrefix("optimized %s: -> %s", a, b)
|
||||||
|
debug.LeavePrefix()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
switch v := m.(type) {
|
switch v := m.(type) {
|
||||||
case Any:
|
case Any:
|
||||||
if len(v.sep) == 0 {
|
if len(v.sep) == 0 {
|
||||||
return NewSuper()
|
return NewSuper()
|
||||||
}
|
}
|
||||||
|
|
||||||
case Container:
|
|
||||||
ms := v.Content()
|
|
||||||
if len(ms) == 1 {
|
|
||||||
return ms[0]
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
|
|
||||||
case List:
|
case List:
|
||||||
if v.not == false && len(v.rs) == 1 {
|
if v.not == false && len(v.rs) == 1 {
|
||||||
return NewText(string(v.rs))
|
return NewText(string(v.rs))
|
||||||
|
@ -73,12 +77,33 @@ func Optimize(m Matcher) Matcher {
|
||||||
case leftNil && rightAny:
|
case leftNil && rightAny:
|
||||||
return NewPrefixAny(txt.s, ra.sep)
|
return NewPrefixAny(txt.s, ra.sep)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Container:
|
||||||
|
var (
|
||||||
|
first Matcher
|
||||||
|
n int
|
||||||
|
)
|
||||||
|
v.Content(func(m Matcher) {
|
||||||
|
first = m
|
||||||
|
n++
|
||||||
|
})
|
||||||
|
if n == 1 {
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func Compile(ms []Matcher) (Matcher, error) {
|
func Compile(ms []Matcher) (m Matcher, err error) {
|
||||||
|
if debug.Enabled {
|
||||||
|
debug.EnterPrefix("compiling %s", ms)
|
||||||
|
defer func() {
|
||||||
|
debug.Logf("-> %s, %v", m, err)
|
||||||
|
debug.LeavePrefix()
|
||||||
|
}()
|
||||||
|
}
|
||||||
if len(ms) == 0 {
|
if len(ms) == 0 {
|
||||||
return nil, fmt.Errorf("compile error: need at least one matcher")
|
return nil, fmt.Errorf("compile error: need at least one matcher")
|
||||||
}
|
}
|
||||||
|
@ -90,33 +115,40 @@ func Compile(ms []Matcher) (Matcher, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
idx = -1
|
x = -1
|
||||||
maxLen = -2
|
max = -2
|
||||||
|
|
||||||
|
wantText bool
|
||||||
indexer MatchIndexer
|
indexer MatchIndexer
|
||||||
)
|
)
|
||||||
for i, m := range ms {
|
for i, m := range ms {
|
||||||
mi, ok := m.(MatchIndexer)
|
mx, ok := m.(MatchIndexer)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n := m.MinLen(); n > maxLen {
|
_, isText := m.(Text)
|
||||||
maxLen = n
|
if wantText && !isText {
|
||||||
idx = i
|
continue
|
||||||
indexer = mi
|
}
|
||||||
|
n := m.MinLen()
|
||||||
|
if (!wantText && isText) || n > max {
|
||||||
|
max = n
|
||||||
|
x = i
|
||||||
|
indexer = mx
|
||||||
|
wantText = isText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if indexer == nil {
|
if indexer == nil {
|
||||||
return nil, fmt.Errorf("can not index on matchers")
|
return nil, fmt.Errorf("can not index on matchers")
|
||||||
}
|
}
|
||||||
|
|
||||||
left := ms[:idx]
|
left := ms[:x]
|
||||||
var right []Matcher
|
var right []Matcher
|
||||||
if len(ms) > idx+1 {
|
if len(ms) > x+1 {
|
||||||
right = ms[idx+1:]
|
right = ms[x+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
var l, r Matcher
|
var l, r Matcher
|
||||||
var err error
|
|
||||||
if len(left) > 0 {
|
if len(left) > 0 {
|
||||||
l, err = Compile(left)
|
l, err = Compile(left)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -239,40 +271,139 @@ func glueMatchersAsEvery(ms []Matcher) Matcher {
|
||||||
return NewEveryOf(every)
|
return NewEveryOf(every)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Minimize(ms []Matcher) []Matcher {
|
type result struct {
|
||||||
var (
|
ms []Matcher
|
||||||
result Matcher
|
matchers int
|
||||||
left int
|
minLen int
|
||||||
right int
|
nesting int
|
||||||
count int
|
}
|
||||||
|
|
||||||
|
func compareResult(a, b result) int {
|
||||||
|
if x := len(a.ms) - len(b.ms); x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
if x := a.matchers - b.matchers; x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
if x := b.minLen - a.minLen; x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
if x := a.nesting - b.nesting; x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collapse(ms []Matcher, x Matcher, i, j int) (cp []Matcher) {
|
||||||
|
cp = make([]Matcher, len(ms)-(j-i)+1)
|
||||||
|
copy(cp[0:i], ms[0:i])
|
||||||
|
copy(cp[i+1:], ms[j:])
|
||||||
|
cp[i] = x
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchersCount(ms []Matcher) (n int) {
|
||||||
|
n = len(ms)
|
||||||
|
for _, m := range ms {
|
||||||
|
n += countNestedMatchers(m)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func countNestedMatchers(m Matcher) (n int) {
|
||||||
|
if c, _ := m.(Container); c != nil {
|
||||||
|
c.Content(func(m Matcher) {
|
||||||
|
n += 1 + countNestedMatchers(m)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func nestingDepth(m Matcher) (depth int) {
|
||||||
|
c, ok := m.(Container)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var max int
|
||||||
|
c.Content(func(m Matcher) {
|
||||||
|
if d := nestingDepth(m); d > max {
|
||||||
|
max = d
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return max + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxMinLen(ms []Matcher) (max int) {
|
||||||
|
for _, m := range ms {
|
||||||
|
if n := m.MinLen(); n > max {
|
||||||
|
max = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxNestingDepth(ms []Matcher) (max int) {
|
||||||
|
for _, m := range ms {
|
||||||
|
if n := nestingDepth(m); n > max {
|
||||||
|
max = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func minimize(ms []Matcher, i, j int, best *result) *result {
|
||||||
|
if j > len(ms) {
|
||||||
|
j = 0
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i > len(ms)-2 {
|
||||||
|
return best
|
||||||
|
}
|
||||||
|
if j == 0 {
|
||||||
|
j = i + 2
|
||||||
|
}
|
||||||
|
if g := glueMatchers(ms[i:j]); g != nil {
|
||||||
|
cp := collapse(ms, g, i, j)
|
||||||
|
r := result{
|
||||||
|
ms: cp,
|
||||||
|
matchers: matchersCount(cp),
|
||||||
|
minLen: maxMinLen(cp),
|
||||||
|
nesting: maxNestingDepth(cp),
|
||||||
|
}
|
||||||
|
if debug.Enabled {
|
||||||
|
debug.EnterPrefix(
|
||||||
|
"intermediate: %s (matchers:%d, minlen:%d, nesting:%d)",
|
||||||
|
cp, r.matchers, r.minLen, r.nesting,
|
||||||
)
|
)
|
||||||
for l := 0; l < len(ms); l++ {
|
|
||||||
for r := len(ms); r > l; r-- {
|
|
||||||
if glued := glueMatchers(ms[l:r]); glued != nil {
|
|
||||||
var swap bool
|
|
||||||
if result == nil {
|
|
||||||
swap = true
|
|
||||||
} else {
|
|
||||||
swap = glued.MinLen() > result.MinLen() || count < r-l
|
|
||||||
}
|
}
|
||||||
if swap {
|
if best == nil {
|
||||||
result = glued
|
best = new(result)
|
||||||
left = l
|
}
|
||||||
right = r
|
if best.ms == nil || compareResult(r, *best) < 0 {
|
||||||
count = r - l
|
*best = r
|
||||||
|
if debug.Enabled {
|
||||||
|
debug.Logf("new best result")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
best = minimize(cp, 0, 0, best)
|
||||||
|
if debug.Enabled {
|
||||||
|
debug.LeavePrefix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if result == nil {
|
return minimize(ms, i, j+1, best)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Minimize(ms []Matcher) (m []Matcher) {
|
||||||
|
if debug.Enabled {
|
||||||
|
debug.EnterPrefix("minimizing %s", ms)
|
||||||
|
defer func() {
|
||||||
|
debug.Logf("-> %s", m)
|
||||||
|
debug.LeavePrefix()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
best := minimize(ms, 0, 0, nil)
|
||||||
|
if best == nil {
|
||||||
return ms
|
return ms
|
||||||
}
|
}
|
||||||
next := append(append([]Matcher{}, ms[:left]...), result)
|
return best.ms
|
||||||
if right < len(ms) {
|
|
||||||
next = append(next, ms[right:]...)
|
|
||||||
}
|
|
||||||
if len(next) == len(ms) {
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
return Minimize(next)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ package match
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gobwas/glob/match"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var separators = []rune{'.'}
|
||||||
|
|
||||||
func TestCompile(t *testing.T) {
|
func TestCompile(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for _, test := range []struct {
|
||||||
in []Matcher
|
in []Matcher
|
||||||
exp Matcher
|
exp Matcher
|
||||||
}{
|
}{
|
||||||
|
@ -26,7 +26,7 @@ func TestCompile(t *testing.T) {
|
||||||
},
|
},
|
||||||
NewEveryOf([]Matcher{
|
NewEveryOf([]Matcher{
|
||||||
NewMin(1),
|
NewMin(1),
|
||||||
NewContains(string(separators)),
|
NewAny(separators),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,7 @@ func TestCompile(t *testing.T) {
|
||||||
},
|
},
|
||||||
NewEveryOf([]Matcher{
|
NewEveryOf([]Matcher{
|
||||||
NewMin(1),
|
NewMin(1),
|
||||||
NewContains("a"),
|
NewAny([]rune{'a'}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -58,7 +58,7 @@ func TestCompile(t *testing.T) {
|
||||||
},
|
},
|
||||||
NewTree(
|
NewTree(
|
||||||
NewText("c"),
|
NewText("c"),
|
||||||
NewBTree(
|
NewTree(
|
||||||
NewSingle(separators),
|
NewSingle(separators),
|
||||||
NewSuper(),
|
NewSuper(),
|
||||||
nil,
|
nil,
|
||||||
|
@ -93,71 +93,73 @@ func TestCompile(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
act, err := Compile(test.in)
|
act, err := Compile(test.in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("#%d compile matchers error: %s", id, err)
|
t.Fatalf("Compile() error: %s", err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(act, test.exp) {
|
if !reflect.DeepEqual(act, test.exp) {
|
||||||
t.Errorf("#%d unexpected compile matchers result:\nact: %#v;\nexp: %#v", id, act, test.exp)
|
t.Errorf(
|
||||||
continue
|
"Compile():\nact: %#v;\nexp: %#v;\ngraphviz:\n%s\n%s",
|
||||||
|
act, test.exp,
|
||||||
|
Graphviz("act", act), Graphviz("exp", test.exp),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMinimize(t *testing.T) {
|
func TestMinimize(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for _, test := range []struct {
|
||||||
in, exp []match.Matcher
|
in, exp []Matcher
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
[]match.Matcher{
|
in: []Matcher{
|
||||||
match.NewRange('a', 'c', true),
|
NewRange('a', 'c', true),
|
||||||
match.NewList([]rune{'z', 't', 'e'}, false),
|
NewList([]rune{'z', 't', 'e'}, false),
|
||||||
match.NewText("c"),
|
NewText("c"),
|
||||||
match.NewSingle(nil),
|
NewSingle(nil),
|
||||||
match.NewAny(nil),
|
NewAny(nil),
|
||||||
},
|
},
|
||||||
[]match.Matcher{
|
exp: []Matcher{
|
||||||
match.NewRow(
|
NewRow([]MatchIndexSizer{
|
||||||
4,
|
NewRange('a', 'c', true),
|
||||||
[]match.Matcher{
|
NewList([]rune{'z', 't', 'e'}, false),
|
||||||
match.NewRange('a', 'c', true),
|
NewText("c"),
|
||||||
match.NewList([]rune{'z', 't', 'e'}, false),
|
}),
|
||||||
match.NewText("c"),
|
NewMin(1),
|
||||||
match.NewSingle(nil),
|
|
||||||
}...,
|
|
||||||
),
|
|
||||||
match.NewAny(nil),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]match.Matcher{
|
in: []Matcher{
|
||||||
match.NewRange('a', 'c', true),
|
NewRange('a', 'c', true),
|
||||||
match.NewList([]rune{'z', 't', 'e'}, false),
|
NewList([]rune{'z', 't', 'e'}, false),
|
||||||
match.NewText("c"),
|
NewText("c"),
|
||||||
match.NewSingle(nil),
|
NewSingle(nil),
|
||||||
match.NewAny(nil),
|
NewAny(nil),
|
||||||
match.NewSingle(nil),
|
NewSingle(nil),
|
||||||
match.NewSingle(nil),
|
NewSingle(nil),
|
||||||
match.NewAny(nil),
|
NewAny(nil),
|
||||||
},
|
},
|
||||||
[]match.Matcher{
|
exp: []Matcher{
|
||||||
match.NewRow(
|
NewRow([]MatchIndexSizer{
|
||||||
3,
|
NewRange('a', 'c', true),
|
||||||
match.Matchers{
|
NewList([]rune{'z', 't', 'e'}, false),
|
||||||
match.NewRange('a', 'c', true),
|
NewText("c"),
|
||||||
match.NewList([]rune{'z', 't', 'e'}, false),
|
}),
|
||||||
match.NewText("c"),
|
NewMin(3),
|
||||||
}...,
|
|
||||||
),
|
|
||||||
match.NewMin(3),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
act := minimizeMatchers(test.in)
|
t.Run("", func(t *testing.T) {
|
||||||
|
act := Minimize(test.in)
|
||||||
|
|
||||||
if !reflect.DeepEqual(act, test.exp) {
|
if !reflect.DeepEqual(act, test.exp) {
|
||||||
t.Errorf("#%d unexpected convert matchers 2 result:\nact: %#v\nexp: %#v", id, act, test.exp)
|
t.Errorf(
|
||||||
continue
|
"Minimize():\nact: %#v;\nexp: %#v",
|
||||||
|
act, test.exp,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package match
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob/internal/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Range struct {
|
type Range struct {
|
||||||
|
@ -22,7 +24,11 @@ func (self Range) RunesCount() int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Range) Match(s string) bool {
|
func (self Range) Match(s string) (ok bool) {
|
||||||
|
if debug.Enabled {
|
||||||
|
done := debug.Matching("range", s)
|
||||||
|
defer func() { done(ok) }()
|
||||||
|
}
|
||||||
r, w := utf8.DecodeRuneInString(s)
|
r, w := utf8.DecodeRuneInString(s)
|
||||||
if len(s) > w {
|
if len(s) > w {
|
||||||
return false
|
return false
|
||||||
|
@ -33,7 +39,11 @@ func (self Range) Match(s string) bool {
|
||||||
return inRange == !self.Not
|
return inRange == !self.Not
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Range) Index(s string) (int, []int) {
|
func (self Range) Index(s string) (index int, segments []int) {
|
||||||
|
if debug.Enabled {
|
||||||
|
done := debug.Indexing("range", s)
|
||||||
|
defer func() { done(index, segments) }()
|
||||||
|
}
|
||||||
for i, r := range 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, segmentsByRuneLength[utf8.RuneLen(r)]
|
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||||
|
|
25
match/row.go
25
match/row.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob/internal/debug"
|
||||||
"github.com/gobwas/glob/util/runes"
|
"github.com/gobwas/glob/util/runes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,11 @@ func NewRow(ms []MatchIndexSizer) Row {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Row) Match(s string) bool {
|
func (r Row) Match(s string) (ok bool) {
|
||||||
|
if debug.Enabled {
|
||||||
|
done := debug.Matching("row", s)
|
||||||
|
defer func() { done(ok) }()
|
||||||
|
}
|
||||||
if !runes.ExactlyRunesCount(s, r.runes) {
|
if !runes.ExactlyRunesCount(s, r.runes) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -40,8 +45,14 @@ func (r Row) RunesCount() int {
|
||||||
return r.runes
|
return r.runes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Row) Index(s string) (int, []int) {
|
func (r Row) Index(s string) (index int, segments []int) {
|
||||||
for j := 0; j < len(s)-r.runes; {
|
if debug.Enabled {
|
||||||
|
done := debug.Indexing("row", s)
|
||||||
|
debug.Logf("row: %d vs %d", len(s), r.runes)
|
||||||
|
defer func() { done(index, segments) }()
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j <= len(s)-r.runes; { // NOTE: using len() here to avoid counting runes.
|
||||||
i, _ := r.ms[0].Index(s[j:])
|
i, _ := r.ms[0].Index(s[j:])
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
|
@ -55,8 +66,14 @@ func (r Row) Index(s string) (int, []int) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Row) Content(cb func(Matcher)) {
|
||||||
|
for _, m := range r.ms {
|
||||||
|
cb(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r Row) String() string {
|
func (r Row) String() string {
|
||||||
return fmt.Sprintf("<row_%d:[%s]>", r.runes, r.ms)
|
return fmt.Sprintf("<row_%d:%s>", r.runes, r.ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Row) matchAll(s string) bool {
|
func (r Row) matchAll(s string) bool {
|
||||||
|
|
|
@ -42,5 +42,8 @@ func (s Single) Index(v string) (int, []int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Single) String() string {
|
func (s Single) String() string {
|
||||||
|
if len(s.sep) == 0 {
|
||||||
|
return "<single>"
|
||||||
|
}
|
||||||
return fmt.Sprintf("<single:![%s]>", string(s.sep))
|
return fmt.Sprintf("<single:![%s]>", string(s.sep))
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,29 +74,32 @@ func (t Tree) MinLen() int {
|
||||||
return t.minLen
|
return t.minLen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Tree) Content(cb func(Matcher)) {
|
||||||
|
if t.left != nil {
|
||||||
|
cb(t.left)
|
||||||
|
}
|
||||||
|
cb(t.value)
|
||||||
|
if t.right != nil {
|
||||||
|
cb(t.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t Tree) Match(s string) (ok bool) {
|
func (t Tree) Match(s string) (ok bool) {
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
debug.Enter()
|
done := debug.Matching("tree", s)
|
||||||
debug.Logf("tree: matching %q: %v", s, t)
|
defer func() { done(ok) }()
|
||||||
defer func(s string) {
|
|
||||||
debug.Logf("tree: result: %q -> %v", s, ok)
|
|
||||||
debug.Leave()
|
|
||||||
}(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset, limit := t.offsetLimit(s)
|
offset, limit := t.offsetLimit(s)
|
||||||
q := s[offset : len(s)-limit]
|
q := s[offset : len(s)-limit]
|
||||||
|
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
debug.Logf("tree: offset/limit: %d/%d %q of %q", offset, limit, q, s)
|
debug.Logf("offset/limit: %d/%d: %q of %q", offset, limit, q, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
for len(q) >= t.vrunes {
|
for len(q) >= t.vrunes {
|
||||||
// search for matching part in substring
|
// search for matching part in substring
|
||||||
index, segments := t.value.Index(q)
|
index, segments := t.value.Index(q)
|
||||||
if debug.Enabled {
|
|
||||||
debug.Logf("tree: index #%d %q (%v)", index, q, t.value)
|
|
||||||
}
|
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
releaseSegments(segments)
|
releaseSegments(segments)
|
||||||
return false
|
return false
|
||||||
|
@ -110,7 +113,7 @@ func (t Tree) Match(s string) (ok bool) {
|
||||||
left = l == ""
|
left = l == ""
|
||||||
}
|
}
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
debug.Logf("tree: left %q %v", l, left)
|
debug.Logf("left %q: -> %t", l, left)
|
||||||
}
|
}
|
||||||
if left {
|
if left {
|
||||||
for _, seg := range segments {
|
for _, seg := range segments {
|
||||||
|
@ -124,7 +127,7 @@ func (t Tree) Match(s string) (ok bool) {
|
||||||
right = r == ""
|
right = r == ""
|
||||||
}
|
}
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
debug.Logf("tree: right %q %v", r, right)
|
debug.Logf("right %q: -> %t", r, right)
|
||||||
}
|
}
|
||||||
if right {
|
if right {
|
||||||
releaseSegments(segments)
|
releaseSegments(segments)
|
||||||
|
|
|
@ -9,3 +9,13 @@ func minLen(ms []Matcher) (min int) {
|
||||||
}
|
}
|
||||||
return min
|
return min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maxLen(ms []Matcher) (max int) {
|
||||||
|
for i, m := range ms {
|
||||||
|
n := m.MinLen()
|
||||||
|
if i == 0 || n > max {
|
||||||
|
max = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue