/* Copyright (c) 1993 by Sanjay Ghemawat */

#include <values.h>
#include <ctype.h>

#include "lexer.h"
#include "arrays.h"

char const* Lexer::lastError = "";

Lexer::Lexer(char const* file) {
    tempBuffer = new charArray;
    input = fopen(file, "r");
    if (input == 0) {
	SetError("could not open file");
	return;
    }

    status = Valid;
    NextChar();
}

Lexer::~Lexer() {
    if (input) fclose(input);
    delete tempBuffer;
}

int Lexer::SkipWS() {
    char c;

    while (Peek(c)) {
	if (isascii(c) && isspace(c)) {
	    NextChar();
	    continue;
	}
	else {
	    return 1;
	}
    }

    return 0;
}

int Lexer::GetId(char const*& x) {
    char c;

    if (! Next(c)) return 0;

    if ((! isascii(c)) || ((c != '_') && (! isalpha(c)))) {
	SetError("illegal character when expecting id");
	return 0;
    }

    tempBuffer->clear();
    tempBuffer->append(c);
    while (Peek(c)) {
	if (isascii(c) && ((c == '_') || (isalpha(c)))) {
	    tempBuffer->append(c);
	    NextChar();
	    continue;
	}

	/* Hit end of id */
	tempBuffer->append('\0');
	x = tempBuffer->as_pointer();
	return 1;
    }

    return 0;
}

int Lexer::GetUntil(char terminator, char const*& x) {
    tempBuffer->clear();

    char c;
    while (Peek(c) && (c != terminator)) {
	tempBuffer->append(c);
	NextChar();
    }

    switch (Status()) {
      case Valid:
	x = tempBuffer->as_pointer();
	return 1;
      case Eof:
	SetError("unexpected end of file");
	return 0;
      case Error:
	return 0;
    }
}
 
int Lexer::GetNumber(int& x) {
    char c;

    if (! Peek(c)) return 0;

    if ((! isascii(c)) || (! isdigit(c))) {
	SetError("non-digit while reading number");
	return 0;
    }

    int num = 0;
    while (Peek(c)) {
	if (isascii(c) && isdigit(c)) {
	    int digit = c - '0';

	    if (((MAXINT - digit) / 10) < num) {
		/* Would overflow - skip all remaining digits */
		while (Peek(c)) {
		    if (isascii(c) && isdigit(c)) {
			NextChar();
		    }
		    break;
		}
		SetError("integer overflow");
		return 0;
	    }

	    num = num * 10 + digit;
	    NextChar();
	    continue;
	}

	x = num;
	return 1;
    }

    return 0;
}

int Lexer::GetText(char* buf, int len) {
    for (int i = 0; i < len; i++) {
	char c;
	if (! Next(c)) return 0;
	buf[i] = c;
    }
    return 1;
}

int Lexer::Skip(char const* str) {
    while (*str) {
	char c;

	if (! Peek(c)) return 0;
	if (c != *str) {
	    SetError("unexpected character");
	    return 0;
	}

	str++;
	NextChar();
    }
    return 1;
}

void Lexer::NextChar() {
    int c = getc(input);

    if (c == EOF) {
	if (feof(input)) status = Eof;
	if (ferror(input)) {
	    SetError("IO error");
	}
    }
    else {
	lookahead = (char) c;
    }
}
