4 Copyright (C) 2000-2005 SKYRIX Software AG
7 Author: Helge Hess <helge.hess@opengroupware.org>
9 This file is part of libFoundation.
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
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.
26 #include "NSPredicate.h"
27 #include "NSComparisonPredicate.h"
28 #include "NSCompoundPredicate.h"
29 #include "NSExpression.h"
31 #include "NSException.h"
32 #include "NSDictionary.h"
34 #include "NSCalendarDate.h"
37 //#define USE_DESCRIPTION_FOR_AT 1
39 static int qDebug = 0;
40 static NSMutableDictionary *NSPredicateParserTypeMappings = nil;
43 The literals understood by the value parser.
45 NOTE: Any literal used here can never be used as a key ! So add as little
49 const unsigned char *token;
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 }
64 static inline void _setupLiterals(void) {
65 static BOOL didSetup = NO;
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;
77 static Class StringClass = Nil;
78 static Class NumberClass = Nil;
79 static NSNull *null = nil;
81 /* parsing functions */
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,
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,
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,
99 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
101 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen);
102 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
105 @interface NSPredicateParserContext : NSObject
107 NSMapTable *predicateCache;
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;
117 @interface NSPredicateVAParserContext : NSPredicateParserContext
121 + (id)contextWithVaList:(va_list *)_va;
122 - (id)initWithVaList:(va_list *)_va;
125 @interface NSPredicateEnumeratorParserContext : NSPredicateParserContext
127 NSEnumerator *enumerator;
129 + (id)contextWithEnumerator:(NSEnumerator *)_enumerator;
130 - (id)initWithEnumerator:(NSEnumerator *)_enumerator;
133 @implementation NSPredicateVAParserContext
135 + (id)contextWithVaList:(va_list *)_va {
136 return [[[NSPredicateVAParserContext alloc] initWithVaList:_va] autorelease];
139 - (id)initWithVaList:(va_list *)_va {
140 if ((self = [super init])) {
146 - (id)getObjectFromStackFor:(char)_c {
149 if (StringClass == Nil) StringClass = [NSString class];
150 if (NumberClass == Nil) NumberClass = [NSNumber class];
151 if (null == nil) null = [NSNull null];
154 char *str = va_arg(*self->va, char*);
155 obj = [StringClass stringWithCString:str];
157 else if (_c == 'd') {
158 int i= va_arg(*self->va, int);
159 obj = [NumberClass numberWithInt:i];
161 else if (_c == 'f') {
162 double d = va_arg(*self->va, double);
163 obj = [NumberClass numberWithDouble:d];
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];
170 obj = (o == nil) ? (id)null : (id)o;
174 [NSException raise:@"NSInvalidArgumentException"
175 format:@"unknown conversation char %c", _c];
180 @end /* NSPredicateVAParserContext */
182 @implementation NSPredicateEnumeratorParserContext
184 + (id)contextWithEnumerator:(NSEnumerator *)_enumerator {
185 return [[[NSPredicateEnumeratorParserContext alloc]
186 initWithEnumerator:_enumerator] autorelease];
189 - (id)initWithEnumerator:(NSEnumerator *)_enumerator {
190 if ((self = [super init])) {
191 ASSIGN(self->enumerator, _enumerator);
197 [self->enumerator release];
201 - (id)getObjectFromStackFor:(char)_c {
202 static Class NumberClass = Nil;
205 if (NumberClass == Nil) NumberClass = [NSNumber class];
207 o = [self->enumerator nextObject];
210 #if USE_DESCRIPTION_FOR_AT
211 return [o description];
217 return [NumberClass numberWithDouble:[o doubleValue]];
220 return [NumberClass numberWithInt:[o intValue]];
223 // return [NSString stringWithCString:[o cString]];
224 return [[o copy] autorelease];
227 [NSException raise:@"NSInvalidArgumentException"
228 format:@"unknown or not allowed conversation char %c", _c];
233 @end /* NSPredicateEnumeratorParserContext */
235 @implementation NSPredicateParserContext
238 if (StringClass == Nil) StringClass = [NSString class];
240 if ((self = [super init])) {
241 self->predicateCache = NSCreateMapTable(NSObjectMapKeyCallBacks,
242 NSObjectMapValueCallBacks,
249 if (self->predicateCache) NSFreeMapTable(self->predicateCache);
253 - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos
255 return NSMapGet(self->predicateCache,
256 [StringClass stringWithFormat:@"%@_%d", _fct, _pos]);
259 - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct
262 NSMapInsert(self->predicateCache,
263 [StringClass stringWithFormat:@"%@_%d", _fct, _pos],
267 - (id)getObjectFromStackFor:(char)_c {
268 [self doesNotRecognizeSelector:_cmd];
272 @end /* NSPredicateParserContext */
274 @implementation NSPredicate(Parsing)
276 + (void)registerValueClass:(Class)_valueClass forTypeName:(NSString *)_type {
277 if (NSPredicateParserTypeMappings == nil)
278 NSPredicateParserTypeMappings = [[NSMutableDictionary alloc] init];
281 NSLog(@"ERROR(%s): got passed no type name!", __PRETTY_FUNCTION__);
284 if (_valueClass == nil) {
285 NSLog(@"ERROR(%s): got passed no value-class for type '%@'!",
286 __PRETTY_FUNCTION__, _type);
290 [NSPredicateParserTypeMappings setObject:_valueClass forKey:_type];
293 + (NSPredicate *)predicateWithFormat:(NSString *)_format,... {
295 NSPredicate *qualifier;
302 if (StringClass == Nil) StringClass = [NSString class];
304 bufLen = [_format cStringLength];
305 cbuf = malloc(bufLen + 1);
306 [_format getCString:cbuf]; cbuf[bufLen] = '\0';
309 va_start(va, _format);
311 _parsePredicates([NSPredicateVAParserContext contextWithVaList:&va],
312 buf, bufLen, &length);
315 if (qualifier != nil) { /* check whether the rest of the string is OK */
317 length += _countWhiteSpaces(buf + length, bufLen - length);
319 if (length < bufLen) {
320 NSLog(@"WARNING(%s): unexpected chars at the end of the "
321 @"string(class=%@,len=%i) '%@'",
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],
331 else if (length > bufLen) {
332 NSLog(@"WARNING(%s): length should never be longer than bufLen ?, "
333 @"internal parsing error !",
334 __PRETTY_FUNCTION__);
341 + (NSPredicate *)predicateWithFormat:(NSString *)_format
342 argumentArray:(NSArray *)_arguments
344 NSPredicate *qual = nil;
346 const char *buf = NULL;
348 NSPredicateEnumeratorParserContext *ctx;
351 if (StringClass == Nil) StringClass = [NSString class];
353 ctx = [NSPredicateEnumeratorParserContext contextWithEnumerator:
354 [_arguments objectEnumerator]];
356 //NSLog(@"qclass: %@", [_format class]);
357 buf = [_format cString];
358 bufLen = [_format cStringLength];
359 qual = _parsePredicates(ctx, buf, bufLen, &length);
361 if (qual != nil) { /* check whether the rest of the string is OK */
362 if (length < bufLen) {
363 length += _countWhiteSpaces(buf + length, bufLen - length);
365 if (length != bufLen) {
366 NSLog(@"WARNING(%s): unexpected chars at the end of the string '%@'",
367 __PRETTY_FUNCTION__, _format);
374 @end /* NSPredicate(Parsing) */
376 static NSPredicate *_parseSinglePredicate(id _ctx, const char *_buf,
380 NSPredicate *res = nil;
382 if ((res = _parseParenthesisPredicate(_ctx, _buf, _bufLen, _predLen)) != nil) {
384 NSLog(@"_parseSinglePredicate return <%@> for <%s> ", res, _buf);
388 if ((res = _parseNotPredicate(_ctx, _buf, _bufLen, _predLen)) != nil) {
390 NSLog(@"_parseSinglePredicate return <%@> for <%s> ", res, _buf);
394 if ((res = _parseKeyCompPredicate(_ctx, _buf, _bufLen, _predLen)) != nil) {
396 NSLog(@"_parseSinglePredicate return <%@> for <%s> length %d",
397 res, _buf, *_predLen);
404 static NSPredicate *_parsePredicates(id _ctx, const char *_buf, unsigned _bufLen,
407 NSPredicate *res = nil;
410 if ((res = _parseCompoundPredicate(_ctx, _buf, _bufLen, _predLen))) {
412 NSLog(@"_parsePredicates return <%@> for <%s> ", res, _buf);
416 if ((res = _parseSinglePredicate(_ctx, _buf, _bufLen, _predLen))) {
418 NSLog(@"_parsePredicates return <%@> for <%s> ", res, _buf);
423 NSLog(@"_parsePredicates return nil for <%s> ", _buf);
428 static NSPredicate *_parseParenthesisPredicate(id _ctx, const char *_buf,
433 unsigned qualLen = 0;
434 NSPredicate *qual = nil;
436 pos = _countWhiteSpaces(_buf, _bufLen);
438 if (_bufLen <= pos + 2) /* at least open and close parenthesis */ {
440 NSLog(@"1_parseParenthesisPredicate return nil for <%s> ", _buf);
444 if (_buf[pos] != '(') {
446 NSLog(@"2_parseParenthesisPredicate return nil for <%s> ", _buf);
451 if (!(qual = _parsePredicates(_ctx, _buf + pos, _bufLen - pos,
454 NSLog(@"3_parseParenthesisPredicate return nil for <%s> ", _buf);
460 if (_bufLen <= pos) {
462 NSLog(@"4_parseParenthesisPredicate return nil for <%s> qual[%@] %@ bufLen %d "
463 @"pos %d", _buf, [qual class], qual, _bufLen, pos);
467 pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
468 if (_buf[pos] != ')') {
470 NSLog(@"5_parseParenthesisPredicate return nil for <%s> [%s] ", _buf, _buf+pos);
475 NSLog(@"6_parseParenthesisPredicate return <%@> for <%s> ", qual, _buf);
477 *_predLen = pos + 1; /* one step after the parenthesis */
481 static NSPredicate *_parseNotPredicate(id _ctx, const char *_buf,
482 unsigned _bufLen, unsigned *_predLen)
484 unsigned pos, len = 0;
486 NSPredicate *qual = nil;
488 pos = _countWhiteSpaces(_buf, _bufLen);
490 if (_bufLen - pos < 4) { /* at least 3 chars for 'NOT' */
492 NSLog(@"_parseNotPredicate return nil for <%s> ", _buf);
499 if (!(((c0 == 'n') || (c0 == 'N')) &&
500 ((c1 == 'o') || (c1 == 'O')) &&
501 ((c2 == 't') || (c2 == 'T')))) {
503 NSLog(@"_parseNotPredicate return nil for <%s> ", _buf);
507 qual = _parseSinglePredicate(_ctx, _buf + pos, _bufLen - pos, &len);
510 NSLog(@"_parseNotPredicate return nil for <%s> ", _buf);
514 *_predLen = pos +len;
516 NSLog(@"_parseNotPredicate return %@ for <%s> ", qual, _buf);
518 return [NSCompoundPredicate notPredicateWithSubpredicates:
519 [NSArray arrayWithObjects:&qual count:1]];
522 static SEL operatorSelectorForString(NSString *_str)
524 // TODO: fix for NSPredicate
525 static NSMapTable *operatorToSelector = NULL; // THREAD
528 if (operatorToSelector == NULL) {
529 operatorToSelector = NSCreateMapTable(NSObjectMapKeyCallBacks,
530 NSIntMapValueCallBacks,
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);
545 NSMapInsert(operatorToSelector, @"<=",
546 (void *)NSLessThanOrEqualToPredicateOperatorType);
547 NSMapInsert(operatorToSelector, @">=",
548 (void *)NSGreaterThanOrEqualToPredicateOperatorType);
550 NSMapInsert(operatorToSelector, @"like",
551 (void *)NSLikePredicateOperatorType);
552 NSMapInsert(operatorToSelector, @"LIKE",
553 (void *)NSLikePredicateOperatorType);
556 // TODO: need to have options here
557 NSMapInsert(operatorToSelector, @"caseInsensitiveLike",
558 NSLikePredicateOperatorType);
562 if ((s = NSMapGet(operatorToSelector, _str)))
565 return NSSelectorFromString(_str);
568 static NSPredicate *_parseKeyCompPredicate(id _ctx, const char *_buf,
572 NSExpression *lhs, *rhs;
575 NSString *value = nil;
576 NSPredicate *qual = nil;
577 NSDictionary *dict = nil;
581 BOOL valueIsKey = NO;
583 dict = [_ctx resultForFunction:@"parseKeyCompPredicate" atPos:(unsigned)_buf];
586 NSLog(@"_parseKeyCompQual return <%@> [cached] for <%s> ", dict, _buf);
588 *_predLen = [[dict objectForKey:@"length"] unsignedIntValue];
589 return [dict objectForKey:@"object"];
591 pos = _countWhiteSpaces(_buf, _bufLen);
593 if ((key = _parseKey(_ctx , _buf + pos, _bufLen - pos, &length)) == nil) {
595 NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
600 pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
602 if (!(op = _parseOp(_buf + pos, _bufLen - pos, &length))) {
604 NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
608 sel = operatorSelectorForString(op);
610 NSLog(@"WARNING(%s): possible unknown operator <%@>", __PRETTY_FUNCTION__,
613 NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
617 pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
620 value = _parseValue(_ctx, _buf + pos, _bufLen - pos, &length);
622 value = _parseKey(_ctx, _buf + pos, _bufLen - pos, &length);
625 NSLog(@"_parseKeyCompPredicate return nil for <%s> ", _buf);
634 lhs = [NSExpression expressionForKeyPath:key];
636 ? [NSExpression expressionForKeyPath:value]
637 : [NSExpression expressionForConstantValue:value];
639 qual = [NSComparisonPredicate predicateWithLeftExpression:lhs
643 NSLog(@"_parseKeyCompPredicate return <%@> for <%s> ", qual, _buf);
646 id keys[2], values[2];
647 keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
648 keys[1] = @"object"; values[1] = qual;
650 [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
651 forFunction:@"parseKeyCompPredicate"
652 atPos:(unsigned)_buf];
658 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
667 NSLog(@"_parseOp _bufLen == 0 --> return nil");
670 pos = _countWhiteSpaces(_buf, _bufLen);
671 if (_bufLen - pos > 1) {/* at least an operation and a value */
675 if (((c0 >= '<') && (c0 <= '>')) || (c0 == '!')) {
678 if ((c1 >= '<') && (c1 <= '>')) {
680 result = [StringClass stringWithCString:_buf + pos length:2];
682 NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
686 result = [StringClass stringWithCString:&c0 length:1];
688 NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
692 else { /* string designator operator */
693 unsigned opStart = pos;
694 while (pos < _bufLen) {
695 if (_buf[pos] == ' ')
699 if (pos >= _bufLen) {
700 NSLog(@"WARNING(%s): found end of string during operator parsing",
701 __PRETTY_FUNCTION__);
705 NSLog(@"%s: _parseOp return <%@> for <%s> ", __PRETTY_FUNCTION__,
706 [StringClass stringWithCString:_buf + opStart
707 length:pos - opStart], _buf);
711 return [StringClass stringWithCString:_buf + opStart length:pos - opStart];
715 NSLog(@"_parseOp return nil for <%s> ", _buf);
719 static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen,
723 NSDictionary *dict = nil;
725 unsigned startKey = 0;
729 if (qDebug) NSLog(@"%s: _bufLen == 0 --> return nil", __PRETTY_FUNCTION__);
732 dict = [_ctx resultForFunction:@"parseKey" atPos:(unsigned)_buf];
735 NSLog(@"%s: return <%@> [cached] for <%s> ", __PRETTY_FUNCTION__,
738 *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
739 return [dict objectForKey:@"object"];
741 pos = _countWhiteSpaces(_buf, _bufLen);
746 if (_bufLen - pos < 2) {
748 NSLog(@"%s: [c==%%,bufLen-pos<2]: _parseValue return nil for <%s> ",
749 __PRETTY_FUNCTION__, _buf);
754 result = [_ctx getObjectFromStackFor:_buf[pos]];
758 /* '{' for namspaces */
759 register BOOL isQuotedKey = NO;
763 else if (!(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
766 NSLog(@"%s: [c!=AZaz{]: _parseKey return nil for <%s> ",
767 __PRETTY_FUNCTION__, _buf);
773 while (pos < _bufLen) {
775 if (isQuotedKey && c == '"')
778 ((c == ' ') || (c == '<') || (c == '>') || (c == '=') || (c == '!') ||
779 c == ')' || c == '(')
785 result = [StringClass stringWithCString:(_buf + startKey + 1)
786 length:(pos - startKey - 2)];
789 result = [StringClass stringWithCString:(_buf + startKey)
790 length:(pos - startKey)];
795 NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, result, _buf);
798 id keys[2], values[2];
800 keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
801 keys[1] = @"object"; values[1] = result;
804 [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
805 forFunction:@"parseKey"
806 atPos:(unsigned)_buf];
812 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
815 NSString *cast = nil;
816 NSDictionary *dict = nil;
821 if (NumberClass == Nil) NumberClass = [NSNumber class];
822 if (null == nil) null = [[NSNull null] retain];
825 if (qDebug) NSLog(@"_parseValue _bufLen == 0 --> return nil");
829 dict = [_ctx resultForFunction:@"parseValue" atPos:(unsigned)_buf];
832 NSLog(@"_parseKeyCompPredicate return <%@> [cached] for <%s> ",
835 *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
836 return [dict objectForKey:@"object"];
839 pos = _countWhiteSpaces(_buf, _bufLen);
842 if (c == '$') { /* found NSPredicateVariable */
843 unsigned startVar = 0;
848 while (pos < _bufLen) {
849 if ((_buf[pos] == ' ') || (_buf[pos] == ')'))
854 varKey = [StringClass stringWithCString:(_buf + startVar)
855 length:pos - startVar];
856 obj = [NSExpression expressionForVariable:varKey];
859 /* first, check for CAST */
860 BOOL parseComplexCast = NO;
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 */
868 if (_buf[pos] != '(') {
869 NSLog(@"WARNING(%s): got unexpected cast string: '%s'",
870 __PRETTY_FUNCTION__, _buf);
873 pos++; /* skip opening bracket '(' */
875 parseComplexCast = YES;
879 else if (c == '(') { /* starting with a cast */
880 /* for example: (NSCalendarDate)"1999-12-12" [min 5 chars] */
881 unsigned startCast = 0;
885 while (pos < _bufLen) {
886 if (_buf[pos] == ')')
891 if (pos >= _bufLen) {
892 NSLog(@"WARNING(%s): found end of string while reading a cast",
893 __PRETTY_FUNCTION__);
897 cast = [StringClass stringWithCString:(_buf + startCast)
898 length:(pos - 1 - startCast)];
900 NSLog(@"%s: got cast %@", __PRETTY_FUNCTION__, cast);
903 /* next, check for FORMAT SPECIFIER */
905 if (_bufLen - pos < 2) {
907 NSLog(@"_parseValue return nil for <%s> ", _buf);
912 obj = [_ctx getObjectFromStackFor:_buf[pos]];
916 /* next, check for A NUMBER */
917 else if (((c >= '0') && (c <= '9')) || (c == '-')) { /* got a number */
918 unsigned startNumber;
922 while (pos < _bufLen) {
924 if (!((c >= '0') && (c <= '9')))
928 obj = [NumberClass numberWithInt:atoi(_buf + startNumber)];
931 /* check for some text literals */
932 if ((obj == nil) && ((_bufLen - pos) > 1)) {
935 for (i = 0; i < 20 && (toks[i].token != NULL) && (obj == nil); i++) {
936 const unsigned char *tok;
937 unsigned char toklen;
941 toklen = strlen((const char *)tok);
942 if ((_bufLen - pos) < toklen)
943 /* remaining string not long enough */
947 ? strncmp(&(_buf[pos]), (const char *)tok, toklen)
948 : strncasecmp(&(_buf[pos]), (const char *)tok, toklen);
952 if (!(_buf[pos + toklen] == '\0' || isspace(_buf[pos + toklen])))
953 /* not at the string end or folloed by a space */
956 /* wow, found the token */
957 pos += toklen; /* skip it */
962 /* next, check for STRING */
964 if ((c == '\'') || (c == '"')) {
966 char string[_bufLen - pos];
970 while (pos < _bufLen) {
974 if ((ch == '\\') && (_bufLen > (pos + 1))) {
975 if (_buf[pos + 1] == c) {
983 if (pos >= _bufLen) {
984 NSLog(@"WARNING(%s): found end of string before end of quoted text",
985 __PRETTY_FUNCTION__);
988 res = [StringClass stringWithCString:string length:cnt];
989 pos++; /* don`t forget quotations */
990 if (qDebug) NSLog(@"_parseValue return <%@> for <%s> ", res, _buf);
995 /* complete parsing of cast */
996 if (parseComplexCast && (pos + 6) < _bufLen) {
997 /* now we need " as 'dateTime'" [min 7 #] */
1000 while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1002 //printf("POS: '%s'\n", &(_buf[pos]));
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__);
1013 while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1015 /* read cast type */
1016 if (_buf[pos] != '\'') {
1017 NSLog(@"%s: expected type of complex cast ...", __PRETTY_FUNCTION__);
1020 const unsigned char *cs, *ce;
1022 //printf("POS: '%s'\n", &(_buf[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)];
1028 NSLog(@"%s: parsed complex cast: '%@' to '%@'",
1029 __PRETTY_FUNCTION__, obj, cast);
1034 //printf("POS: '%s'\n", &(_buf[pos]));
1040 if (cast != nil && obj != nil) {
1044 if ((class = [NSPredicateParserTypeMappings objectForKey:cast]) == nil) {
1045 /* no value explicitly mapped to class, try to construct class name... */
1046 NSString *className;
1049 if ((class = NSClassFromString(className)) == Nil) {
1050 /* check some default cast types ... */
1051 className = [cast lowercaseString];
1053 if ([className isEqualToString:@"datetime"])
1054 class = [NSCalendarDate class];
1055 else if ([className isEqualToString:@"datetime.tz"])
1056 class = [NSCalendarDate class];
1060 obj = [[[class alloc] initWithString:[orig description]] autorelease];
1063 NSLog(@"%s: could not init object '%@' of cast class %@(%@) !",
1064 __PRETTY_FUNCTION__, orig, class, cast);
1069 NSLog(@"WARNING(%s): could not map cast '%@' to a class "
1070 @"(returning null) !",
1071 __PRETTY_FUNCTION__, cast);
1077 NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__,
1078 obj?obj:@"<nil>", _buf);
1082 id keys[2], values[2];
1084 keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
1085 keys[1] = @"object"; values[1] = obj;
1088 [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
1089 forFunction:@"parseValue" atPos:(unsigned)_buf];
1095 static NSPredicate *_testOperator(id _ctx, const char *_buf,
1096 unsigned _bufLen, unsigned *_opLen,
1099 NSPredicate *qual = nil;
1100 char c0, c1, c2 = 0;
1101 unsigned pos, len = 0;
1103 pos = _countWhiteSpaces(_buf, _bufLen);
1105 if (_bufLen < 4) {/* at least OR or AND and somethink more */
1107 NSLog(@"_testOperator return nil for <%s> ", _buf);
1114 if (((c0 == 'a') || (c0 == 'A')) &&
1115 ((c1 == 'n') || (c1 == 'N')) &&
1116 ((c2 == 'd') || (c2 == 'D'))) {
1120 else if (((c0 == 'o') || (c0 == 'O')) && ((c1 == 'r') || (c1 == 'R'))) {
1124 pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
1125 qual = _parseSinglePredicate(_ctx, _buf + pos, _bufLen - pos, &len);
1126 *_opLen = pos + len;
1128 NSLog(@"_testOperator return %@ for <%s> ", qual, _buf);
1133 static NSPredicate *_parseCompoundPredicate(id _ctx, const char *_buf,
1137 NSPredicate *q0, *q1 = nil;
1138 NSMutableArray *array = nil;
1139 unsigned pos, len = 0;
1140 NSPredicate *result;
1145 if ((q0 = _parseSinglePredicate(_ctx, _buf, _bufLen, &len)) == nil) {
1147 NSLog(@"_parseAndOrPredicate return nil for <%s> ", _buf);
1153 if (!(q1 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &isAnd))) {
1155 NSLog(@"_parseAndOrPredicate return nil for <%s> ", _buf);
1159 array = [NSMutableArray arrayWithObjects:q0, q1, nil];
1165 q0 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &newIsAnd);
1170 if (newIsAnd != isAnd) {
1173 a = [[array copy] autorelease];
1176 ? [NSCompoundPredicate andPredicateWithSubpredicates:a]
1177 : [NSCompoundPredicate orPredicateWithSubpredicates:a];
1179 [array removeAllObjects];
1180 [array addObject:q1];
1183 [array addObject:q0];
1190 ? [NSCompoundPredicate andPredicateWithSubpredicates:array]
1191 : [NSCompoundPredicate orPredicateWithSubpredicates:array];
1194 NSLog(@"_parseAndOrPredicate return <%@> for <%s> ", result, _buf);
1199 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen) {
1204 NSLog(@"_parseString _bufLen == 0 --> return nil");
1208 while (_buf[cnt] == ' ' || _buf[cnt] == '\t' ||
1209 _buf[cnt] == '\n' || _buf[cnt] == '\r') {