-
Notifications
You must be signed in to change notification settings - Fork 0
/
trie.go
100 lines (87 loc) · 1.62 KB
/
trie.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package regexp
import (
"regexp"
"sort"
"strings"
)
type trie map[string]trie
func newTrie() trie {
return make(trie)
}
type TrieOptimizer struct {
path trie
}
func NewTrieOptimizer() *TrieOptimizer {
return &TrieOptimizer{
path: newTrie(),
}
}
func (self *TrieOptimizer) Add(pat string) {
t := self.path
for _, ch := range pat {
key := string(ch)
if _, exist := t[key]; !exist {
t[key] = newTrie()
}
t = t[key]
}
t["__terminal__"] = nil
}
func (self *TrieOptimizer) Compile() (*regexp.Regexp, error) {
return regexp.Compile(self.Re())
}
func (self *TrieOptimizer) Re() string {
if len(self.path) == 0 {
// this pattern is never matched
return "^\\b\x00"
}
return self.re(self.path)
}
func (self *TrieOptimizer) re(path trie) string {
keys := []string{}
for key, _ := range path {
keys = append(keys, key)
}
sort.Strings(keys)
if _, hasTerminalKey := path["__terminal__"]; hasTerminalKey && len(keys) == 1 {
return ""
}
alt := []string{}
cc := []string{}
q := false
for _, key := range keys {
qpat := regexp.QuoteMeta(key)
if key != "__terminal__" {
recurse := self.re(path[key])
if recurse != "" {
alt = append(alt, qpat+recurse)
} else {
cc = append(cc, qpat)
}
} else {
q = true
}
}
cconly := len(alt) == 0
if len(cc) > 0 {
if len(cc) == 1 {
alt = append(alt, cc[0])
} else {
alt = append(alt, "["+strings.Join(cc, "")+"]")
}
}
result := ""
if len(alt) == 1 {
result = alt[0]
} else {
result = "(?:" + strings.Join(alt, "|") + ")"
}
if q {
if cconly {
result += "?"
} else {
result = "(?:" + result + ")?"
}
}
return result
}