//
//  EXPParser.m
//  ParserTest
//
//  Created by Ashley on 29/05/2007.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "EXPParser.h"

#define TAGUNKNOWN -2
#define TAGENDOFFILE -1
#define TAGBINARYOP 0
#define TAGUNARYOP 1
#define TAGLITERAL 2
#define TAGIDENTIFIER 3
#define TAGSTRINGLITERAL 4
#define TAGOTHER 5

@implementation EXPParser

- (id) init
{
	self = [super init];
	if (self==nil) {	
		return nil;
	}
	
	_string = nil;
	_length = 0;
	_symbolType = TAGENDOFFILE;
	_ch = '\0';
	_echo = YES;

	return self;
}

- (void) setString:(id)string
{
	[string retain];
	[_string release];
	_string = string;

	_count = 0;
	_length = [_string length];
	_symbol = nil;
//	_haveSymbol = NO;
 
	_skipComments = YES;
	_newLine = YES;
	_lineNumber = 1;
	[self readChar];
	[self readSymbol];
}

- (id) string
{
	return _string;
}

- (void) setEcho:(BOOL)echo
{
	_echo = echo;
}

- (BOOL) echo
{
	return _echo;
}

- (void) readChar
{
//	unichar ch;
	if (_count>=_length) {
		_ch = '\0';
	} else {
		_ch = [_string characterAtIndex:_count++];
		if ((_ch>='a') && (_ch<='z')) {
			_ch = _ch -  'a' + 'A';
		}
	}
	
	if (_echo) {
		if (_newLine) {
			printf("%04d: ", _lineNumber++);
		}
		if (_ch!='\0') {
			putchar((int)_ch);
		}
		_newLine = (_ch=='\n');
	}
	
}

- (unichar) nextChar
{
	unichar ch;
	if (_count>=_length) {
		ch = '\0';
	} else {
		ch = [_string characterAtIndex:(_count)];
		if ((ch>='a') && (ch<='z')) {
			ch = ch -  'a' + 'A';
		}
	} 
	
	return ch;
}

