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

#ifndef _LEXERH
#define _LEXERH

#include <stdio.h>

class charArray;

/*
 * Help in lexing.
 */

class Lexer {
  public:
    Lexer(char const* file);
    ~Lexer();

    /*
     * Status.
     */
    enum StatusType {
	Valid,
	Eof,
	Error
	};

    StatusType Status();		/* Get current status */

    void SetError(char const*);		/* Set error and corresponding msg */
    static char const* LastError();	/* Return text of last error */

    /*
     * Input operations.
     * Return true iff operation succeeded.
     * May have skipped undetermined amount of input in case of error.
     */

    /*
     * Character operations.
     */
    int Peek(char&);		/* Return next char without advancing */
    int Next(char&);		/* Return next char and advance over it */
    int Advance(char&);		/* Advance and then Peek() */

    /*
     * Skip all whitespace.
     */
    int SkipWS();

    /*
     * Read an identifier.
     * An identifier is a non-empty sequence of alphanumeric characters
     * (including underscores), where the first character is not a digit.
     * 
     * X is set to point to a temporary buffer managed by the Lexer.
     * The temporary buffer contains the null-terminated identifier.
     * The next GetId operation on the lexer will invalidate this
     * temporary buffer.
     */
    int GetId(char const*& x);

    /*
     * Read text until the specified character is hit.
     * X is set as by GetId(char const*&).
     */
    int GetUntil(char, char const*& x);

    /*
     * Read number.
     */
    int GetNumber(int& num);

    /*
     * Read specified number of characters into supplied buffer.
     */
    int GetText(char*, int len);

    /*
     * Skip past expected text.
     */
    int Skip(char);
    int Skip(char const*);
  protected:
    StatusType	status;		/* Current status */
    FILE*	input;		/* Input stream */
    char	lookahead;	/* Lookahead - legal iff status is valid */
    charArray*	tempBuffer;	/* Temporary buffer */

    void NextChar();		/* Read next char into lookahead */

    /* Last error message */
    static char const* lastError;
};

inline Lexer::StatusType Lexer::Status() {
    return status;
}

inline void Lexer::SetError(char const* msg) {
    lastError = msg;
    status = Error;
}

inline char const* Lexer::LastError() {
    return lastError;
}

inline int Lexer::Peek(char& c) {
    if (status == Valid) {
	c = lookahead;
    }
    return (status == Valid);
}

inline int Lexer::Next(char& c) {
    if (status == Valid) {
	c = lookahead;
	NextChar();
    }
    return (status == Valid);
}

inline int Lexer::Advance(char& c) {
    int result;

    if (status == Valid) {
	NextChar();
	result = Peek(c);
    }
    else {
	result = 0;
    }
    return result;
}

inline int Lexer::Skip(char c) {
    if (status == Valid) {
	if (lookahead == c) {
	    NextChar();
	    return 1;
	}
	else {
	    SetError("unexpected character");
	}
    }
    return 0;
}

#endif /* _LEXERH */
