-
Notifications
You must be signed in to change notification settings - Fork 3
/
match.go
107 lines (95 loc) · 2.03 KB
/
match.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
101
102
103
104
105
106
107
package trie
// Match is matched data.
type Match struct {
Value interface{}
}
// MatchTree compares a string with multiple strings using Aho-Corasick
// algorithm.
type MatchTree struct {
root *Node
}
type matchData struct {
value interface{}
fail *Node
}
// Compile compiles a MatchTree from a Tree.
func Compile(tr *Tree) *MatchTree {
mt := &MatchTree{
root: &tr.Root,
}
mt.root.Value = &matchData{fail: mt.root}
tr.Each(func(n0 *Node) bool {
n0.Child.Each(func(n1 *Node) bool {
mt.fillFail(n1, n0)
return true
})
return true
})
return mt
}
func (mt *MatchTree) fillFail(curr, parent *Node) {
d := &matchData{value: curr.Value}
curr.Value = d
if parent == mt.root {
d.fail = mt.root
return
}
d.fail = mt.nextNode(mt.failNode(parent), curr.Label)
}
func (mt *MatchTree) failNode(node *Node) *Node {
fail := (node.Value.(*matchData)).fail
if fail == nil {
return mt.root
}
return fail
}
func (mt *MatchTree) nextNode(node *Node, r rune) *Node {
for {
if next := node.Get(r); next != nil {
return next
}
if node == mt.root {
return mt.root
}
node = mt.failNode(node)
}
}
// MatchAll matches text and return all matched data.I
func (mt *MatchTree) MatchAll(text string, matches []Match) []Match {
m := mt.Matcher()
for _, r := range text {
matches = m.Next(r, matches)
}
return matches
}
// Matcher implements an iterator to match.
type Matcher struct {
mt *MatchTree
curr *Node
}
// Matcher creates a new Matcher which is matching context.
func (mt *MatchTree) Matcher() *Matcher {
return (&Matcher{mt: mt}).Reset()
}
// Reset resets matchin context.
func (m *Matcher) Reset() *Matcher {
m.curr = m.mt.root
return m
}
// Next appends a rune to match string, then get matches.
func (m *Matcher) Next(r rune, matches []Match) []Match {
m.curr = m.mt.nextNode(m.curr, r)
if m.curr == m.mt.root {
return nil
}
for n := m.curr; n != m.mt.root; {
d := n.Value.(*matchData)
if d.value != nil {
matches = append(matches, Match{
Value: d.value,
})
}
n = d.fail
}
return matches
}