libs-base/Source/NSPredicate.m

3235 lines
79 KiB
Mathematica
Raw Permalink Normal View History

/** Interface for NSPredicate for GNUStep
Copyright (C) 2005 Free Software Foundation, Inc.
Written by: Dr. H. Nikolaus Schaller
Created: 2005
Modifications: Fred Kiefer <FredKiefer@gmx.de>
Date: May 2007
Modifications: Richard Frith-Macdoanld <rfm@gnu.org>
Date: June 2007
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
*/
#import "common.h"
#define EXPOSE_NSComparisonPredicate_IVARS 1
#define EXPOSE_NSCompoundPredicate_IVARS 1
#define EXPOSE_NSExpression_IVARS 1
#import "Foundation/NSComparisonPredicate.h"
#import "Foundation/NSCompoundPredicate.h"
#import "Foundation/NSExpression.h"
#import "Foundation/NSPredicate.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSDate.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSEnumerator.h"
#import "Foundation/NSException.h"
#import "Foundation/NSKeyValueCoding.h"
#import "Foundation/NSNull.h"
#import "Foundation/NSScanner.h"
#import "Foundation/NSValue.h"
#import "GSPrivate.h"
#import "GSFastEnumeration.h"
// For pow()
#include <math.h>
#if defined(HAVE_UNICODE_UREGEX_H)
#include <unicode/uregex.h>
#elif defined(HAVE_ICU_H)
#include <icu.h>
#endif
/* Object to represent the expression beign evaluated.
*/
static NSExpression *evaluatedObjectExpression = nil;
extern void GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*);
@interface GSPredicateScanner : NSScanner
{
NSEnumerator *_args; // Not retained.
unsigned _retrieved;
}
- (id) initWithString: (NSString*)format
args: (NSArray*)args;
- (id) nextArg;
- (BOOL) scanPredicateKeyword: (NSString *) key;
- (NSPredicate *) parse;
- (NSPredicate *) parsePredicate;
- (NSPredicate *) parseAnd;
- (NSPredicate *) parseNot;
- (NSPredicate *) parseOr;
- (NSPredicate *) parseComparison;
- (NSExpression *) parseExpression;
- (NSExpression *) parseFunctionalExpression;
- (NSExpression *) parsePowerExpression;
- (NSExpression *) parseMultiplicationExpression;
- (NSExpression *) parseAdditionExpression;
- (NSExpression *) parseBinaryExpression;
- (NSExpression *) parseSimpleExpression;
@end
@interface GSTruePredicate : NSPredicate
@end
@interface GSFalsePredicate : NSPredicate
@end
@interface GSAndCompoundPredicate : NSCompoundPredicate
@end
@interface GSOrCompoundPredicate : NSCompoundPredicate
@end
@interface GSNotCompoundPredicate : NSCompoundPredicate
@end
@interface NSExpression (Private)
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables;
@end
@interface GSConstantValueExpression : NSExpression
{
@public
id _obj;
}
@end
@interface GSEvaluatedObjectExpression : NSExpression
@end
@interface GSVariableExpression : NSExpression
{
@public
NSString *_variable;
}
@end
@interface GSKeyPathExpression : NSExpression
{
@public
NSString *_keyPath;
}
@end
2022-02-12 13:06:23 +00:00
@interface GSKeyPathCompositionExpression : NSExpression
{
@public
NSExpression *_left;
NSExpression *_right;
}
@end
@interface GSUnionSetExpression : NSExpression
2024-05-26 13:11:21 +00:00
{
@public
NSExpression *_left;
NSExpression *_right;
}
@end
@interface GSIntersectSetExpression : NSExpression
2024-05-26 13:11:21 +00:00
{
@public
NSExpression *_left;
NSExpression *_right;
}
@end
@interface GSMinusSetExpression : NSExpression
{
@public
NSExpression *_left;
NSExpression *_right;
}
@end
@interface GSSubqueryExpression : NSExpression
@end
@interface GSAggregateExpression : NSExpression
{
@public
id _collection;
}
@end
@interface GSFunctionExpression : NSExpression
{
@public
NSString *_function;
NSArray *_args;
unsigned int _argc;
SEL _selector;
NSString *_op; // Not retained;
}
@end
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST)
@interface GSBlockPredicate : NSPredicate
{
GSBlockPredicateBlock _block;
}
- (instancetype) initWithBlock: (GSBlockPredicateBlock)block;
@end
@interface GSBoundBlockPredicate : GSBlockPredicate
{
GS_GENERIC_CLASS(NSDictionary,NSString*,id)* _bindings;
}
- (instancetype) initWithBlock: (GSBlockPredicateBlock)block
bindings: (GS_GENERIC_CLASS(NSDictionary,NSString*,id)*)bindings;
@end
#endif
@implementation NSPredicate
+ (NSPredicate *) predicateWithFormat: (NSString *) format, ...
{
NSPredicate *p;
va_list va;
va_start(va, format);
p = [self predicateWithFormat: format arguments: va];
va_end(va);
return p;
}
+ (NSPredicate *) predicateWithFormat: (NSString *)format
argumentArray: (NSArray *)args
{
GSPredicateScanner *s;
NSPredicate *p;
s = [[GSPredicateScanner alloc] initWithString: format
args: args];
p = [s parse];
RELEASE(s);
return p;
}
+ (NSPredicate *) predicateWithFormat: (NSString *)format
arguments: (va_list)args
{
GSPredicateScanner *s;
NSPredicate *p;
const char *ptr = [format UTF8String];
NSMutableArray *arr = [NSMutableArray arrayWithCapacity: 10];
while (*ptr != 0)
{
char c = *ptr++;
if (c == '%')
{
c = *ptr;
switch (c)
{
case '%':
ptr++;
break;
case 'K':
case '@':
ptr++;
[arr addObject: va_arg(args, id)];
break;
case 'c':
ptr++;
[arr addObject: [NSNumber numberWithChar:
(char)va_arg(args, NSInteger)]];
break;
case 'C':
ptr++;
[arr addObject: [NSNumber numberWithShort:
(short)va_arg(args, NSInteger)]];
break;
case 'd':
case 'D':
case 'i':
ptr++;
[arr addObject: [NSNumber numberWithInt:
va_arg(args, int)]];
break;
case 'o':
case 'O':
case 'u':
case 'U':
case 'x':
case 'X':
ptr++;
[arr addObject: [NSNumber numberWithUnsignedInt:
va_arg(args, unsigned)]];
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
ptr++;
[arr addObject: [NSNumber numberWithDouble:
va_arg(args, double)]];
break;
case 'h':
ptr++;
if (*ptr != 0)
{
c = *ptr;
if (c == 'i')
{
[arr addObject: [NSNumber numberWithShort:
(short)va_arg(args, NSInteger)]];
}
if (c == 'u')
{
[arr addObject: [NSNumber numberWithUnsignedShort:
(unsigned short)va_arg(args, NSInteger)]];
}
}
break;
case 'q':
ptr++;
if (*ptr != 0)
{
c = *ptr;
if (c == 'i')
{
[arr addObject: [NSNumber numberWithLongLong:
va_arg(args, long long)]];
}
if (c == 'u' || c == 'x' || c == 'X')
{
[arr addObject: [NSNumber numberWithUnsignedLongLong:
va_arg(args, unsigned long long)]];
}
}
break;
}
}
else if (c == '\'')
{
while (*ptr != 0)
{
if (*ptr++ == '\'')
{
break;
}
}
}
else if (c == '"')
{
while (*ptr != 0)
{
if (*ptr++ == '"')
{
break;
}
}
}
}
s = [[GSPredicateScanner alloc] initWithString: format
args: arr];
p = [s parse];
RELEASE(s);
return p;
}
+ (NSPredicate *) predicateWithValue: (BOOL)value
{
if (value)
{
return AUTORELEASE([GSTruePredicate new]);
}
else
{
return AUTORELEASE([GSFalsePredicate new]);
}
}
// we don't ever instantiate NSPredicate
- (id) copyWithZone: (NSZone *)z
{
return NSCopyObject(self, 0, z);
}
- (BOOL) evaluateWithObject: (id)object
{
[self subclassResponsibility: _cmd];
return NO;
}
- (NSString *) description
{
return [self predicateFormat];
}
- (NSString *) predicateFormat
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
return AUTORELEASE([self copy]);
}
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
- (BOOL) evaluateWithObject: (id)object
substitutionVariables: (GS_GENERIC_CLASS(NSDictionary, NSString*, id)*)variables
{
return [[self predicateWithSubstitutionVariables: variables]
evaluateWithObject: object];
}
#endif
- (Class) classForCoder
{
return [NSPredicate class];
}
2023-09-20 22:53:52 +00:00
- (void) encodeWithCoder: (NSCoder *) coder
{
// FIXME
[self subclassResponsibility: _cmd];
}
2023-09-20 22:53:52 +00:00
- (id) initWithCoder: (NSCoder *) coder
{
// FIXME
[self subclassResponsibility: _cmd];
return self;
}
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST)
+ (NSPredicate*)predicateWithBlock: (GSBlockPredicateBlock)block
{
return [[[GSBlockPredicate alloc] initWithBlock: block] autorelease];
}
#endif
@end
@implementation GSTruePredicate
- (id) copyWithZone: (NSZone *)z
{
return RETAIN(self);
}
- (BOOL) evaluateWithObject: (id)object
{
return YES;
}
- (NSString *) predicateFormat
{
return @"TRUEPREDICATE";
}
@end
@implementation GSFalsePredicate
- (id) copyWithZone: (NSZone *)z
{
return RETAIN(self);
}
- (BOOL) evaluateWithObject: (id)object
{
return NO;
}
- (NSString *) predicateFormat
{
return @"FALSEPREDICATE";
}
@end
@implementation NSCompoundPredicate
+ (NSPredicate *) andPredicateWithSubpredicates: (NSArray *)list
{
return AUTORELEASE([[GSAndCompoundPredicate alloc]
initWithType: NSAndPredicateType subpredicates: list]);
}
+ (NSPredicate *) notPredicateWithSubpredicate: (NSPredicate *)predicate
{
NSArray *list;
list = [NSArray arrayWithObject: predicate];
return AUTORELEASE([[GSNotCompoundPredicate alloc]
initWithType: NSNotPredicateType subpredicates: list]);
}
+ (NSPredicate *) orPredicateWithSubpredicates: (NSArray *)list
{
return AUTORELEASE([[GSOrCompoundPredicate alloc]
initWithType: NSOrPredicateType subpredicates: list]);
}
- (NSCompoundPredicateType) compoundPredicateType
{
return _type;
}
- (id) initWithType: (NSCompoundPredicateType)type
subpredicates: (NSArray *)list
{
if ((self = [super init]) != nil)
{
_type = type;
ASSIGNCOPY(_subs, list);
}
return self;
}
- (void) dealloc
{
RELEASE(_subs);
[super dealloc];
}
- (id) copyWithZone: (NSZone *)z
{
return [[[self class] alloc] initWithType: _type subpredicates: _subs];
}
- (NSArray *) subpredicates
{
return _subs;
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
unsigned int count = [_subs count];
NSMutableArray *esubs = [NSMutableArray arrayWithCapacity: count];
unsigned int i;
NSPredicate *p;
for (i = 0; i < count; i++)
{
[esubs addObject: [[_subs objectAtIndex: i]
predicateWithSubstitutionVariables: variables]];
}
p = [[[self class] alloc] initWithType: _type subpredicates: esubs];
return AUTORELEASE(p);
}
- (Class) classForCoder
{
return [NSCompoundPredicate class];
}
- (void) encodeWithCoder: (NSCoder *)coder
{
// FIXME
[self subclassResponsibility: _cmd];
}
- (id) initWithCoder: (NSCoder *)coder
{
// FIXME
[self subclassResponsibility: _cmd];
return self;
}
@end
@implementation GSAndCompoundPredicate
- (BOOL) evaluateWithObject: (id) object
{
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *p;
while ((p = [e nextObject]) != nil)
{
if ([p evaluateWithObject: object] == NO)
{
return NO; // any NO returns NO
}
}
return YES; // all are true
}
- (NSString *) predicateFormat
{
NSString *fmt = @"";
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *sub;
unsigned cnt = 0;
while ((sub = [e nextObject]) != nil)
{
// when to add ()? -> if sub is compound and of type "or"
if (cnt == 0)
{
fmt = [sub predicateFormat]; // first
}
else
{
if (cnt == 1
&& [[_subs objectAtIndex: 0]
isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)[_subs objectAtIndex: 0]
compoundPredicateType] == NSOrPredicateType)
{
// we need () around first OR on left side
fmt = [NSString stringWithFormat: @"(%@)", fmt];
}
if ([sub isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *) sub compoundPredicateType]
== NSOrPredicateType)
{
// we need () around right OR
fmt = [NSString stringWithFormat: @"%@ AND (%@)",
fmt, [sub predicateFormat]];
}
else
{
fmt = [NSString stringWithFormat: @"%@ AND %@",
fmt, [sub predicateFormat]];
}
}
cnt++;
}
return fmt;
}
@end
@implementation GSOrCompoundPredicate
- (BOOL) evaluateWithObject: (id)object
{
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *p;
while ((p = [e nextObject]) != nil)
{
if ([p evaluateWithObject: object] == YES)
{
return YES; // any YES returns YES
}
}
return NO; // none is true
}
- (NSString *) predicateFormat
{
NSString *fmt = @"";
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *sub;
while ((sub = [e nextObject]) != nil)
{
if ([fmt length] > 0)
{
fmt = [NSString stringWithFormat: @"%@ OR %@",
fmt, [sub predicateFormat]];
}
else
{
fmt = [sub predicateFormat]; // first
}
}
return fmt;
}
@end
@implementation GSNotCompoundPredicate
- (BOOL) evaluateWithObject: (id)object
{
NSPredicate *sub = [_subs objectAtIndex: 0];
return ![sub evaluateWithObject: object];
}
- (NSString *) predicateFormat
{
NSPredicate *sub = [_subs objectAtIndex: 0];
if ([sub isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)sub compoundPredicateType]
!= NSNotPredicateType)
{
return [NSString stringWithFormat: @"NOT(%@)", [sub predicateFormat]];
}
return [NSString stringWithFormat: @"NOT %@", [sub predicateFormat]];
}
@end
@implementation NSComparisonPredicate
+ (NSPredicate *) predicateWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
customSelector: (SEL) sel
{
return AUTORELEASE([[self alloc] initWithLeftExpression: left
rightExpression: right
customSelector: sel]);
}
+ (NSPredicate *) predicateWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
modifier: (NSComparisonPredicateModifier)modifier
type: (NSPredicateOperatorType)type
options: (NSUInteger)opts
{
return AUTORELEASE([[self alloc] initWithLeftExpression: left
rightExpression: right
modifier: modifier
type: type
options: opts]);
}
- (NSPredicate *) initWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
customSelector: (SEL)sel
{
if ((self = [super init]) != nil)
{
ASSIGN(_left, left);
ASSIGN(_right, right);
_selector = sel;
_type = NSCustomSelectorPredicateOperatorType;
}
return self;
}
- (id) initWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
modifier: (NSComparisonPredicateModifier)modifier
type: (NSPredicateOperatorType)type
options: (NSUInteger)opts
{
if ((self = [super init]) != nil)
{
ASSIGN(_left, left);
ASSIGN(_right, right);
_modifier = modifier;
_type = type;
_options = opts;
}
return self;
}
- (void) dealloc;
{
RELEASE(_left);
RELEASE(_right);
[super dealloc];
}
- (NSComparisonPredicateModifier) comparisonPredicateModifier
{
return _modifier;
}
- (SEL) customSelector
{
return _selector;
}
- (NSExpression *) leftExpression
{
return _left;
}
- (NSUInteger) options
{
return _options;
}
- (NSPredicateOperatorType) predicateOperatorType
{
return _type;
}
- (NSExpression *) rightExpression
{
return _right;
}
- (NSString *) predicateFormat
{
NSString *modi = @"";
NSString *comp = @"?comparison?";
NSString *opt = @"";
switch (_modifier)
{
case NSDirectPredicateModifier:
break;
case NSAnyPredicateModifier:
modi = @"ANY ";
break;
case NSAllPredicateModifier:
modi = @"ALL";
break;
default:
modi = @"?modifier?";
break;
}
switch (_type)
{
case NSLessThanPredicateOperatorType:
comp = @"<";
break;
case NSLessThanOrEqualToPredicateOperatorType:
comp = @"<=";
break;
case NSGreaterThanPredicateOperatorType:
comp = @">=";
break;
case NSGreaterThanOrEqualToPredicateOperatorType:
comp = @">";
break;
case NSEqualToPredicateOperatorType:
comp = @"=";
break;
case NSNotEqualToPredicateOperatorType:
comp = @"!=";
break;
case NSMatchesPredicateOperatorType:
comp = @"MATCHES";
break;
case NSLikePredicateOperatorType:
comp = @"LIKE";
break;
case NSBeginsWithPredicateOperatorType:
comp = @"BEGINSWITH";
break;
case NSEndsWithPredicateOperatorType:
comp = @"ENDSWITH";
break;
case NSInPredicateOperatorType:
comp = @"IN";
break;
case NSCustomSelectorPredicateOperatorType:
comp = NSStringFromSelector(_selector);
break;
case NSContainsPredicateOperatorType:
comp = @"CONTAINS";
break;
case NSBetweenPredicateOperatorType:
comp = @"BETWEEN";
break;
}
switch (_options)
{
case NSCaseInsensitivePredicateOption:
opt = @"[c]";
break;
case NSDiacriticInsensitivePredicateOption:
opt = @"[d]";
break;
case NSCaseInsensitivePredicateOption
| NSDiacriticInsensitivePredicateOption:
opt = @"[cd]";
break;
default:
//opt = @"[?options?]";
break;
}
return [NSString stringWithFormat: @"%@%@ %@%@ %@",
modi, _left, comp, opt, _right];
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
NSExpression *left;
NSExpression *right;
left = [_left _expressionWithSubstitutionVariables: variables];
right = [_right _expressionWithSubstitutionVariables: variables];
if (_type == NSCustomSelectorPredicateOperatorType)
{
return [NSComparisonPredicate predicateWithLeftExpression: left
rightExpression: right
customSelector: _selector];
}
else
{
return [NSComparisonPredicate predicateWithLeftExpression: left
rightExpression: right
modifier: _modifier
type: _type
options: _options];
}
}
#if GS_USE_ICU == 1
static BOOL
GSICUStringMatchesRegex(NSString *string, NSString *regex, NSStringCompareOptions opts)
{
BOOL result = NO;
UErrorCode error = 0;
uint32_t flags = 0;
NSUInteger stringLength = [string length];
NSUInteger regexLength = [regex length];
unichar *stringBuffer;
unichar *regexBuffer;
URegularExpression *icuregex = NULL;
stringBuffer = malloc(stringLength * sizeof(unichar));
if (NULL == stringBuffer) { return NO; }
regexBuffer = malloc(regexLength * sizeof(unichar));
if (NULL == regexBuffer) { free(stringBuffer); return NO; }
[string getCharacters: stringBuffer range: NSMakeRange(0, stringLength)];
[regex getCharacters: regexBuffer range: NSMakeRange(0, regexLength)];
flags |= UREGEX_DOTALL; // . is supposed to recognize newlines
if ((opts & NSCaseInsensitiveSearch) != 0) { flags |= UREGEX_CASE_INSENSITIVE; }
icuregex = uregex_open(regexBuffer, regexLength, flags, NULL, &error);
if (icuregex != NULL && U_SUCCESS(error))
{
uregex_setText(icuregex, stringBuffer, stringLength, &error);
result = uregex_matches(icuregex, 0, &error);
}
uregex_close(icuregex);
free(stringBuffer);
free(regexBuffer);
return result;
}
#endif
- (BOOL) _evaluateLeftValue: (id)leftResult
rightValue: (id)rightResult
object: (id)object
{
unsigned compareOptions = 0;
BOOL leftIsNil;
BOOL rightIsNil;
Class constantValueClass;
leftIsNil = (leftResult == nil || [leftResult isEqual: [NSNull null]]);
rightIsNil = (rightResult == nil || [rightResult isEqual: [NSNull null]]);
if (leftIsNil || rightIsNil)
{
if (leftIsNil == rightIsNil)
{
/* Both of the values are nil.
* The result is YES if equality is requested.
*/
if (NSEqualToPredicateOperatorType == _type
|| NSLessThanOrEqualToPredicateOperatorType == _type
|| NSGreaterThanOrEqualToPredicateOperatorType == _type)
{
return YES;
}
}
else if (NSNotEqualToPredicateOperatorType == _type)
{
/* One, but not both of the values are nil.
* The result is YES if inequality is requested.
*/
return YES;
}
return NO;
}
// Change predicate options into string options.
if (!(_options & NSDiacriticInsensitivePredicateOption))
{
compareOptions |= NSLiteralSearch;
}
if (_options & NSCaseInsensitivePredicateOption)
{
compareOptions |= NSCaseInsensitiveSearch;
}
/* If the left or right result is a constant value expression, we need to
* extract the constant value from it.
*/
constantValueClass = [GSConstantValueExpression class];
if ([leftResult isKindOfClass: constantValueClass])
{
leftResult = [(GSConstantValueExpression *)leftResult constantValue];
}
if ([rightResult isKindOfClass: constantValueClass])
{
rightResult = [(GSConstantValueExpression *)rightResult constantValue];
}
/* We are assuming that the API is stable and enumeration values
* won't change. This covers:
* - NSLessThanPredicateOperatorType = 0,
* - NSLessThanOrEqualToPredicateOperatorType = 1,
* - NSGreaterThanPredicateOperatorType = 2,
* - NSGreaterThanOrEqualToPredicateOperatorType = 3
*/
if (_type < NSEqualToPredicateOperatorType)
{
NSComparisonResult comparisonResult;
Class stringClass;
stringClass = [NSString class];
/* We first check if the left and right result are strings.
* If this is not the case, check if we can do a comparison with
* doubleValue: (Mainly useful as a shortcut for expressions like
* "abc" == 3
*/
if ([leftResult isKindOfClass:stringClass] &&
[rightResult isKindOfClass:stringClass])
{
comparisonResult = [leftResult compare:rightResult
options:compareOptions];
}
else if ([leftResult respondsToSelector:@selector(compare:)])
{
// Attempt a comparison
comparisonResult = [leftResult compare:rightResult];
}
else
{
// We can't compare these objects
[NSException raise:NSInvalidArgumentException
format:@"Cannot compare objects of type %@ and %@",
NSStringFromClass([leftResult class]),
NSStringFromClass([rightResult class])];
return NO;
}
switch (_type)
{
case NSLessThanPredicateOperatorType:
{
return (comparisonResult == NSOrderedAscending) ? YES : NO;
}
case NSLessThanOrEqualToPredicateOperatorType:
{
/* True if left value is less then (NSOrderedAscending) or equal
* (NSOrderedSame) */
return (comparisonResult != NSOrderedDescending) ? YES : NO;
}
case NSGreaterThanPredicateOperatorType:
{
return (comparisonResult == NSOrderedDescending) ? YES : NO;
}
case NSGreaterThanOrEqualToPredicateOperatorType:
{
return (comparisonResult != NSOrderedAscending) ? YES : NO;
}
default: // This should never happen
return NO;
}
}
/* Handle remaining cases */
switch (_type)
{
case NSEqualToPredicateOperatorType:
return [leftResult isEqual: rightResult];
case NSNotEqualToPredicateOperatorType:
return ![leftResult isEqual: rightResult];
case NSMatchesPredicateOperatorType:
#if GS_USE_ICU == 1
return GSICUStringMatchesRegex(leftResult, rightResult, compareOptions);
#else
return [leftResult compare: rightResult options: compareOptions]
== NSOrderedSame ? YES : NO;
#endif
case NSLikePredicateOperatorType:
#if GS_USE_ICU == 1
{
NSString *regex;
/* The right hand is a pattern with '?' meaning match one character,
* and '*' meaning match zero or more characters, so translate that
* into a regex.
*/
regex = [rightResult stringByReplacingOccurrencesOfString: @"*"
withString: @".*"];
regex = [regex stringByReplacingOccurrencesOfString: @"?"
withString: @".?"];
regex = [NSString stringWithFormat: @"^%@$", regex];
return GSICUStringMatchesRegex(leftResult, regex, compareOptions);
}
#else
return [leftResult compare: rightResult options: compareOptions]
== NSOrderedSame ? YES : NO;
#endif
case NSBeginsWithPredicateOperatorType:
{
2022-03-07 10:13:44 +00:00
NSRange range;
NSUInteger ll = [leftResult length];
NSUInteger rl = [rightResult length];
2022-03-07 10:13:44 +00:00
if (rl > ll)
{
return NO;
}
range = NSMakeRange(0, rl);
return ([leftResult compare: rightResult
options: compareOptions
range: range] == NSOrderedSame ? YES : NO);
}
case NSEndsWithPredicateOperatorType:
{
2022-03-07 10:13:44 +00:00
NSRange range;
NSUInteger ll = [leftResult length];
NSUInteger rl = [rightResult length];
if (ll < rl)
{
return NO;
}
range = NSMakeRange(ll - rl, rl);
return ([leftResult compare: rightResult
options: compareOptions
range: range] == NSOrderedSame ? YES : NO);
}
case NSInPredicateOperatorType:
/* Handle special case where rightResult is a collection
* and leftResult an element of it.
*/
if (![rightResult isKindOfClass: [NSString class]])
{
NSEnumerator *e;
id value;
if (![rightResult respondsToSelector: @selector(objectEnumerator)])
{
[NSException raise: NSInvalidArgumentException
format: @"The right hand side for an IN operator "
@"must be a collection"];
}
e = [rightResult objectEnumerator];
while ((value = [e nextObject]))
{
if ([value isEqual: leftResult])
return YES;
}
return NO;
}
return ([rightResult rangeOfString: leftResult
options: compareOptions].location
!= NSNotFound ? YES : NO);
case NSCustomSelectorPredicateOperatorType:
{
BOOL (*function)(id,SEL,id)
= (BOOL (*)(id,SEL,id))[leftResult methodForSelector: _selector];
return function(leftResult, _selector, rightResult);
}
default:
return NO;
}
}
- (BOOL) evaluateWithObject: (id)object
{
id leftValue = [_left expressionValueWithObject: object context: nil];
id rightValue = [_right expressionValueWithObject: object context: nil];
if (_modifier == NSDirectPredicateModifier)
{
return [self _evaluateLeftValue: leftValue
rightValue: rightValue
object: object];
}
else
{
BOOL result = (_modifier == NSAllPredicateModifier);
NSEnumerator *e;
id value;
if (![leftValue respondsToSelector: @selector(objectEnumerator)])
{
[NSException raise: NSInvalidArgumentException
format: @"The left hand side for an ALL or ANY operator must be a collection"];
}
e = [leftValue objectEnumerator];
while ((value = [e nextObject]))
{
BOOL eval = [self _evaluateLeftValue: value
rightValue: rightValue
object: object];
if (eval != result)
return eval;
}
return result;
}
}
- (id) copyWithZone: (NSZone *)z
{
NSComparisonPredicate *copy;
copy = (NSComparisonPredicate *)NSCopyObject(self, 0, z);
copy->_left = [_left copyWithZone: z];
copy->_right = [_right copyWithZone: z];
return copy;
}
- (Class) classForCoder
{
return [NSComparisonPredicate class];
}
- (void) encodeWithCoder: (NSCoder *)coder
{
// FIXME
[self subclassResponsibility: _cmd];
}
- (id) initWithCoder: (NSCoder *)coder
{
// FIXME
[self subclassResponsibility: _cmd];
return self;
}
@end
@implementation NSExpression
+ (void) initialize
{
if (self == [NSExpression class] && nil == evaluatedObjectExpression)
{
evaluatedObjectExpression = [GSEvaluatedObjectExpression new];
}
}
+ (NSExpression *) expressionForConstantValue: (id)obj
{
GSConstantValueExpression *e;
e = [[GSConstantValueExpression alloc]
initWithExpressionType: NSConstantValueExpressionType];
ASSIGN(e->_obj, obj);
return AUTORELEASE(e);
}
+ (NSExpression *) expressionForEvaluatedObject
{
return evaluatedObjectExpression;
}
+ (NSExpression *) expressionForFunction: (NSString *)name
arguments: (NSArray *)args
{
GSFunctionExpression *e;
NSString *s;
e = [[GSFunctionExpression alloc]
initWithExpressionType: NSFunctionExpressionType];
s = [NSString stringWithFormat: @"_eval_%@:", name];
e->_selector = NSSelectorFromString(s);
if (![e respondsToSelector: e->_selector])
{
[NSException raise: NSInvalidArgumentException
format: @"Unknown function implementation: %@", name];
}
ASSIGN(e->_function, name);
e->_argc = [args count];
ASSIGN(e->_args, args);
if ([name isEqualToString: @"_add"]) e->_op = @"+";
else if ([name isEqualToString: @"_sub"]) e->_op = @"-";
else if ([name isEqualToString: @"_mul"]) e->_op = @"*";
else if ([name isEqualToString: @"_div"]) e->_op = @"/";
else if ([name isEqualToString: @"_pow"]) e->_op = @"**";
return AUTORELEASE(e);
}
+ (NSExpression *) expressionForKeyPath: (NSString *)path
{
GSKeyPathExpression *e;
if (![path isKindOfClass: [NSString class]])
{
[NSException raise: NSInvalidArgumentException
format: @"Keypath is not NSString: %@", path];
}
e = [[GSKeyPathExpression alloc]
initWithExpressionType: NSKeyPathExpressionType];
ASSIGN(e->_keyPath, path);
return AUTORELEASE(e);
}
2022-02-12 13:06:23 +00:00
+ (NSExpression *) expressionForKeyPathCompositionWithLeft: (NSExpression*)left
right: (NSExpression*)right
{
GSKeyPathCompositionExpression *e;
e = [[GSKeyPathCompositionExpression alloc]
initWithExpressionType: NSKeyPathCompositionExpressionType];
ASSIGN(e->_left, left);
ASSIGN(e->_right, right);
return AUTORELEASE(e);
}
+ (NSExpression *) expressionForVariable: (NSString *)string
{
GSVariableExpression *e;
e = [[GSVariableExpression alloc]
initWithExpressionType: NSVariableExpressionType];
ASSIGN(e->_variable, string);
return AUTORELEASE(e);
}
2024-05-24 20:55:16 +00:00
// 10.5 methods...
+ (NSExpression *) expressionForIntersectSet: (NSExpression *)left
with: (NSExpression *)right
{
2024-05-26 13:11:21 +00:00
GSIntersectSetExpression *e;
e = [[GSIntersectSetExpression alloc]
initWithExpressionType: NSIntersectSetExpressionType];
ASSIGN(e->_left, left);
ASSIGN(e->_right, right);
return AUTORELEASE(e);
2024-05-24 20:55:16 +00:00
}
+ (NSExpression *) expressionForAggregate: (NSArray *)subExpressions
{
GSAggregateExpression *e;
e = [[GSAggregateExpression alloc]
initWithExpressionType: NSAggregateExpressionType];
ASSIGN(e->_collection, [NSSet setWithArray: subExpressions]);
return AUTORELEASE(e);
2024-05-24 20:55:16 +00:00
}
+ (NSExpression *) expressionForUnionSet: (NSExpression *)left
with: (NSExpression *)right
{
2024-05-26 13:11:21 +00:00
GSUnionSetExpression *e;
e = [[GSUnionSetExpression alloc]
initWithExpressionType: NSUnionSetExpressionType];
ASSIGN(e->_left, left);
ASSIGN(e->_right, right);
return AUTORELEASE(e);
2024-05-24 20:55:16 +00:00
}
+ (NSExpression *) expressionForMinusSet: (NSExpression *)left
with: (NSExpression *)right
{
2024-05-26 13:11:21 +00:00
GSMinusSetExpression *e;
e = [[GSMinusSetExpression alloc]
initWithExpressionType: NSMinusSetExpressionType];
ASSIGN(e->_left, left);
ASSIGN(e->_right, right);
return AUTORELEASE(e);
2024-05-24 20:55:16 +00:00
}
// end 10.5 methods
2024-05-24 18:28:04 +00:00
// 10.6 methods...
+ (NSExpression *) expressionWithFormat: (NSString *)format, ...
{
va_list ap;
NSExpression *obj;
if (NULL == format)
{
[NSException raise: NSInvalidArgumentException
format: @"[NSExpression+expressionWithFormat:]: NULL format"];
}
2024-05-24 18:37:24 +00:00
2024-05-24 18:28:04 +00:00
va_start(ap, format);
obj = [self expressionWithFormat: format
arguments: ap];
2024-05-24 18:28:04 +00:00
va_end(ap);
2024-05-24 18:37:24 +00:00
2024-05-24 18:28:04 +00:00
return obj;
}
+ (NSExpression *) expressionWithFormat: (NSString *)format
2024-05-24 18:37:24 +00:00
arguments: (va_list)args
2024-05-24 18:28:04 +00:00
{
NSString *expString = AUTORELEASE([[NSString alloc] initWithFormat: format
arguments: args]);
GSPredicateScanner *scanner = AUTORELEASE([[GSPredicateScanner alloc]
initWithString: expString
args: nil]);
return [scanner parseExpression];
}
+ (NSExpression *) expressionWithFormat: (NSString *)format
2024-05-24 18:37:24 +00:00
argumentArray: (NSArray *)args
2024-05-24 18:28:04 +00:00
{
GSPredicateScanner *scanner = AUTORELEASE([[GSPredicateScanner alloc]
initWithString: format
args: args]);
return [scanner parseExpression];
}
// End 10.6 methods
- (id) initWithExpressionType: (NSExpressionType)type
{
if ((self = [super init]) != nil)
{
_type = type;
}
return self;
}
- (id) copyWithZone: (NSZone *)z
{
return NSCopyObject(self, 0, z);
}
- (NSArray *) arguments
{
[self subclassResponsibility: _cmd];
return nil;
}
- (id) constantValue
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) description
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSExpressionType) expressionType
{
return _type;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) function
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) keyPath
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSExpression *) operand
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) variable
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSExpression *) leftExpression
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSExpression *) rightExpression
{
[self subclassResponsibility: _cmd];
return nil;
}
- (id) collection
{
[self subclassResponsibility: _cmd];
return nil;
}
- (Class) classForCoder
{
return [NSExpression class];
}
- (void) encodeWithCoder: (NSCoder *)coder
{
// FIXME
[self subclassResponsibility: _cmd];
}
- (id) initWithCoder: (NSCoder *)coder
{
// FIXME
[self subclassResponsibility: _cmd];
return nil;
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables
{
[self subclassResponsibility: _cmd];
return nil;
}
@end
@implementation GSConstantValueExpression
- (id) constantValue
{
return _obj;
}
- (NSString *) description
{
if ([_obj isKindOfClass: [NSString class]])
{
NSMutableString *result = nil;
/* Quote the result string as necessary.
*/
GSPropertyListMake(_obj, nil, NO, YES, 2, &result);
return result;
}
else if ([_obj isKindOfClass: [NSDate class]])
{
return [NSString stringWithFormat: @"CAST(%15.6f, \"NSDate\")",
[(NSDate*)_obj timeIntervalSinceReferenceDate]];
}
return [_obj description];
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
2022-01-30 17:24:32 +00:00
if ([_obj isKindOfClass: [NSArray class]])
{
NSUInteger count = [(NSArray*)_obj count];
NSMutableArray *tmp = [NSMutableArray arrayWithCapacity: count];
NSUInteger index = 0;
2022-01-30 17:24:32 +00:00
while (index < count)
2022-01-30 17:24:32 +00:00
{
id e = [(NSArray*)_obj objectAtIndex: index++];
id o;
/* Array index is not always a NSExpression object
* (e.g. When specified as an argument instead of
* an inline expression).
*/
if ([e isKindOfClass: [NSExpression class]]) {
o = [e expressionValueWithObject: e context: context];
} else {
o = e;
}
2022-01-30 17:24:32 +00:00
[tmp addObject: o];
2022-01-30 17:24:32 +00:00
}
return tmp;
2022-01-30 17:24:32 +00:00
}
else
{
return _obj;
}
}
- (void) dealloc
{
RELEASE(_obj);
[super dealloc];
}
- (id) copyWithZone: (NSZone*)zone
{
GSConstantValueExpression *copy;
copy = (GSConstantValueExpression *)[super copyWithZone: zone];
copy->_obj = [_obj copyWithZone: zone];
return copy;
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables
{
return self;
}
@end
@implementation GSEvaluatedObjectExpression
- (NSString *) description
{
return @"SELF";
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return object;
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables
{
return self;
}
- (NSString *) keyPath
{
return @"SELF";
}
@end
@implementation GSVariableExpression
- (NSString *) description
{
return [NSString stringWithFormat: @"$%@", _variable];
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return [context objectForKey: _variable];
}
- (NSString *) variable
{
return _variable;
}
- (void) dealloc;
{
RELEASE(_variable);
[super dealloc];
}
- (id) copyWithZone: (NSZone*)zone
{
GSVariableExpression *copy;
copy = (GSVariableExpression *)[super copyWithZone: zone];
copy->_variable = [_variable copyWithZone: zone];
return copy;
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables
{
id result = [variables objectForKey: _variable];
if (result != nil)
{
return [NSExpression expressionForConstantValue: result];
}
else
{
return self;
}
}
@end
@implementation GSKeyPathExpression
- (NSString *) description
{
return _keyPath;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return [object valueForKeyPath: _keyPath];
}
- (NSString *) keyPath
{
return _keyPath;
}
- (void) dealloc;
{
RELEASE(_keyPath);
[super dealloc];
}
- (id) copyWithZone: (NSZone*)zone
{
GSKeyPathExpression *copy;
copy = (GSKeyPathExpression *)[super copyWithZone: zone];
copy->_keyPath = [_keyPath copyWithZone: zone];
return copy;
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables
{
return self;
}
@end
2022-02-12 13:06:23 +00:00
@implementation GSKeyPathCompositionExpression
- (NSString *) description
{
return [NSString stringWithFormat: @"%@.%@", _left, _right];
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
object = [_left expressionValueWithObject: object context: context];
return [_right expressionValueWithObject: object context: context];
}
- (NSString *) keyPath
{
return nil;
}
- (void) dealloc
{
RELEASE(_left);
RELEASE(_right);
[super dealloc];
}
- (id) copyWithZone: (NSZone*)zone
{
GSKeyPathCompositionExpression *copy;
copy = (GSKeyPathCompositionExpression *)[super copyWithZone: zone];
copy->_left = [_left copyWithZone: zone];
copy->_right = [_right copyWithZone: zone];
return copy;
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary*)variables
{
NSExpression *left;
NSExpression *right;
left = [_left _expressionWithSubstitutionVariables: variables];
right = [_right _expressionWithSubstitutionVariables: variables];
return [NSExpression expressionForKeyPathCompositionWithLeft: left
right: right];
}
- (NSExpression *) leftExpression
{
return _left;
}
- (NSExpression *) rightExpression
{
return _right;
}
@end
// Macro for checking set related expressions
#define CHECK_SETS \
do { \
if ([rightValue isKindOfClass: [NSArray class]]) \
{ \
rightSet = [NSSet setWithArray: rightValue]; \
} \
if (!rightSet) \
{ \
[NSException raise: NSInvalidArgumentException \
format: @"Can't evaluate set expression; right subexpression is not a set (lhs = %@ rhs = %@)", leftValue, rightValue]; \
} \
if ([leftValue isKindOfClass: [NSArray class]]) \
{ \
leftSet = [NSSet setWithArray: leftValue]; \
} \
if (!leftSet) \
{ \
[NSException raise: NSInvalidArgumentException \
format: @"Can't evaluate set expression; left subexpression is not a set (lhs = %@ rhs = %@)", leftValue, rightValue]; \
} \
} while (0)
@implementation GSUnionSetExpression
2024-05-26 13:11:21 +00:00
- (NSString *) description
{
return [NSString stringWithFormat: @"%@.%@", _left, _right];
}
2024-05-26 13:11:21 +00:00
- (NSExpression *) leftExpression
{
return _left;
}
- (NSExpression *) rightExpression
{
return _right;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
id leftValue = [_left expressionValueWithObject: object context: context];
id rightValue = [_right expressionValueWithObject: object context: context];
NSSet *leftSet = nil;
NSSet *rightSet = nil;
NSMutableSet *result = nil;
CHECK_SETS;
result = [NSMutableSet setWithSet: leftSet];
[result unionSet: rightSet];
return result;
}
@end
@implementation GSIntersectSetExpression
2024-05-26 13:11:21 +00:00
- (NSString *) description
{
return [NSString stringWithFormat: @"%@.%@", _left, _right];
}
2024-05-26 13:11:21 +00:00
- (NSExpression *) leftExpression
{
return _left;
}
- (NSExpression *) rightExpression
{
return _right;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
id leftValue = [_left expressionValueWithObject: object context: context];
id rightValue = [_right expressionValueWithObject: object context: context];
NSSet *leftSet = nil;
NSSet *rightSet = nil;
NSMutableSet *result = nil;
CHECK_SETS;
result = [NSMutableSet setWithSet: leftSet];
[result intersectSet: rightSet];
return result;
}
2024-05-26 13:11:21 +00:00
@end
@implementation GSMinusSetExpression
- (NSString *) description
{
return [NSString stringWithFormat: @"%@.%@", _left, _right];
}
2024-05-26 13:11:21 +00:00
- (NSExpression *) leftExpression
{
return _left;
}
- (NSExpression *) rightExpression
{
return _right;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
id leftValue = [_left expressionValueWithObject: object context: context];
id rightValue = [_right expressionValueWithObject: object context: context];
NSSet *leftSet = nil;
NSSet *rightSet = nil;
NSMutableSet *result = nil;
CHECK_SETS;
result = [NSMutableSet setWithSet: leftSet];
[result minusSet: rightSet];
return result;
}
@end
@implementation GSSubqueryExpression
@end
@implementation GSAggregateExpression
- (NSString *) description
{
return [NSString stringWithFormat: @"%@", _collection];
}
- (id) collection
{
return _collection;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
NSMutableArray *result = [NSMutableArray arrayWithCapacity:
[_collection count]];
FOR_IN(NSExpression*, exp, _collection)
{
NSExpression *value = [exp expressionValueWithObject: object context: context];
[result addObject: value];
}
END_FOR_IN(_collection);
return result;
}
2022-02-12 13:06:23 +00:00
@end
@implementation GSFunctionExpression
- (NSArray *) arguments
{
return _args;
}
- (NSString *) description
{
if (nil != _op && 1 == [_args count])
{
GSFunctionExpression *a0 = [_args objectAtIndex: 0];
if (YES == [a0 isKindOfClass: [self class]] && nil != a0->_op)
{
return [NSString stringWithFormat: @"%@(%@)", _op, a0];
}
return [NSString stringWithFormat: @"%@%@", _op, a0];
}
if (nil != _op)
{
GSFunctionExpression *a0 = [_args objectAtIndex: 0];
GSFunctionExpression *a1 = [_args objectAtIndex: 1];
if (YES == [a0 isKindOfClass: [self class]] && nil != a0->_op)
{
if (YES == [a1 isKindOfClass: [self class]] && nil != a1->_op)
{
return [NSString stringWithFormat: @"(%@) %@ (%@)", a0, _op, a1];
}
return [NSString stringWithFormat: @"(%@) %@ %@", a0, _op, a1];
}
if (YES == [a1 isKindOfClass: [self class]] && nil != a1->_op)
{
return [NSString stringWithFormat: @"%@ %@ (%@)", a0, _op, a1];
}
return [NSString stringWithFormat: @"%@ %@ %@", a0, _op, a1];
}
return [NSString stringWithFormat: @"%@(%@)", [self function], _args];
}
- (NSString *) function
{
return _function;
}
- (NSString *) keyPath
{
return nil;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
// temporary space
NSMutableArray *eargs = [NSMutableArray arrayWithCapacity: _argc];
unsigned int i;
for (i = 0; i < _argc; i++)
{
[eargs addObject: [[_args objectAtIndex: i]
expressionValueWithObject: object context: context]];
}
// apply method selector
return [self performSelector: _selector
withObject: eargs];
}
- (void) dealloc;
{
RELEASE(_args);
RELEASE(_function);
[super dealloc];
}
- (id) copyWithZone: (NSZone*)zone
{
GSFunctionExpression *copy;
copy = (GSFunctionExpression *)[super copyWithZone: zone];
copy->_function = [_function copyWithZone: zone];
copy->_args = [_args copyWithZone: zone];
return copy;
}
- (NSEnumerator*) _enum: (NSArray *)expressions
{
id o;
/* Check to see if this is aggregating over a collection.
*/
if (1 == _argc && [(o = [expressions lastObject])
respondsToSelector: @selector(objectEnumerator)])
{
return [o objectEnumerator];
}
return [expressions objectEnumerator];
}
- (id) _expressionWithSubstitutionVariables: (NSDictionary *)variables
{
NSMutableArray *args = [NSMutableArray arrayWithCapacity: _argc];
unsigned int i;
for (i = 0; i < _argc; i++)
{
[args addObject: [[_args objectAtIndex: i]
_expressionWithSubstitutionVariables: variables]];
}
return [NSExpression expressionForFunction: _function arguments: args];
}
- (id) _eval__chs: (NSArray *)expressions
{
return [NSNumber numberWithInt: -[[expressions objectAtIndex: 0] intValue]];
}
- (id) _eval__first: (NSArray *)expressions
{
return [[expressions objectAtIndex: 0] objectAtIndex: 0];
}
- (id) _eval__last: (NSArray *)expressions
{
return [[expressions objectAtIndex: 0] lastObject];
}
- (id) _eval__index: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
if ([left isKindOfClass: [NSDictionary class]])
{
return [left objectForKey: right];
}
else
{
// raises exception if invalid
return [left objectAtIndex: [right unsignedIntValue]];
}
}
- (id) _eval__pow: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
return [NSNumber numberWithDouble:
pow([left doubleValue], [right doubleValue])];
}
- (id) _eval__mul: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
return [NSNumber numberWithDouble: [left doubleValue] * [right doubleValue]];
}
- (id) _eval__div: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
return [NSNumber numberWithDouble: [left doubleValue] / [right doubleValue]];
}
- (id) _eval__add: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
return [NSNumber numberWithDouble: [left doubleValue] + [right doubleValue]];
}
- (id) _eval__sub: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
return [NSNumber numberWithDouble: [left doubleValue] - [right doubleValue]];
}
- (id) _eval_count: (NSArray *)expressions
{
NSAssert(_argc == 1, NSInternalInconsistencyException);
return [NSNumber numberWithUnsignedInt:
[[expressions objectAtIndex: 0] count]];
}
- (id) _eval_avg: (NSArray *)expressions
{
NSEnumerator *e = [self _enum: expressions];
double sum = 0.0;
unsigned count = 0;
id o;
while (nil != (o = [e nextObject]))
{
sum += [o doubleValue];
count++;
}
if (count == 0)
{
return [NSNumber numberWithDouble: 0.0];
}
return [NSNumber numberWithDouble: sum / count];
}
- (id) _eval_max: (NSArray *)expressions
{
NSEnumerator *e = [self _enum: expressions];
id o = [e nextObject];
double max = (nil == o) ? 0.0 : [o doubleValue];
double cur;
while (nil != (o = [e nextObject]))
{
cur = [o doubleValue];
if (max < cur)
{
max = cur;
}
}
return [NSNumber numberWithDouble: max];
}
- (id) _eval_min: (NSArray *)expressions
{
NSEnumerator *e = [self _enum: expressions];
id o = [e nextObject];
double min = (nil == o ? 0.0 : [o doubleValue]);
double cur;
while (nil != (o = [e nextObject]))
{
cur = [o doubleValue];
if (min > cur)
{
min = cur;
}
}
return [NSNumber numberWithDouble: min];
}
- (id) _eval_sum: (NSArray *)expressions
{
NSEnumerator *e = [self _enum: expressions];
double sum = 0.0;
id o;
while (nil != (o = [e nextObject]))
{
sum += [o doubleValue];
}
return [NSNumber numberWithDouble: sum];
}
- (id) _eval_CAST: (NSArray *)expressions
{
id left = [expressions objectAtIndex: 0];
id right = [expressions objectAtIndex: 1];
if ([right isEqualToString: @"NSDate"])
{
2021-01-18 11:56:44 +00:00
return [NSDate dateWithTimeIntervalSinceReferenceDate:
[left doubleValue]];
}
NSLog(@"Cast to unknown type %@", right);
return nil;
}
// add arithmetic functions: average, median, mode, stddev, sqrt, log, ln, exp, floor, ceiling, abs, trunc, random, randomn, now
@end
@implementation NSArray (NSPredicate)
- (NSArray *) filteredArrayUsingPredicate: (NSPredicate *)predicate
{
NSMutableArray *result;
NSEnumerator *e = [self objectEnumerator];
id object;
result = [NSMutableArray arrayWithCapacity: [self count]];
while ((object = [e nextObject]) != nil)
{
if ([predicate evaluateWithObject: object] == YES)
{
[result addObject: object]; // passes filter
}
}
return GS_IMMUTABLE(result);
}
@end
@implementation NSMutableArray (NSPredicate)
- (void) filterUsingPredicate: (NSPredicate *)predicate
{
unsigned count = [self count];
while (count-- > 0)
{
id object = [self objectAtIndex: count];
if ([predicate evaluateWithObject: object] == NO)
{
[self removeObjectAtIndex: count];
}
}
}
@end
@implementation NSSet (NSPredicate)
- (NSSet *) filteredSetUsingPredicate: (NSPredicate *)predicate
{
NSMutableSet *result;
NSEnumerator *e = [self objectEnumerator];
id object;
result = [NSMutableSet setWithCapacity: [self count]];
while ((object = [e nextObject]) != nil)
{
if ([predicate evaluateWithObject: object] == YES)
{
[result addObject: object]; // passes filter
}
}
return GS_IMMUTABLE(result);
}
@end
@implementation NSMutableSet (NSPredicate)
- (void) filterUsingPredicate: (NSPredicate *)predicate
{
NSMutableSet *rejected;
NSEnumerator *e = [self objectEnumerator];
id object;
rejected = [NSMutableSet setWithCapacity: [self count]];
while ((object = [e nextObject]) != nil)
{
if ([predicate evaluateWithObject: object] == NO)
{
[rejected addObject: object];
}
}
[self minusSet: rejected];
}
@end
@implementation GSPredicateScanner
- (id) initWithString: (NSString*)format
args: (NSArray*)args
{
self = [super initWithString: format];
if (self != nil)
{
_args = [args objectEnumerator];
}
return self;
}
- (id) nextArg
{
return [_args nextObject];
}
- (BOOL) scanPredicateKeyword: (NSString *)key
{
// save to back up
unsigned loc = [self scanLocation];
unichar c;
[self setCaseSensitive: NO];
if (![self scanString: key intoString: NULL])
{
// no match
return NO;
}
if ([self isAtEnd])
{
// ok
return YES;
}
// Does the next character still belong to the token?
c = [[self string] characterAtIndex: [self scanLocation]];
if (![[NSCharacterSet alphanumericCharacterSet] characterIsMember: c])
{
// ok
return YES;
}
// back up
[self setScanLocation: loc];
// no match
return NO;
}
- (NSPredicate *) parse
{
NSPredicate *r = nil;
NS_DURING
{
r = [self parsePredicate];
}
NS_HANDLER
{
NSLog(@"Parsing failed for %@ with %@", [self string], localException);
[localException raise];
}
NS_ENDHANDLER
if (![self isAtEnd])
{
[NSException raise: NSInvalidArgumentException
format: @"Format string contains extra characters: \"%@\"",
[self string]];
}
return r;
}
- (NSPredicate *) parsePredicate
{
return [self parseAnd];
}
- (NSPredicate *) parseAnd
{
NSPredicate *l = [self parseOr];
while ([self scanPredicateKeyword: @"AND"]
|| [self scanPredicateKeyword: @"&&"])
{
NSPredicate *r = [self parseOr];
if ([r isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)r compoundPredicateType]
== NSAndPredicateType)
{
NSCompoundPredicate *right = (NSCompoundPredicate*)r;
// merge
2022-02-12 13:06:23 +00:00
if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSAndPredicateType)
{
NSCompoundPredicate *left;
NSMutableArray *subs;
left = (NSCompoundPredicate*)l;
subs = [[left subpredicates] mutableCopy];
[subs addObjectsFromArray: [right subpredicates]];
l = [NSCompoundPredicate andPredicateWithSubpredicates: subs];
[subs release];
}
else
{
NSMutableArray *subs;
subs = [[right subpredicates] mutableCopy];
[subs insertObject: l atIndex: 0];
l = [NSCompoundPredicate andPredicateWithSubpredicates: subs];
[subs release];
}
}
else if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSAndPredicateType)
{
NSCompoundPredicate *left;
NSMutableArray *subs;
left = (NSCompoundPredicate*)l;
subs = [[left subpredicates] mutableCopy];
[subs addObject: r];
l = [NSCompoundPredicate andPredicateWithSubpredicates: subs];
[subs release];
}
else
{
l = [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects: l, r, nil]];
}
}
return l;
}
- (NSPredicate *) parseNot
{
if ([self scanString: @"(" intoString: NULL])
{
NSPredicate *r = [self parsePredicate];
if (![self scanString: @")" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ) in compound predicate"];
}
return r;
}
if ([self scanPredicateKeyword: @"NOT"] || [self scanPredicateKeyword: @"!"])
{
// -> NOT NOT x or NOT (y)
return [NSCompoundPredicate
notPredicateWithSubpredicate: [self parseNot]];
}
if ([self scanPredicateKeyword: @"TRUEPREDICATE"])
{
return [NSPredicate predicateWithValue: YES];
}
if ([self scanPredicateKeyword: @"FALSEPREDICATE"])
{
return [NSPredicate predicateWithValue: NO];
}
return [self parseComparison];
}
- (NSPredicate *) parseOr
{
NSPredicate *l = [self parseNot];
while ([self scanPredicateKeyword: @"OR"]
|| [self scanPredicateKeyword: @"||"])
{
NSPredicate *r = [self parseNot];
if ([r isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)r compoundPredicateType]
== NSOrPredicateType)
{
NSCompoundPredicate *right = (NSCompoundPredicate*)r;
// merge
if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSOrPredicateType)
{
NSCompoundPredicate *left = (NSCompoundPredicate*)l;
NSMutableArray *subs;
subs = [[left subpredicates] mutableCopy];
[subs addObjectsFromArray: [right subpredicates]];
l = [NSCompoundPredicate orPredicateWithSubpredicates: subs];
[subs release];
}
else
{
NSMutableArray *subs;
subs = [[right subpredicates] mutableCopy];
[subs insertObject: l atIndex: 0];
l = [NSCompoundPredicate orPredicateWithSubpredicates: subs];
[subs release];
}
}
else if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSOrPredicateType)
{
NSCompoundPredicate *left = (NSCompoundPredicate*)l;
NSMutableArray *subs;
subs = [[left subpredicates] mutableCopy];
2022-02-12 13:06:23 +00:00
[subs addObject: r];
l = [NSCompoundPredicate orPredicateWithSubpredicates: subs];
[subs release];
}
else
{
l = [NSCompoundPredicate orPredicateWithSubpredicates:
[NSArray arrayWithObjects: l, r, nil]];
}
}
return l;
}
- (NSPredicate *) parseComparison
{
// there must always be a comparison
NSComparisonPredicateModifier modifier = NSDirectPredicateModifier;
NSPredicateOperatorType type = 0;
unsigned opts = 0;
NSExpression *left;
NSExpression *right;
NSPredicate *p;
BOOL negate = NO;
BOOL swap = NO;
if ([self scanPredicateKeyword: @"ANY"])
{
modifier = NSAnyPredicateModifier;
}
else if ([self scanPredicateKeyword: @"ALL"])
{
modifier = NSAllPredicateModifier;
}
else if ([self scanPredicateKeyword: @"NONE"])
{
modifier = NSAnyPredicateModifier;
negate = YES;
}
else if ([self scanPredicateKeyword: @"SOME"])
{
modifier = NSAllPredicateModifier;
negate = YES;
}
left = [self parseExpression];
if ([self scanString: @"!=" intoString: NULL]
|| [self scanString: @"<>" intoString: NULL])
{
type = NSNotEqualToPredicateOperatorType;
}
else if ([self scanString: @"<=" intoString: NULL]
|| [self scanString: @"=<" intoString: NULL])
{
type = NSLessThanOrEqualToPredicateOperatorType;
}
else if ([self scanString: @">=" intoString: NULL]
|| [self scanString: @"=>" intoString: NULL])
{
type = NSGreaterThanOrEqualToPredicateOperatorType;
}
else if ([self scanString: @"<" intoString: NULL])
{
type = NSLessThanPredicateOperatorType;
}
else if ([self scanString: @">" intoString: NULL])
{
type = NSGreaterThanPredicateOperatorType;
}
else if ([self scanString: @"==" intoString: NULL]
|| [self scanString: @"=" intoString: NULL])
{
type = NSEqualToPredicateOperatorType;
}
else if ([self scanPredicateKeyword: @"MATCHES"])
{
type = NSMatchesPredicateOperatorType;
}
else if ([self scanPredicateKeyword: @"LIKE"])
{
type = NSLikePredicateOperatorType;
}
else if ([self scanPredicateKeyword: @"BEGINSWITH"])
{
type = NSBeginsWithPredicateOperatorType;
}
else if ([self scanPredicateKeyword: @"ENDSWITH"])
{
type = NSEndsWithPredicateOperatorType;
}
else if ([self scanPredicateKeyword: @"IN"])
{
type = NSInPredicateOperatorType;
}
else if ([self scanPredicateKeyword: @"CONTAINS"])
{
type = NSInPredicateOperatorType;
swap = YES;
}
else if ([self scanPredicateKeyword: @"BETWEEN"])
{
// Requires special handling to transfer into AND of
// two normal comparison predicates
NSExpression *exp = [self parseSimpleExpression];
NSArray *a = (NSArray *)[exp constantValue];
NSNumber *lower, *upper;
NSExpression *lexp, *uexp;
NSPredicate *lp, *up;
if (![a isKindOfClass: [NSArray class]])
{
[NSException raise: NSInvalidArgumentException
format: @"BETWEEN operator requires array argument"];
}
lower = [a objectAtIndex: 0];
upper = [a objectAtIndex: 1];
lexp = [NSExpression expressionForConstantValue: lower];
uexp = [NSExpression expressionForConstantValue: upper];
lp = [NSComparisonPredicate predicateWithLeftExpression: left
rightExpression: lexp
modifier: modifier
type: NSGreaterThanOrEqualToPredicateOperatorType
options: opts];
up = [NSComparisonPredicate predicateWithLeftExpression: left
rightExpression: uexp
modifier: modifier
type: NSLessThanOrEqualToPredicateOperatorType
options: opts];
return [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects: lp, up, nil]];
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid comparison predicate: %@",
[[self string] substringFromIndex: [self scanLocation]]];
}
if ([self scanString: @"[cd]" intoString: NULL])
{
opts = NSCaseInsensitivePredicateOption
| NSDiacriticInsensitivePredicateOption;
}
else if ([self scanString: @"[c]" intoString: NULL])
{
opts = NSCaseInsensitivePredicateOption;
}
else if ([self scanString: @"[d]" intoString: NULL])
{
opts = NSDiacriticInsensitivePredicateOption;
}
right = [self parseExpression];
if (swap == YES)
{
NSExpression *tmp = left;
left = right;
right = tmp;
}
p = [NSComparisonPredicate predicateWithLeftExpression: left
rightExpression: right
modifier: modifier
type: type
options: opts];
return negate ? [NSCompoundPredicate notPredicateWithSubpredicate: p] : p;
}
- (NSExpression *) parseExpression
{
return [self parseBinaryExpression];
}
- (NSExpression *) parseIdentifierExpression
{
static NSCharacterSet *_identifier;
NSString *ident;
// skip # as prefix if present (reserved words)
(void)[self scanString: @"#" intoString: NULL];
if (!_identifier)
{
ASSIGN(_identifier, [NSCharacterSet characterSetWithCharactersInString:
@"_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"]);
}
if (![self scanCharactersFromSet: _identifier intoString: &ident])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing identifier: %@",
[[self string] substringFromIndex: [self scanLocation]]];
}
return [NSExpression expressionForKeyPath: ident];
}
- (NSExpression *) parseSimpleExpression
{
unsigned location;
double dbl;
if ([self scanDouble: &dbl])
{
return [NSExpression expressionForConstantValue:
[NSNumber numberWithDouble: dbl]];
}
// FIXME: handle integer, hex constants, 0x 0o 0b
if ([self scanString: @"-" intoString: NULL])
{
return [NSExpression expressionForFunction: @"_chs"
arguments: [NSArray arrayWithObject: [self parseExpression]]];
}
if ([self scanString: @"(" intoString: NULL])
{
NSExpression *arg = [self parseExpression];
if (![self scanString: @")" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ) in expression"];
}
return arg;
}
if ([self scanString: @"{" intoString: NULL])
{
NSMutableArray *a = [NSMutableArray arrayWithCapacity: 10];
if ([self scanString: @"}" intoString: NULL])
{
// empty
return [NSExpression expressionForConstantValue: a];
}
// first element
[a addObject: [self parseExpression]];
while ([self scanString: @"," intoString: NULL])
{
// more elements
[a addObject: [self parseExpression]];
}
if (![self scanString: @"}" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing } in aggregate"];
}
return [NSExpression expressionForConstantValue: a];
}
if ([self scanPredicateKeyword: @"NULL"]
|| [self scanPredicateKeyword: @"NIL"])
{
return [NSExpression expressionForConstantValue: [NSNull null]];
}
if ([self scanPredicateKeyword: @"TRUE"]
|| [self scanPredicateKeyword: @"YES"])
{
return [NSExpression expressionForConstantValue:
[NSNumber numberWithBool: YES]];
}
if ([self scanPredicateKeyword: @"FALSE"]
|| [self scanPredicateKeyword: @"NO"])
{
return [NSExpression expressionForConstantValue:
[NSNumber numberWithBool: NO]];
}
if ([self scanPredicateKeyword: @"SELF"])
{
return [NSExpression expressionForEvaluatedObject];
}
if ([self scanString: @"$" intoString: NULL])
{
// variable
NSExpression *var = [self parseIdentifierExpression];
if (![var keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid variable identifier: %@", var];
}
return [NSExpression expressionForVariable: [var keyPath]];
}
location = [self scanLocation];
if ([self scanString: @"%" intoString: NULL])
{
if ([self isAtEnd] == NO)
{
unichar c = [[self string] characterAtIndex: [self scanLocation]];
switch (c)
{
case '%': // '%%' is treated as '%'
location = [self scanLocation];
break;
case 'K':
[self setScanLocation: [self scanLocation] + 1];
return [NSExpression expressionForKeyPath:
[self nextArg]];
case '@':
case 'c':
case 'C':
case 'd':
case 'D':
case 'i':
case 'o':
case 'O':
case 'u':
case 'U':
case 'x':
case 'X':
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
[self setScanLocation: [self scanLocation] + 1];
return [NSExpression expressionForConstantValue:
[self nextArg]];
case 'h':
(void)[self scanString: @"h" intoString: NULL];
if ([self isAtEnd] == NO)
{
c = [[self string] characterAtIndex: [self scanLocation]];
if (c == 'i' || c == 'u')
{
[self setScanLocation: [self scanLocation] + 1];
return [NSExpression expressionForConstantValue:
[self nextArg]];
}
}
break;
case 'q':
(void)[self scanString: @"q" intoString: NULL];
if ([self isAtEnd] == NO)
{
c = [[self string] characterAtIndex: [self scanLocation]];
if (c == 'i' || c == 'u' || c == 'x' || c == 'X')
{
[self setScanLocation: [self scanLocation] + 1];
return [NSExpression expressionForConstantValue:
[self nextArg]];
}
}
break;
}
}
[self setScanLocation: location];
}
if ([self scanString: @"\"" intoString: NULL])
{
NSCharacterSet *skip = [self charactersToBeSkipped];
NSString *str = nil;
[self setCharactersToBeSkipped: nil];
if ([self scanUpToString: @"\"" intoString: &str] == NO)
{
[self setCharactersToBeSkipped: skip];
[NSException raise: NSInvalidArgumentException
format: @"Invalid double quoted literal at %u", location];
}
[self setCharactersToBeSkipped: skip];
2018-02-06 18:05:28 +00:00
if (NO == [self scanString: @"\"" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Unterminated double quoted literal at %u", location];
}
return [NSExpression expressionForConstantValue: str];
}
if ([self scanString: @"'" intoString: NULL])
{
NSCharacterSet *skip = [self charactersToBeSkipped];
NSString *str = nil;
[self setCharactersToBeSkipped: nil];
if ([self scanUpToString: @"'" intoString: &str] == NO)
{
[self setCharactersToBeSkipped: skip];
[NSException raise: NSInvalidArgumentException
format: @"Invalid single quoted literal at %u", location];
}
[self setCharactersToBeSkipped: skip];
2018-02-06 18:05:28 +00:00
if (NO == [self scanString: @"'" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Unterminated single quoted literal at %u", location];
}
return [NSExpression expressionForConstantValue: str];
}
if ([self scanString: @"@" intoString: NULL])
{
NSExpression *e = [self parseIdentifierExpression];
if (![e keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid keypath identifier: %@", e];
}
// prefix with keypath
return [NSExpression expressionForKeyPath:
[NSString stringWithFormat: @"@%@", [e keyPath]]];
}
return [self parseIdentifierExpression];
}
- (NSExpression *) parseFunctionalExpression
{
NSExpression *left = [self parseSimpleExpression];
while (YES)
{
if ([self scanString: @"(" intoString: NULL])
{
// function - this parser allows for (max)(a, b, c) to be properly
// recognized and even (%K)(a, b, c) if %K evaluates to "max"
NSMutableArray *args = [NSMutableArray arrayWithCapacity: 5];
if (![left keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid function identifier: %@", left];
}
if (![self scanString: @")" intoString: NULL])
{
// any arguments
// first argument
[args addObject: [self parseExpression]];
while ([self scanString: @"," intoString: NULL])
{
// more arguments
[args addObject: [self parseExpression]];
}
if (![self scanString: @")" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ) in function arguments"];
}
}
left = [NSExpression expressionForFunction: [left keyPath]
arguments: args];
}
else if ([self scanString: @"[" intoString: NULL])
{
// index expression
if ([self scanPredicateKeyword: @"FIRST"])
{
left = [NSExpression expressionForFunction: @"_first"
arguments: [NSArray arrayWithObject: left]];
}
else if ([self scanPredicateKeyword: @"LAST"])
{
left = [NSExpression expressionForFunction: @"_last"
arguments: [NSArray arrayWithObject: left]];
}
else if ([self scanPredicateKeyword: @"SIZE"])
{
left = [NSExpression expressionForFunction: @"count"
arguments: [NSArray arrayWithObject: left]];
}
else
{
left = [NSExpression expressionForFunction: @"_index"
arguments: [NSArray arrayWithObjects: left,
[self parseExpression], nil]];
}
if (![self scanString: @"]" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ] in index argument"];
}
}
else if ([self scanString: @"." intoString: NULL])
{
// keypath - this parser allows for (a).(b.c)
// to be properly recognized
// and even %K.((%K)) if the first %K evaluates to "a" and the
// second %K to "b.c"
NSExpression *right;
right = [self parseExpression];
if (evaluatedObjectExpression != left)
{
2022-02-12 13:06:23 +00:00
// if both are simple key expressions (identifiers)
if ([left keyPath] && [right keyPath])
{
// concatenate
left = [NSExpression expressionForKeyPath:
[NSString stringWithFormat: @"%@.%@",
[left keyPath], [right keyPath]]];
}
else
{
left = [NSExpression
expressionForKeyPathCompositionWithLeft: left
right: right];
}
}
else
{
left = [NSExpression expressionForKeyPath: [right keyPath]];
}
}
else
{
// done with suffixes
return left;
}
}
}
- (NSExpression *) parsePowerExpression
{
NSExpression *left = [self parseFunctionalExpression];
while (YES)
{
NSExpression *right;
if ([self scanString: @"**" intoString: NULL])
{
right = [self parseFunctionalExpression];
left = [NSExpression expressionForFunction: @"_pow"
arguments: [NSArray arrayWithObjects: left, right, nil]];
}
else
{
return left;
}
}
}
- (NSExpression *) parseMultiplicationExpression
{
NSExpression *left = [self parsePowerExpression];
while (YES)
{
NSExpression *right;
if ([self scanString: @"*" intoString: NULL])
{
right = [self parsePowerExpression];
left = [NSExpression expressionForFunction: @"_mul"
arguments: [NSArray arrayWithObjects: left, right, nil]];
}
else if ([self scanString: @"/" intoString: NULL])
{
right = [self parsePowerExpression];
left = [NSExpression expressionForFunction: @"_div"
arguments: [NSArray arrayWithObjects: left, right, nil]];
}
else
{
return left;
}
}
}
- (NSExpression *) parseAdditionExpression
{
NSExpression *left = [self parseMultiplicationExpression];
while (YES)
{
NSExpression *right;
if ([self scanString: @"+" intoString: NULL])
{
right = [self parseMultiplicationExpression];
left = [NSExpression expressionForFunction: @"_add"
arguments: [NSArray arrayWithObjects: left, right, nil]];
}
else if ([self scanString: @"-" intoString: NULL])
{
right = [self parseMultiplicationExpression];
left = [NSExpression expressionForFunction: @"_sub"
arguments: [NSArray arrayWithObjects: left, right, nil]];
}
else
{
return left;
}
}
}
- (NSExpression *) parseBinaryExpression
{
NSExpression *left = [self parseAdditionExpression];
while (YES)
{
NSExpression *right;
if ([self scanString: @":=" intoString: NULL]) // assignment
{
// check left to be a variable?
right = [self parseAdditionExpression];
// FIXME
}
else
{
return left;
}
}
}
@end
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST)
@implementation GSBlockPredicate
- (instancetype) initWithBlock: (GSBlockPredicateBlock)block
{
if (nil == (self = [super init]))
{
return nil;
}
_block = (GSBlockPredicateBlock)[(id)block retain];
return self;
}
- (instancetype) predicateWithSubstitutionVariables:
(GS_GENERIC_CLASS(NSDictionary,NSString*,id)*)variables
{
return [[[GSBoundBlockPredicate alloc] initWithBlock: _block
bindings: variables] autorelease];
}
- (BOOL) evaluateWithObject: (id)object
substitutionVariables: (GS_GENERIC_CLASS(NSDictionary,
NSString*,id)*)variables
{
return CALL_NON_NULL_BLOCK(_block, object, variables);
}
- (BOOL) evaluateWithObject: (id)object
{
return [self evaluateWithObject: object
substitutionVariables: nil];
}
- (void) dealloc
{
[(id)_block release];
_block = NULL;
[super dealloc];
}
- (NSString*) predicateFormat
{
return [NSString stringWithFormat: @"BLOCKPREDICATE(%p)", (void*)_block];
}
@end
@implementation GSBoundBlockPredicate
- (instancetype) initWithBlock: (GSBlockPredicateBlock)block
bindings: (GS_GENERIC_CLASS(NSDictionary,
NSString*,id)*)bindings
{
if (nil == (self = [super initWithBlock: block]))
{
return nil;
}
ASSIGN(_bindings, bindings);
return self;
}
- (BOOL) evaluateWithObject: (id)object
{
return [self evaluateWithObject: object
substitutionVariables: _bindings];
}
- (void) dealloc
{
DESTROY(_bindings);
[super dealloc];
}
@end
#endif