source: trunk/gsdl/src/mgpp/text/GSDLQueryParser.cpp@ 6129

Last change on this file since 6129 was 6129, checked in by kjdon, 20 years ago

changed var name from near to prox - reserved word?

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/**************************************************************************
2 *
3 * QueryParser.cpp -- Query parser for a simple query language
4 * Copyright (C) 2000 Rodger McNab
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 **************************************************************************/
21
22#include "GSDLQueryParser.h"
23#include "GSDLQueryLex.h"
24
25
26static QueryNode *ParseExpression (UCArray::const_iterator &here,
27 UCArray::const_iterator end,
28 int defaultBoolCombine,
29 int defaultStemMethod);
30
31static QueryNode *AndAdd (QueryNode *t1, QueryNode *t2) {
32 if (t1 == NULL) return t2;
33 if (t2 == NULL) return t1;
34
35 AndQueryNode *andNode = new AndQueryNode;
36 andNode->leftNode = t1;
37 andNode->rightNode = t2;
38 return andNode;
39}
40
41static QueryNode *OrAdd (QueryNode *t1, QueryNode *t2) {
42 if (t1 == NULL) return t2;
43 if (t2 == NULL) return t1;
44
45 OrQueryNode *orNode = new OrQueryNode;
46 orNode->leftNode = t1;
47 orNode->rightNode = t2;
48 return orNode;
49}
50
51static QueryNode *NotAdd (QueryNode *t1, QueryNode *t2) {
52 if (t1 == NULL) return t2;
53 if (t2 == NULL) return t1;
54
55 NotQueryNode *notNode = new NotQueryNode;
56 notNode->queryNode = t1;
57 notNode->notNode = t2;
58 return notNode;
59}
60
61// expects the opening bracket to have already been parsed
62// and discarded
63static QueryNode *ParseBracketExpression (UCArray::const_iterator &here,
64 UCArray::const_iterator end,
65 int defaultBoolCombine,
66 int defaultStemMethod) {
67 // get everything in the expression
68 QueryNode *curTree = ParseExpression (here, end, defaultBoolCombine,
69 defaultStemMethod);
70
71 // gobble up tokens until a closing bracket is found
72 // or the end of the string
73 LexEl el;
74 while (ParseLexEl (here, end, el)) {
75 if (el.lexType == CloseBracketE) break;
76 }
77
78 return curTree;
79}
80
81static int ParseInt (UCArray::const_iterator &here,
82 UCArray::const_iterator end) {
83 LexEl el;
84 UCArray::const_iterator oldHere = here;
85 if (ParseLexEl (here, end, el) && el.lexType == IntegerE)
86 return el.num;
87
88 here = oldHere; // not an integer
89 return 0;
90}
91
92// default is within 20 words
93static void SetRangeValues (TermNode &termNode,
94 UCArray &nearby,
95 bool reverse) {
96 UCArray NEARBY; SetCStr(NEARBY, "NEAR");
97 UCArray WITHIN; SetCStr(WITHIN, "WITHIN");
98
99 if (nearby == NEARBY) { // no modifier
100 termNode.startRange = (NEAR_DEFAULT+1)*-1;
101 termNode.endRange = NEAR_DEFAULT;
102
103 } else if (nearby == WITHIN) { // no modifier
104 if (reverse) {
105 termNode.startRange = (NEAR_DEFAULT+1)*-1;
106 termNode.endRange = -1;
107 } else {
108 termNode.startRange = NEAR_DEFAULT;
109 termNode.endRange = 0;
110 }
111 }
112 else { // extract number
113 UCArray::const_iterator here;
114 bool within = false;
115 if (PrefixLen(nearby, WITHIN)==6) {
116 within=true;
117 here = nearby.begin()+6;
118 } else {
119 here = nearby.begin()+4;
120 }
121 UCArray::const_iterator end = nearby.end();
122 int size=0;
123 while (here != end) {
124 size = size*10 + (*here-'0');
125 here++;
126 }
127 if (within) {
128 if (reverse) {
129 termNode.startRange = size;
130 termNode.endRange = 0;
131 } else {
132 termNode.startRange = -1 * (size+1);
133 termNode.endRange = -1;
134 }
135 } else {
136 termNode.startRange = -1 * (size+1);
137 termNode.endRange = size;
138 }
139 }
140}
141
142static unsigned long GetStemMethod(LexEl &el, int defaultStemMethod) {
143 // here expect el to contain some of c,s,i,u
144 // stem method 0 = c,u 00
145 // stem method 1 = i,u 01 - default for DL
146 // stem method 2 = c, s 10
147 // stem method 3 = i,s 11
148
149 unsigned long stem = (unsigned long)defaultStemMethod;
150
151 UCArray::const_iterator here = el.text.begin();
152 UCArray::const_iterator end = el.text.end();
153
154 unsigned char c1 = *here;
155 if (!(c1 == 'c'| c1 == 'i' | c1 == 'u' | c1 == 's'))
156 return 4; // incorrect format
157
158 here++;
159 unsigned char c2 = 'a';
160 if (here !=end) {
161 c2 = *here;
162 if (!(c2 == 'c'| c2 == 'i' | c2 == 'u' | c2 == 's'))
163 return 4; // incorrect format
164 }
165
166 if (c1 == 'i'|| c2=='i') stem |= 1; // set bit 0 to 1
167 if (c1 == 'c' || c2 == 'c') stem &=0xe; //set bit 0 to 0
168 if (c1 == 's'|| c2 == 's') stem |= 2; // set bit 1 to 1
169 if (c1 == 'u' || c2 =='u') stem &=0xd; // set bit 1 to 0
170 return stem;
171}
172
173
174static void ParseTermModifiers (UCArray::const_iterator &here,
175 UCArray::const_iterator end,
176 TermNode &termNode,
177 int defaultStemMethod) {
178
179 termNode.stemMethod = defaultStemMethod;
180
181 LexEl el;
182 UCArray::const_iterator oldHere = here;
183 while (ParseLexEl (here, end, el)) {
184 if (el.lexType == TermWeightE) {
185 termNode.termWeight = ParseInt (here, end);
186
187 } else if (el.lexType == StemMethodE) {
188 oldHere = here;
189 LexEl stem;
190 if (ParseLexEl (here, end, stem) && stem.lexType == TermE) {
191 termNode.stemMethod = GetStemMethod(stem, defaultStemMethod);
192 if (termNode.stemMethod == 4) { // error so backtrack
193 here = oldHere;
194 termNode.stemMethod = (unsigned long)defaultStemMethod;
195 }
196 }else here = oldHere; //ignore - wrong syntax
197
198 } else if (el.lexType == RangeE) {
199 termNode.startRange = ParseInt (here, end);
200 termNode.endRange = ParseInt (here, end);
201
202 } else if (el.lexType == AtE) {
203 termNode.startRange = termNode.endRange = ParseInt (here, end);
204
205 } else {
206 // no term modifiers
207 here = oldHere;
208 break;
209 }
210
211 oldHere = here;
212 }
213}
214
215static void ParseProxModifiers (UCArray::const_iterator &here,
216 UCArray::const_iterator end,
217 ProxMatchQueryNode *proxNode) {
218 // so far only have one - the tag stuff
219 LexEl el;
220 UCArray::const_iterator oldHere = here;
221 while (ParseLexEl (here, end, el)) {
222 if (el.lexType == TagE) {
223 oldHere = here; // don't backtrack past here
224 if (ParseLexEl (here, end, el) && el.lexType == TermE) {
225 proxNode->tagNodePtr = new TagNode;
226 proxNode->tagNodePtr->tagName = el.text;
227
228 }
229 else { // error in tag
230 here = oldHere;
231 }
232 } // TagE
233 // add in other cases here
234 else {
235 // no modifiers
236 here = oldHere;
237 break;
238 }
239 oldHere = here;
240 }//while
241
242
243}
244
245// expects starting brackets to have been parsed
246// sets error to true if something has gone wrong
247static ProxMatchQueryNode *ParseSquareBrackets(UCArray::const_iterator &here,
248 UCArray::const_iterator end,
249 /*ProxMatchQueryNode *proxNode,*/
250 int defaultStemMethod,
251 bool & error) {
252
253 ProxMatchQueryNode *proxNode = new ProxMatchQueryNode;
254 LexEl el;
255 bool phrase=false;
256 bool first=true;
257 bool prox = false;
258 UCArray near_string;
259 while (ParseLexEl (here, end, el)) {
260 if (el.lexType == TermE || el.lexType == IntegerE) {
261 TermNode termNode;
262 termNode.term = el.text;
263 ParseTermModifiers (here, end, termNode, defaultStemMethod);
264 if (phrase) {
265 if (first) first=false;
266 else {
267 termNode.startRange = -2;
268 termNode.endRange = -1;
269 }
270 } else if (prox) {
271 SetRangeValues(termNode, near_string, false);
272 prox = false;
273 }
274 proxNode->terms.push_back(termNode);
275 }
276 else if (el.lexType == CloseSquareBracketE) {
277 break;
278 }
279 else if (el.lexType == QuoteE) {
280 // phrase inside square brackets
281 if (phrase) { // end of phrase
282 phrase=false;
283 first = true;
284 } else {
285 phrase=true; // start of phrase
286 }
287 } else if (el.lexType == NearOpE || el.lexType == WithinOpE) {
288 if (phrase) {
289 // cant have proximity op in a phrase - just assume its an actual word
290 TermNode termNode;
291 termNode.term = el.text;
292 ParseTermModifiers (here, end, termNode, defaultStemMethod);
293 proxNode->terms.push_back(termNode);
294 } else {
295 // its a NEAR or within op
296 prox = true;
297 near_string = el.text;
298 }
299
300 }
301 else if (el.lexType == UnknownE) {
302 // just ignore it
303 }
304 else {
305 //error - we set the proxNode to NULL,
306 cerr <<"GSDLQueryParser: bad syntax inside []\n";
307 error = true;
308 return NULL;
309 }
310 } // while
311 return proxNode;
312}
313// expects the starting quote to have been parsed
314// and discarded
315// now phrases use the case and stem preference options
316// ie can search for a phrase ignoring case
317static void ParsePhrase (UCArray::const_iterator &here,
318 UCArray::const_iterator end,
319 ProxMatchQueryNode &proxNode,
320 int defaultStemMethod,
321 bool &error) {
322 LexEl el;
323 bool first = true;
324 while (ParseLexEl (here, end, el)) {
325 if (el.lexType == TermE || el.lexType == IntegerE) {
326 TermNode termNode;
327 termNode.term = el.text;
328 //termNode.stemMethod = defaultStemMethod;
329 ParseTermModifiers (here, end, termNode, defaultStemMethod);
330 if (first) {
331 first = false;
332 }
333 else {
334 termNode.startRange = -2;
335 termNode.endRange = -1;
336 }
337 proxNode.terms.push_back (termNode);
338
339 } else if (el.lexType == QuoteE) {
340 break;
341
342 } else if (el.lexType == UnknownE) {
343 // just ignore it
344 } else {
345 // error
346 error = true;
347 return;
348 }
349 }
350}
351
352static QueryNode *ParseTerm (UCArray::const_iterator &here,
353 UCArray::const_iterator end,
354 int defaultBoolCombine,
355 int defaultStemMethod) {
356 LexEl el;
357
358 UCArray::const_iterator oldHere = here;
359 if (!ParseLexEl (here, end, el)) return NULL;
360
361 if (el.lexType == OpenBracketE)
362 return ParseBracketExpression (here, end, defaultBoolCombine,
363 defaultStemMethod);
364
365 ProxMatchQueryNode *proxNode = new ProxMatchQueryNode;
366
367 if (el.lexType == TermE || el.lexType == IntegerE) {
368 TermNode termNode;
369 termNode.term = el.text;
370 ParseTermModifiers (here, end, termNode, defaultStemMethod);
371 oldHere = here; // dont backtrack past here
372 if (ParseLexEl(here, end, el) && (el.lexType == NearOpE || el.lexType == WithinOpE )) {
373 delete proxNode;
374 oldHere = here;
375 // this is calling ParseTerm again, but only a subset of the things accepted by ParseTerm are appropriate here. add in some hacks to avoid segmentation faults - kjdon, 04/2003
376
377 // if the next element is a '(' have a syntax error, return NULL
378 LexEl temp_el;
379 if (ParseLexEl(here, end, temp_el) && temp_el.lexType == OpenBracketE) {
380 cerr << "GSDLQueryParser: NEAR/WITHIN cannot be followed by a '('\n";
381 return NULL;
382 }
383 here = oldHere; // else backtrack
384
385 proxNode = (ProxMatchQueryNode *)ParseTerm(here, end, defaultBoolCombine,
386 defaultStemMethod);
387 SetRangeValues(termNode, el.text, true);
388 proxNode->terms.push_back (termNode);
389 return proxNode;
390
391 } else {
392 here = oldHere; // backtrack
393 proxNode->terms.push_back (termNode);
394 ParseProxModifiers(here, end, proxNode);
395 return proxNode;
396 }
397 } else if (el.lexType == QuoteE) {
398 bool error = false;
399 ParsePhrase (here, end, *proxNode, defaultStemMethod, error);
400 if (error) {
401 delete proxNode;
402 return NULL;
403 }
404 return proxNode;
405 }
406 else if (el.lexType == OpenSquareBracketE) {
407 bool error = false;
408 proxNode = ParseSquareBrackets (here, end, /*proxNode, */defaultStemMethod, error);
409 if (error) {
410 delete proxNode;
411 return NULL;
412 }
413 ParseProxModifiers (here, end, proxNode);
414 return proxNode;
415 }
416
417 // not a term
418 here = oldHere;
419 delete proxNode;
420 return NULL;
421}
422
423
424static QueryNode *ParseExpression (UCArray::const_iterator &here,
425 UCArray::const_iterator end,
426 int defaultBoolCombine,
427 int defaultStemMethod) {
428 LexEl el;
429 QueryNode *curTree = NULL;
430 UCArray::const_iterator oldHere = here;
431 while (ParseLexEl (here, end, el)) {
432 if (el.lexType == CloseBracketE) {
433 // parsebracketexpression is waiting for the last bracket, so put it back
434 here = oldHere;
435 break;
436
437 } else if (el.lexType == OpenSquareBracketE ||
438 el.lexType == OpenBracketE ||
439 el.lexType == TermE ||
440 el.lexType == QuoteE ||
441 el.lexType == IntegerE ) {
442
443 // some type of term, back track and parse it
444 here = oldHere;
445
446 // parse the term
447 QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
448 defaultStemMethod);
449 if (newTerm == NULL) {
450 delete curTree;
451 return NULL;
452 }
453
454 // if default==1, AND, else if==0, OR
455 if (defaultBoolCombine) {
456 curTree = AndAdd (curTree, newTerm);
457 }
458 else {
459 curTree = OrAdd (curTree, newTerm);
460 }
461
462 } else if (el.lexType == AndOpE) {
463 QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
464 defaultStemMethod);
465 if (newTerm == NULL) {
466 delete curTree;
467 return NULL;
468 }
469 curTree = AndAdd (curTree, newTerm);
470
471 } else if (el.lexType == OrOpE) {
472 QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
473 defaultStemMethod);
474 if (newTerm == NULL) {
475 delete curTree;
476 return NULL;
477 }
478 curTree = OrAdd (curTree, newTerm);
479
480 } else if (el.lexType == NotOpE) {
481 QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
482 defaultStemMethod);
483 if (newTerm == NULL) {
484 delete curTree;
485 return NULL;
486 }
487 curTree = NotAdd (curTree, newTerm);
488
489 } else if (el.lexType == UnknownE) {
490 // just ignore it
491 } else {
492
493 // syntax error, return NUll
494 delete curTree;
495 return NULL;
496 }
497
498 oldHere = here;
499 }
500
501 return curTree;
502}
503
504QueryNode *ParseQuery (const UCArray &queryStr, int defaultBoolCombine,
505 int defaultStemMethod) {
506 UCArray::const_iterator here = queryStr.begin();
507 UCArray::const_iterator end = queryStr.end();
508 return ParseExpression (here, end, defaultBoolCombine, defaultStemMethod);
509}
Note: See TracBrowser for help on using the repository browser.