diff --git a/Foundation/CPScanner.j b/Foundation/CPScanner.j index 619d35a87..cd362ad5a 100644 --- a/Foundation/CPScanner.j +++ b/Foundation/CPScanner.j @@ -33,41 +33,6 @@ CPCharacterSet _charactersToBeSkipped; } -// TODO Not all methods of NSScanner are available! - -/* -- (BOOL)scanLongLong:(long long *)longLongValue -{ -} -- (BOOL)scanDecimal:(NSDecimal *)decimalValue -{ -} -- (BOOL)scanHexDouble:(double *)result -{ -} -- (BOOL)scanHexFloat:(float *)result -{ -} -- (BOOL)scanHexInt:(unsigned *)intValue -{ -} -- (BOOL)scanHexLongLong:(unsigned long long *)result -{ -} -- (BOOL)scanInteger:(NSInteger *)value -{ -} -+ (id)localizedScannerWithString:(CPString)string -{ - var scanner = [self scannerWithString:string]; - - [scanner setLocale:[CPLocale currentLocale]]; - - return scanner; -} - -*/ - + (id)scannerWithString:(CPString)aString { return [[self alloc] initWithString:aString]; @@ -158,10 +123,13 @@ { var ret = [self performSelector:s withObject:arg]; + if (ret == nil) + return NO; + if (ref != nil) ref(ret); - return ret != NULL; + return YES; } /* ================================ */ @@ -307,88 +275,39 @@ /* = Scanning numbers = */ /* ==================== */ -- (float)scanFloat +- (float)scanWithParseFunction:(Function)parseFunction { [self _movePastCharactersToBeSkipped]; var str = [self string], - current = [self scanLocation]; + loc = [self scanLocation]; if ([self isAtEnd]) return 0; - var s = str.substring(current, str.length), - f = parseFloat(s); // wont work with non . decimal separator !! + var s = str.substring(loc, str.length), + f = parseFunction(s); - if (f) - { - var pos = current, - foundDash = NO; -/* - var decimalSeparatorString; - if (_locale != nil) - decimalSeparatorString = [_locale objectForKey:CPLocaleDecimalSeparator]; - else - decimalSeparatorString = [[CPLocale systemLocale] objectForKey:CPLocaleDecimalSeparator]; + if (isNaN(f)) + return nil; - var separatorCode = (decimalSeparatorString.length >0) decimalSeparatorString.charCodeAt(0) : 45; -*/ - var separatorCode = 45; + loc += (""+f).length; + var i = 0; + while (!isNaN(parseFloat(str.substring(loc+i, str.length)))) + {i++;} - for (; pos < current + str.length; pos++) - { - var charCode = str.charCodeAt(pos); - if (charCode == separatorCode) - { - if (foundDash == YES) - break; // We already found a decimal separator so this one is an extra char - foundDash = YES; - } - else if (charCode < 48 || charCode > 57 || (charCode == 45 && pos != current)) // not a digit or a "-" but not prefix - break; - } + [self setScanLocation:loc + i]; + return f; - [self setScanLocation:pos]; - return f; - } +} - return nil; +- (float)scanFloat +{ + return [self scanWithParseFunction:parseFloat]; } - (int)scanInt { - [self _movePastCharactersToBeSkipped]; - var str = [self string], - current = [self scanLocation]; - - if ([self isAtEnd]) - return 0; - - var s = str.substring(current, str.length), - i = parseInt(s); - - if (i) - { - var pos = current, - foundDash = NO; - - for (; pos < current + str.length; pos++) - { - var charCode = str.charCodeAt(pos); - if (charCode == 46) - { - if (foundDash == YES) - break; - foundDash = YES; - } - else if (charCode < 48 || charCode > 57 || (charCode == 45 && pos != current)) - break; - } - - [self setScanLocation:pos]; - return i; - } - - return nil; + return [self scanWithParseFunction:parseInt]; } - (BOOL)scanInt:(int)intoInt diff --git a/Tests/Foundation/CPScannerTest.j b/Tests/Foundation/CPScannerTest.j new file mode 100644 index 000000000..d59dbfcd8 --- /dev/null +++ b/Tests/Foundation/CPScannerTest.j @@ -0,0 +1,74 @@ + +@import + +@implementation CPScannerTest : OJTestCase + +- (void)performScanMethod:(SEL)selector searchFor:(id)search inScanner:(CPString)scanner succeeded:(BOOL)success found:/*accumulated result or nil if nothing should be accumulated*/(id)accumulator endedAt:(int)endLocation +{ + if ([scanner isKindOfClass:[CPString class]]) + scanner = [CPScanner scannerWithString:scanner]; + + var objj_args = [scanner, selector], + value = @"initialValue"; + + if (accumulator == nil) + accumulator = value; + + if (search != nil) + objj_args.push(search); + + objj_args.push(function(v){value = v;}); + + var didScan = objj_msgSend.apply(this, objj_args); + + [self assert:success equals:didScan message:scanner + " Scan operation: "]; + [self assert:accumulator equals:value message:scanner + " Accumulator: "]; + if (endLocation != nil) + [self assert:endLocation equals:[scanner scanLocation] message:scanner + " Scan location: "]; +} + +- (void)testScanInt +{ + [self performScanMethod:@selector(scanInt:) searchFor:nil inScanner:@"1" succeeded:YES found:1 endedAt:1]; + [self performScanMethod:@selector(scanInt:) searchFor:nil inScanner:@" 12" succeeded:YES found:12 endedAt:3]; + [self performScanMethod:@selector(scanInt:) searchFor:nil inScanner:@"01" succeeded:YES found:1 endedAt:2]; + [self performScanMethod:@selector(scanInt:) searchFor:nil inScanner:@"131F02" succeeded:YES found:131 endedAt:3]; + [self performScanMethod:@selector(scanInt:) searchFor:nil inScanner:@"word" succeeded:NO found:nil endedAt:0]; + [self performScanMethod:@selector(scanInt:) searchFor:nil inScanner:@"0S" succeeded:YES found:0 endedAt:1]; +} + +- (void)testScanFloat +{ + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"1" succeeded:YES found:1 endedAt:1]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"1.2" succeeded:YES found:1.2 endedAt:3]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"-1.2" succeeded:YES found:-1.2 endedAt:4]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"1.2x" succeeded:YES found:1.2 endedAt:3]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"word" succeeded:NO found:nil endedAt:0]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"0.00" succeeded:YES found:0 endedAt:4]; + +// wrong end location + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@".1-2" succeeded:YES found:0.1 endedAt:nil]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"1.2.3" succeeded:YES found:1.2 endedAt:nil]; + [self performScanMethod:@selector(scanFloat:) searchFor:nil inScanner:@"-1.2-3" succeeded:YES found:-1.2 endedAt:nil]; +} + +- (void)testScanString +{ + [self performScanMethod:@selector(scanString:intoString:) searchFor:@"t" inScanner:@"topaz" succeeded:YES found:@"t" endedAt:1]; + [self performScanMethod:@selector(scanString:intoString:) searchFor:@"z" inScanner:@"topaz" succeeded:NO found:nil endedAt:0]; + [self performScanMethod:@selector(scanString:intoString:) searchFor:@"tt" inScanner:@"topaz" succeeded:NO found:nil endedAt:0]; + + [self performScanMethod:@selector(scanUpToString:intoString:) searchFor:@"a" inScanner:@"topaz" succeeded:YES found:@"top" endedAt:3]; + [self performScanMethod:@selector(scanUpToString:intoString:) searchFor:@"x" inScanner:@"topaz" succeeded:YES found:@"topaz" endedAt:5]; + [self performScanMethod:@selector(scanUpToString:intoString:) searchFor:@"x" inScanner:@"" succeeded:NO found:nil endedAt:0]; +} + +- (void)testScanCharactersFromSet +{ + [self performScanMethod:@selector(scanCharactersFromSet:intoString:) searchFor:[CPCharacterSet lowercaseLetterCharacterSet] inScanner:@"topAz" succeeded:YES found:@"top" endedAt:3]; + + [self performScanMethod:@selector(scanCharactersFromSet:intoString:) searchFor:[CPCharacterSet alphanumericCharacterSet] inScanner:@"top12Az&x" succeeded:YES found:@"top12Az" endedAt:7]; + [self performScanMethod:@selector(scanCharactersFromSet:intoString:) searchFor:[CPCharacterSet decomposableCharacterSet] inScanner:@"êñço" succeeded:YES found:@"êñç" endedAt:3]; + +} +@end \ No newline at end of file