- (void) readSymbol
{
	_readPastEol = NO;

	while ((_ch==' ') || (_ch=='\t') || (_ch=='\r') || (_ch=='\n')) {
		if (_ch=='\n') {
			_readPastEol = YES;
		}
		[self readChar]; 
	}
		
	if (_skipComments) {
		for(;;) {
			while ((_ch==' ') || (_ch=='\t') || (_ch=='\r') || (_ch=='\n')) {
				if (_ch=='\n') {
					_readPastEol = YES;
				}
				[self readChar]; 
			}
			if (!((_ch=='/') && (([self nextChar]=='/') || ([self nextChar]=='*')))) {
				break;
			}
			[self readChar]; 
			if (_ch=='/') {
				[self readChar];
				while ((_ch!='\0') && (_ch!='\n')) {
					[self readChar];
				}
			} else {
				[self readChar];
			}
		}

	}
	
	NSMutableString *symbol = [[NSMutableString alloc] init];
	NSString *str;
	
	if (isalpha(_ch) || (_ch=='_')) {
		str = [[NSString alloc] initWithCharacters:&_ch length:1];
		[symbol appendString:str];
		[str release];
		for(;;) {
			[self readChar];
			if (!isalpha(_ch) && !isdigit(_ch) && (_ch!='_')) {
				break;
			}
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
		}
		if (_ch=='!') {
			[symbol appendString:@"!"];
			for(;;) {
				[self readChar];
				if (!isalpha(_ch) && !isdigit(_ch) && (_ch!='_')) {
					break;
				}
				str = [[NSString alloc] initWithCharacters:&_ch length:1];
				[symbol appendString:str];
				[str release];
			}
		}
		if ([symbol isEqualToString:@"and"] || [symbol isEqualToString:@"or"]) {
			_symbolType = TAGBINARYOP;
		} else {
			_symbolType = TAGIDENTIFIER;
		}
		
	} else if (isdigit(_ch) || (_ch=='.')) {
//		NSString *str;
		while (isdigit(_ch)) {
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
			[self readChar];
		}

		if (_ch=='.') {
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
			[self readChar];
		}

		while (isdigit(_ch)) {
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
			[self readChar];
		}

		if ((_ch=='e') || (_ch=='E')) {
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
			[self readChar];
			if ((_ch=='+') || (_ch=='-')) {
				str = [[NSString alloc] initWithCharacters:&_ch length:1];
				[symbol appendString:str];
				[str release];
				[self readChar];
			}
			while (isdigit(_ch)) {
				str = [[NSString alloc] initWithCharacters:&_ch length:1];
				[symbol appendString:str];
				[str release];
				[self readChar];
			}

		}
		_symbolType = TAGLITERAL;
		
	} else if ((_ch=='=') || (_ch=='>') || (_ch=='<') || (_ch=='!')) {
		str = [[NSString alloc] initWithCharacters:&_ch length:1];
		[symbol appendString:str];
		[str release];
		[self readChar];
		if (_ch=='=') {
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
			[self readChar]; 
		}
		_symbolType = TAGBINARYOP; 
		
	} else if ((_ch=='-') || (_ch=='*') || (_ch=='/') || (_ch=='^') || (_ch=='+') || (_ch=='?') || (_ch==':') ) {
		str = [[NSString alloc] initWithCharacters:&_ch length:1];
		[symbol appendString:str];
		[str release];
		[self readChar];
		_symbolType = TAGBINARYOP;

	} else if ((_ch=='(')  || (_ch==')') || (_ch=='[') || (_ch==']') || (_ch=='{') || (_ch=='}') || 
			   (_ch==';') || (_ch==',') || (_ch=='\'') || (_ch=='\'') || (_ch=='#')) {
		str = [[NSString alloc] initWithCharacters:&_ch length:1];
		[symbol appendString:str];
		[str release];
		[self readChar];
		_symbolType = TAGOTHER;

	} else if (_ch=='"') {
		[self readChar];
		while ((_ch!='"') && (_ch!='\n') && (_ch!='\0')) {
			str = [[NSString alloc] initWithCharacters:&_ch length:1];
			[symbol appendString:str];
			[str release];
			[self readChar];
		}
		if (_ch!='\0') {
			[self readChar];
		}
		_symbolType = TAGSTRINGLITERAL;

	} else if (_ch=='\0') {
		_symbolType = TAGENDOFFILE;
		
	} else {
		str = [[NSString alloc] initWithCharacters:&_ch length:1];
		[symbol appendString:str];
		[str release];
		[self readChar];
		_symbolType = TAGUNKNOWN;
	}
	
	[_symbol release];
	_symbol = [[NSString alloc] initWithString:symbol];
	[symbol release];
//	_haveSymbol = YES;
}

/* - (id) nextSymbol
{
	if (!_haveSymbol) {
		[self readSymbol];
	}
	
	return _symbol;
} */

- (BOOL) testSymbol:(NSString *)symbol
{	
	BOOL success = [[self symbol] isEqualToString:symbol];
	if (success) {
		[self readSymbol];
	}
	
	return success;
}

- (BOOL) checkSymbol:(NSString *)symbol
{
	BOOL success = [self testSymbol:symbol];
	
	if (!success) {
		printf("Error: %s found where %s expected", [[self symbol] UTF8String], [symbol UTF8String]);
	}
	
	return success;
}

- (id) symbol
{
	return _symbol;
}

- (int) lineNumber
{
	return _lineNumber;
}

- (BOOL) isBinaryOp
{
	return (_symbolType==TAGBINARYOP);
}

- (BOOL) isLiteral
{
	return (_symbolType==TAGLITERAL);
}

- (BOOL) isStringLiteral
{
	return (_symbolType==TAGSTRINGLITERAL);
}

- (BOOL)isIdentifier
{
	return (_symbolType==TAGIDENTIFIER);
}

- (BOOL) isEndOfLine
{
//	unichar ch;
	
/*	for (;;) {
		ch = [self nextChar]; 
		if ((ch!=' ') && (ch!='\t')) {
			break;
		}
		ch = [self readChar]; 
	} */
/*	while ((_ch==' ') || (_ch=='\t')) {
		[self readChar];
	}
			
	return (_ch=='\n'); */
	return _readPastEol;
}

- (BOOL) isEndOfFile
{
/*	unichar ch;
	
	while ((_ch==' ') || (_ch=='\t') || (_ch=='\r') || (_ch=='\n')) {
		[self readChar];
	} */
			
	return (_ch=='\0');
}

- (void) dealloc
{
	[_symbol release];
	[_string release];
	[super dealloc];
}

@end
