Files
cappuccino/Foundation/CPScanner.j
T

338 lines
7.7 KiB
Plaintext

/*
* CPScanner.j
* Foundation
*
* Created by Emanuele Vulcano.
* Copyright 2008, Emanuele Vulcano.
*
* 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 "CPCharacterSet.j"
@import "CPDictionary.j"
@import "CPString.j"
@implementation CPScanner : CPObject
{
CPString _string;
CPDictionary _locale;
int _scanLocation;
BOOL _caseSensitive;
CPCharacterSet _charactersToBeSkipped;
}
+ (id)scannerWithString:(CPString)aString
{
return [[self alloc] initWithString:aString];
}
- (id)initWithString:(CPString)aString
{
if (self = [super init])
{
_string = [aString copy];
_scanLocation = 0;
_charactersToBeSkipped = [CPCharacterSet whitespaceCharacterSet];
_caseSensitive = NO;
}
return self;
}
- (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;
}
- (CPDictionary)locale
{
return _locale;
}
- (void)setLocale:(CPDictionary)aLocale
{
_locale = aLocale;
}
- (void)setCaseSensitive:(BOOL)flag
{
_caseSensitive = flag;
}
- (BOOL)caseSensitive
{
return _caseSensitive;
}
- (CPString)string
{
return _string;
}
- (CPCharacterSet)charactersToBeSkipped
{
return _charactersToBeSkipped;
}
- (void)setCharactersToBeSkipped:(CPCharacterSet)c
{
_charactersToBeSkipped = c;
}
- (BOOL)isAtEnd
{
return _scanLocation == _string.length;
}
- (int)scanLocation
{
return _scanLocation;
}
- (void)setScanLocation:(int)aLocation
{
if (aLocation > _string.length)
aLocation = _string.length; // clamp to just after the last character
else if (aLocation < 0)
aLocation = 0; // clamp to the first
_scanLocation = aLocation;
}
// Method body for all methods that return their value by reference.
- (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 YES;
}
/* ================================ */
/* = Scanning with CPCharacterSet = */
/* ================================ */
- (BOOL)scanCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanCharactersFromSet:) withObject:scanSet into:ref];
}
- (CPString)scanCharactersFromSet:(CPCharacterSet)scanSet
{
return [self _scanWithSet:scanSet breakFlag:NO];
}
- (BOOL)scanUpToCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanUpToCharactersFromSet:) withObject:scanSet into:ref];
}
- (CPString)scanUpToCharactersFromSet:(CPCharacterSet)scanSet
{
return [self _scanWithSet:scanSet breakFlag:YES];
}
// If stop == YES, it will stop when it sees a character from
// the set (scanUpToCharactersFromSet:); if stop == NO, it will
// stop when it sees a character NOT from the set
// (scanCharactersFromSet:).
- (CPString)_scanWithSet:(CPCharacterSet)scanSet breakFlag:(BOOL)stop
{
if ([self isAtEnd])
return nil;
var current = [self scanLocation],
str = nil;
while (current < _string.length)
{
var c = (_string.charAt(current));
if ([scanSet characterIsMember:c] == stop)
break;
if (![_charactersToBeSkipped characterIsMember:c])
{
if (!str)
str = '';
str += c;
}
current++;
}
if (str)
[self setScanLocation:current];
return str;
}
/* ==================== */
/* = Scanning strings = */
/* ==================== */
- (void)_movePastCharactersToBeSkipped
{
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];
}
- (BOOL)scanString:(CPString)aString intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanString:) withObject:aString into:ref];
}
- (CPString)scanString:(CPString)s
{
[self _movePastCharactersToBeSkipped];
if ([self isAtEnd])
return nil;
var currentStr = [self string].substr([self scanLocation], s.length);
if ((_caseSensitive && currentStr != s) || (!_caseSensitive && (currentStr.toLowerCase() != s.toLowerCase())))
{
return nil;
}
else
{
[self setScanLocation:[self scanLocation] + s.length];
return s;
}
}
- (BOOL)scanUpToString:(CPString)aString intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanUpToString:) withObject:aString into:ref];
}
- (CPString)scanUpToString:(CPString)s
{
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;
}
/* ==================== */
/* = Scanning numbers = */
/* ==================== */
- (float)scanWithParseFunction:(Function)parseFunction
{
[self _movePastCharactersToBeSkipped];
var str = [self string],
loc = [self scanLocation];
if ([self isAtEnd])
return 0;
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
{
return [self scanWithParseFunction:parseInt];
}
- (BOOL)scanInt:(int)intoInt
{
return [self _performScanWithSelector:@selector(scanInt) withObject:nil into:intoInt];
}
- (BOOL)scanFloat:(float)intoFloat
{
return [self _performScanWithSelector:@selector(scanFloat) withObject:nil into:intoFloat];
}
- (BOOL)scanDouble:(float)intoDouble
{
return [self scanFloat:intoDouble];
}
/* ========= */
/* = Debug = */
/* ========= */
- (void)description
{
return [super description] + " {" + CPStringFromClass([self class]) + ", state = '" + ([self string].substr(0, _scanLocation) + "{{ SCAN LOCATION ->}}" + [self string].substr(_scanLocation)) + "'; }";
}
@end