1 %{
2 package query
3 import (
4 	"fmt"
5 	"strconv"
6 	"strings"
7 	"time"
8 )
9 
logDebugGrammar(format string,v...interface{})10 func logDebugGrammar(format string, v ...interface{}) {
11 	if debugParser {
12     	logger.Printf(format, v...)
13     }
14 }
15 %}
16 
17 %union {
18 s string
19 n int
20 f float64
21 q Query
22 pf *float64}
23 
24 %token tSTRING tPHRASE tPLUS tMINUS tCOLON tBOOST tNUMBER tSTRING tGREATER tLESS
25 tEQUAL tTILDE
26 
27 %type <s>                tSTRING
28 %type <s>                tPHRASE
29 %type <s>                tNUMBER
30 %type <s>                posOrNegNumber
31 %type <s>                tTILDE
32 %type <s>                tBOOST
33 %type <q>                searchBase
34 %type <pf>                searchSuffix
35 %type <n>                searchPrefix
36 
37 %%
38 
39 input:
40 searchParts {
41 	logDebugGrammar("INPUT")
42 };
43 
44 searchParts:
45 searchPart searchParts {
46 	logDebugGrammar("SEARCH PARTS")
47 }
48 |
49 searchPart {
50 	logDebugGrammar("SEARCH PART")
51 };
52 
53 searchPart:
54 searchPrefix searchBase searchSuffix {
55 	query := $2
56 	if $3 != nil {
57 		if query, ok := query.(BoostableQuery); ok {
58 			query.SetBoost(*$3)
59 			}
60 	}
61 	switch($1) {
62 		case queryShould:
63 			yylex.(*lexerWrapper).query.AddShould(query)
64 		case queryMust:
65 			yylex.(*lexerWrapper).query.AddMust(query)
66 		case queryMustNot:
67 			yylex.(*lexerWrapper).query.AddMustNot(query)
68 	}
69 };
70 
71 
72 searchPrefix:
73 /* empty */ {
74 	$$ = queryShould
75 }
76 |
77 tPLUS {
78 	logDebugGrammar("PLUS")
79 	$$ = queryMust
80 }
81 |
82 tMINUS {
83 	logDebugGrammar("MINUS")
84 	$$ = queryMustNot
85 };
86 
87 searchBase:
88 tSTRING {
89 	str := $1
90 	logDebugGrammar("STRING - %s", str)
91 	var q FieldableQuery
92 	if strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
93 	  q = NewRegexpQuery(str[1:len(str)-1])
94 	} else if strings.ContainsAny(str, "*?"){
95 	  q = NewWildcardQuery(str)
96 	} else {
97 	  q = NewMatchQuery(str)
98 	}
99 	$$ = q
100 }
101 |
102 tSTRING tTILDE {
103 	str := $1
104 	fuzziness, err := strconv.ParseFloat($2, 64)
105 	if err != nil {
106 	  yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid fuzziness value: %v", err))
107 	}
108 	logDebugGrammar("FUZZY STRING - %s %f", str, fuzziness)
109 	q := NewMatchQuery(str)
110 	q.SetFuzziness(int(fuzziness))
111 	$$ = q
112 }
113 |
114 tSTRING tCOLON tSTRING tTILDE {
115 	field := $1
116 	str := $3
117 	fuzziness, err := strconv.ParseFloat($4, 64)
118 	if err != nil {
119 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid fuzziness value: %v", err))
120 	}
121 	logDebugGrammar("FIELD - %s FUZZY STRING - %s %f", field, str, fuzziness)
122 	q := NewMatchQuery(str)
123 	q.SetFuzziness(int(fuzziness))
124 	q.SetField(field)
125 	$$ = q
126 }
127 |
128 tNUMBER {
129 	str := $1
130 	logDebugGrammar("STRING - %s", str)
131 	q1 := NewMatchQuery(str)
132 	val, err := strconv.ParseFloat($1, 64)
133 	if err != nil {
134 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err))
135 	}
136 	inclusive := true
137 	q2 := NewNumericRangeInclusiveQuery(&val, &val, &inclusive, &inclusive)
138 	q := NewDisjunctionQuery([]Query{q1,q2})
139 	q.queryStringMode = true
140 	$$ = q
141 }
142 |
143 tPHRASE {
144 	phrase := $1
145 	logDebugGrammar("PHRASE - %s", phrase)
146 	q := NewMatchPhraseQuery(phrase)
147 	$$ = q
148 }
149 |
150 tSTRING tCOLON tSTRING {
151 	field := $1
152 	str := $3
153 	logDebugGrammar("FIELD - %s STRING - %s", field, str)
154 	var q FieldableQuery
155 	if strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
156 		q = NewRegexpQuery(str[1:len(str)-1])
157 	} else if strings.ContainsAny(str, "*?"){
158 	  q = NewWildcardQuery(str)
159 	}  else {
160 		q = NewMatchQuery(str)
161 	}
162 	q.SetField(field)
163 	$$ = q
164 }
165 |
166 tSTRING tCOLON posOrNegNumber {
167 	field := $1
168 	str := $3
169 	logDebugGrammar("FIELD - %s STRING - %s", field, str)
170 	q1 := NewMatchQuery(str)
171 	q1.SetField(field)
172 	val, err := strconv.ParseFloat($3, 64)
173 	if err != nil {
174 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err))
175 	}
176 	inclusive := true
177 	q2 := NewNumericRangeInclusiveQuery(&val, &val, &inclusive, &inclusive)
178 	q2.SetField(field)
179 	q := NewDisjunctionQuery([]Query{q1,q2})
180 	q.queryStringMode = true
181 	$$ = q
182 }
183 |
184 tSTRING tCOLON tPHRASE {
185 	field := $1
186 	phrase := $3
187 	logDebugGrammar("FIELD - %s PHRASE - %s", field, phrase)
188 	q := NewMatchPhraseQuery(phrase)
189 	q.SetField(field)
190 	$$ = q
191 }
192 |
193 tSTRING tCOLON tGREATER posOrNegNumber {
194 	field := $1
195 	min, err := strconv.ParseFloat($4, 64)
196 	if err != nil {
197 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err))
198 	}
199 	minInclusive := false
200 	logDebugGrammar("FIELD - GREATER THAN %f", min)
201 	q := NewNumericRangeInclusiveQuery(&min, nil, &minInclusive, nil)
202 	q.SetField(field)
203 	$$ = q
204 }
205 |
206 tSTRING tCOLON tGREATER tEQUAL posOrNegNumber {
207 	field := $1
208 	min, err := strconv.ParseFloat($5, 64)
209 	if err != nil {
210 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err))
211 	}
212 	minInclusive := true
213 	logDebugGrammar("FIELD - GREATER THAN OR EQUAL %f", min)
214 	q := NewNumericRangeInclusiveQuery(&min, nil, &minInclusive, nil)
215 	q.SetField(field)
216 	$$ = q
217 }
218 |
219 tSTRING tCOLON tLESS posOrNegNumber {
220 	field := $1
221 	max, err := strconv.ParseFloat($4, 64)
222 	if err != nil {
223 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err))
224 	}
225 	maxInclusive := false
226 	logDebugGrammar("FIELD - LESS THAN %f", max)
227 	q := NewNumericRangeInclusiveQuery(nil, &max, nil, &maxInclusive)
228 	q.SetField(field)
229 	$$ = q
230 }
231 |
232 tSTRING tCOLON tLESS tEQUAL posOrNegNumber {
233 	field := $1
234 	max, err := strconv.ParseFloat($5, 64)
235 	if err != nil {
236 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err))
237 	}
238 	maxInclusive := true
239 	logDebugGrammar("FIELD - LESS THAN OR EQUAL %f", max)
240 	q := NewNumericRangeInclusiveQuery(nil, &max, nil, &maxInclusive)
241 	q.SetField(field)
242 	$$ = q
243 }
244 |
245 tSTRING tCOLON tGREATER tPHRASE {
246 	field := $1
247 	minInclusive := false
248 	phrase := $4
249 
250 	logDebugGrammar("FIELD - GREATER THAN DATE %s", phrase)
251 	minTime, err := queryTimeFromString(phrase)
252 	if err != nil {
253 	  yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid time: %v", err))
254 	}
255 	q := NewDateRangeInclusiveQuery(minTime, time.Time{}, &minInclusive, nil)
256 	q.SetField(field)
257 	$$ = q
258 }
259 |
260 tSTRING tCOLON tGREATER tEQUAL tPHRASE {
261 	field := $1
262 	minInclusive := true
263 	phrase := $5
264 
265 	logDebugGrammar("FIELD - GREATER THAN OR EQUAL DATE %s", phrase)
266 	minTime, err := queryTimeFromString(phrase)
267 	if err != nil {
268 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid time: %v", err))
269 	}
270 	q := NewDateRangeInclusiveQuery(minTime, time.Time{}, &minInclusive, nil)
271 	q.SetField(field)
272 	$$ = q
273 }
274 |
275 tSTRING tCOLON tLESS tPHRASE {
276 	field := $1
277 	maxInclusive := false
278 	phrase := $4
279 
280 	logDebugGrammar("FIELD - LESS THAN DATE %s", phrase)
281 	maxTime, err := queryTimeFromString(phrase)
282 	if err != nil {
283 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid time: %v", err))
284 	}
285 	q := NewDateRangeInclusiveQuery(time.Time{}, maxTime, nil, &maxInclusive)
286 	q.SetField(field)
287 	$$ = q
288 }
289 |
290 tSTRING tCOLON tLESS tEQUAL tPHRASE {
291 	field := $1
292 	maxInclusive := true
293 	phrase := $5
294 
295 	logDebugGrammar("FIELD - LESS THAN OR EQUAL DATE %s", phrase)
296 	maxTime, err := queryTimeFromString(phrase)
297 	if err != nil {
298 		yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid time: %v", err))
299 	}
300 	q := NewDateRangeInclusiveQuery(time.Time{}, maxTime, nil, &maxInclusive)
301 	q.SetField(field)
302 	$$ = q
303 };
304 
305 searchSuffix:
306 /* empty */ {
307 	$$ = nil
308 }
309 |
310 tBOOST {
311   $$ = nil
312 	boost, err := strconv.ParseFloat($1, 64)
313 	if err != nil {
314 	  yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("invalid boost value: %v", err))
315 	} else {
316 		$$ = &boost
317 	}
318 	logDebugGrammar("BOOST %f", boost)
319 };
320 
321 posOrNegNumber:
322 tNUMBER {
323 	$$ = $1
324 }
325 |
326 tMINUS tNUMBER {
327 	$$ = "-" + $2
328 };
329