Merge pull request #2402 from cacaodev/CPExpression-constructors

NEW : CPExpression +expressionForBlock:arguments:
                                   +expressionForConditional:trueExpression:falseExpression:
This commit is contained in:
cacaodev
2016-01-25 10:22:18 +01:00
12 changed files with 313 additions and 58 deletions
+2
View File
@@ -28,3 +28,5 @@
@import "_CPAggregateExpression.j"
@import "_CPSetExpression.j"
@import "_CPSubqueryExpression.j"
@import "_CPBlockExpression.j"
@import "_CPConditionalExpression.j"
@@ -25,7 +25,7 @@
@implementation _CPAggregateExpression : CPExpression
{
CPArray _aggregate;
CPArray _aggregate @accessors(getter=collection);
}
- (id)initWithAggregate:(CPArray)collection
@@ -48,11 +48,6 @@
return YES;
}
- (id)collection
{
return _aggregate;
}
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
{
var eval_array = [CPArray array],
@@ -0,0 +1,83 @@
/*
* _CPBlockExpression.j
*
* Created by cacaodev.
* Copyright 2015.
*
* 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.1 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-1301 USA
*/
@import "_CPExpression.j"
@implementation _CPBlockExpression : CPExpression
{
Function _block @accessors(getter=expressionBlock);
CPArray _arguments @accessors(getter=arguments);
}
- (id)initWithBlock:(Function)aBlock arguments:(CPArray)arguments
{
self = [super initWithExpressionType:CPBlockExpressionType];
if (self)
{
_block = aBlock;
_arguments = arguments;
}
return self;
}
- (BOOL)isEqual:(id)object
{
if (self === object)
return YES;
if (object === nil || object.isa !== self.isa || [object expressionBlock] !== _block || ![[object arguments] isEqual:_arguments])
return NO;
return YES;
}
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
{
var args = [];
[_arguments enumerateObjectsUsingBlock:function(exp, idx)
{
[args addObject:[exp expressionValueWithObject:object context:context]];
}];
return _block(object, args, context);
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)bindings
{
var args = [];
[_arguments enumerateObjectsUsingBlock:function(exp, idx)
{
[args addObject:[exp _expressionWithSubstitutionVariables:bindings]];
}];
return [[_CPBlockExpression alloc] initWithBlock:_block arguments:args];
}
- (CPString)description
{
return [CPString stringWithFormat:@"Block(function, %@)", [_arguments description]];
}
@end
@@ -0,0 +1,78 @@
/*
* _CPConditionalExpression.j
*
* Created by cacaodev.
* Copyright 2015.
*
* 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.1 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-1301 USA
*/
@import "_CPPredicate.j"
@import "_CPExpression.j"
@implementation _CPConditionalExpression : CPExpression
{
CPPredicate _predicate @accessors(getter=predicate);
CPExpression _trueExpression @accessors(getter=trueExpression);
CPExpression _falseExpression @accessors(getter=falseExpression);
}
- (id)initWithPredicate:(CPPredicate)aPredicate trueExpression:(CPExpression)trueExpression falseExpression:(CPExpression)falseExpression
{
self = [super initWithExpressionType:CPConditionalExpressionType];
if (self)
{
_predicate = aPredicate;
_trueExpression = trueExpression;
_falseExpression = falseExpression;
}
return self;
}
- (BOOL)isEqual:(id)object
{
if (self === object)
return YES;
if (object === nil || object.isa !== self.isa || ![[object predicate] isEqual:_predicate] || ![[object trueExpression] isEqual:_trueExpression] || ![[object falseExpression] isEqual:_falseExpression])
return NO;
return YES;
}
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
{
var eval = [_predicate evaluateWithObject:object substitutionVariables:context],
exp = eval ? _trueExpression : _falseExpression;
return [exp expressionValueWithObject:object context:context];
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)bindings
{
var predicate = [_predicate predicateWithSubstitutionVariables:bindings],
trueExp = [_trueExpression _expressionWithSubstitutionVariables:bindings],
falseExp = [_falseExpression _expressionWithSubstitutionVariables:bindings];
return [[_CPConditionalExpression alloc] initWithPredicate:predicate trueExpression:trueExp falseExpression:falseExp];
}
- (CPString)description
{
return [CPString stringWithFormat:@"TERNARY(%@,%@,%@)", [_predicate predicateFormat], [_trueExpression description], [_falseExpression description]];
}
@end
@@ -27,7 +27,7 @@
@implementation _CPConstantValueExpression : CPExpression
{
id _value;
id _value @accessors(getter=constantValue);
}
- (id)initWithValue:(id)value
@@ -51,11 +51,6 @@
return YES;
}
- (id)constantValue
{
return _value;
}
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
{
return _value;
+70 -3
View File
@@ -65,6 +65,14 @@ CPIntersectSetExpressionType = 8;
An expression that combines two nested expression results by set subtraction.
*/
CPMinusSetExpressionType = 9;
/*!
An expression that returns the result of evaluating a block.
*/
CPBlockExpressionType = 10;
/*!
An expression that returns an expression that depends on the evaluation of a predicate.
*/
CPConditionalExpressionType = 11;
/*!
@ingroup foundation
@@ -259,6 +267,32 @@ CPMinusSetExpressionType = 9;
return [[_CPSubqueryExpression alloc] initWithExpression:expression usingIteratorVariable:variable predicate:predicate];
}
/*!
Returns Creates an NSExpression object that will use the Block for evaluating objects.
@param aBlock The Block is applied to the object to be evaluated.
The Block takes three arguments and returns a value:
evaluatedObject
The object to be evaluated.
expressions
An array of predicate expressions that evaluates to a collection.
context
A dictionary that the expression can use to store temporary state for one predicate evaluation.
@discussion Note that context is mutable, and that it can only be accessed during the evaluation of the expression.
@param arguments An array containing NSExpression objects that will be used as parameters during the invocation of the block.
*/
+ (CPExpression)expressionForBlock:(Function)aBlock arguments:(CPArray)args
{
return [[_CPBlockExpression alloc] initWithBlock:aBlock arguments:args];
}
+ (CPExpression)expressionForConditional:(CPPredicate)aPredicate trueExpression:(CPExpression)trueExpression falseExpression:(CPExpression)falseExpression
{
return [[_CPConditionalExpression alloc] initWithPredicate:aPredicate trueExpression:trueExpression falseExpression:falseExpression];
}
// Getting Information About an Expression
/*!
Returns the expression type for the receiver.
@@ -316,7 +350,7 @@ CPMinusSetExpressionType = 9;
/*!
Returns the arguments for the receiver.
@return The arguments for the receiver—that is, the array of expressions that will be passed as parameters during invocation of the selector on the operand of a function expression.
@return The arguments for the receiver—that is, the array of expressions that will be passed as parameters during invocation of the selector on the operand of a function expression or as a parameter of the block of a block expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPArray)arguments
@@ -337,8 +371,8 @@ CPMinusSetExpressionType = 9;
}
/*!
Returns the predicate in a subquery expression.
@return The predicate in a subquery expression..
Returns the predicate in a subquery expression or a conditional expression.
@return The predicate in a subquery expression or a conditional expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPPredicate)predicate
@@ -380,6 +414,39 @@ CPMinusSetExpressionType = 9;
return nil;
}
/*!
Returns the block of a block expression.
@return The block of a block expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (Function)expressionBlock
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return nil;
}
/*!
Returns the true expression of a conditional expression.
@return The true expression of a conditional expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPExpression)trueExpression
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return nil;
}
/*!
Returns the false expression of a conditional expression.
@return The false expression of a conditional expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPExpression)falseExpression
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return nil;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
return self;
+2 -12
View File
@@ -28,9 +28,9 @@
@implementation _CPFunctionExpression : CPExpression
{
CPExpression _operand;
CPExpression _operand @accessors(getter=operand);
SEL _selector;
CPArray _arguments;
CPArray _arguments @accessors(getter=arguments);
int _argc;
int _maxargs;
}
@@ -88,16 +88,6 @@
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],
+28 -1
View File
@@ -744,16 +744,43 @@
if (![self scanString:@"," intoString:NULL])
CPRaiseParseError(self, @"expression");
variableExpression = [self parseExpression];
if (![self scanString:@"," intoString:NULL])
CPRaiseParseError(self, @"expression");
subpredicate = [self parsePredicate];
if (![self scanString:@")" intoString:NULL])
CPRaiseParseError(self, @"predicate");
return [[_CPSubqueryExpression alloc] initWithExpression:collection usingIteratorExpression:variableExpression predicate:subpredicate];
}
if ([self scanString:@"TERNARY" intoString:NULL])
{
if (![self scanString:@"(" intoString:NULL])
CPRaiseParseError(self, @"expression");
var predicate = [self parsePredicate],
trueExpression,
falseExpression;
if (![self scanString:@"," intoString:NULL])
CPRaiseParseError(self, @"predicate");
trueExpression = [self parseExpression];
if (![self scanString:@"," intoString:NULL])
CPRaiseParseError(self, @"expression");
falseExpression = [self parseExpression];
if (![self scanString:@")" intoString:NULL])
CPRaiseParseError(self, @"expression");
return [[_CPSubqueryExpression alloc] initWithExpression:collection usingIteratorExpression:variableExpression predicate:subpredicate];
return [CPExpression expressionForConditional:predicate trueExpression:trueExpression falseExpression:falseExpression];
}
if ([self scanString:@"FUNCTION" intoString:NULL])
+2 -12
View File
@@ -25,8 +25,8 @@
@implementation _CPSetExpression : CPExpression
{
CPExpression _left;
CPExpression _right;
CPExpression _left @accessors(getter=leftExpression);
CPExpression _right @accessors(getter=rightExpression);
}
- (id)initWithType:(int)type left:(CPExpression)left right:(CPExpression)right
@@ -88,16 +88,6 @@
return self;
}
- (CPExpression)leftExpression
{
return _left;
}
- (CPExpression)rightExpression
{
return _right;
}
- (CPString)description
{
var desc;
+2 -12
View File
@@ -26,9 +26,9 @@
@implementation _CPSubqueryExpression : CPExpression
{
CPExpression _collection;
CPExpression _collection @accessors(getter=collection);
CPExpression _variableExpression;
CPPredicate _subpredicate;
CPPredicate _subpredicate @accessors(getter=predicate);
}
- (id)initWithExpression:(CPExpression)collection usingIteratorVariable:(CPString)variable predicate:(CPPredicate)subpredicate
@@ -81,21 +81,11 @@
return YES;
}
- (CPExpression)collection
{
return _collection;
}
- (id)copy
{
return [[_CPSubqueryExpression alloc] initWithExpression:[_collection copy] usingIteratorExpression:[_variableExpression copy] predicate:[_subpredicate copy]];
}
- (CPPredicate)predicate
{
return _subpredicate;
}
- (CPString)description
{
return [self predicateFormat];
@@ -29,7 +29,7 @@
@implementation _CPVariableExpression : CPExpression
{
CPString _variable;
CPString _variable @accessors(getter=variable);
}
- (id)initWithVariable:(CPString)variable
@@ -54,11 +54,6 @@
return YES;
}
- (CPString)variable
{
return _variable;
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
var expression = [self _expressionWithSubstitutionVariables:context];
+43
View File
@@ -79,6 +79,17 @@
var expression_minusset = [CPExpression expressionForMinusSet:set with:array];
[self assertNotNull:expression_minusset message:"MinusSet Expression should not be nil"];
var expression_block = [CPExpression expressionForBlock:function(obj, args, bindings)
{
return obj;
} arguments:nil];
[self assertNotNull:expression_block message:"Block Expression should not be nil"];
var expression_conditional = [CPExpression expressionForConditional:[CPPredicate predicateWithValue:YES] trueExpression:expression_minusset falseExpression:expression_block];
[self assertNotNull:expression_conditional message:"Conditional Expression should not be nil"];
}
- (void)testSetExpressionEvaluation
@@ -147,6 +158,27 @@
[self assertTrue:([eval isEqual:expected]) message:"'" + [expression predicateFormat] + "' result is "+ eval + "but should be " + expected];
}
- (void)testBlockExpressionEvaluation
{
var block = function(obj, args, bindings)
{
return [obj stringByAppendingString:[args componentsJoinedByString:"-"]];
};
var expression = [CPExpression expressionForBlock:block arguments:[[CPExpression expressionForConstantValue:"A"], [CPExpression expressionForConstantValue:"B"]]];
var eval = [expression expressionValueWithObject:"OBJ" context:@{}];
[self assertTrue:([eval isEqual:"OBJA-B"]) message:"'" + [expression description] + "' result is "+ eval + "but should be " + "OBJA-B"];
}
- (void)testConditionalExpressionEvaluation
{
var expression = [CPExpression expressionForConditional:[CPPredicate predicateWithFormat:"SELF = $variable"] trueExpression:[CPExpression expressionForConstantValue:"TRUE_EXP"] falseExpression:[CPExpression expressionForConstantValue:"FALSE_EXP"]];
var eval = [expression expressionValueWithObject:"OBJ" context:@{"variable":"OBJ"}];
[self assertTrue:([eval isEqual:"TRUE_EXP"]) message:"'" + [expression description] + "' result is "+ eval + "but should be " + "TRUE_EXP"];
}
- (void)testOptions
{
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:@"àa"] rightExpression:[CPExpression expressionForConstantValue:@"aà"] modifier:CPDirectPredicateModifier type:CPLikePredicateOperatorType options:3];
@@ -381,6 +413,9 @@
predicate = [CPPredicate predicateWithFormat:@"a UNION {'b'} = {'a','b'}"];
var left = [[predicate leftExpression] expressionValueWithObject:object context:nil];
[self assertTrue:[left isEqualToSet:result] message:"Expression eval " + left + " should be " + result];
predicate = [CPPredicate predicateWithFormat:@"TERNARY(Record1.Children[SIZE] = 1, 'Single', Record1.Name) = 'John'"];
[self assertTrue:[predicate evaluateWithObject:dict] message:"Predicate " + predicate + " should evaluate to TRUE"];
}
- (void)testExpressionAndPredicateIsEqual
@@ -439,6 +474,10 @@
pred1 = [CPPredicate predicateWithFormat:@"a = 'a' AND b = 'b'"];
pred2 = [CPPredicate predicateWithFormat:@"a = 'a' AND b = 'b'"];
[self assert:pred1 equals:pred2];
pred1 = [CPPredicate predicateWithFormat:@"TERNARY(Record1.Children[FIRST] BEGINSWITH Record1.Name, 'SAME', 'DIFFERENT') = 'DIFFERENT'"];
pred2 = [CPPredicate predicateWithFormat:@"TERNARY(Record1.Children[FIRST] BEGINSWITH Record1.Name, 'SAME', 'DIFFERENT') = 'DIFFERENT'"];
[self assert:pred1 equals:pred2];
}
- (void)testExpressionAndPredicateIsNotEqualToNil
@@ -490,6 +529,10 @@
[self assert:pred1 notEqual:nil];
pred1 = [CPPredicate predicateWithFormat:@"TERNARY(Record1.Children[FIRST] BEGINSWITH Record1.Name, 'SAME', 'DIFFERENT') = 'DIFFERENT'"];
[self assert:pred1 notEqual:nil];
pred1 = [CPPredicate predicateWithFormat:@"$x CONTAINS 'a'"];
[self assert:pred1 notEqual:nil];