1*16467b97STreehugger Robot// [The "BSD licence"] 2*16467b97STreehugger Robot// Copyright (c) 2006-2007 Kay Roepke 2010 Alan Condit 3*16467b97STreehugger Robot// All rights reserved. 4*16467b97STreehugger Robot// 5*16467b97STreehugger Robot// Redistribution and use in source and binary forms, with or without 6*16467b97STreehugger Robot// modification, are permitted provided that the following conditions 7*16467b97STreehugger Robot// are met: 8*16467b97STreehugger Robot// 1. Redistributions of source code must retain the above copyright 9*16467b97STreehugger Robot// notice, this list of conditions and the following disclaimer. 10*16467b97STreehugger Robot// 2. Redistributions in binary form must reproduce the above copyright 11*16467b97STreehugger Robot// notice, this list of conditions and the following disclaimer in the 12*16467b97STreehugger Robot// documentation and/or other materials provided with the distribution. 13*16467b97STreehugger Robot// 3. The name of the author may not be used to endorse or promote products 14*16467b97STreehugger Robot// derived from this software without specific prior written permission. 15*16467b97STreehugger Robot// 16*16467b97STreehugger Robot// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17*16467b97STreehugger Robot// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18*16467b97STreehugger Robot// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19*16467b97STreehugger Robot// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20*16467b97STreehugger Robot// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21*16467b97STreehugger Robot// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22*16467b97STreehugger Robot// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23*16467b97STreehugger Robot// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24*16467b97STreehugger Robot// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25*16467b97STreehugger Robot// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26*16467b97STreehugger Robot 27*16467b97STreehugger Robot 28*16467b97STreehugger Robot#import "CommonToken.h" 29*16467b97STreehugger Robot 30*16467b97STreehugger Robotstatic CommonToken *SKIP_TOKEN; 31*16467b97STreehugger Robotstatic CommonToken *EOF_TOKEN; 32*16467b97STreehugger Robotstatic CommonToken *INVALID_TOKEN; 33*16467b97STreehugger Robot 34*16467b97STreehugger Robot@implementation CommonToken 35*16467b97STreehugger Robot 36*16467b97STreehugger Robot static NSInteger DEFAULT_CHANNEL = TokenChannelDefault; 37*16467b97STreehugger Robot static NSInteger INVALID_TOKEN_TYPE = TokenTypeInvalid; 38*16467b97STreehugger Robot 39*16467b97STreehugger Robot 40*16467b97STreehugger Robot@synthesize text; 41*16467b97STreehugger Robot@synthesize type; 42*16467b97STreehugger Robot@synthesize line; 43*16467b97STreehugger Robot@synthesize charPositionInLine; 44*16467b97STreehugger Robot@synthesize channel; 45*16467b97STreehugger Robot@synthesize index; 46*16467b97STreehugger Robot@synthesize startIndex; 47*16467b97STreehugger Robot@synthesize stopIndex; 48*16467b97STreehugger Robot@synthesize input; 49*16467b97STreehugger Robot 50*16467b97STreehugger Robot+ (void) initialize 51*16467b97STreehugger Robot{ 52*16467b97STreehugger Robot EOF_TOKEN = [CommonToken newToken:TokenTypeEOF Text:@"EOF"]; 53*16467b97STreehugger Robot SKIP_TOKEN = [CommonToken newToken:TokenTypeInvalid Text:@"Skip"]; 54*16467b97STreehugger Robot INVALID_TOKEN = [CommonToken newToken:TokenTypeInvalid Text:@"Invalid"]; 55*16467b97STreehugger Robot [EOF_TOKEN retain]; 56*16467b97STreehugger Robot [SKIP_TOKEN retain]; 57*16467b97STreehugger Robot [INVALID_TOKEN retain]; 58*16467b97STreehugger Robot} 59*16467b97STreehugger Robot 60*16467b97STreehugger Robot+ (CommonToken *)INVALID_TOKEN 61*16467b97STreehugger Robot{ 62*16467b97STreehugger Robot return INVALID_TOKEN; 63*16467b97STreehugger Robot} 64*16467b97STreehugger Robot 65*16467b97STreehugger Robot+ (NSInteger) DEFAULT_CHANNEL 66*16467b97STreehugger Robot{ 67*16467b97STreehugger Robot return DEFAULT_CHANNEL; 68*16467b97STreehugger Robot} 69*16467b97STreehugger Robot 70*16467b97STreehugger Robot+ (NSInteger) INVALID_TOKEN_TYPE 71*16467b97STreehugger Robot{ 72*16467b97STreehugger Robot return INVALID_TOKEN_TYPE; 73*16467b97STreehugger Robot} 74*16467b97STreehugger Robot 75*16467b97STreehugger Robot+ (CommonToken *) newToken 76*16467b97STreehugger Robot{ 77*16467b97STreehugger Robot return [[CommonToken alloc] init]; 78*16467b97STreehugger Robot} 79*16467b97STreehugger Robot 80*16467b97STreehugger Robot+ (CommonToken *) newToken:(id<CharStream>)anInput Type:(NSInteger)aTType Channel:(NSInteger)aChannel Start:(NSInteger)aStart Stop:(NSInteger)aStop 81*16467b97STreehugger Robot{ 82*16467b97STreehugger Robot return [[CommonToken alloc] initWithInput:(id<CharStream>)anInput Type:(NSInteger)aTType Channel:(NSInteger)aChannel Start:(NSInteger)aStart Stop:(NSInteger)aStop]; 83*16467b97STreehugger Robot} 84*16467b97STreehugger Robot 85*16467b97STreehugger Robot+ (CommonToken *) newToken:(TokenType)tokenType 86*16467b97STreehugger Robot{ 87*16467b97STreehugger Robot return( [[CommonToken alloc] initWithType:tokenType] ); 88*16467b97STreehugger Robot} 89*16467b97STreehugger Robot 90*16467b97STreehugger Robot+ (CommonToken *) newToken:(NSInteger)tokenType Text:(NSString *)tokenText 91*16467b97STreehugger Robot{ 92*16467b97STreehugger Robot return( [[CommonToken alloc] initWithType:tokenType Text:tokenText] ); 93*16467b97STreehugger Robot} 94*16467b97STreehugger Robot 95*16467b97STreehugger Robot+ (CommonToken *) newTokenWithToken:(CommonToken *)fromToken 96*16467b97STreehugger Robot{ 97*16467b97STreehugger Robot return( [[CommonToken alloc] initWithToken:fromToken] ); 98*16467b97STreehugger Robot} 99*16467b97STreehugger Robot 100*16467b97STreehugger Robot// return the singleton EOF Token 101*16467b97STreehugger Robot+ (id<Token>) eofToken 102*16467b97STreehugger Robot{ 103*16467b97STreehugger Robot if (EOF_TOKEN == nil) { 104*16467b97STreehugger Robot EOF_TOKEN = [[CommonToken newToken:TokenTypeEOF Text:@"EOF"] retain]; 105*16467b97STreehugger Robot } 106*16467b97STreehugger Robot return EOF_TOKEN; 107*16467b97STreehugger Robot} 108*16467b97STreehugger Robot 109*16467b97STreehugger Robot// return the singleton skip Token 110*16467b97STreehugger Robot+ (id<Token>) skipToken 111*16467b97STreehugger Robot{ 112*16467b97STreehugger Robot if (SKIP_TOKEN == nil) { 113*16467b97STreehugger Robot SKIP_TOKEN = [[CommonToken newToken:TokenTypeInvalid Text:@"Skip"] retain]; 114*16467b97STreehugger Robot } 115*16467b97STreehugger Robot return SKIP_TOKEN; 116*16467b97STreehugger Robot} 117*16467b97STreehugger Robot 118*16467b97STreehugger Robot// return the singleton skip Token 119*16467b97STreehugger Robot+ (id<Token>) invalidToken 120*16467b97STreehugger Robot{ 121*16467b97STreehugger Robot if (INVALID_TOKEN == nil) { 122*16467b97STreehugger Robot INVALID_TOKEN = [[CommonToken newToken:TokenTypeInvalid Text:@"Invalid"] retain]; 123*16467b97STreehugger Robot } 124*16467b97STreehugger Robot return SKIP_TOKEN; 125*16467b97STreehugger Robot} 126*16467b97STreehugger Robot 127*16467b97STreehugger Robot// the default channel for this class of Tokens 128*16467b97STreehugger Robot+ (TokenChannel) defaultChannel 129*16467b97STreehugger Robot{ 130*16467b97STreehugger Robot return TokenChannelDefault; 131*16467b97STreehugger Robot} 132*16467b97STreehugger Robot 133*16467b97STreehugger Robot- (id) init 134*16467b97STreehugger Robot{ 135*16467b97STreehugger Robot if ((self = [super init]) != nil) { 136*16467b97STreehugger Robot input = nil; 137*16467b97STreehugger Robot type = TokenTypeInvalid; 138*16467b97STreehugger Robot channel = TokenChannelDefault; 139*16467b97STreehugger Robot startIndex = 0; 140*16467b97STreehugger Robot stopIndex = 0; 141*16467b97STreehugger Robot } 142*16467b97STreehugger Robot return self; 143*16467b97STreehugger Robot} 144*16467b97STreehugger Robot 145*16467b97STreehugger Robot// designated initializer 146*16467b97STreehugger Robot- (id) initWithInput:(id<CharStream>)anInput 147*16467b97STreehugger Robot Type:(NSInteger)aTType 148*16467b97STreehugger Robot Channel:(NSInteger)aChannel 149*16467b97STreehugger Robot Start:(NSInteger)aStart 150*16467b97STreehugger Robot Stop:(NSInteger)aStop 151*16467b97STreehugger Robot{ 152*16467b97STreehugger Robot if ((self = [super init]) != nil) { 153*16467b97STreehugger Robot input = anInput; 154*16467b97STreehugger Robot if ( input ) [input retain]; 155*16467b97STreehugger Robot type = aTType; 156*16467b97STreehugger Robot channel = aChannel; 157*16467b97STreehugger Robot startIndex = aStart; 158*16467b97STreehugger Robot stopIndex = aStop; 159*16467b97STreehugger Robot if (type == TokenTypeEOF) 160*16467b97STreehugger Robot text = @"EOF"; 161*16467b97STreehugger Robot else 162*16467b97STreehugger Robot text = [input substringWithRange:NSMakeRange(startIndex, (stopIndex-startIndex)+1)]; 163*16467b97STreehugger Robot if ( text ) [text retain]; 164*16467b97STreehugger Robot } 165*16467b97STreehugger Robot return self; 166*16467b97STreehugger Robot} 167*16467b97STreehugger Robot 168*16467b97STreehugger Robot- (id) initWithToken:(CommonToken *)oldToken 169*16467b97STreehugger Robot{ 170*16467b97STreehugger Robot if ((self = [super init]) != nil) { 171*16467b97STreehugger Robot text = [NSString stringWithString:oldToken.text]; 172*16467b97STreehugger Robot if ( text ) [text retain]; 173*16467b97STreehugger Robot type = oldToken.type; 174*16467b97STreehugger Robot line = oldToken.line; 175*16467b97STreehugger Robot index = oldToken.index; 176*16467b97STreehugger Robot charPositionInLine = oldToken.charPositionInLine; 177*16467b97STreehugger Robot channel = oldToken.channel; 178*16467b97STreehugger Robot input = oldToken.input; 179*16467b97STreehugger Robot if ( input ) [input retain]; 180*16467b97STreehugger Robot if ( [oldToken isKindOfClass:[CommonToken class]] ) { 181*16467b97STreehugger Robot startIndex = oldToken.startIndex; 182*16467b97STreehugger Robot stopIndex = oldToken.stopIndex; 183*16467b97STreehugger Robot } 184*16467b97STreehugger Robot } 185*16467b97STreehugger Robot return self; 186*16467b97STreehugger Robot} 187*16467b97STreehugger Robot 188*16467b97STreehugger Robot- (id) initWithType:(TokenType)aTType 189*16467b97STreehugger Robot{ 190*16467b97STreehugger Robot if ((self = [super init]) != nil) { 191*16467b97STreehugger Robot self.type = aTType; 192*16467b97STreehugger Robot } 193*16467b97STreehugger Robot return self; 194*16467b97STreehugger Robot} 195*16467b97STreehugger Robot 196*16467b97STreehugger Robot- (id) initWithType:(TokenType)aTType Text:(NSString *)tokenText 197*16467b97STreehugger Robot{ 198*16467b97STreehugger Robot if ((self = [super init]) != nil) { 199*16467b97STreehugger Robot self.type = aTType; 200*16467b97STreehugger Robot self.text = [NSString stringWithString:tokenText]; 201*16467b97STreehugger Robot if ( text ) [text retain]; 202*16467b97STreehugger Robot } 203*16467b97STreehugger Robot return self; 204*16467b97STreehugger Robot} 205*16467b97STreehugger Robot 206*16467b97STreehugger Robot- (void)dealloc 207*16467b97STreehugger Robot{ 208*16467b97STreehugger Robot#ifdef DEBUG_DEALLOC 209*16467b97STreehugger Robot NSLog( @"called dealloc in CommonToken" ); 210*16467b97STreehugger Robot#endif 211*16467b97STreehugger Robot if ( input ) [input release]; 212*16467b97STreehugger Robot if ( text ) [text release]; 213*16467b97STreehugger Robot [super dealloc]; 214*16467b97STreehugger Robot} 215*16467b97STreehugger Robot 216*16467b97STreehugger Robot// create a copy, including the text if available 217*16467b97STreehugger Robot// the input stream is *not* copied! 218*16467b97STreehugger Robot- (id) copyWithZone:(NSZone *)theZone 219*16467b97STreehugger Robot{ 220*16467b97STreehugger Robot CommonToken *copy = [[[self class] allocWithZone:theZone] init]; 221*16467b97STreehugger Robot 222*16467b97STreehugger Robot if (text) 223*16467b97STreehugger Robot copy.text = [text copyWithZone:nil]; 224*16467b97STreehugger Robot copy.type = type; 225*16467b97STreehugger Robot copy.line = line; 226*16467b97STreehugger Robot copy.charPositionInLine = charPositionInLine; 227*16467b97STreehugger Robot copy.channel = channel; 228*16467b97STreehugger Robot copy.index = index; 229*16467b97STreehugger Robot copy.startIndex = startIndex; 230*16467b97STreehugger Robot copy.stopIndex = stopIndex; 231*16467b97STreehugger Robot copy.input = input; 232*16467b97STreehugger Robot return copy; 233*16467b97STreehugger Robot} 234*16467b97STreehugger Robot 235*16467b97STreehugger Robot 236*16467b97STreehugger Robot//---------------------------------------------------------- 237*16467b97STreehugger Robot// charPositionInLine 238*16467b97STreehugger Robot//---------------------------------------------------------- 239*16467b97STreehugger Robot- (NSUInteger) getCharPositionInLine 240*16467b97STreehugger Robot{ 241*16467b97STreehugger Robot return charPositionInLine; 242*16467b97STreehugger Robot} 243*16467b97STreehugger Robot 244*16467b97STreehugger Robot- (void) setCharPositionInLine:(NSUInteger)aCharPositionInLine 245*16467b97STreehugger Robot{ 246*16467b97STreehugger Robot charPositionInLine = aCharPositionInLine; 247*16467b97STreehugger Robot} 248*16467b97STreehugger Robot 249*16467b97STreehugger Robot//---------------------------------------------------------- 250*16467b97STreehugger Robot// line 251*16467b97STreehugger Robot//---------------------------------------------------------- 252*16467b97STreehugger Robot- (NSUInteger) getLine 253*16467b97STreehugger Robot{ 254*16467b97STreehugger Robot return line; 255*16467b97STreehugger Robot} 256*16467b97STreehugger Robot 257*16467b97STreehugger Robot- (void) setLine:(NSUInteger)aLine 258*16467b97STreehugger Robot{ 259*16467b97STreehugger Robot line = aLine; 260*16467b97STreehugger Robot} 261*16467b97STreehugger Robot 262*16467b97STreehugger Robot//---------------------------------------------------------- 263*16467b97STreehugger Robot// text 264*16467b97STreehugger Robot//---------------------------------------------------------- 265*16467b97STreehugger Robot- (NSString *) text 266*16467b97STreehugger Robot{ 267*16467b97STreehugger Robot if (text != nil) { 268*16467b97STreehugger Robot return text; 269*16467b97STreehugger Robot } 270*16467b97STreehugger Robot if (input == nil) { 271*16467b97STreehugger Robot return nil; 272*16467b97STreehugger Robot } 273*16467b97STreehugger Robot int n = [input size]; 274*16467b97STreehugger Robot if ( startIndex < n && stopIndex < n) { 275*16467b97STreehugger Robot return [input substringWithRange:NSMakeRange(startIndex, (stopIndex-startIndex)+1)]; 276*16467b97STreehugger Robot } 277*16467b97STreehugger Robot else { 278*16467b97STreehugger Robot return @"<EOF>"; 279*16467b97STreehugger Robot } 280*16467b97STreehugger Robot} 281*16467b97STreehugger Robot 282*16467b97STreehugger Robot- (void) setText:(NSString *)aText 283*16467b97STreehugger Robot{ 284*16467b97STreehugger Robot if (text != aText) { 285*16467b97STreehugger Robot if ( text ) [text release]; 286*16467b97STreehugger Robot text = aText; 287*16467b97STreehugger Robot [text retain]; 288*16467b97STreehugger Robot } 289*16467b97STreehugger Robot} 290*16467b97STreehugger Robot 291*16467b97STreehugger Robot 292*16467b97STreehugger Robot//---------------------------------------------------------- 293*16467b97STreehugger Robot// type 294*16467b97STreehugger Robot//---------------------------------------------------------- 295*16467b97STreehugger Robot- (NSInteger)type 296*16467b97STreehugger Robot{ 297*16467b97STreehugger Robot return type; 298*16467b97STreehugger Robot} 299*16467b97STreehugger Robot 300*16467b97STreehugger Robot- (void) setType:(NSInteger)aType 301*16467b97STreehugger Robot{ 302*16467b97STreehugger Robot type = aType; 303*16467b97STreehugger Robot} 304*16467b97STreehugger Robot 305*16467b97STreehugger Robot//---------------------------------------------------------- 306*16467b97STreehugger Robot// channel 307*16467b97STreehugger Robot//---------------------------------------------------------- 308*16467b97STreehugger Robot- (NSUInteger)channel 309*16467b97STreehugger Robot{ 310*16467b97STreehugger Robot return channel; 311*16467b97STreehugger Robot} 312*16467b97STreehugger Robot 313*16467b97STreehugger Robot- (void) setChannel:(NSUInteger)aChannel 314*16467b97STreehugger Robot{ 315*16467b97STreehugger Robot channel = aChannel; 316*16467b97STreehugger Robot} 317*16467b97STreehugger Robot 318*16467b97STreehugger Robot 319*16467b97STreehugger Robot//---------------------------------------------------------- 320*16467b97STreehugger Robot// input 321*16467b97STreehugger Robot//---------------------------------------------------------- 322*16467b97STreehugger Robot- (id<CharStream>) input 323*16467b97STreehugger Robot{ 324*16467b97STreehugger Robot return input; 325*16467b97STreehugger Robot} 326*16467b97STreehugger Robot 327*16467b97STreehugger Robot- (void) setInput: (id<CharStream>) anInput 328*16467b97STreehugger Robot{ 329*16467b97STreehugger Robot if (input != anInput) { 330*16467b97STreehugger Robot if ( input ) [input release]; 331*16467b97STreehugger Robot [anInput retain]; 332*16467b97STreehugger Robot } 333*16467b97STreehugger Robot input = anInput; 334*16467b97STreehugger Robot} 335*16467b97STreehugger Robot 336*16467b97STreehugger Robot 337*16467b97STreehugger Robot//---------------------------------------------------------- 338*16467b97STreehugger Robot// start 339*16467b97STreehugger Robot//---------------------------------------------------------- 340*16467b97STreehugger Robot- (NSInteger) getStart 341*16467b97STreehugger Robot{ 342*16467b97STreehugger Robot return startIndex; 343*16467b97STreehugger Robot} 344*16467b97STreehugger Robot 345*16467b97STreehugger Robot- (void) setStart: (NSInteger) aStart 346*16467b97STreehugger Robot{ 347*16467b97STreehugger Robot startIndex = aStart; 348*16467b97STreehugger Robot} 349*16467b97STreehugger Robot 350*16467b97STreehugger Robot//---------------------------------------------------------- 351*16467b97STreehugger Robot// stop 352*16467b97STreehugger Robot//---------------------------------------------------------- 353*16467b97STreehugger Robot- (NSInteger) getStop 354*16467b97STreehugger Robot{ 355*16467b97STreehugger Robot return stopIndex; 356*16467b97STreehugger Robot} 357*16467b97STreehugger Robot 358*16467b97STreehugger Robot- (void) setStop: (NSInteger) aStop 359*16467b97STreehugger Robot{ 360*16467b97STreehugger Robot stopIndex = aStop; 361*16467b97STreehugger Robot} 362*16467b97STreehugger Robot 363*16467b97STreehugger Robot//---------------------------------------------------------- 364*16467b97STreehugger Robot// index 365*16467b97STreehugger Robot//---------------------------------------------------------- 366*16467b97STreehugger Robot- (NSInteger) getTokenIndex; 367*16467b97STreehugger Robot{ 368*16467b97STreehugger Robot return index; 369*16467b97STreehugger Robot} 370*16467b97STreehugger Robot 371*16467b97STreehugger Robot- (void) setTokenIndex: (NSInteger) aTokenIndex; 372*16467b97STreehugger Robot{ 373*16467b97STreehugger Robot index = aTokenIndex; 374*16467b97STreehugger Robot} 375*16467b97STreehugger Robot 376*16467b97STreehugger Robot 377*16467b97STreehugger Robot// provide a textual representation for debugging 378*16467b97STreehugger Robot- (NSString *) description 379*16467b97STreehugger Robot{ 380*16467b97STreehugger Robot NSString *channelStr; 381*16467b97STreehugger Robot NSMutableString *txtString; 382*16467b97STreehugger Robot 383*16467b97STreehugger Robot channelStr = @""; 384*16467b97STreehugger Robot if ( channel > 0 ) { 385*16467b97STreehugger Robot channelStr = [NSString stringWithFormat:@",channel=%d\n", channel]; 386*16467b97STreehugger Robot } 387*16467b97STreehugger Robot if ([self text] != nil) { 388*16467b97STreehugger Robot txtString = [NSMutableString stringWithString:[self text]]; 389*16467b97STreehugger Robot [txtString replaceOccurrencesOfString:@"\n" withString:@"\\\\n" options:NSAnchoredSearch range:NSMakeRange(0, [txtString length])]; 390*16467b97STreehugger Robot [txtString replaceOccurrencesOfString:@"\r" withString:@"\\\\r" options:NSAnchoredSearch range:NSMakeRange(0, [txtString length])]; 391*16467b97STreehugger Robot [txtString replaceOccurrencesOfString:@"\t" withString:@"\\\\t" options:NSAnchoredSearch range:NSMakeRange(0, [txtString length])]; 392*16467b97STreehugger Robot } else { 393*16467b97STreehugger Robot txtString = [NSMutableString stringWithString:@"<no text>"]; 394*16467b97STreehugger Robot } 395*16467b97STreehugger Robot return [NSString stringWithFormat:@"[@%d, %d:%d='%@',<%d>%@,%d:%d]", index, startIndex, stopIndex, txtString, type, channelStr, line, charPositionInLine]; 396*16467b97STreehugger Robot} 397*16467b97STreehugger Robot 398*16467b97STreehugger Robot- (NSString *)toString 399*16467b97STreehugger Robot{ 400*16467b97STreehugger Robot return [self description]; 401*16467b97STreehugger Robot} 402*16467b97STreehugger Robot 403*16467b97STreehugger Robot@end 404