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