mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 13:07:02 +00:00
"($)path.($)variable" "SUBQUERY(collection, $x,$x = 2)" "sum:(arg1, ...)" "FUNCTION(target, selector, arg1, ...) "object[expression]" "set UNION|INTERSECT|MINUS otherset" Fixed a bug where CPExpression_set was evaluating to an expression instead of a set. Replaced parsing exceptions with a generic function indicating where it failed.
287 lines
6.3 KiB
Plaintext
287 lines
6.3 KiB
Plaintext
|
|
@import "CPExpression.j"
|
|
@import "CPString.j"
|
|
@import "CPArray.j"
|
|
@import "CPDictionary.j"
|
|
|
|
@implementation CPExpression_function : CPExpression
|
|
{
|
|
CPExpression _operand;
|
|
SEL _selector;
|
|
CPArray _arguments;
|
|
int _argc;
|
|
int _maxargs;
|
|
}
|
|
|
|
- (id)initWithSelector:(SEL)aSelector arguments:(CPArray)parameters
|
|
{
|
|
var target = [CPPredicateUtilities class];
|
|
if (![target respondsToSelector:aSelector])
|
|
[CPException raise:CPInvalidArgumentException reason:@"Unknown function implementation: " + aSelector];
|
|
|
|
var operand = [CPExpression expressionForConstantValue:target];
|
|
return [self initWithTarget:operand selector:aSelector arguments:parameters];
|
|
}
|
|
|
|
- (id)initWithTarget:(CPExpression)operand selector:(SEL)aSelector arguments:(CPArray)parameters
|
|
{
|
|
return [self initWithTarget:operand selector:aSelector arguments:parameters type:CPFunctionExpressionType];
|
|
}
|
|
|
|
- (id)initWithTarget:(CPExpression)operand selector:(SEL)aSelector arguments:(CPArray)parameters type:(int)type
|
|
{
|
|
[super initWithExpressionType:type];
|
|
|
|
// Cocoa doc: "This method throws an exception immediately if the selector is unknown"
|
|
// but operand's value (the target) may be resolved only at runtime.
|
|
_selector = aSelector;
|
|
_operand = operand;
|
|
_arguments = parameters;
|
|
_argc = [parameters count];
|
|
_maxargs = [[CPStringFromSelector(_selector) componentsSeparatedByString:@":"] count] - 1;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)object
|
|
{
|
|
if (self == object)
|
|
return YES;
|
|
|
|
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object _function] isEqualToString:[self _function]] || ![[object operand] isEqual:[self operand]] || ![[object arguments] isEqualToArray:[self arguments]])
|
|
return NO;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (CPString)_function // OBJJ preprocessor does not like function as a method
|
|
{
|
|
return CPStringFromSelector(_selector);
|
|
}
|
|
|
|
- (CPString)function
|
|
{
|
|
return [self _function];
|
|
}
|
|
|
|
- (CPArray)arguments
|
|
{
|
|
return _arguments;
|
|
}
|
|
|
|
- (CPExpression)operand
|
|
{
|
|
return _operand;
|
|
}
|
|
|
|
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
|
|
{
|
|
var target = [_operand expressionValueWithObject:object context:context],
|
|
objj_args = [target, _selector],
|
|
i;
|
|
|
|
for (i = 0; i < _argc; i++)
|
|
{
|
|
var arg = [_arguments[i] expressionValueWithObject:object context:context];
|
|
objj_args.push(arg);
|
|
}
|
|
|
|
// If we have too much arguments, concatenate remaining args on the last one.
|
|
if (_argc > _maxargs)
|
|
{
|
|
var r = MAX(_maxargs + 1, 2);
|
|
objj_args = objj_args.slice(0, r).concat([objj_args.slice(r)]);
|
|
}
|
|
|
|
return objj_msgSend.apply(this, objj_args);
|
|
}
|
|
|
|
- (CPString)description
|
|
{
|
|
var result = "";
|
|
if ([_operand isEqual:[CPExpression expressionForConstantValue:[CPPredicateUtilities class]]])
|
|
result += CPStringFromSelector(_selector) + "(";
|
|
else
|
|
{
|
|
result += "FUNCTION(";
|
|
result += _operand ? [_operand description] + ", ":"";
|
|
result += _selector ? CPStringFromSelector(_selector) + ", ":"";
|
|
}
|
|
|
|
for (var i = 0; i < _argc; i++)
|
|
result = result + [_arguments[i] description] + ((i + 1 < _argc) ? ", " : "");
|
|
|
|
result += ")";
|
|
|
|
return result ;
|
|
}
|
|
|
|
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
|
|
{
|
|
var operand = [[self operand] _expressionWithSubstitutionVariables:variables],
|
|
args = [CPArray array];
|
|
|
|
for (var i = 0; i < _argc; i++)
|
|
[args addObject:[_arguments[i] _expressionWithSubstitutionVariables:variables]];
|
|
|
|
return [CPExpression expressionForFunction:operand selectorName:[self _function] arguments:args];
|
|
}
|
|
|
|
@end
|
|
|
|
var CPSelectorNameKey = @"CPSelectorName",
|
|
CPArgumentsKey = @"CPArguments",
|
|
CPOperandKey = @"CPOperand",
|
|
CPExpressionTypeKey = @"CPExpressionType";
|
|
|
|
@implementation CPExpression_function (CPCoding)
|
|
|
|
- (id)initWithCoder:(CPCoder)coder
|
|
{
|
|
var type = [coder decodeIntForKey:CPExpressionTypeKey],
|
|
operand = [coder decodeObjectForKey:CPOperandKey],
|
|
selector = CPSelectorFromString([coder decodeObjectForKey:CPSelectorNameKey]),
|
|
parameters = [coder decodeObjectForKey:CPArgumentsKey];
|
|
|
|
return [self initWithTarget:operand selector:selector arguments:parameters type:type];
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)coder
|
|
{
|
|
[coder encodeObject:[self _function] forKey:CPSelectorNameKey];
|
|
[coder encodeObject:_arguments forKey:CPArgumentsKey];
|
|
[coder encodeObject:_operand forKey:CPOperandKey];
|
|
[coder encodeInt:_type forKey:CPExpressionTypeKey];
|
|
}
|
|
|
|
@end
|
|
|
|
// Built-in functions
|
|
@implementation CPPredicateUtilities : CPObject
|
|
{
|
|
}
|
|
|
|
+ (float)sum:(CPArray)parameters
|
|
{
|
|
var sum = 0,
|
|
count = parameters.length;
|
|
|
|
while (count--)
|
|
sum += parameters[count];
|
|
|
|
return sum;
|
|
}
|
|
|
|
+ (float)count:(CPArray)parameters
|
|
{
|
|
return [parameters count];
|
|
}
|
|
|
|
+ (float)min:(CPArray)parameters
|
|
{
|
|
return parameters.sort()[0];
|
|
}
|
|
|
|
+ (float)max:(CPArray)parameters
|
|
{
|
|
return parameters.sort()[parameters.length - 1];
|
|
}
|
|
|
|
+ (float)average:(CPArray)parameters
|
|
{
|
|
return [self sum:parameters] / parameters.length;
|
|
}
|
|
|
|
+ (id)first:(CPArray)parameters
|
|
{
|
|
return parameters[0];
|
|
}
|
|
|
|
+ (id)last:(CPArray)parameters
|
|
{
|
|
return parameters[parameters.length - 1];
|
|
}
|
|
|
|
+ (id)fromObject:(id)object index:(id)anIndex
|
|
{
|
|
if ([object isKindOfClass:[CPDictionary class]])
|
|
return [object objectForKey:anIndex];
|
|
else ([object isKindOfClass:[CPArray class]])
|
|
return [object objectAtIndex:anIndex];
|
|
|
|
[CPException raise:CPInvalidArgumentException reason:@"object[#] requires a CPDictionary or CPArray"];
|
|
}
|
|
|
|
+ (float)add:(int)n to:(int)m
|
|
{
|
|
return n + m;
|
|
}
|
|
|
|
+ (float)from:(int)n substract:(int)m
|
|
{
|
|
return n - m;
|
|
}
|
|
|
|
+ (float)multiply:(float)n by:(int)m
|
|
{
|
|
return n * m;
|
|
}
|
|
|
|
+ (float)divide:(float)n by:(float)m
|
|
{
|
|
return n / m;
|
|
}
|
|
|
|
+ (float)sqrt:(float)n
|
|
{
|
|
return SQRT(n);
|
|
}
|
|
|
|
+ (float)raise:(float)num to:(int)power
|
|
{
|
|
return POW(num, power);
|
|
}
|
|
|
|
+ (float)abs:(float)num
|
|
{
|
|
return ABS(num);
|
|
}
|
|
|
|
+ (CPDate)now
|
|
{
|
|
return [CPDate date];
|
|
}
|
|
|
|
+ (float)ln:(float)num
|
|
{
|
|
return LN10(num);
|
|
}
|
|
|
|
+ (float)exp:(float)num
|
|
{
|
|
return EXP(num);
|
|
}
|
|
|
|
+ (float)ceiling:(float)num
|
|
{
|
|
return CEIL(num);
|
|
}
|
|
|
|
+ (int)random:(int)num
|
|
{
|
|
return ROUND(RAND() * num);
|
|
}
|
|
|
|
+ (int)modulus:(int)n by:(int)m
|
|
{
|
|
return n % m;
|
|
}
|
|
|
|
+ (float)chs:(int)num
|
|
{
|
|
return -num;
|
|
}
|
|
|
|
@end
|
|
|