]> err.no Git - sope/blob - libFoundation/Foundation/NSPredicateParser.m
fixed some NGMail framework build issue
[sope] / libFoundation / Foundation / NSPredicateParser.m
1 /* 
2    NSPredicateParser.m
3
4    Copyright (C) 2000-2005 SKYRIX Software AG
5    All rights reserved.
6    
7    Author: Helge Hess <helge.hess@opengroupware.org>
8
9    This file is part of libFoundation.
10
11    Permission to use, copy, modify, and distribute this software and its
12    documentation for any purpose and without fee is hereby granted, provided
13    that the above copyright notice appear in all copies and that both that
14    copyright notice and this permission notice appear in supporting
15    documentation.
16
17    We disclaim all warranties with regard to this software, including all
18    implied warranties of merchantability and fitness, in no event shall
19    we be liable for any special, indirect or consequential damages or any
20    damages whatsoever resulting from loss of use, data or profits, whether in
21    an action of contract, negligence or other tortious action, arising out of
22    or in connection with the use or performance of this software.
23 */
24
25 #include <stdio.h>
26 #include "NSPredicate.h"
27 #include "NSComparisonPredicate.h"
28 #include "NSCompoundPredicate.h"
29 #include "NSExpression.h"
30 #include "NSValue.h"
31 #include "NSException.h"
32 #include "NSDictionary.h"
33 #include "NSNull.h"
34 #include "NSCalendarDate.h"
35 #include "common.h"
36
37 //#define USE_DESCRIPTION_FOR_AT 1
38
39 static int qDebug = 0;
40 static NSMutableDictionary *NSPredicateParserTypeMappings = nil;
41
42 /* 
43    The literals understood by the value parser.
44    
45    NOTE: Any literal used here can never be used as a key ! So add as little
46    as possible.
47 */
48 typedef struct {
49   const unsigned char *token;
50   id  value;
51   int scase;
52 } NSQPTokEntry;
53
54 static NSQPTokEntry toks[] = {
55   { (const unsigned char *)"NULL",  nil, 0 },
56   { (const unsigned char *)"nil",   nil, 1 },
57   { (const unsigned char *)"YES",   nil, 0 },
58   { (const unsigned char *)"NO",    nil, 0 },
59   { (const unsigned char *)"TRUE",  nil, 0 },
60   { (const unsigned char *)"FALSE", nil, 0 },
61   { (const unsigned char *)NULL,    nil, 0 }
62 };
63
64 static inline void _setupLiterals(void) {
65   static BOOL didSetup = NO;
66   if (didSetup) return;
67   didSetup = YES;
68   toks[0].value = [[NSNull null] retain];
69   toks[1].value = toks[0].value;
70   toks[2].value = [[NSNumber numberWithBool:YES] retain];
71   toks[3].value = [[NSNumber numberWithBool:NO]  retain];
72   toks[4].value = toks[2].value;
73   toks[5].value = toks[3].value;
74 }
75
76 /* cache */
77 static Class  StringClass = Nil;
78 static Class  NumberClass = Nil;
79 static NSNull *null       = nil;
80
81 /* parsing functions */
82
83 static NSPredicate *_parseCompoundPredicate(id _ctx, const char *_buf,
84                                             unsigned _bufLen, unsigned *_predLen);
85 static NSPredicate *_testOperator(id _ctx, const char *_buf,
86                                   unsigned _bufLen, unsigned *_opLen,
87                                   BOOL *_testAnd);
88 static NSPredicate *_parsePredicates(id _ctx, const char *_buf,
89                                      unsigned _bufLen, unsigned *_predLen);
90 static NSPredicate *_parseParenthesisPredicate(id _ctx,
91                                                const char *_buf, unsigned _bufLen,
92                                                unsigned *_predLen);
93 static NSPredicate *_parseNotPredicate(id _ctx, const char *_buf,
94                                        unsigned _bufLen, unsigned *_predLen);
95 static NSPredicate *_parseKeyCompPredicate(id _ctx, const char *_buf,
96                                            unsigned _bufLen, unsigned *_predLen);
97 static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen,
98                            unsigned *_keyLen);
99 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
100                       unsigned *_keyLen);
101 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen);
102 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
103                           unsigned *_opLen);
104
105 @interface NSPredicateParserContext : NSObject
106 {
107   NSMapTable *predicateCache;
108 }
109
110 - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos;
111 - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct
112   atPos:(unsigned)_pos;
113 - (id)getObjectFromStackFor:(char)_c;
114
115 @end
116
117 @interface NSPredicateVAParserContext : NSPredicateParserContext
118 {
119   va_list    *va;  
120 }
121 + (id)contextWithVaList:(va_list *)_va;
122 - (id)initWithVaList:(va_list *)_va;
123 @end
124
125 @interface NSPredicateEnumeratorParserContext : NSPredicateParserContext
126 {
127   NSEnumerator *enumerator;
128 }
129 + (id)contextWithEnumerator:(NSEnumerator *)_enumerator;
130 - (id)initWithEnumerator:(NSEnumerator  *)_enumerator;
131 @end
132
133 @implementation NSPredicateVAParserContext
134
135 + (id)contextWithVaList:(va_list *)_va {
136   return [[[NSPredicateVAParserContext alloc] initWithVaList:_va] autorelease];
137 }
138
139 - (id)initWithVaList:(va_list *)_va {
140   if ((self = [super init])) {
141     self->va = _va;
142   }
143   return self;
144 }
145
146 - (id)getObjectFromStackFor:(char)_c {
147   id obj = nil;
148
149   if (StringClass == Nil) StringClass = [NSString class];
150   if (NumberClass == Nil) NumberClass = [NSNumber class];
151   if (null == nil)        null        = [NSNull null];
152   
153   if (_c == 's') {
154     char *str = va_arg(*self->va, char*);
155     obj = [StringClass stringWithCString:str];
156   }
157   else if (_c == 'd') {
158     int i= va_arg(*self->va, int);
159     obj = [NumberClass numberWithInt:i];
160   }
161   else if (_c == 'f') {
162     double d = va_arg(*self->va, double);
163     obj = [NumberClass numberWithDouble:d];
164   }
165   else if (_c == '@') {
166     id o = va_arg(*self->va, id);
167 #if USE_DESCRIPTION_FOR_AT
168     obj = (o == nil) ? (id)null : (id)[o description];
169 #else
170     obj = (o == nil) ? (id)null : (id)o;
171 #endif
172   }
173   else {
174     [NSException raise:@"NSInvalidArgumentException"
175                  format:@"unknown conversation char %c", _c];
176   }
177   return obj;
178 }
179
180 @end /* NSPredicateVAParserContext */
181
182 @implementation NSPredicateEnumeratorParserContext
183
184 + (id)contextWithEnumerator:(NSEnumerator *)_enumerator {
185   return [[[NSPredicateEnumeratorParserContext alloc]
186                                       initWithEnumerator:_enumerator] autorelease];
187 }
188
189 - (id)initWithEnumerator:(NSEnumerator *)_enumerator {
190   if ((self = [super init])) {
191     ASSIGN(self->enumerator, _enumerator);
192   }
193   return self;
194 }
195
196 - (void)dealloc {
197   [self->enumerator release];
198   [super dealloc];;
199 }
200
201 - (id)getObjectFromStackFor:(char)_c {
202   static Class NumberClass = Nil;
203   id o;
204
205   if (NumberClass == Nil) NumberClass = [NSNumber class];
206
207   o = [self->enumerator nextObject];
208   switch (_c) {
209     case '@':
210 #if USE_DESCRIPTION_FOR_AT
211       return [o description];
212 #else
213       return o;
214 #endif
215     
216     case 'f':
217       return [NumberClass numberWithDouble:[o doubleValue]];
218       
219     case 'd':
220       return [NumberClass numberWithInt:[o intValue]];
221       
222     case 's':
223       // return [NSString stringWithCString:[o cString]];
224       return [[o copy] autorelease];
225       
226     default:
227       [NSException raise:@"NSInvalidArgumentException"
228                    format:@"unknown or not allowed conversation char %c", _c];
229   }
230   return nil;
231 }
232
233 @end /* NSPredicateEnumeratorParserContext */
234
235 @implementation NSPredicateParserContext
236
237 - (id)init {
238   if (StringClass == Nil) StringClass = [NSString class];
239   
240   if ((self = [super init])) {
241     self->predicateCache = NSCreateMapTable(NSObjectMapKeyCallBacks,
242                                             NSObjectMapValueCallBacks,
243                                             200);
244   }
245   return self;
246 }
247
248 - (void)dealloc {
249   if (self->predicateCache) NSFreeMapTable(self->predicateCache);
250   [super dealloc];
251 }
252
253 - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos
254 {
255   return NSMapGet(self->predicateCache,
256                   [StringClass stringWithFormat:@"%@_%d", _fct, _pos]);
257 }
258
259 - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct
260   atPos:(unsigned)_pos
261 {
262   NSMapInsert(self->predicateCache,
263               [StringClass stringWithFormat:@"%@_%d", _fct, _pos],
264               _dict);
265 }
266
267 - (id)getObjectFromStackFor:(char)_c {
268   [self doesNotRecognizeSelector:_cmd];
269   return nil;
270 }
271
272 @end /* NSPredicateParserContext */
273
274 @implementation NSPredicate(Parsing)
275
276 + (void)registerValueClass:(Class)_valueClass forTypeName:(NSString *)_type {
277   if (NSPredicateParserTypeMappings == nil)
278     NSPredicateParserTypeMappings = [[NSMutableDictionary alloc] init];
279   
280   if (_type == nil) {
281     NSLog(@"ERROR(%s): got passed no type name!", __PRETTY_FUNCTION__);
282     return;
283   }
284   if (_valueClass == nil) {
285     NSLog(@"ERROR(%s): got passed no value-class for type '%@'!",
286           __PRETTY_FUNCTION__, _type);
287     return;
288   }
289   
290   [NSPredicateParserTypeMappings setObject:_valueClass forKey:_type];
291 }
292
293 + (NSPredicate *)predicateWithFormat:(NSString *)_format,... {
294   va_list     va;
295   NSPredicate *qualifier;
296   unsigned    length = 0;
297   const char  *buf;
298   unsigned    bufLen;
299   char        *cbuf;
300
301   _setupLiterals();
302   if (StringClass == Nil) StringClass = [NSString class];
303   
304   bufLen = [_format cStringLength];
305   cbuf   = malloc(bufLen + 1);
306   [_format getCString:cbuf]; cbuf[bufLen] = '\0';
307   buf = cbuf;
308   
309   va_start(va, _format);
310   qualifier =
311     _parsePredicates([NSPredicateVAParserContext contextWithVaList:&va],
312                      buf, bufLen, &length);
313   va_end(va);
314   
315   if (qualifier != nil) { /* check whether the rest of the string is OK */
316     if (length < bufLen)
317       length += _countWhiteSpaces(buf + length, bufLen - length);
318     
319     if (length < bufLen) {
320       NSLog(@"WARNING(%s): unexpected chars at the end of the "
321             @"string(class=%@,len=%i) '%@'",
322             __PRETTY_FUNCTION__,
323             [_format class],
324             [_format length], _format);
325       NSLog(@"  buf-length: %i", bufLen);
326       NSLog(@"  length:     %i", length);
327       NSLog(@"  char[length]: '%c' (%i) '%s'", buf[length], buf[length],
328             (buf+length));
329       qualifier = nil;
330     }
331     else if (length > bufLen) {
332       NSLog(@"WARNING(%s): length should never be longer than bufLen ?, "
333             @"internal parsing error !",
334             __PRETTY_FUNCTION__);
335     }
336   }
337   free(cbuf);
338   return qualifier;
339 }
340
341 + (NSPredicate *)predicateWithFormat:(NSString *)_format 
342   argumentArray:(NSArray *)_arguments
343 {
344   NSPredicate *qual  = nil;
345   unsigned    length = 0;
346   const char  *buf   = NULL;
347   unsigned    bufLen = 0;
348   NSPredicateEnumeratorParserContext *ctx;
349
350   _setupLiterals();
351   if (StringClass == Nil) StringClass = [NSString class];
352   
353   ctx = [NSPredicateEnumeratorParserContext contextWithEnumerator:
354                                               [_arguments objectEnumerator]];
355   
356   //NSLog(@"qclass: %@", [_format class]);
357   buf    = [_format cString];
358   bufLen = [_format cStringLength];
359   qual   = _parsePredicates(ctx, buf, bufLen, &length);
360   
361   if (qual != nil) { /* check whether the rest of the string is OK */
362     if (length < bufLen) {
363       length += _countWhiteSpaces(buf + length, bufLen - length);
364     }
365     if (length != bufLen) {
366       NSLog(@"WARNING(%s): unexpected chars at the end of the string '%@'",
367             __PRETTY_FUNCTION__, _format);
368       qual = nil;
369     }
370   }
371   return qual;
372 }
373  
374 @end /* NSPredicate(Parsing) */
375
376 static NSPredicate *_parseSinglePredicate(id _ctx, const char *_buf,
377                                             unsigned _bufLen,
378                                             unsigned *_predLen)
379 {
380   NSPredicate *res = nil;
381
382   if ((res = _parseParenthesisPredicate(_ctx, _buf, _bufLen, _predLen))  != nil) {
383     if (qDebug)
384       NSLog(@"_parseSinglePredicate return <%@> for <%s> ", res, _buf);
385
386     return res;
387   }
388   if ((res = _parseNotPredicate(_ctx, _buf, _bufLen, _predLen)) != nil) {
389     if (qDebug)
390       NSLog(@"_parseSinglePredicate return <%@> for <%s> ", res, _buf);
391
392     return res;
393   }
394   if ((res = _parseKeyCompPredicate(_ctx, _buf, _bufLen, _predLen)) != nil) {
395     if (qDebug) {
396       NSLog(@"_parseSinglePredicate return <%@> for <%s> length %d", 
397             res, _buf, *_predLen);
398     }
399     return res;
400   }
401   return nil;
402 }
403
404 static NSPredicate *_parsePredicates(id _ctx, const char *_buf, unsigned _bufLen,
405                                      unsigned *_predLen)
406 {
407   NSPredicate *res = nil;
408
409
410   if ((res = _parseCompoundPredicate(_ctx, _buf, _bufLen, _predLen))) {
411     if (qDebug)
412       NSLog(@"_parsePredicates return <%@> for <%s> ", res, _buf);
413     return res;
414   }
415
416   if ((res = _parseSinglePredicate(_ctx, _buf, _bufLen, _predLen))) {
417     if (qDebug)
418       NSLog(@"_parsePredicates return <%@> for <%s> ", res, _buf);
419     return res;
420   }
421   
422   if (qDebug)
423     NSLog(@"_parsePredicates return nil for <%s> ", _buf);
424
425   return nil;
426 }
427
428 static NSPredicate *_parseParenthesisPredicate(id _ctx, const char *_buf,
429                                                unsigned _bufLen,
430                                                unsigned *_predLen)
431 {
432   unsigned    pos     = 0;
433   unsigned    qualLen = 0;
434   NSPredicate *qual   = nil;
435
436   pos = _countWhiteSpaces(_buf, _bufLen);
437
438   if (_bufLen <= pos + 2) /* at least open and close parenthesis */ {
439     if (qDebug)
440       NSLog(@"1_parseParenthesisPredicate return nil for <%s> ", _buf);
441  
442     return nil;
443   }
444   if (_buf[pos] != '(') {
445     if (qDebug)
446       NSLog(@"2_parseParenthesisPredicate return nil for <%s> ", _buf);
447     
448     return nil;
449   }
450   pos++;
451   if (!(qual = _parsePredicates(_ctx, _buf + pos, _bufLen - pos,
452                                 &qualLen))) {
453     if (qDebug)
454       NSLog(@"3_parseParenthesisPredicate return nil for <%s> ", _buf);
455     
456     return nil;
457   }
458   
459   pos += qualLen;
460   if (_bufLen <= pos) {
461     if (qDebug)
462       NSLog(@"4_parseParenthesisPredicate return nil for <%s> qual[%@] %@ bufLen %d "
463             @"pos %d", _buf, [qual class], qual, _bufLen, pos);
464
465     return nil;
466   }
467   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
468   if (_buf[pos] != ')') {
469     if (qDebug)
470       NSLog(@"5_parseParenthesisPredicate return nil for <%s> [%s] ", _buf, _buf+pos);
471
472     return nil;
473   }
474   if (qDebug)
475     NSLog(@"6_parseParenthesisPredicate return <%@> for <%s> ", qual, _buf);
476   
477   *_predLen = pos + 1; /* one step after the parenthesis */
478   return qual;
479 }
480
481 static NSPredicate *_parseNotPredicate(id _ctx, const char *_buf,
482                                        unsigned _bufLen, unsigned *_predLen)
483 {
484   unsigned    pos, len   = 0;
485   char        c0, c1, c2 = 0;
486   NSPredicate *qual      = nil;
487
488   pos = _countWhiteSpaces(_buf, _bufLen);
489
490   if (_bufLen - pos < 4) { /* at least 3 chars for 'NOT' */
491     if (qDebug)
492       NSLog(@"_parseNotPredicate return nil for <%s> ", _buf);
493     
494     return nil;
495   }
496   c0 = _buf[pos];
497   c1 = _buf[pos + 1];
498   c2 = _buf[pos + 2];
499   if (!(((c0 == 'n') || (c0 == 'N')) &&
500         ((c1 == 'o') || (c1 == 'O')) &&
501         ((c2 == 't') || (c2 == 'T')))) {
502     if (qDebug)
503       NSLog(@"_parseNotPredicate return nil for <%s> ", _buf);
504     return nil;
505   }
506   pos += 3;
507   qual = _parseSinglePredicate(_ctx, _buf + pos, _bufLen - pos, &len);
508   if (qual == nil) {
509     if (qDebug)
510       NSLog(@"_parseNotPredicate return nil for <%s> ", _buf);
511     
512     return nil;
513   }
514   *_predLen = pos +len;
515   if (qDebug)
516     NSLog(@"_parseNotPredicate return %@ for <%s> ", qual, _buf);
517   
518   return [NSCompoundPredicate notPredicateWithSubpredicates:
519                                 [NSArray arrayWithObjects:&qual count:1]];
520 }
521
522 static SEL operatorSelectorForString(NSString *_str)
523 {
524   // TODO: fix for NSPredicate
525   static NSMapTable *operatorToSelector = NULL; // THREAD
526   SEL s;
527
528   if (operatorToSelector == NULL) {
529     operatorToSelector = NSCreateMapTable(NSObjectMapKeyCallBacks,
530                                           NSIntMapValueCallBacks,
531                                           10);
532     NSMapInsert(operatorToSelector, @"=",  
533                 (void *)NSEqualToPredicateOperatorType);
534     NSMapInsert(operatorToSelector, @"==", 
535                 (void *)NSEqualToPredicateOperatorType);
536     NSMapInsert(operatorToSelector, @"!=", 
537                 (void *)NSNotEqualToPredicateOperatorType);
538     NSMapInsert(operatorToSelector, @"<>", 
539                 (void *)NSNotEqualToPredicateOperatorType);
540     NSMapInsert(operatorToSelector, @"<",  
541                 (void *)NSLessThanPredicateOperatorType);
542     NSMapInsert(operatorToSelector, @">",  
543                 (void *)NSGreaterThanPredicateOperatorType);
544     
545     NSMapInsert(operatorToSelector, @"<=",  
546                 (void *)NSLessThanOrEqualToPredicateOperatorType);
547     NSMapInsert(operatorToSelector, @">=",
548                 (void *)NSGreaterThanOrEqualToPredicateOperatorType);
549
550     NSMapInsert(operatorToSelector, @"like", 
551                 (void *)NSLikePredicateOperatorType);
552     NSMapInsert(operatorToSelector, @"LIKE", 
553                 (void *)NSLikePredicateOperatorType);
554     
555 #if 0 // TODO
556     // TODO: need to have options here
557     NSMapInsert(operatorToSelector, @"caseInsensitiveLike",
558                 NSLikePredicateOperatorType);
559 #endif
560   }
561   
562   if ((s = NSMapGet(operatorToSelector, _str)))
563     return s;
564   
565   return NSSelectorFromString(_str);
566 }
567
568 static NSPredicate *_parseKeyCompPredicate(id _ctx, const char *_buf,
569                                            unsigned _bufLen, 
570                                            unsigned *_predLen)
571 {
572   NSExpression *lhs, *rhs;
573   NSString     *key       = nil;
574   NSString     *op        = nil;
575   NSString     *value     = nil;
576   NSPredicate  *qual      = nil;
577   NSDictionary *dict      = nil;
578   SEL          sel        = NULL;
579   unsigned     length     = 0;
580   unsigned     pos        = 0;
581   BOOL         valueIsKey = NO;
582
583   dict = [_ctx resultForFunction:@"parseKeyCompPredicate" atPos:(unsigned)_buf];
584   if (dict != nil) {
585     if (qDebug)
586       NSLog(@"_parseKeyCompQual return <%@> [cached] for <%s> ", dict, _buf);
587     
588     *_predLen = [[dict objectForKey:@"length"] unsignedIntValue];
589     return [dict objectForKey:@"object"];
590   }
591   pos = _countWhiteSpaces(_buf, _bufLen);
592
593   if ((key = _parseKey(_ctx , _buf + pos, _bufLen - pos, &length)) == nil) {
594     if (qDebug)
595       NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
596     
597     return nil;
598   }
599   pos += length;
600   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
601
602   if (!(op = _parseOp(_buf + pos, _bufLen - pos, &length))) {
603     if (qDebug)
604       NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
605     return nil;
606   }
607
608   sel = operatorSelectorForString(op);
609   if (sel == NULL) {
610     NSLog(@"WARNING(%s): possible unknown operator <%@>", __PRETTY_FUNCTION__,
611           op);
612     if (qDebug)
613       NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
614     return nil;
615   }
616   pos       +=length;
617   pos       += _countWhiteSpaces(_buf + pos, _bufLen - pos);
618   valueIsKey = NO;  
619   
620   value = _parseValue(_ctx, _buf + pos, _bufLen - pos, &length);
621   if (value == nil) {
622     value = _parseKey(_ctx, _buf + pos, _bufLen - pos, &length);
623     if (value == nil) {
624       if (qDebug)
625         NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
626       return nil;
627     }
628     else
629       valueIsKey = YES;
630   }
631   pos      +=length;  
632   *_predLen = pos;
633
634   lhs = [NSExpression expressionForKeyPath:key];
635   rhs = valueIsKey
636     ? [NSExpression expressionForKeyPath:value]
637     : [NSExpression expressionForConstantValue:value];
638   
639   qual = [NSComparisonPredicate predicateWithLeftExpression:lhs
640                                 rightExpression:rhs
641                                 customSelector:sel];
642   if (qDebug)
643     NSLog(@"_parseKeyCompPredicate return <%@> for <%s> ", qual, _buf);
644
645   if (qual != nil) {
646     id keys[2], values[2];
647     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
648     keys[1] = @"object"; values[1] = qual;
649     [_ctx setResult:
650             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
651           forFunction:@"parseKeyCompPredicate"
652           atPos:(unsigned)_buf];
653     *_predLen = pos;
654   }
655   return qual;
656 }
657
658 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
659                           unsigned *_opLen)
660 {
661   unsigned pos = 0;
662   char     c0  = 0;
663   char     c1  = 0;  
664
665   if (_bufLen == 0) {
666     if (qDebug)
667       NSLog(@"_parseOp _bufLen == 0 --> return nil");
668     return nil;
669   }
670   pos = _countWhiteSpaces(_buf, _bufLen);
671   if (_bufLen - pos > 1) {/* at least an operation and a value */
672     c0 = _buf[pos];
673     c1 = _buf[pos+1];  
674
675     if (((c0 >= '<') && (c0 <= '>')) || (c0 == '!')) {
676       NSString *result;
677       
678       if ((c1 >= '<') && (c1 <= '>')) {
679         *_opLen = 2;
680         result = [StringClass stringWithCString:_buf + pos length:2];
681         if (qDebug)
682           NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
683       }
684       else {
685         *_opLen = 1;
686         result = [StringClass stringWithCString:&c0 length:1];
687         if (qDebug)
688           NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
689       }
690       return result;
691     }
692     else { /* string designator operator */
693       unsigned opStart = pos;
694       while (pos < _bufLen) {
695         if (_buf[pos] == ' ')
696           break;
697         pos++;
698       }
699       if (pos >= _bufLen) {
700         NSLog(@"WARNING(%s): found end of string during operator parsing",
701               __PRETTY_FUNCTION__);
702       }
703
704       if (qDebug) {
705         NSLog(@"%s: _parseOp return <%@> for <%s> ", __PRETTY_FUNCTION__,
706               [StringClass stringWithCString:_buf + opStart
707                            length:pos - opStart], _buf);
708       }
709       
710       *_opLen = pos;
711       return [StringClass stringWithCString:_buf + opStart length:pos - opStart];
712     }
713   }
714   if (qDebug)
715     NSLog(@"_parseOp return nil for <%s> ", _buf);
716   return nil;
717 }
718
719 static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen,
720                            unsigned *_keyLen)
721
722   id           result   = nil;
723   NSDictionary *dict    = nil;
724   unsigned     pos      = 0;
725   unsigned     startKey = 0;
726   char         c        = 0;
727
728   if (_bufLen == 0) {
729     if (qDebug) NSLog(@"%s: _bufLen == 0 --> return nil", __PRETTY_FUNCTION__);
730     return nil;
731   }
732   dict = [_ctx resultForFunction:@"parseKey" atPos:(unsigned)_buf];
733   if (dict != nil) {
734     if (qDebug) {
735       NSLog(@"%s: return <%@> [cached] for <%s> ", __PRETTY_FUNCTION__,
736             dict, _buf);
737     }
738     *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
739     return [dict objectForKey:@"object"];
740   }
741   pos      = _countWhiteSpaces(_buf, _bufLen);
742   startKey = pos;
743   c        = _buf[pos];
744
745   if (c == '%') {
746     if (_bufLen - pos < 2) {
747       if (qDebug) {
748         NSLog(@"%s: [c==%%,bufLen-pos<2]: _parseValue return nil for <%s> ", 
749               __PRETTY_FUNCTION__, _buf);
750       }
751       return nil;
752     }
753     pos++;
754     result = [_ctx getObjectFromStackFor:_buf[pos]];
755     pos++;
756   }
757   else {
758     /* '{' for namspaces */
759     register BOOL isQuotedKey = NO;
760
761     if (c == '"')
762       isQuotedKey = YES;
763     else if (!(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
764              c == '{')) {
765       if (qDebug) {
766         NSLog(@"%s: [c!=AZaz{]: _parseKey return nil for <%s> ", 
767               __PRETTY_FUNCTION__, _buf);
768       }
769       return nil;
770     }
771     
772     pos++;
773     while (pos < _bufLen) {
774       c = _buf[pos];
775       if (isQuotedKey && c == '"')
776         break;
777       else if
778         ((c == ' ') || (c == '<') || (c == '>') || (c == '=') || (c == '!') ||
779          c == ')' || c == '(')
780         break;
781       pos++;    
782     }
783     if (isQuotedKey) {
784       pos++; // skip quote
785       result = [StringClass stringWithCString:(_buf + startKey + 1) 
786                             length:(pos - startKey - 2)];
787     }
788     else {
789       result = [StringClass stringWithCString:(_buf + startKey) 
790                             length:(pos - startKey)];
791     }
792   }
793   *_keyLen = pos;  
794   if (qDebug)
795     NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, result, _buf);
796   
797   if (result != nil) {
798     id keys[2], values[2];
799     
800     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
801     keys[1] = @"object"; values[1] = result;
802     
803     [_ctx setResult:
804             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
805           forFunction:@"parseKey"
806           atPos:(unsigned)_buf];
807     *_keyLen = pos;
808   }
809   return result;
810 }
811
812 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
813                       unsigned *_keyLen)
814 {
815   NSString     *cast = nil;
816   NSDictionary *dict = nil;
817   id           obj   = nil;
818   unsigned     pos   = 0;
819   char         c     = 0;
820   
821   if (NumberClass == Nil) NumberClass = [NSNumber class];
822   if (null == nil) null = [[NSNull null] retain];
823   
824   if (_bufLen == 0) {
825     if (qDebug) NSLog(@"_parseValue _bufLen == 0 --> return nil");
826     return nil;
827   }
828   
829   dict = [_ctx resultForFunction:@"parseValue" atPos:(unsigned)_buf];
830   if (dict != nil) {
831     if (qDebug) {
832       NSLog(@"_parseKeyCompPredicate return <%@> [cached] for <%s> ",
833             dict, _buf);
834     }
835     *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
836     return [dict objectForKey:@"object"];
837   }
838   
839   pos = _countWhiteSpaces(_buf, _bufLen);
840   c   = _buf[pos];
841   
842   if (c == '$') { /* found NSPredicateVariable */
843     unsigned startVar = 0;
844     NSString *varKey;
845     
846     pos++;
847     startVar = pos;
848     while (pos < _bufLen) {
849       if ((_buf[pos] == ' ') || (_buf[pos] == ')'))
850         break;
851       pos++;
852     }
853
854     varKey = [StringClass stringWithCString:(_buf + startVar)
855                           length:pos - startVar];
856     obj = [NSExpression expressionForVariable:varKey];
857   }
858   else {
859     /* first, check for CAST */
860     BOOL parseComplexCast = NO;
861     
862     if (c == 'c' && _bufLen > 14) {
863       if (strstr(_buf, "cast") == _buf && (isspace(_buf[4]) || _buf[4]=='(')) {
864         /* for example: cast("1970-01-01T00:00:00Z" as 'dateTime') [min 15 #]*/
865         pos += 4; /* skip 'cast' */
866         while (isspace(_buf[pos])) /* skip spaces */
867           pos++;
868         if (_buf[pos] != '(') {
869           NSLog(@"WARNING(%s): got unexpected cast string: '%s'",
870                 __PRETTY_FUNCTION__, _buf);
871         }
872         else
873           pos++; /* skip opening bracket '(' */
874         
875         parseComplexCast = YES;
876         c = _buf[pos];
877       }
878     }
879     else if (c == '(') { /* starting with a cast */
880       /* for example: (NSCalendarDate)"1999-12-12" [min 5 chars] */
881       unsigned startCast = 0;
882       
883       pos++;
884       startCast = pos;
885       while (pos < _bufLen) {
886         if (_buf[pos] == ')')
887           break;
888         pos++;
889       }
890       pos++;
891       if (pos >= _bufLen) {
892         NSLog(@"WARNING(%s): found end of string while reading a cast",
893               __PRETTY_FUNCTION__);
894         return nil;
895       }
896       c    = _buf[pos];
897       cast = [StringClass stringWithCString:(_buf + startCast)
898                           length:(pos - 1 - startCast)];
899       if (qDebug)
900         NSLog(@"%s: got cast %@", __PRETTY_FUNCTION__, cast);
901     }
902     
903     /* next, check for FORMAT SPECIFIER */
904     if (c == '%') {
905       if (_bufLen - pos < 2) {
906         if (qDebug)
907           NSLog(@"_parseValue return nil for <%s> ", _buf);
908         
909         return nil;
910       }
911       pos++;
912       obj = [_ctx getObjectFromStackFor:_buf[pos]];
913       pos++;
914     }
915     
916     /* next, check for A NUMBER */
917     else if (((c >= '0') && (c <= '9')) || (c == '-')) { /* got a number */
918       unsigned startNumber;
919
920       startNumber = pos;
921       pos++;
922       while (pos < _bufLen) {
923         c = _buf[pos];
924         if (!((c >= '0') && (c <= '9')))
925           break;
926         pos++;
927       }
928       obj = [NumberClass numberWithInt:atoi(_buf + startNumber)];
929     }
930
931     /* check for some text literals */
932     if ((obj == nil) && ((_bufLen - pos) > 1)) {
933       unsigned char i;
934       
935       for (i = 0; i < 20 && (toks[i].token != NULL) && (obj == nil); i++) {
936         const unsigned char *tok;
937         unsigned char toklen;
938         int rc;
939         
940         tok = toks[i].token;
941         toklen = strlen((const char *)tok);
942         if ((_bufLen - pos) < toklen)
943           /* remaining string not long enough */
944           continue;
945         
946         rc = toks[i].scase 
947           ? strncmp(&(_buf[pos]),     (const char *)tok, toklen)
948           : strncasecmp(&(_buf[pos]), (const char *)tok, toklen);
949         if (rc != 0)
950           /* does not match */
951           continue;
952         if (!(_buf[pos + toklen] == '\0' || isspace(_buf[pos + toklen])))
953           /* not at the string end or folloed by a space */
954           continue;
955
956         /* wow, found the token */
957         pos += toklen; /* skip it */
958         obj = toks[i].value;
959       }
960     }
961     
962     /* next, check for STRING */
963     if (obj == nil) {
964       if ((c == '\'') || (c == '"')) {
965         NSString *res                  = nil;
966         char     string[_bufLen - pos];
967         unsigned cnt                   = 0;
968       
969         pos++;
970         while (pos < _bufLen) {
971           char ch = _buf[pos];
972           if (ch == c)
973             break;
974           if ((ch == '\\') && (_bufLen > (pos + 1))) {
975             if (_buf[pos + 1] == c) {
976               pos += 1;
977               ch = c;
978             }
979           }
980           string[cnt++] = ch;
981           pos++;
982         }
983         if (pos >= _bufLen) {
984           NSLog(@"WARNING(%s): found end of string before end of quoted text",
985                 __PRETTY_FUNCTION__);
986           return nil;
987         }
988         res = [StringClass stringWithCString:string length:cnt];
989         pos++; /* don`t forget quotations */
990         if (qDebug) NSLog(@"_parseValue return <%@> for <%s> ", res, _buf);
991         obj = res;
992       }
993     }
994     
995     /* complete parsing of cast */
996     if (parseComplexCast && (pos + 6) < _bufLen) {
997       /* now we need " as 'dateTime'" [min 7 #] */
998       
999       /* skip spaces */
1000       while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1001       
1002       //printf("POS: '%s'\n", &(_buf[pos]));
1003       /* parse 'as' */
1004       if (_buf[pos] != 'a' && _buf[pos] != 'A')
1005         NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__);
1006       else if (_buf[pos + 1] != 's' && _buf[pos + 1] != 'S')
1007         NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__);
1008       else {
1009         /* skip AS */
1010         pos += 2;
1011         
1012         /* skip spaces */
1013         while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1014         
1015         /* read cast type */
1016         if (_buf[pos] != '\'') {
1017           NSLog(@"%s: expected type of complex cast ...", __PRETTY_FUNCTION__);
1018         }
1019         else {
1020           const unsigned char *cs, *ce;
1021           
1022           //printf("POS: '%s'\n", &(_buf[pos]));
1023           pos++;
1024           cs = (const unsigned char *)&(_buf[pos]);
1025           ce = (const unsigned char *)index((const char *)cs, '\'');
1026           cast = [NSString stringWithCString:(const char*)cs length:(ce - cs)];
1027           if (qDebug) {
1028             NSLog(@"%s: parsed complex cast: '%@' to '%@'", 
1029                   __PRETTY_FUNCTION__, obj, cast);
1030           }
1031           pos += (ce - cs);
1032           pos++; // skip '
1033           pos++; // skip )
1034           //printf("POS: '%s'\n", &(_buf[pos]));
1035         }
1036       }
1037     }
1038   }
1039   
1040   if (cast != nil && obj != nil) {
1041     Class class = Nil;
1042     id orig = obj;
1043     
1044     if ((class = [NSPredicateParserTypeMappings objectForKey:cast]) == nil) {
1045       /* no value explicitly mapped to class, try to construct class name... */
1046       NSString *className;
1047
1048       className = cast;
1049       if ((class = NSClassFromString(className)) == Nil) {
1050         /* check some default cast types ... */
1051         className = [cast lowercaseString];
1052         
1053         if ([className isEqualToString:@"datetime"])
1054           class = [NSCalendarDate class];
1055         else if ([className isEqualToString:@"datetime.tz"])
1056           class = [NSCalendarDate class];
1057       }
1058     }
1059     if (class) {
1060       obj = [[[class alloc] initWithString:[orig description]] autorelease];
1061       
1062       if (obj == nil) {
1063         NSLog(@"%s: could not init object '%@' of cast class %@(%@) !",
1064               __PRETTY_FUNCTION__, orig, class, cast);
1065         obj = null;
1066       }
1067     }
1068     else {
1069       NSLog(@"WARNING(%s): could not map cast '%@' to a class "
1070             @"(returning null) !", 
1071             __PRETTY_FUNCTION__, cast);
1072       obj = null;
1073     }
1074   }
1075   
1076   if (qDebug) {
1077     NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, 
1078           obj?obj:@"<nil>", _buf);
1079   }
1080   
1081   if (obj != nil) {
1082     id keys[2], values[2];
1083     
1084     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
1085     keys[1] = @"object"; values[1] = obj;
1086     
1087     [_ctx setResult:
1088             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
1089           forFunction:@"parseValue" atPos:(unsigned)_buf];
1090     *_keyLen = pos;
1091   }
1092   return obj;
1093 }
1094
1095 static NSPredicate *_testOperator(id _ctx, const char *_buf,
1096                                   unsigned _bufLen, unsigned *_opLen,
1097                                   BOOL *isAnd)
1098 {
1099   NSPredicate *qual       = nil;
1100   char        c0, c1, c2  = 0;
1101   unsigned    pos, len    = 0;
1102
1103   pos = _countWhiteSpaces(_buf, _bufLen);  
1104   
1105   if (_bufLen < 4) {/* at least OR or AND and somethink more */   
1106     if (qDebug)
1107       NSLog(@"_testOperator return nil for <%s> ", _buf);
1108     return nil;
1109   }
1110   c0 = _buf[pos + 0];
1111   c1 = _buf[pos + 1];
1112   c2 = _buf[pos + 2];
1113   
1114   if (((c0 == 'a') || (c0  == 'A')) &&
1115         ((c1 == 'n') || (c1  == 'N')) &&
1116         ((c2 == 'd') || (c2  == 'D'))) {
1117       pos    += 3;
1118       *isAnd  = YES;
1119   }
1120   else if (((c0 == 'o') || (c0  == 'O')) && ((c1 == 'r') || (c1  == 'R'))) {
1121       pos    += 2;
1122       *isAnd  = NO;
1123   }
1124   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
1125   qual = _parseSinglePredicate(_ctx, _buf + pos, _bufLen - pos, &len);
1126   *_opLen = pos + len;
1127   if (qDebug)
1128     NSLog(@"_testOperator return %@ for <%s> ", qual, _buf);
1129   
1130   return qual;
1131 }
1132
1133 static NSPredicate *_parseCompoundPredicate(id _ctx, const char *_buf,
1134                                             unsigned _bufLen, 
1135                                             unsigned *_predLen)
1136 {
1137   NSPredicate    *q0, *q1 = nil;
1138   NSMutableArray *array   = nil;
1139   unsigned       pos, len = 0;
1140   NSPredicate    *result;
1141   BOOL           isAnd;
1142
1143   isAnd = YES;
1144
1145   if ((q0 = _parseSinglePredicate(_ctx, _buf, _bufLen, &len)) == nil) {
1146     if (qDebug)
1147       NSLog(@"_parseAndOrPredicate return nil for <%s> ", _buf);
1148     
1149     return nil;
1150   }
1151   pos = len;
1152
1153   if (!(q1 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &isAnd))) {
1154     if (qDebug)
1155       NSLog(@"_parseAndOrPredicate return nil for <%s> ", _buf);
1156     return nil;
1157   }
1158   pos  += len;
1159   array = [NSMutableArray arrayWithObjects:q0, q1, nil];
1160   
1161   while (YES) {
1162     BOOL newIsAnd;
1163
1164     newIsAnd = YES;
1165     q0       = _testOperator(_ctx,  _buf + pos, _bufLen - pos, &len, &newIsAnd);
1166
1167     if (!q0)
1168       break;
1169     
1170     if (newIsAnd != isAnd) {
1171       NSArray *a;
1172
1173       a = [[array copy] autorelease];
1174       
1175       q1 = (isAnd)
1176         ? [NSCompoundPredicate andPredicateWithSubpredicates:a]
1177         : [NSCompoundPredicate orPredicateWithSubpredicates:a];
1178       
1179       [array removeAllObjects];
1180       [array addObject:q1];
1181       isAnd = newIsAnd;
1182     }
1183     [array addObject:q0];
1184
1185     pos += len;
1186   }
1187
1188   *_predLen = pos;
1189   result = (isAnd)
1190     ? [NSCompoundPredicate andPredicateWithSubpredicates:array]
1191     : [NSCompoundPredicate orPredicateWithSubpredicates:array];
1192   
1193   if (qDebug)
1194     NSLog(@"_parseAndOrPredicate return <%@> for <%s> ", result, _buf);
1195
1196   return result;
1197 }
1198
1199 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen) {
1200   unsigned cnt = 0;
1201   
1202   if (_bufLen == 0) {
1203     if (qDebug)
1204       NSLog(@"_parseString _bufLen == 0 --> return nil");
1205     return 0;
1206   }
1207   
1208   while (_buf[cnt] == ' ' || _buf[cnt] == '\t' || 
1209          _buf[cnt] == '\n' || _buf[cnt] == '\r') {
1210     cnt++;
1211     if (cnt == _bufLen)
1212       break;
1213   }
1214   return cnt;
1215 }