-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathast.go
More file actions
160 lines (138 loc) · 3.28 KB
/
Copy pathast.go
File metadata and controls
160 lines (138 loc) · 3.28 KB
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
157
158
159
160
package fql
import "strings"
// Expr is a node in the boolean expression tree of a Falcon query.
type Expr interface {
render(sb *strings.Builder)
}
// Query is a parsed Falcon query: a boolean filter expression followed by
// zero or more pipe commands.
type Query struct {
Expr Expr
Pipes []Pipe
}
// Pipe is a piped command such as `| timerange(24h)`.
type Pipe struct {
Name string
Args string // raw argument text inside the parentheses, "" when absent
}
// OrExpr is a comma-joined disjunction.
type OrExpr struct {
Terms []Expr
}
// AndExpr is a plus-joined (or space-adjacent) conjunction.
type AndExpr struct {
Terms []Expr
}
// NotExpr negates its child expression (`!(...)` or the NOT keyword).
type NotExpr struct {
X Expr
}
// GroupExpr is a parenthesized sub-expression.
type GroupExpr struct {
X Expr
}
// ConditionExpr is a single field comparison, e.g. CommandLine:'*-enc*'.
type ConditionExpr struct {
Field string
Operator string // ":", "=", "~", "!:", "!~", "!=", ">", "<", ">=", "<="
Value Value
Negated bool // leading ! on the field
}
// SearchExpr is a bare search term with no field (free-text search).
type SearchExpr struct {
Term string
Quoted bool
}
// Value is a condition's right-hand side: a scalar or a bracketed list.
type Value struct {
Scalar string
Quoted bool // scalar came from a quoted string literal
List []Value // non-nil for [a, b] lists
}
// IsList reports whether the value is a bracketed list.
func (v Value) IsList() bool { return v.List != nil }
func quoteScalar(s string, quoted bool) string {
if !quoted {
return s
}
escaped := strings.ReplaceAll(s, `\`, `\\`)
escaped = strings.ReplaceAll(escaped, `'`, `\'`)
return "'" + escaped + "'"
}
func (v Value) render(sb *strings.Builder) {
if v.IsList() {
sb.WriteByte('[')
for i, item := range v.List {
if i > 0 {
sb.WriteString(", ")
}
item.render(sb)
}
sb.WriteByte(']')
return
}
sb.WriteString(quoteScalar(v.Scalar, v.Quoted))
}
func (e *OrExpr) render(sb *strings.Builder) {
for i, t := range e.Terms {
if i > 0 {
sb.WriteString(", ")
}
t.render(sb)
}
}
func (e *AndExpr) render(sb *strings.Builder) {
for i, t := range e.Terms {
if i > 0 {
sb.WriteString(" + ")
}
t.render(sb)
}
}
func (e *NotExpr) render(sb *strings.Builder) {
sb.WriteByte('!')
e.X.render(sb)
}
func (e *GroupExpr) render(sb *strings.Builder) {
sb.WriteByte('(')
e.X.render(sb)
sb.WriteByte(')')
}
func (e *ConditionExpr) render(sb *strings.Builder) {
if e.Negated {
sb.WriteByte('!')
}
sb.WriteString(e.Field)
sb.WriteString(e.Operator)
e.Value.render(sb)
}
func (e *SearchExpr) render(sb *strings.Builder) {
sb.WriteString(quoteScalar(e.Term, e.Quoted))
}
// String renders the query back to canonical FQL text. The rendering
// round-trips: parsing the output yields an equivalent tree.
func (q *Query) String() string {
var sb strings.Builder
if q.Expr != nil {
q.Expr.render(&sb)
}
for _, p := range q.Pipes {
sb.WriteString(" | ")
sb.WriteString(p.Name)
if p.Args != "" {
sb.WriteByte('(')
sb.WriteString(p.Args)
sb.WriteByte(')')
}
}
return sb.String()
}
// ExprString renders a single expression node to text.
func ExprString(e Expr) string {
if e == nil {
return ""
}
var sb strings.Builder
e.render(&sb)
return sb.String()
}