API 0.9.5
Foundation/CPPredicate/CPPredicate.j
Go to the documentation of this file.
00001 /*
00002  * CPPredicate.j
00003  *
00004  * CPPredicate parsing based on NSPredicate.m in GNUStep Base Library (http://www.gnustep.org/)
00005  * Copyright (c) 2005 Free Software Foundation.
00006  *
00007  * Created by cacaodev.
00008  * Copyright 2010.
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * Lesser General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with this library; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00023  */
00024 
00025 
00052 @implementation CPPredicate : CPObject
00053 {
00054     id __doxygen__;
00055 }
00056 
00063 + (CPPredicate)predicateWithFormat:(CPString)format, ...
00064 {
00065     if (!format)
00066         [CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"];
00067 
00068     var args = Array.prototype.slice.call(arguments, 3);
00069     return [self predicateWithFormat:arguments[2] argumentArray:args];
00070 }
00071 
00078 + (CPPredicate)predicateWithFormat:(CPString)format argumentArray:(CPArray)args
00079 {
00080     if (!format)
00081         [CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"];
00082 
00083     var s = [[CPPredicateScanner alloc] initWithString:format args:args],
00084         p = [s parse];
00085 
00086     return p;
00087 }
00088 
00095 + (CPPredicate)predicateWithFormat:(CPString)format arguments:(va_list)argList
00096 {
00097     // UNIMPLEMENTED
00098     return nil;
00099 }
00100 
00106 - (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
00107 {
00108     // IMPLEMENTED BY SUBCLASSES
00109 }
00110 
00116 + (CPPredicate)predicateWithValue:(BOOL)value
00117 {
00118     return [[CPPredicate_BOOL alloc] initWithBool:value];
00119 }
00120 
00121 // Evaluating a Predicate
00127 - (BOOL)evaluateWithObject:(id)object
00128 {
00129     // IMPLEMENTED BY SUBCLASSES
00130 }
00131 
00138 - (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
00139 {
00140     // IMPLEMENTED BY SUBCLASSES
00141 }
00142 
00143 // Getting Format Information
00148 - (CPString)predicateFormat
00149 {
00150     // IMPLEMENTED BY SUBCLASSES
00151 }
00152 
00153 - (CPString)description
00154 {
00155     return [self predicateFormat];
00156 }
00157 
00158 @end
00159 
00160 @implementation CPPredicate_BOOL : CPPredicate
00161 {
00162     BOOL _value;
00163 }
00164 
00165 - (id)initWithBool:(BOOL)value
00166 {
00167     _value = value;
00168     return self;
00169 }
00170 
00171 - (BOOL)evaluateObject:(id)object
00172 {
00173     return _value;
00174 }
00175 
00176 - (CPString)predicateFormat
00177 {
00178     return (_value) ? @"TRUEPREDICATE" : @"FALSEPREDICATE";
00179 }
00180 
00181 @end
00182 
00183 
00184 @implementation CPArray (CPPredicate)
00185 
00186 - (CPArray)filteredArrayUsingPredicate:(CPPredicate)predicate
00187 {
00188     var count = [self count],
00189         result = [CPArray array],
00190         i = 0;
00191 
00192     for (; i < count; i++)
00193     {
00194         var object = self[i];
00195         if ([predicate evaluateWithObject:object])
00196             result.push(object);
00197     }
00198 
00199     return result;
00200 }
00201 
00202 - (void)filterUsingPredicate:(CPPredicate)predicate
00203 {
00204     var count = [self count];
00205 
00206     while (count--)
00207     {
00208         if (![predicate evaluateWithObject:self[count]])
00209             splice(count, 1);
00210     }
00211 }
00212 
00213 @end
00214 
00215 @implementation CPSet (CPPredicate)
00216 
00217 - (CPSet)filteredSetUsingPredicate:(CPPredicate)predicate
00218 {
00219     var count = [self count],
00220         result = [CPSet set],
00221         i = 0;
00222 
00223     for (; i < count; i++)
00224     {
00225         var object = [self objectAtIndex:i];
00226 
00227         if ([predicate evaluateWithObject:object])
00228             [result addObject:object];
00229     }
00230 
00231     return result;
00232 }
00233 
00234 - (void)filterUsingPredicate:(CPPredicate)predicate
00235 {
00236     var count = [self count];
00237 
00238     while (--count >= 0)
00239     {
00240         var object = [self objectAtIndex:count];
00241 
00242         if (![predicate evaluateWithObject:object])
00243             [self removeObjectAtIndex:count];
00244     }
00245 }
00246 
00247 @end
00248 
00249 #define REFERENCE(variable) \
00250 function(newValue)\
00251 {\
00252     var oldValue = variable;\
00253     if (typeof newValue != 'undefined')\
00254         variable = newValue;\
00255     return oldValue;\
00256 }
00257 
00258 @implementation CPPredicateScanner : CPScanner
00259 {
00260     CPEnumerator    _args;
00261     unsigned        _retrieved;
00262 }
00263 
00264 - (id)initWithString:(CPString)format args:(CPArray)args
00265 {
00266     self = [super initWithString:format]
00267 
00268     if (self)
00269     {
00270         _args = [args objectEnumerator];
00271     }
00272     return self;
00273 }
00274 
00275 - (id)nextArg
00276 {
00277     return [_args nextObject];
00278 }
00279 
00280 - (BOOL)scanPredicateKeyword:(CPString)key
00281 {
00282     var loc = [self scanLocation];
00283 
00284     [self setCaseSensitive:NO];
00285     if (![self scanString:key intoString:NULL])
00286         return NO;
00287 
00288     if ([self isAtEnd])
00289         return YES;
00290 
00291     var c = [[self string] characterAtIndex:[self scanLocation]];
00292     if (![[CPCharacterSet alphanumericCharacterSet] characterIsMember:c])
00293         return YES;
00294 
00295     [self setScanLocation:loc];
00296 
00297     return NO;
00298 }
00299 
00300 - (CPPredicate)parse
00301 {
00302     var r = nil;
00303 
00304     try
00305     {
00306         [self setCharactersToBeSkipped:[CPCharacterSet whitespaceCharacterSet]];
00307         r = [self parsePredicate];
00308     }
00309     catch(error)
00310     {
00311         CPLogConsole(@"Unable to parse predicate '" + [self string] + "' with " + error);
00312     }
00313     finally
00314     {
00315         if (![self isAtEnd])
00316         {
00317             var pstr = [self string],
00318                 loc = [self scanLocation];
00319             CPLogConsole(@"Format string contains extra characters: '" + [pstr substringToIndex:loc] + "**" + [pstr substringFromIndex:loc] + "**'");
00320         }
00321     }
00322 
00323     return r;
00324 }
00325 
00326 - (CPPredicate)parsePredicate
00327 {
00328     return [self parseAnd];
00329 }
00330 
00331 - (CPPredicate)parseAnd
00332 {
00333     var l = [self parseOr];
00334 
00335     while ([self scanPredicateKeyword:@"AND"] || [self scanPredicateKeyword:@"&&"])
00336     {
00337         var r = [self parseOr];
00338 
00339         if ([r isKindOfClass:[CPCompoundPredicate class]] && [r compoundPredicateType] == CPAndPredicateType)
00340         {
00341             if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPAndPredicateType)
00342             {
00343                 [[l subpredicates] addObjectsFromArray:[r subpredicates]];
00344             }
00345             else
00346             {
00347                 [[r subpredicates] insertObject:l atIndex:0];
00348                 l = r;
00349             }
00350         }
00351         else if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPAndPredicateType)
00352         {
00353             [[l subpredicates] addObject:r];
00354         }
00355         else
00356         {
00357             l = [CPCompoundPredicate andPredicateWithSubpredicates:[CPArray arrayWithObjects:l, r]];
00358         }
00359     }
00360     return l;
00361 }
00362 
00363 - (CPPredicate)parseNot
00364 {
00365     if ([self scanString:@"(" intoString:NULL])
00366     {
00367         var r = [self parsePredicate];
00368 
00369         if (![self scanString:@")" intoString:NULL])
00370             CPRaiseParseError(self, @"predicate");
00371 
00372         return r;
00373     }
00374 
00375     if ([self scanPredicateKeyword:@"NOT"] || [self scanPredicateKeyword:@"!"])
00376     {
00377         return [CPCompoundPredicate notPredicateWithSubpredicate:[self parseNot]];
00378     }
00379     if ([self scanPredicateKeyword:@"TRUEPREDICATE"])
00380     {
00381         return [CPPredicate predicateWithValue:YES];
00382     }
00383     if ([self scanPredicateKeyword:@"FALSEPREDICATE"])
00384     {
00385         return [CPPredicate predicateWithValue:NO];
00386     }
00387 
00388     return [self parseComparison];
00389 }
00390 
00391 - (CPPredicate)parseOr
00392 {
00393     var l = [self parseNot];
00394     while ([self scanPredicateKeyword:@"OR"] || [self scanPredicateKeyword:@"||"])
00395     {
00396         var r = [self parseNot];
00397 
00398         if ([r isKindOfClass:[CPCompoundPredicate class]] && [r compoundPredicateType] == CPOrPredicateType)
00399         {
00400             if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPOrPredicateType)
00401             {
00402                 [[l subpredicates] addObjectsFromArray:[r subpredicates]];
00403             }
00404             else
00405             {
00406                 [[r subpredicates] insertObject:l atIndex:0];
00407                 l = r;
00408             }
00409         }
00410         else if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPOrPredicateType)
00411         {
00412             [[l subpredicates] addObject:r];
00413         }
00414         else
00415         {
00416             l = [CPCompoundPredicate orPredicateWithSubpredicates:[CPArray arrayWithObjects:l, r]];
00417         }
00418     }
00419     return l;
00420 }
00421 
00422 - (CPPredicate)parseComparison
00423 {
00424     var modifier = CPDirectPredicateModifier,
00425         type = 0,
00426         opts = 0,
00427         left,
00428         right,
00429         p,
00430         negate = NO;
00431 
00432     if ([self scanPredicateKeyword:@"ANY"])
00433     {
00434         modifier = CPAnyPredicateModifier;
00435     }
00436     else if ([self scanPredicateKeyword:@"ALL"])
00437     {
00438         modifier = CPAllPredicateModifier;
00439     }
00440     else if ([self scanPredicateKeyword:@"NONE"])
00441     {
00442         modifier = CPAnyPredicateModifier;
00443         negate = YES;
00444     }
00445     else if ([self scanPredicateKeyword:@"SOME"])
00446     {
00447         modifier = CPAllPredicateModifier;
00448         negate = YES;
00449     }
00450 
00451     left = [self parseExpression];
00452     if ([self scanString:@"!=" intoString:NULL] || [self scanString:@"<>" intoString:NULL])
00453     {
00454         type = CPNotEqualToPredicateOperatorType;
00455     }
00456     else if ([self scanString:@"<=" intoString:NULL] || [self scanString:@"=<" intoString:NULL])
00457     {
00458         type = CPLessThanOrEqualToPredicateOperatorType;
00459     }
00460     else if ([self scanString:@">=" intoString:NULL] || [self scanString:@"=>" intoString:NULL])
00461     {
00462         type = CPGreaterThanOrEqualToPredicateOperatorType;
00463     }
00464     else if ([self scanString:@"<" intoString:NULL])
00465     {
00466         type = CPLessThanPredicateOperatorType;
00467     }
00468     else if ([self scanString:@">" intoString:NULL])
00469     {
00470         type = CPGreaterThanPredicateOperatorType;
00471     }
00472     else if ([self scanString:@"==" intoString:NULL] || [self scanString:@"=" intoString:NULL])
00473     {
00474         type = CPEqualToPredicateOperatorType;
00475     }
00476     else if ([self scanPredicateKeyword:@"MATCHES"])
00477     {
00478         type = CPMatchesPredicateOperatorType;
00479     }
00480     else if ([self scanPredicateKeyword:@"LIKE"])
00481     {
00482         type = CPLikePredicateOperatorType;
00483     }
00484     else if ([self scanPredicateKeyword:@"BEGINSWITH"])
00485     {
00486         type = CPBeginsWithPredicateOperatorType;
00487     }
00488     else if ([self scanPredicateKeyword:@"ENDSWITH"])
00489     {
00490         type = CPEndsWithPredicateOperatorType;
00491     }
00492     else if ([self scanPredicateKeyword:@"IN"])
00493     {
00494         type = CPInPredicateOperatorType;
00495     }
00496     else if ([self scanPredicateKeyword:@"CONTAINS"])
00497     {
00498         type = CPContainsPredicateOperatorType;
00499     }
00500     else if ([self scanPredicateKeyword:@"BETWEEN"])
00501     {
00502         type = CPBetweenPredicateOperatorType;
00503     }
00504     else
00505         CPRaiseParseError(self, @"comparison predicate");
00506 
00507     if ([self scanString:@"[cd]" intoString:NULL])
00508     {
00509         opts = CPCaseInsensitivePredicateOption | CPDiacriticInsensitivePredicateOption;
00510     }
00511     else if ([self scanString:@"[c]" intoString:NULL])
00512     {
00513         opts = CPCaseInsensitivePredicateOption;
00514     }
00515     else if ([self scanString:@"[d]" intoString:NULL])
00516     {
00517         opts = CPDiacriticInsensitivePredicateOption;
00518     }
00519 
00520     right = [self parseExpression];
00521 
00522     p = [CPComparisonPredicate predicateWithLeftExpression:left
00523          rightExpression:right
00524          modifier:modifier
00525          type:type
00526          options:opts];
00527 
00528     return negate ? [CPCompoundPredicate notPredicateWithSubpredicate:p]:p;
00529 }
00530 
00531 - (CPExpression)parseExpression
00532 {
00533     return [self parseBinaryExpression];
00534 }
00535 
00536 - (CPExpression)parseSimpleExpression
00537 {
00538     var identifier,
00539         location,
00540         ident,
00541         dbl;
00542 
00543     if ([self scanDouble:REFERENCE(dbl)])
00544         return [CPExpression expressionForConstantValue:dbl];
00545 
00546     // FIXME: handle integer, hex constants, 0x 0o 0b
00547     if ([self scanString:@"-" intoString:NULL])
00548         return [CPExpression expressionForFunction:@"chs:" arguments:[CPArray arrayWithObject:[self parseExpression]]];
00549 
00550     if ([self scanString:@"(" intoString:NULL])
00551     {
00552         var arg = [self parseExpression];
00553 
00554         if (![self scanString:@")" intoString:NULL])
00555             CPRaiseParseError(self, @"expression");
00556 
00557         return arg;
00558     }
00559 
00560     if ([self scanString:@"{" intoString:NULL])
00561     {
00562         if ([self scanString:@"}" intoString:NULL])
00563             return [CPExpression expressionForConstantValue:a];
00564 
00565         var a = [];
00566 
00567         [a addObject:[self parseExpression]];
00568 
00569         while ([self scanString:@"," intoString:NULL])
00570             [a addObject:[self parseExpression]];
00571 
00572         if (![self scanString:@"}" intoString:NULL])
00573             CPRaiseParseError(self, @"expression");
00574 
00575         return [CPExpression expressionForAggregate:a];
00576     }
00577 
00578     if ([self scanPredicateKeyword:@"NULL"] || [self scanPredicateKeyword:@"NIL"])
00579     {
00580         return [CPExpression expressionForConstantValue:[CPNull null]];
00581     }
00582     if ([self scanPredicateKeyword:@"TRUE"] || [self scanPredicateKeyword:@"YES"])
00583     {
00584         return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:YES]];
00585     }
00586     if ([self scanPredicateKeyword:@"FALSE"] || [self scanPredicateKeyword:@"NO"])
00587     {
00588         return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:NO]];
00589     }
00590     if ([self scanPredicateKeyword:@"SELF"])
00591     {
00592         return [CPExpression expressionForEvaluatedObject];
00593     }
00594 
00595     if ([self scanString:@"$" intoString:NULL])
00596     {
00597         var variable = [self parseSimpleExpression];
00598 
00599         if (![variable keyPath])
00600             CPRaiseParseError(self, @"expression");
00601 
00602         return [CPExpression expressionForVariable:variable];
00603     }
00604 
00605     location = [self scanLocation];
00606 
00607     if ([self scanString:@"%" intoString:NULL])
00608     {
00609         if ([self isAtEnd] == NO)
00610         {
00611             var c = [[self string] characterAtIndex:[self scanLocation]];
00612 
00613             switch (c)
00614             {
00615                 case '%':// '%%' is treated as '%'
00616                     location = [self scanLocation];
00617                     break;
00618                 case 'K':
00619                     [self setScanLocation:[self scanLocation] + 1];
00620                     return [CPExpression expressionForKeyPath:[self nextArg]];
00621                 case '@':
00622                 case 'c':
00623                 case 'C':
00624                 case 'd':
00625                 case 'D':
00626                 case 'i':
00627                 case 'o':
00628                 case 'O':
00629                 case 'u':
00630                 case 'U':
00631                 case 'x':
00632                 case 'X':
00633                 case 'e':
00634                 case 'E':
00635                 case 'f':
00636                 case 'g':
00637                 case 'G':
00638                     [self setScanLocation:[self scanLocation] + 1];
00639                     return [CPExpression expressionForConstantValue:[self nextArg]];
00640                 case 'h':
00641                     [self scanString:@"h" intoString:NULL];
00642                     if ([self isAtEnd] == NO)
00643                     {
00644                         c = [[self string] characterAtIndex:[self scanLocation]];
00645                         if (c == 'i' || c == 'u')
00646                         {
00647                             [self setScanLocation:[self scanLocation] + 1];
00648                             return [CPExpression expressionForConstantValue:[self nextArg]];
00649                         }
00650                     }
00651                     break;
00652                 case 'q':
00653                     [self scanString:@"q" intoString:NULL];
00654                     if ([self isAtEnd] == NO)
00655                     {
00656                         c = [[self string] characterAtIndex:[self scanLocation]];
00657                         if (c == 'i' || c == 'u' || c == 'x' || c == 'X')
00658                         {
00659                             [self setScanLocation:[self scanLocation] + 1];
00660                             return [CPExpression expressionForConstantValue:[self nextArg]];
00661                         }
00662                     }
00663                     break;
00664             }
00665         }
00666 
00667         [self setScanLocation:location];
00668     }
00669 
00670     if ([self scanString:@"\"" intoString:NULL])
00671     {
00672         var skip = [self charactersToBeSkipped],
00673             str = @"";
00674 
00675         [self setCharactersToBeSkipped:nil];
00676         [self scanUpToString:@"\"" intoString:REFERENCE(str)];
00677 
00678         if ([self scanString:@"\"" intoString:NULL] == NO)
00679             CPRaiseParseError(self, @"expression");
00680 
00681         [self setCharactersToBeSkipped:skip];
00682 
00683         return [CPExpression expressionForConstantValue:str];
00684     }
00685 
00686     if ([self scanString:@"'" intoString:NULL])
00687     {
00688         var skip = [self charactersToBeSkipped],
00689             str = @"";
00690 
00691         [self setCharactersToBeSkipped:nil];
00692         [self scanUpToString:@"'" intoString:REFERENCE(str)];
00693 
00694         if ([self scanString:@"'" intoString:NULL] == NO)
00695             CPRaiseParseError(self, @"expression");
00696 
00697         [self setCharactersToBeSkipped:skip];
00698 
00699         return [CPExpression expressionForConstantValue:str];
00700     }
00701 
00702     if ([self scanString:@"@" intoString:NULL])
00703     {
00704         var e = [self parseExpression];
00705 
00706         if (![e keyPath])
00707             CPRaiseParseError(self, @"expression");
00708 
00709         return [CPExpression expressionForKeyPath:[e keyPath] + "@"];
00710     }
00711 
00712     if ([self scanString:@"SUBQUERY" intoString:NULL])
00713     {
00714         if (![self scanString:@"(" intoString:NULL])
00715             CPRaiseParseError(self, @"expression");
00716 
00717         var collection = [self parseExpression],
00718             variableExpression,
00719             subpredicate;
00720 
00721         if (![self scanString:@"," intoString:NULL])
00722             CPRaiseParseError(self, @"expression");
00723         variableExpression = [self parseExpression];
00724 
00725         if (![self scanString:@"," intoString:NULL])
00726             CPRaiseParseError(self, @"expression");
00727         subpredicate = [self parsePredicate];
00728 
00729         if (![self scanString:@")" intoString:NULL])
00730             CPRaiseParseError(self, @"expression");
00731 
00732         return [[CPExpression_subquery alloc] initWithExpression:collection usingIteratorExpression:variableExpression predicate:subpredicate];
00733     }
00734 
00735     if ([self scanString:@"FUNCTION" intoString:NULL])
00736     {
00737         if (![self scanString:@"(" intoString:NULL])
00738             CPRaiseParseError(self, @"expression");
00739 
00740         var args = [CPArray arrayWithObject:[self parseExpression]];
00741         while ([self scanString:@"," intoString:NULL])
00742             [args addObject:[self parseExpression]];
00743 
00744         if (![self scanString:@")" intoString:NULL] || [args count] < 2 || [args[1] expressionType] != CPConstantValueExpressionType)
00745             CPRaiseParseError(self, @"expression");
00746 
00747          return [CPExpression expressionForFunction:args[0] selectorName:[args[1] constantValue] arguments:args.slice(2)];
00748     }
00749 
00750     [self scanString:@"#" intoString:NULL];
00751     if (!identifier)
00752         identifier = [CPCharacterSet characterSetWithCharactersInString:@"_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"];
00753 
00754     if (![self scanCharactersFromSet:identifier intoString:REFERENCE(ident)])
00755         CPRaiseParseError(self, @"expression");
00756 
00757     return [CPExpression expressionForKeyPath:ident];
00758 }
00759 
00760 - (CPExpression)parseFunctionalExpression
00761 {
00762     var left = [self parseSimpleExpression];
00763 
00764     while (YES)
00765     {
00766         if ([self scanString:@"." intoString:NULL])
00767         {
00768             var right = [self parseSimpleExpression],
00769                 expressionType = [right expressionType];
00770 
00771             if (expressionType == CPKeyPathExpressionType)
00772                 left = [[CPExpression_keypath alloc] initWithOperand:left andKeyPath:[right keyPath]];
00773             else if (expressionType == CPVariableExpressionType)
00774                 left = [CPExpression expressionForFunction:left selectorName:@"valueForKey:" arguments:[right]];
00775             else
00776                 CPRaiseParseError(self, @"expression");
00777         }
00778         else if ([self scanString:@"[" intoString:NULL])
00779         {
00780             // index expression
00781             if ([self scanPredicateKeyword:@"FIRST"])
00782             {
00783                 left = [CPExpression expressionForFunction:@"first:" arguments:[CPArray arrayWithObject:left]];
00784             }
00785             else if ([self scanPredicateKeyword:@"LAST"])
00786             {
00787                 left = [CPExpression expressionForFunction:@"last:" arguments:[CPArray arrayWithObject:left]];
00788             }
00789             else if ([self scanPredicateKeyword:@"SIZE"])
00790             {
00791                 left = [CPExpression expressionForFunction:@"count:" arguments:[CPArray arrayWithObject:left]];
00792             }
00793             else
00794             {
00795                 var index = [self parseExpression];
00796                 left = [CPExpression expressionForFunction:@"fromObject:index:" arguments:[CPArray arrayWithObjects:left, index]];
00797             }
00798 
00799             if (![self scanString:@"]" intoString:NULL])
00800                 CPRaiseParseError(self, @"expression");
00801         }
00802         else if ([self scanString:@":" intoString:NULL])
00803         {
00804             // function - this parser allows for (max)(a, b, c) to be properly
00805             // recognized and even (%K)(a, b, c) if %K evaluates to "max"
00806 
00807             if (![left keyPath])
00808                 CPRaiseParseError(self, @"expression");
00809 
00810             var selector = [left keyPath] + @":",
00811                 args = [];
00812 
00813             if (![self scanString:@"(" intoString:NULL])
00814             {
00815                 var str;
00816                 [self scanCharactersFromSet:[CPCharacterSet lowercaseLetterCharacterSet] intoString:REFERENCE(str)];
00817 
00818                 if (![self scanString:@":(" intoString:NULL])
00819                     CPRaiseParseError(self, @"expression");
00820 
00821                 selector += str + @":";
00822             }
00823 
00824             if (![self scanString:@")" intoString:NULL])
00825             {
00826                 [args addObject:[self parseExpression]];
00827                 while ([self scanString:@"," intoString:NULL])
00828                     [args addObject:[self parseExpression]];
00829 
00830                 if (![self scanString:@")" intoString:NULL])
00831                     CPRaiseParseError(self, @"expression");
00832             }
00833 
00834             left = [CPExpression expressionForFunction:selector arguments:args];
00835         }
00836         else if ([self scanString:@"UNION" intoString:NULL])
00837         {
00838             left = [CPExpression expressionForUnionSet:left with:[self parseExpression]];
00839         }
00840         else if ([self scanString:@"INTERSECT" intoString:NULL])
00841         {
00842             left = [CPExpression expressionForIntersectSet:left with:[self parseExpression]];
00843         }
00844         else if ([self scanString:@"MINUS" intoString:NULL])
00845         {
00846             left = [CPExpression expressionForMinusSet:left with:[self parseExpression]];
00847         }
00848         else
00849         {
00850             // done with suffixes
00851             return left;
00852         }
00853     }
00854 }
00855 
00856 - (CPExpression)parsePowerExpression
00857 {
00858     var left = [self parseFunctionalExpression];
00859 
00860     while (YES)
00861     {
00862         var right;
00863 
00864         if ([self scanString:@"**" intoString:NULL])
00865         {
00866             right = [self parseFunctionalExpression];
00867             left = [CPExpression expressionForFunction:@"raise:to:" arguments:[CPArray arrayWithObjects:left, right]];
00868         }
00869         else
00870         {
00871             return left;
00872         }
00873     }
00874 }
00875 
00876 - (CPExpression)parseMultiplicationExpression
00877 {
00878     var left = [self parsePowerExpression];
00879 
00880     while (YES)
00881     {
00882         var right;
00883 
00884         if ([self scanString:@"*" intoString:NULL])
00885         {
00886             right = [self parsePowerExpression];
00887             left = [CPExpression expressionForFunction:@"multiply:by:" arguments:[CPArray arrayWithObjects:left, right]];
00888         }
00889         else if ([self scanString:@"/" intoString:NULL])
00890         {
00891             right = [self parsePowerExpression];
00892             left = [CPExpression expressionForFunction:@"divide:by:" arguments:[CPArray arrayWithObjects:left, right]];
00893         }
00894         else
00895         {
00896             return left;
00897         }
00898     }
00899 }
00900 
00901 - (CPExpression)parseAdditionExpression
00902 {
00903     var left = [self parseMultiplicationExpression];
00904 
00905     while (YES)
00906     {
00907         var right;
00908 
00909         if ([self scanString:@"+" intoString:NULL])
00910         {
00911             right = [self parseMultiplicationExpression];
00912             left = [CPExpression expressionForFunction:@"add:to:" arguments:[CPArray arrayWithObjects:left, right]];
00913         }
00914         else if ([self scanString:@"-" intoString:NULL])
00915         {
00916             right = [self parseMultiplicationExpression];
00917             left = [CPExpression expressionForFunction:@"from:substract:" arguments:[CPArray arrayWithObjects:left, right]];
00918         }
00919         else
00920         {
00921             return left;
00922         }
00923     }
00924 }
00925 
00926 - (CPExpression)parseBinaryExpression
00927 {
00928     var left = [self parseAdditionExpression];
00929 
00930     while (YES)
00931     {
00932         var right;
00933 
00934         if ([self scanString:@":=" intoString:NULL])    // assignment
00935         {
00936             // check left to be a variable?
00937             right = [self parseAdditionExpression];
00938             // FIXME
00939         }
00940         else
00941         {
00942             return left;
00943         }
00944     }
00945 }
00946 
00947 @end
00948 
00949 var CPRaiseParseError = function CPRaiseParseError(aScanner, target)
00950 {
00951     [CPException raise:CPInvalidArgumentException reason:@"unable to parse " + target + " at index " + [aScanner scanLocation]];
00952 }
00953 
 All Classes Files Functions Variables Defines