![]() |
API 0.9.5
|
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