From 3cfbca5a199556ca19f1885db31279f274c7e5ce Mon Sep 17 00:00:00 2001 From: cacaodev Date: Sat, 23 Oct 2010 10:14:26 +0200 Subject: [PATCH 1/2] CPScanner: When the scanner did not accumulate any string, do not change (to nil) the object passed as a ref in ...intoSring:ref() methods. With tests. --- Foundation/CPScanner.j | 7 +++++-- Tests/Foundation/CPScannerTest.j | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Tests/Foundation/CPScannerTest.j diff --git a/Foundation/CPScanner.j b/Foundation/CPScanner.j index b5373f0af..e789c8603 100644 --- a/Foundation/CPScanner.j +++ b/Foundation/CPScanner.j @@ -140,11 +140,14 @@ - (BOOL)_performScanWithSelector:(SEL)s withObject:(id)arg into:(id)ref { var ret = [self performSelector:s withObject:arg]; - + + if (ret == nil) + return NO; + if (ref != nil) ref(ret); - return ret != NULL; + return YES; } /* ================================ */ diff --git a/Tests/Foundation/CPScannerTest.j b/Tests/Foundation/CPScannerTest.j new file mode 100644 index 000000000..8a74ffbdc --- /dev/null +++ b/Tests/Foundation/CPScannerTest.j @@ -0,0 +1,25 @@ +@import + +@implementation CPScannerTest : OJTestCase +{ +} + +- (void)testNothingScanned +{ + var x = @"x", + str = x, + scanner, + result; + + scanner = [CPScanner scannerWithString:@"a"]; + result = [scanner scanString:@"b" intoString:str]; + [self assertFalse:result message:"Result should be FALSE"]; + [self assertTrue:str === x message:"The string passed by reference should not change, is " + str]; + + scanner = [CPScanner scannerWithString:@"a"]; + result = [scanner scanUpToString:@"a" intoString:str]; + [self assertFalse:result message:"Result should be FALSE"]; + [self assertTrue:str === x message:"The string passed by reference should not change, is " + str]; +} + +@end \ No newline at end of file From 602a2c47c2d2d5dbf199005c2a183ea4474b4cbc Mon Sep 17 00:00:00 2001 From: cacaodev Date: Sat, 13 Nov 2010 02:29:04 +0100 Subject: [PATCH 2/2] CPScanner fixes with tests. --- Foundation/CPScanner.j | 177 +++++++++---------------------- Tests/Foundation/CPScannerTest.j | 81 +++++++++++--- 2 files changed, 116 insertions(+), 142 deletions(-) diff --git a/Foundation/CPScanner.j b/Foundation/CPScanner.j index e789c8603..a19b37b99 100644 --- a/Foundation/CPScanner.j +++ b/Foundation/CPScanner.j @@ -16,41 +16,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]; @@ -72,12 +37,12 @@ - (id)copy { var copy = [[CPScanner alloc] initWithString:[self string]]; - + [copy setCharactersToBeSkipped:[self charactersToBeSkipped]]; [copy setCaseSensitive:[self caseSensitive]]; [copy setLocale:[self locale]]; [copy setScanLocation:[self scanLocation]]; - + return copy; } @@ -140,10 +105,10 @@ - (BOOL)_performScanWithSelector:(SEL)s withObject:(id)arg into:(id)ref { var ret = [self performSelector:s withObject:arg]; - + if (ret == nil) return NO; - + if (ref != nil) ref(ret); @@ -182,14 +147,14 @@ { if ([self isAtEnd]) return nil; - - var current = [self scanLocation]; - var str = nil; - + + var current = [self scanLocation], + str = nil; + while (current < _string.length) { var c = (_string.charAt(current)); - + if ([scanSet characterIsMember:c] == stop) break; @@ -199,13 +164,13 @@ str = ''; str += c; } - + current++; } - + if (str) [self setScanLocation:current]; - + return str; } @@ -215,18 +180,18 @@ - (void)_movePastCharactersToBeSkipped { - var current = [self scanLocation]; - var string = [self string]; - var toSkip = [self charactersToBeSkipped]; - + var current = [self scanLocation], + string = [self string], + toSkip = [self charactersToBeSkipped]; + while (current < string.length) { if (![toSkip characterIsMember:string.charAt(current)]) break; - + current++; } - + [self setScanLocation:current]; } @@ -261,29 +226,31 @@ - (CPString)scanUpToString:(CPString)s { - var current = [self scanLocation], str = [self string]; - var captured = nil; + var current = [self scanLocation], + str = [self string], + captured = nil; + while (current < str.length) { var currentStr = str.substr(current, s.length); if (currentStr == s || (!_caseSensitive && currentStr.toLowerCase() == s.toLowerCase())) break; - + if (!captured) captured = ''; captured += str.charAt(current); current++; } - + if (captured) [self setScanLocation:current]; - + // evil private method use! // this method is defined in the category on CPString // in CPCharacterSet.j if ([self charactersToBeSkipped]) captured = [captured _stringByTrimmingCharactersInSet:[self charactersToBeSkipped] options:_CPCharacterSetTrimAtBeginning]; - + return captured; } @@ -291,81 +258,39 @@ /* = Scanning numbers = */ /* ==================== */ -- (float)scanFloat +- (float)scanWithParseFunction:(Function)parseFunction { [self _movePastCharactersToBeSkipped]; - var str = [self string], current = [self scanLocation]; - + var str = [self string], + loc = [self scanLocation]; + if ([self isAtEnd]) return 0; - - var s = str.substring(current, str.length); - var f = parseFloat(s); // wont work with non . decimal separator !! - if (f) - { - var pos, foundDash = NO; -/* - var decimalSeparatorString; - if(_locale != nil) - decimalSeparatorString = [_locale objectForKey:CPLocaleDecimalSeparator]; - else - decimalSeparatorString = [[CPLocale systemLocale] objectForKey:CPLocaleDecimalSeparator]; - - var separatorCode = (decimalSeparatorString.length >0) decimalSeparatorString.charCodeAt(0) : 45; -*/ - var separatorCode = 45; - for(pos = current; 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:pos]; - return f; - } - - return nil; + var s = str.substring(loc, str.length), + f = parseFunction(s); + + if (isNaN(f)) + return nil; + + loc += (""+f).length; + var i = 0; + while (!isNaN(parseFloat(str.substring(loc+i, str.length)))) + {i++;} + + [self setScanLocation:loc + i]; + return f; + +} + +- (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); - - var i = parseInt(s); - if (i) - { - var pos, foundDash = NO; - for (pos = current; 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 @@ -392,4 +317,4 @@ return [super description] + " {" + CPStringFromClass([self class]) + ", state = '" + ([self string].substr(0, _scanLocation) + "{{ SCAN LOCATION ->}}" + [self string].substr(_scanLocation)) + "'; }"; } -@end \ No newline at end of file +@end diff --git a/Tests/Foundation/CPScannerTest.j b/Tests/Foundation/CPScannerTest.j index 8a74ffbdc..d59dbfcd8 100644 --- a/Tests/Foundation/CPScannerTest.j +++ b/Tests/Foundation/CPScannerTest.j @@ -1,25 +1,74 @@ -@import + +@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)testNothingScanned +- (void)testScanInt { - var x = @"x", - str = x, - scanner, - result; - - scanner = [CPScanner scannerWithString:@"a"]; - result = [scanner scanString:@"b" intoString:str]; - [self assertFalse:result message:"Result should be FALSE"]; - [self assertTrue:str === x message:"The string passed by reference should not change, is " + str]; - - scanner = [CPScanner scannerWithString:@"a"]; - result = [scanner scanUpToString:@"a" intoString:str]; - [self assertFalse:result message:"Result should be FALSE"]; - [self assertTrue:str === x message:"The string passed by reference should not change, is " + str]; + [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