/********************************************************************
  To make the lexical analyser look prettier it is handy to have a
  a function which characterises a byte. We really would like
  constants defined but JavaScript does not support this just yet.
*********************************************************************/
var WHITE_SPACE = ' ',
    QUOTE = '\"',
    ALPHA = 'A',
    DIGIT = '9',
    PUNCT = '.',
    HASH ='#',
    EOS = 'z'

function kindOfCharacter(c){
if (c=='') return EOS;
if (c<=' ') return WHITE_SPACE;
if (c=='\"') return QUOTE;
if (((c>='A')&&(c<='Z'))||((c>='a')&&(c<='z'))||(c=='_'))
  return ALPHA;
if ((c>='0')&&(c<='9')) return DIGIT;
if (c=='#') return HASH;
return PUNCT;
}


/********************************************************************
  Lexical analyser. This is a finite state machine with actions
  associated with each transition.

  States:
    DROP     Drop the current character
    TAKE     Accumulate the current character - add it to "wrd"

  LexOne     Get one token from the CharStream and add it to the
             SymbolStream
*********************************************************************/
function LexOne(CharStream, SymbolStream){
/* These are the actions which may occur at each "transition" */
var DROP = 0, TAKE = 1;                  //actions constants
var SKIPPING = 0, NAME = 1, SYMBOL = 2,  //states  constants
    LITERAL = 3, STOP = 4, COMMENT = 5,
    NUMBER = 6

wrd='';
state = SKIPPING;
action= DROP;
while (state != STOP){
  c = Get(CharStream);
  k = kindOfCharacter(c);
  switch (state) {
    case 0 : //SKIPPING
      switch (k) {
        case ' ' :
          action = DROP;
          break;
        case '\"' :
          action = DROP;
          state  = LITERAL;
          break;
        case '.' :
          action = TAKE;
          state  = SYMBOL;
          break;
        case '#' :
          action = DROP;
          state = COMMENT;
          break;
        case '9' :
          action = TAKE;
          state  = NUMBER;
          break;
        case 'z' : //End of stream
          action = DROP;
          state = STOP;
          break;
        default:
          action = TAKE;
          state  = NAME;
        }
      break;
    case 1 : // NAME
      switch (k) {
        case 'A' :
        case '9' :
          action=TAKE;
          break;
        default :
          action = DROP;
          state  = STOP;
          UnGet(CharStream);
          Put(SymbolStream, new Array("LABEL",wrd,Line(CharStream)));
        }
      break;
    case 2 : // SYMBOL
      action = DROP;
      state  = STOP;
      UnGet(CharStream);
      Put(SymbolStream, new Array("SYMBOL", wrd,Line(CharStream)));
      break;
    case 3 : // LITERAL
      switch (k) {
        case '\"' :
          action = DROP;
          state  = STOP;
          Put(SymbolStream, new Array("STRING", wrd,Line(CharStream)));
          break;
        case 'z' : //Unterminated string ... bad news!
          state = STOP;
          break;
        default :
          action = TAKE;
        }
      break;
    case 5 : // COMMENT
      switch (k) {
        case ' ' :
          action = DROP;
          if (c=='\n')
            state = SKIPPING;
          break;
        case 'z' : //End of stream
          state = STOP;
          break;
        default :
          break;
        }
      break;
    case 6 : // NUMBER
      switch(k) {
        case '9' :
          action = TAKE;
          break;
        default :
          action = DROP;
          state = STOP;
          UnGet(CharStream);
          Put(SymbolStream, new Array("INTEGER",wrd,Line(CharStream)));
          break;
        }
      break;
    default :;
    }
  if (action == TAKE) {
    wrd=wrd+c
    }
  }
}

function Lex(CharStream){
var SymbolStream = new Stream();
while (! IsEmpty(CharStream)) {
  LexOne(CharStream,SymbolStream);
  }
return (SymbolStream);
}


