glob/compiler.go

439 lines
7.5 KiB
Go

package glob
import (
"fmt"
"github.com/gobwas/glob/match"
)
func optimize(matcher match.Matcher) match.Matcher {
switch m := matcher.(type) {
case match.Any:
if m.Separators == "" {
return match.Super{}
}
case match.BTree:
m.Left = optimize(m.Left)
m.Right = optimize(m.Right)
r, ok := m.Value.(match.Raw)
if !ok {
return m
}
leftNil := m.Left == nil
rightNil := m.Right == nil
if leftNil && rightNil {
return match.Raw{r.Str}
}
_, leftSuper := m.Left.(match.Super)
lp, leftPrefix := m.Left.(match.Prefix)
_, rightSuper := m.Right.(match.Super)
rs, rightSuffix := m.Right.(match.Suffix)
if leftSuper && rightSuper {
return match.Contains{r.Str, false}
}
if leftSuper && rightNil {
return match.Suffix{r.Str}
}
if rightSuper && leftNil {
return match.Prefix{r.Str}
}
if leftNil && rightSuffix {
return match.PrefixSuffix{Prefix: r.Str, Suffix: rs.Suffix}
// return match.EveryOf{match.Matchers{match.Prefix{r.Str}, rs}}
}
if rightNil && leftPrefix {
return match.PrefixSuffix{Prefix: lp.Prefix, Suffix: r.Str}
// return match.EveryOf{match.Matchers{lp, match.Suffix{r.Str}}}
}
return m
}
return 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) {
case 0:
return nil
case 1:
return matchers[0]
}
var (
hasAny bool
hasSuper bool
hasSingle bool
min int
separator string
)
for i, matcher := range matchers {
var sep string
switch m := matcher.(type) {
case match.Super:
sep = ""
hasSuper = true
case match.Any:
sep = m.Separators
hasAny = true
case match.Single:
sep = m.Separators
hasSingle = true
min++
case match.List:
if !m.Not {
return nil
}
sep = m.List
hasSingle = true
min++
default:
return nil
}
// initialize
if i == 0 {
separator = sep
}
if sep == separator {
continue
}
return nil
}
if hasSuper && !hasAny && !hasSingle {
return match.Super{}
}
if hasAny && !hasSuper && !hasSingle {
return match.Any{separator}
}
if (hasAny || hasSuper) && min > 0 && separator == "" {
return match.Min{min}
}
every := match.EveryOf{}
if min > 0 {
every.Add(match.Min{min})
if !hasAny && !hasSuper {
every.Add(match.Max{min})
}
}
if separator != "" {
every.Add(match.Contains{separator, true})
}
return every
}
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 {
return m, nil
}
var (
val match.Matcher
idx int
)
maxLen := -1
for i, matcher := range matchers {
l := matcher.Len()
if l >= maxLen {
maxLen = l
idx = i
val = matcher
}
}
if val == nil {
return nil, fmt.Errorf("could not convert matchers %s: need at least one matcher", match.Matchers(matchers))
}
left := matchers[:idx]
var right []match.Matcher
if len(matchers) > idx+1 {
right = matchers[idx+1:]
}
tree := match.BTree{Value: val}
if len(left) > 0 {
l, err := compileMatchers(left)
if err != nil {
return nil, err
}
tree.Left = l
}
if len(right) > 0 {
r, err := compileMatchers(right)
if err != nil {
return nil, err
}
tree.Right = r
}
return tree, nil
}
func do(node node, s string) (m match.Matcher, err error) {
switch n := node.(type) {
case *nodePattern, *nodeAnyOf:
var matchers []match.Matcher
for _, desc := range node.children() {
m, err := do(desc, s)
if err != nil {
return nil, err
}
matchers = append(matchers, optimize(m))
}
if _, ok := node.(*nodeAnyOf); ok {
m = match.AnyOf{matchers}
} else {
m, err = compileMatchers(convertMatchers(matchers, nil))
if err != nil {
return nil, err
}
}
case *nodeList:
m = match.List{n.chars, n.not}
case *nodeRange:
m = match.Range{n.lo, n.hi, n.not}
case *nodeAny:
m = match.Any{s}
case *nodeSuper:
m = match.Super{}
case *nodeSingle:
m = match.Single{s}
case *nodeText:
m = match.Raw{n.text}
default:
return nil, fmt.Errorf("could not compile tree: unknown node type")
}
return optimize(m), nil
}
func do2(node node, s string) ([]match.Matcher, error) {
var result []match.Matcher
switch n := node.(type) {
case *nodePattern:
ways := [][]match.Matcher{[]match.Matcher{}}
for _, desc := range node.children() {
variants, err := do2(desc, s)
if err != nil {
return nil, err
}
fmt.Println("variants pat", variants)
for i, l := 0, len(ways); i < l; i++ {
for i := 0; i < len(variants); i++ {
o := optimize(variants[i])
if i == len(variants)-1 {
ways[i] = append(ways[i], o)
} else {
var w []match.Matcher
copy(w, ways[i])
ways = append(ways, append(w, o))
}
}
}
fmt.Println("ways pat", ways)
}
for _, matchers := range ways {
c, err := compileMatchers(convertMatchers(matchers, nil))
if err != nil {
return nil, err
}
result = append(result, c)
}
case *nodeAnyOf:
ways := make([][]match.Matcher, len(node.children()))
for _, desc := range node.children() {
variants, err := do2(desc, s)
if err != nil {
return nil, err
}
fmt.Println("variants any", variants)
for x, l := 0, len(ways); x < l; x++ {
for i := 0; i < len(variants); i++ {
o := optimize(variants[i])
if i == len(variants)-1 {
ways[x] = append(ways[x], o)
} else {
var w []match.Matcher
copy(w, ways[x])
ways = append(ways, append(w, o))
}
}
}
fmt.Println("ways any", ways)
}
for _, matchers := range ways {
c, err := compileMatchers(convertMatchers(matchers, nil))
if err != nil {
return nil, err
}
result = append(result, c)
}
case *nodeList:
result = append(result, match.List{n.chars, n.not})
case *nodeRange:
result = append(result, match.Range{n.lo, n.hi, n.not})
case *nodeAny:
result = append(result, match.Any{s})
case *nodeSuper:
result = append(result, match.Super{})
case *nodeSingle:
result = append(result, match.Single{s})
case *nodeText:
result = append(result, match.Raw{n.text})
default:
return nil, fmt.Errorf("could not compile tree: unknown node type")
}
for i, m := range result {
result[i] = optimize(m)
}
return result, nil
}
func compile(ast *nodePattern, s string) (Glob, error) {
// ms, err := do2(ast, s)
// if err != nil {
// return nil, err
// }
// if len(ms) == 1 {
// return ms[0], nil
// } else {
// return match.AnyOf{ms}, nil
// }
g, err := do(ast, s)
if err != nil {
return nil, err
}
return g, nil
}