
#include <ctype.h>
#include "scanner.h"


Scanner::Scanner() {

	cerr << "Error: Scanner instantiated without input stream or file name."
		 << endl;
	exit(-1);
}

Scanner::Scanner(istream &is, int m) {

	_input = &is;
	_close_input_when_done = 0;

	_tokens = new JLString[m];
	_token_IDs = new int[m];
	_symbol_base_ID = 0;
	_max_tokens = m;
	_num_tokens = 0;

	_token_ID = 0;
	_linenumber = 1;
	_c = 0;
	_putback = 0;
	_eol = _eof = 0;
	_verbose = 0;
}

Scanner::Scanner(const JLString &filename, int m) {

	_input = new ifstream(filename.GetString(), ios::in);
	if (!_input) {
		cerr << "Error in Scanner: couldn't open file: " << filename << endl;
		exit(-1);
	}
	_close_input_when_done = 1;

	_tokens = new JLString[m];
	_token_IDs = new int[m];
	_symbol_base_ID = 0;
	_max_tokens = m;
	_num_tokens = 0;

	_token_ID = 0;
	_linenumber = 1;
	_c = 0;
	_putback = 0;
	_eol = _eof = 0;
	_verbose = 0;
}

Scanner::~Scanner() {

	if (_close_input_when_done) {
		((ifstream *) _input)->close();
		delete _input;
	}

	delete [] _tokens;
	delete _token_IDs;
}

void Scanner::AddToken(const JLString &t, int ID) {

	if (_num_tokens < _max_tokens) {
		_tokens[_num_tokens] = t;
		_token_IDs[_num_tokens] = ID;
		_num_tokens++;
	}
	else {
		cerr << "Error adding token: " << t << " (too many tokens)" << endl;
		exit(-1);
	}
}

void Scanner::SetSymbols(const JLString &s, int base_ID) {

	_symbols = s;
	_symbol_base_ID = base_ID;
}

int Scanner::IsWhiteSpace() {

	if (_c == '\n')
		_eol++;

	return ((_c == ' ') || (_c == '\t') || (_c == '\n'));
}

void Scanner::SkipWhiteSpace() {

	do {
		_input->get(_c);
		if (_input->eof())
			_eof = 1;
	} while (IsWhiteSpace() && !_eof);

	_putback = 1;
}

Scanner& Scanner::GetNextToken() {

	if (_token_ID == TK_EOF) {
		cerr << "Error in Scanner: Tried to read past end of file" << endl;
		exit(-1);
	}

	int KeepTrying = 1, in_comment = 0;

	while (KeepTrying || in_comment) {

		KeepTrying = 0;

		_token = "";

		if (!_eof)
			SkipWhiteSpace();

		if (_eol) {
			_linenumber += _eol;
			_eol = 0;
		}

		if (_eof) {
			_token = "*EOF*";
			_token_ID = TK_EOF;
			if (_verbose)
				cerr << "TK: " << _token << endl;
			return *this;
		}

		if (!_putback)
			_input->get(_c);
		else
			_putback = 0;

		if (_symbol_base_ID) {
			char *ch;
			if ((ch = strchr(_symbols.GetString(), _c))) {
				int index = (int) (ch - _symbols.GetString());
				_token += _symbols.GetString()[index];
				_token_ID = _symbol_base_ID + index;
				if (!in_comment) {
					if (_verbose)
						cerr << "TK: " << _token << endl;
					return *this;
				}
			}
		}

		while (!(IsWhiteSpace() || _eof)) {
			_token += _c;
			if (_token == "//") {
				KeepTrying = 1;
				while (!(_eol || _eof)) {
					_input->get(_c);
					IsWhiteSpace();
				}
				_linenumber--;
			}
			else if (_token == "/*")
				in_comment = 1;
			else if ((_token == "*/") && in_comment) {
				in_comment = 0;
				KeepTrying = 1;
			}
			else
				_input->get(_c);
		}
	}

	_token_ID = TK_NONE;

	for (int i=0; i<_num_tokens; i++)
		if (_token == _tokens[i]) {
			_token_ID = _token_IDs[i];
			i = _num_tokens;
		}

	if (_token_ID == TK_NONE) {
		int decimals=0, ok=1, negative=0, last_e = -2;
		if (_token.GetString()[0] == '-')
			negative = 1;
		for (int i=negative; i<_token.GetLength(); i++) {
			if (_token.GetString()[i] == '.')
				decimals++;
			else if (isdigit(_token.GetString()[i]))
				/* fine */ ;
			else if (_token.GetString()[i] == 'e') {
				if (last_e != -2)
					ok = 0;
				last_e = i;
			}
			else if (_token.GetString()[i] == '-') {
				if (last_e != (i-1))
					ok = 0;
			}
			else
				ok = 0;
		}
		if (ok && (decimals <= 1))
			_token_ID = TK_FLOAT;
	}

	if (_token_ID == TK_NONE)
		if (isalpha(_token.GetString()[0])) {
			int ok=1;
			for (int i=1; i<_token.GetLength(); i++)
				if (!isalnum(_token.GetString()[0]))
					ok = 0;
			if (ok)
				_token_ID = TK_STRING;
		}

	if (_verbose)
		cerr << "TK: " << _token << endl;

	return* this;
}
