import java.util.Stack;
import java.io.Reader;
import java.io.EOFException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;

/**
 * This is a slightly more sophisticated lexer than MiniLexer.  In addition
 * to unsigned ints and words, it also supports a single "punctuation" class,
 * consisting of single-character punctuation characters in the class
 * [()[],";].
 *
 * This assumes that all input is 7-bit ASCII; any
 * other characters are errors and cause an exception.
 *
 * @author Terran Lane
 * @version 1.0
 */
public class WPILexer extends AbstractTableLexer {

  /**
   * Initializes this Lexer to take characters from the designated Reader
   * object.  Note that this Lexer does <em>not</em> close the Reader when
   * lexing is complete (i.e., when EOF is encountered) -- that job is left
   * up to the caller.  This lexer assumes that when EOF is encountered, the
   * token stream is done (i.e., the stream won't reset and no more tokens
   * will become available).
   *
   * @param r The Reader from which to take character data.
   */
  public WPILexer(Reader r) {
    super(r);
    _initTab();
  }

  /**
   * Just a small test rig to ensure that this lexer is doing what we expect.
   */
  public static void main(String[] args) {
    for (int i=0;i<args.length;++i) {
      System.out.println("     ===== " + args[i] + "=====");
      Reader r=null;
      try {
	r=new BufferedReader(new FileReader(args[i]));
	Lexer l=new WPILexer(r);
	Token t;
	String s="";
	int c=0;
	while (l.hasNext()) {
	  t=l.next();
	  switch (t.getTokType()) {
	    case WPIToken.T_INT: s="INT"; break;
	    case WPIToken.T_WORD: s="WORD"; break;
	    case WPIToken.T_PUNCT: s="PUNCT"; break;
	    case WPIToken.T_UNKN: s="UNKNOWN"; break;
	    default:
	      assert false : "TT=" + t.getTokType() + "; Str=" +
	                     t.getTokStr();
	  }
	  System.out.println("[" + c++ + "] '" + t.getTokStr() + "' (TT=" +
			     s + ")");
	}
      }
      catch (IOException e) {
	System.err.println("I/O error while lexing from '" + args[i] +
			   "': " + e);
      }
      catch (ParseException e) {
	System.err.println("Parse error while lexing from '" + args[i] +
			   "': " + e);
      }
      finally {
	// ensure that reader is closed, even if an exception occurs
	if (r!=null) {
	  try {
	    // this is pretty overkill, but close() can
	    // <em>theoretically</em> generate its own bloody IOException.
	    // Ugh.
	    r.close();
	  }
	  catch (IOException e) {
	    System.err.println("Wow.  I/O Exception while trying to " +
			       "close an open file.  Ugh.  Exception=" + e);
	  }
	}
      }
      System.out.println("     ===== " + args[i] + " done =====");
    }
  }

  /* ******************** end of public interface ******************** */

  /**
   * This function actually allocates and constructs the DFA table, and
   * determines the language accepted by this engine.
   */
  private void _initTab() {
    _tab=new _LexAct[ST_NUM_STATES][C_EOF+1];
    for (int i=0;i<C_EOF;++i) {
      if (Character.isDigit((char)i)) {
	_tab[ST_INIT][i]=new _LexAct(ST_ININT,A_SHIFT,WPIToken.T_UNKN);
	_tab[ST_ININT][i]=new _LexAct(ST_ININT,A_SHIFT,WPIToken.T_INT);
	_tab[ST_INWORD][i]=new _LexAct(ST_ININT,A_RET,WPIToken.T_WORD);
	_tab[ST_INPUNCT][i]=new _LexAct(ST_ININT,A_RET,WPIToken.T_PUNCT);
      }
      else if (Character.isLetter((char)i)) {
	_tab[ST_INIT][i]=new _LexAct(ST_INWORD,A_SHIFT,WPIToken.T_UNKN);
	_tab[ST_ININT][i]=new _LexAct(ST_INWORD,A_RET,WPIToken.T_INT);
	_tab[ST_INWORD][i]=new _LexAct(ST_INWORD,A_SHIFT,WPIToken.T_WORD);
	_tab[ST_INPUNCT][i]=new _LexAct(ST_INWORD,A_RET,WPIToken.T_PUNCT);
      }
      // Shorthand way to say, "if i is any of the characters in this string"
      // much more concise to write than
      //   if ((char)i=="(" ||
      //       (char)i==")" ||
      // etc.  Takes longer to run, but since this is done only once during
      // initialization, it's a trivial expense.
      else if (PUNCT_CHARS.indexOf(i)>=0) {
	_tab[ST_INIT][i]=new _LexAct(ST_INPUNCT,A_SHIFT,WPIToken.T_UNKN);
	_tab[ST_ININT][i]=new _LexAct(ST_INPUNCT,A_RET,WPIToken.T_INT);
	_tab[ST_INWORD][i]=new _LexAct(ST_INPUNCT,A_RET,WPIToken.T_WORD);
	_tab[ST_INPUNCT][i]=new _LexAct(ST_INPUNCT,A_RET,WPIToken.T_PUNCT);
      }
      else {
	_tab[ST_INIT][i]=new _LexAct(ST_INIT,A_DROP,WPIToken.T_UNKN);
	_tab[ST_ININT][i]=new _LexAct(ST_INIT,A_DRET,WPIToken.T_INT);
	_tab[ST_INWORD][i]=new _LexAct(ST_INIT,A_DRET,WPIToken.T_WORD);
	_tab[ST_INPUNCT][i]=new _LexAct(ST_INIT,A_DRET,WPIToken.T_PUNCT);
      }
    }
    _tab[ST_INIT][C_EOF]=new
      _LexAct(ST_INIT,A_DRET,WPIToken.T_UNKN,false);
    _tab[ST_ININT][C_EOF]=new _LexAct(ST_INIT,A_DRET,WPIToken.T_INT);
    _tab[ST_INWORD][C_EOF]=new _LexAct(ST_INIT,A_DRET,WPIToken.T_WORD);
    _tab[ST_INPUNCT][C_EOF]=new _LexAct(ST_INIT,A_DRET,WPIToken.T_PUNCT);
  }

  protected static final int ST_INIT=0;		// initial state
  protected static final int ST_ININT=1;	// parsing an int
  protected static final int ST_INWORD=2;	// parsing a word
  protected static final int ST_INPUNCT=3;	// parsing a punct char
  protected static final int ST_NUM_STATES=4;

  // punctuation characters
  protected static final String PUNCT_CHARS="()[],;\"";

  private static final int C_EOF=128;		// special char code for EOF

  protected int _getEOFIdx() { return C_EOF; }
  protected int _startState() { return ST_INIT; }

}
