add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / queryparser / src / java / org / apache / lucene / queryParser / surround / parser / QueryParser.jj
1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 /* Surround query language parser */
19
20 /* Query language operators: OR, AND, NOT, W, N, (, ), ^, *, ?, " and comma */
21
22
23 options {
24   STATIC=false;
25   JAVA_UNICODE_ESCAPE=true;
26   USER_CHAR_STREAM=true;
27 }
28
29 PARSER_BEGIN(QueryParser)
30
31 package org.apache.lucene.queryParser.surround.parser;
32
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.io.StringReader;
36
37
38 import org.apache.lucene.analysis.TokenStream;
39
40 import org.apache.lucene.queryParser.surround.query.SrndQuery;
41 import org.apache.lucene.queryParser.surround.query.FieldsQuery;
42 import org.apache.lucene.queryParser.surround.query.OrQuery;
43 import org.apache.lucene.queryParser.surround.query.AndQuery;
44 import org.apache.lucene.queryParser.surround.query.NotQuery;
45 import org.apache.lucene.queryParser.surround.query.DistanceQuery;
46 import org.apache.lucene.queryParser.surround.query.SrndTermQuery;
47 import org.apache.lucene.queryParser.surround.query.SrndPrefixQuery;
48 import org.apache.lucene.queryParser.surround.query.SrndTruncQuery;
49
50 /**
51  * This class is generated by JavaCC.  The only method that clients should need
52  * to call is <a href="#parse">parse()</a>.
53  */
54
55 public class QueryParser {
56   final int minimumPrefixLength = 3;
57   final int minimumCharsInTrunc = 3;
58   final String truncationErrorMessage = "Too unrestrictive truncation: ";
59   final String boostErrorMessage = "Cannot handle boost value: ";
60  
61   /* CHECKME: These should be the same as for the tokenizer. How? */
62   final char truncator = '*';
63   final char anyChar = '?';
64   final char quote = '\"';
65   final char fieldOperator = ':';
66   final char comma = ','; /* prefix list separator */
67   final char carat = '^'; /* weight operator */
68  
69   static public SrndQuery parse(String query) throws ParseException {
70     QueryParser parser = new QueryParser();
71     return parser.parse2(query);
72   }
73
74   public QueryParser() {
75     this(new FastCharStream(new StringReader("")));
76   }
77
78   public SrndQuery parse2(String query) throws ParseException {
79     ReInit(new FastCharStream(new StringReader(query)));
80     try {
81       return TopSrndQuery();
82     } catch (TokenMgrError tme) {
83       throw new ParseException(tme.getMessage());
84     }
85   }
86     
87   protected SrndQuery getFieldsQuery(
88       SrndQuery q, ArrayList<String> fieldNames) {
89     /* FIXME: check acceptable subquery: at least one subquery should not be
90      * a fields query.
91      */
92     return new FieldsQuery(q, fieldNames, fieldOperator);
93   }
94   
95   protected SrndQuery getOrQuery(List<SrndQuery> queries, boolean infix, Token orToken) {
96     return new OrQuery(queries, infix, orToken.image);
97   }
98
99   protected SrndQuery getAndQuery(List<SrndQuery> queries, boolean infix, Token andToken) {
100     return new AndQuery( queries, infix, andToken.image);
101   }
102   
103   protected SrndQuery getNotQuery(List<SrndQuery> queries, Token notToken) {
104     return new NotQuery( queries, notToken.image);
105   }
106   
107   protected static int getOpDistance(String distanceOp) {
108     /* W, 2W, 3W etc -> 1, 2 3, etc. Same for N, 2N ... */
109     return distanceOp.length() == 1 
110       ? 1
111       : Integer.parseInt( distanceOp.substring( 0, distanceOp.length() - 1));
112   }
113   
114   protected static void checkDistanceSubQueries(DistanceQuery distq, String opName)
115   throws ParseException {
116     String m = distq.distanceSubQueryNotAllowed();
117     if (m != null) {
118       throw new ParseException("Operator " + opName + ": " + m);
119     }
120   }
121   
122   protected SrndQuery getDistanceQuery(
123         List<SrndQuery> queries,
124         boolean infix,
125         Token dToken,
126         boolean ordered) throws ParseException {
127     DistanceQuery dq = new DistanceQuery(queries,
128                                         infix,
129                                         getOpDistance(dToken.image),
130                                         dToken.image,
131                                         ordered);
132     checkDistanceSubQueries(dq, dToken.image);
133     return dq;
134   }
135
136   protected SrndQuery getTermQuery(
137         String term, boolean quoted) {
138     return new SrndTermQuery(term, quoted);
139   }
140
141   protected boolean allowedSuffix(String suffixed) {
142     return (suffixed.length() - 1) >= minimumPrefixLength;
143   }
144
145   protected SrndQuery getPrefixQuery(
146       String prefix, boolean quoted) {
147     return new SrndPrefixQuery(prefix, quoted, truncator);
148   }
149   
150   protected boolean allowedTruncation(String truncated) {
151     /* At least 3 normal characters needed. */
152     int nrNormalChars = 0;
153     for (int i = 0; i < truncated.length(); i++) {
154       char c = truncated.charAt(i);
155       if ((c != truncator) && (c != anyChar)) {
156         nrNormalChars++;
157       }
158     }
159     return nrNormalChars >= minimumCharsInTrunc;
160   }
161
162   protected SrndQuery getTruncQuery(String truncated) {
163     return new SrndTruncQuery(truncated, truncator, anyChar);
164   }
165 }
166
167 PARSER_END(QueryParser)
168
169 /* ***************** */
170 /* Token Definitions */
171 /* ***************** */
172
173 <*> TOKEN : {
174   <#_NUM_CHAR:   ["0"-"9"] >
175 | <#_TERM_CHAR: /* everything except whitespace and operators */
176     ( ~[ " ", "\t", "\n", "\r",
177           ",", "?", "*", "(", ")", ":", "^", "\""]
178      ) >
179 | <#_WHITESPACE: ( " " | "\t" | "\n" | "\r" ) >
180 | <#_STAR:       "*" > /* term truncation */
181 | <#_ONE_CHAR:   "?" > /* precisely one character in a term */
182 /* 2..99 prefix for distance operators */
183 | <#_DISTOP_NUM: ((["2"-"9"](["0"-"9"])?) | ("1" ["0"-"9"]))> 
184 }
185
186 <DEFAULT> SKIP : {
187   < <_WHITESPACE>>
188 }
189
190 /* Operator tokens (in increasing order of precedence): */
191 <DEFAULT> TOKEN :
192 {
193   <OR:        "OR" | "or"> 
194 | <AND:       "AND" | "and">
195 | <NOT:       "NOT" | "not">
196 | <W:         (<_DISTOP_NUM>)? ("W"|"w")>
197 | <N:         (<_DISTOP_NUM>)? ("N"|"n")>
198 /* These are excluded in _TERM_CHAR: */
199 | <LPAREN:    "(">
200 | <RPAREN:    ")">
201 | <COMMA:     ",">
202 | <COLON:     ":">
203 | <CARAT:     "^"> : Boost
204 /* Literal non empty term between single quotes,
205  * escape quoted quote or backslash by backslash.
206  * Evt. truncated.
207  */
208 | <TRUNCQUOTED: "\"" (~["\""])+ "\"" <_STAR>>
209 | <QUOTED:      "\"" ( (~["\"", "\\"]) | ("\\" ["\\", "\""]))+ "\"">
210 | <SUFFIXTERM: (<_TERM_CHAR>)+ <_STAR>>
211 | <TRUNCTERM:  (<_TERM_CHAR>)+
212                (<_STAR> | <_ONE_CHAR> )+ /* at least one * or ? */
213                (<_TERM_CHAR> | <_STAR>  | <_ONE_CHAR> )*
214                >
215 | <TERM:       (<_TERM_CHAR>)+>
216 }
217
218 <Boost> TOKEN : {
219 <NUMBER:    (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )?> : DEFAULT
220 }
221
222
223 SrndQuery TopSrndQuery() : {
224   SrndQuery q;
225 }{
226   q = FieldsQuery()
227   <EOF>
228   {return q;}
229 }
230
231
232 SrndQuery FieldsQuery() : {
233   SrndQuery q;
234   ArrayList<String> fieldNames;
235 }{
236   fieldNames = OptionalFields()
237   q = OrQuery()
238   {return (fieldNames == null) ? q : getFieldsQuery(q, fieldNames);}
239 }
240
241
242 ArrayList<String> OptionalFields() : {
243   Token fieldName;
244   ArrayList<String> fieldNames = null;
245 }{
246   ( LOOKAHEAD(2) // to the colon
247     fieldName = <TERM>
248     <COLON> {
249       if (fieldNames == null) {
250         fieldNames = new ArrayList<String>();
251       }
252       fieldNames.add(fieldName.image);
253     }
254   )*
255   {return fieldNames;}
256 }
257
258
259 SrndQuery OrQuery() : {
260   SrndQuery q;
261   ArrayList<SrndQuery> queries = null;
262   Token oprt = null;
263 }{
264   q = AndQuery()
265   ( oprt = <OR> { /* keep only last used operator */
266       if (queries == null) {
267         queries = new ArrayList<SrndQuery>();
268         queries.add(q);
269       }
270     }    
271     q = AndQuery() {
272       queries.add(q);
273     }
274   )*
275   {return (queries == null) ? q : getOrQuery(queries, true /* infix */, oprt);}
276 }
277
278
279 SrndQuery AndQuery() : {
280   SrndQuery q;
281   ArrayList<SrndQuery> queries = null;
282   Token oprt = null;
283 }{
284   q = NotQuery()
285   ( oprt = <AND> { /* keep only last used operator */
286       if (queries == null) {
287         queries = new ArrayList<SrndQuery>();
288         queries.add(q);
289       }
290     }
291     q = NotQuery() {
292       queries.add(q);
293     }
294   )* 
295   {return (queries == null) ? q : getAndQuery(queries, true /* infix */, oprt);}
296 }
297
298
299 SrndQuery NotQuery() : {
300   SrndQuery q;
301   ArrayList<SrndQuery> queries = null;
302   Token oprt = null;
303 }{
304   q = NQuery()
305   ( oprt = <NOT>  { /* keep only last used operator */
306       if (queries == null) {
307         queries = new ArrayList<SrndQuery>();
308         queries.add(q);
309       }
310     }
311     q = NQuery() {
312       queries.add(q);
313     }
314   )*
315   {return (queries == null) ? q : getNotQuery(queries, oprt);}
316 }
317
318
319 SrndQuery NQuery() : {
320   SrndQuery q;
321   ArrayList<SrndQuery> queries;
322   Token dt;
323 }{
324   q = WQuery()
325   ( dt = <N> {
326       queries = new ArrayList<SrndQuery>();
327       queries.add(q); /* left associative */
328     }
329     q = WQuery() {
330       queries.add(q);
331       q = getDistanceQuery(queries, true /* infix */, dt, false /* not ordered */); 
332     }
333   )* 
334   {return q;}
335 }
336
337
338 SrndQuery WQuery() : {
339   SrndQuery q;
340   ArrayList<SrndQuery> queries;
341   Token wt;
342 }{
343   q = PrimaryQuery()
344   ( wt = <W> {
345       queries = new ArrayList<SrndQuery>();
346       queries.add(q); /* left associative */
347     }
348     q = PrimaryQuery() {
349       queries.add(q); 
350       q = getDistanceQuery(queries, true /* infix */, wt, true /* ordered */); 
351     }
352   )* 
353   {return q;}
354 }
355
356
357 SrndQuery PrimaryQuery() : { /* bracketed weighted query or weighted term */
358   SrndQuery q;
359 }{
360   ( <LPAREN> q = FieldsQuery() <RPAREN>
361   | q = PrefixOperatorQuery()
362   | q = SimpleTerm()
363   )
364   OptionalWeights(q)
365   {return q;}
366 }
367
368
369 SrndQuery PrefixOperatorQuery() : {
370   Token oprt;
371   List<SrndQuery> queries;
372 }{
373   ( oprt = <OR> /* prefix OR */
374     queries = FieldsQueryList()
375     {return getOrQuery(queries, false /* not infix */, oprt);}
376       
377   | oprt = <AND> /* prefix AND */
378     queries = FieldsQueryList()
379     {return getAndQuery(queries, false /* not infix */, oprt);}
380   
381   | oprt = <N> /* prefix N */
382     queries = FieldsQueryList()
383     {return getDistanceQuery(queries, false /* not infix */, oprt, false /* not ordered */);}
384     
385   | oprt = <W> /* prefix W */
386     queries = FieldsQueryList()
387     {return getDistanceQuery(queries, false  /* not infix */, oprt, true /* ordered */);}
388   )
389 }
390
391
392 List<SrndQuery> FieldsQueryList() : {
393   SrndQuery q;
394   ArrayList<SrndQuery> queries = new ArrayList<SrndQuery>();
395 }{
396   <LPAREN>
397   q = FieldsQuery() {queries.add(q);}
398   (<COMMA> q = FieldsQuery() {queries.add(q);})+
399   <RPAREN>
400   {return queries;}
401 }
402
403
404 SrndQuery SimpleTerm() : {
405   Token term;
406 }{
407   ( term=<TERM>
408     {return getTermQuery(term.image, false /* not quoted */);}
409       
410   | term=<QUOTED>
411     {return getTermQuery(term.image.substring(1, term.image.length()-1), true /* quoted */);}
412       
413   | term=<SUFFIXTERM> { /* ending in * */
414       if (! allowedSuffix(term.image)) {
415         throw new ParseException(truncationErrorMessage + term.image);
416       }
417       return getPrefixQuery(term.image.substring(0, term.image.length()-1), false /* not quoted */);
418     }
419       
420   | term=<TRUNCTERM> { /* with at least one * or ? */
421       if (! allowedTruncation(term.image)) {
422         throw new ParseException(truncationErrorMessage + term.image);
423       }
424       return getTruncQuery(term.image);
425     }
426       
427   | term=<TRUNCQUOTED> { /* eg. "9b-b,m"* */
428       if ((term.image.length() - 3) < minimumPrefixLength) {
429         throw new ParseException(truncationErrorMessage + term.image);
430       }
431       return getPrefixQuery(term.image.substring(1, term.image.length()-2), true /* quoted */);
432     }
433   )
434 }
435
436
437 void OptionalWeights(SrndQuery q) : {
438   Token weight=null;
439 }{
440   ( <CARAT> weight=<NUMBER> {
441       float f;
442       try {
443         f = Float.valueOf(weight.image).floatValue();
444       } catch (Exception floatExc) {
445         throw new ParseException(boostErrorMessage + weight.image + " (" + floatExc + ")");
446       }
447       if (f <= 0.0) {
448         throw new ParseException(boostErrorMessage + weight.image);
449       }      
450       q.setWeight(f * q.getWeight()); /* left associative, fwiw */
451     }
452   )*
453 }
454