-
Notifications
You must be signed in to change notification settings - Fork 10
/
filters.go
156 lines (127 loc) · 3.02 KB
/
filters.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package phpgrep
import (
"bytes"
"fmt"
"regexp"
"strings"
)
func isLetter(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
func compileFilter(s string) (phpgrepFilter, error) {
// TODO(quasilyte): refactor this function.
var f phpgrepFilter
if s == "" {
return f, fmt.Errorf("filter must include matcher name, operator and its argument")
}
pos := 0
for i := 0; i < len(s); i++ {
if !isLetter(s[i]) {
break
}
pos++
}
if pos == 0 {
return f, fmt.Errorf("expected matcher name, found %q", s[0])
}
name := s[:pos]
if pos == len(s) {
return f, fmt.Errorf("missing operator")
}
var op string
switch s[pos] {
case '=':
op = "="
case '~':
op = "~"
case '!':
if pos+1 == len(s) {
return f, fmt.Errorf(`operator: expected "!=" or "!~", found only "!"`)
}
switch s[pos+1] {
case '=':
op = "!="
case '~':
op = "!~"
default:
return f, fmt.Errorf(`operator: expected "!=" or "!~", found "!%c"`, s[pos+1])
}
default:
return f, fmt.Errorf("unexpected operator %q", s[pos])
}
pos += len(op)
argument := s[pos:]
switch op {
case "=":
values := strings.Split(argument, ",")
for i := range values {
values[i] = strings.TrimSpace(values[i])
}
return valueInListFilter(name, values), nil
case "!=":
values := strings.Split(argument, ",")
for i := range values {
values[i] = strings.TrimSpace(values[i])
}
return valueNotInListFilter(name, values), nil
case "~":
re, err := regexp.Compile(argument)
if err != nil {
return f, fmt.Errorf("argument: %v", err)
}
return regexpFilter(name, re), nil
case "!~":
re, err := regexp.Compile(argument)
if err != nil {
return f, fmt.Errorf("argument: %v", err)
}
return regexpNotFilter(name, re), nil
default:
panic("unreachable")
}
}
type phpgrepFilter struct {
name string
fn filterFunc
}
type filterFunc func([]byte) bool
func valueNotInListFilter(name string, values []string) phpgrepFilter {
return phpgrepFilter{name: name, fn: makeValueNotInListFilter(values)}
}
func valueInListFilter(name string, values []string) phpgrepFilter {
return phpgrepFilter{name: name, fn: makeValueInListFilter(values)}
}
func regexpNotFilter(name string, re *regexp.Regexp) phpgrepFilter {
return phpgrepFilter{name: name, fn: makeRegexpNotFilter(re)}
}
func regexpFilter(name string, re *regexp.Regexp) phpgrepFilter {
return phpgrepFilter{name: name, fn: makeRegexpFilter(re)}
}
func makeValueNotInListFilter(values []string) filterFunc {
f := makeValueInListFilter(values)
return func(buf []byte) bool {
return !f(buf)
}
}
func makeValueInListFilter(values []string) filterFunc {
list := make([][]byte, len(values))
for i := range values {
list[i] = []byte(values[i])
}
return func(buf []byte) bool {
for _, v := range list {
if bytes.Equal(buf, v) {
return true
}
}
return false
}
}
func makeRegexpFilter(re *regexp.Regexp) filterFunc {
return re.Match
}
func makeRegexpNotFilter(re *regexp.Regexp) filterFunc {
return func(buf []byte) bool {
return !re.Match(buf)
}
}