LARA

class Lexer(ch : CharStream) {
  val EOL = '\n'
  val keywords = Map("while" -> WHILE,
		     "if" -> IF,
		     "println" -> PRINTLN,
		     "readln" -> READLN)
  
  var current : Token = EOF

  private def skippedComment : Boolean = {
    if (ch.current == '/') {
      ch.next
      if (ch.current == '/') {
	ch.next
	while ((ch.current != EOL) && !ch.eof) ch.next
	true
      } else {
	current = DIV
	false
      }
    } else {
      false
    }
  }

  private def skipSpaces = {
    while (((ch.current <= ' ') || skippedComment) && !ch.eof)
      ch.next
  }

  private def isLetter : Boolean = 
    ('A' <= ch.current && ch.current <= 'Z') ||
    ('a' <= ch.current && ch.current <= 'z') ||
    ('_' == ch.current)

  private def isDigit : Boolean = ('0' <= ch.current) & (ch.current <= '9')

  private def getIdent = {
    var s = new StringBuffer()
    while ((isLetter || isDigit) && !ch.eof) {
      s.append(ch.current)
      ch.next
    }
    val str = s.toString
    if (keywords.isDefinedAt(str)) {
      current = keywords(str)
    } else {
      current = ID(str);      
    }
  }

  private def getInt = {
    var k = 0;
    while (isDigit && !ch.eof) {
      val digit = ch.current.asInstanceOf[Int] - '0'.asInstanceOf[Int];
      k = k * 10 + digit
      ch.next
    }
    current = IntConst(k)
  }

  def next : Unit = {
    if (ch.eof) {current = EOF; return}
    current = null
    skipSpaces
    if (current != null) return
    if (ch.eof) {current = EOF; return}
    if (isLetter) {getIdent; return}
    if (isDigit) {getInt; return}
    ch.current match {
      case '(' => {current = OPAREN; ch.next; return}
      case ')' => {current = CPAREN; ch.next; return}
      case '{' => {current = OBRACE; ch.next; return}
      case '}' => {current = CBRACE; ch.next; return}
      case ';' => {current = SEMICOL; ch.next; return}
      case '+' => {current = PLUS; ch.next; return}
      case '-' => {current = MINUS; ch.next; return}
      case '/' => {current = DIV; ch.next; return}
      case '*' => {current = MUL; ch.next; return}
      case '=' => {
	ch.next
	if (ch.current=='=') {ch.next; current = CompareEQ; return} 
	else {current = AssignEQ; return}
      }
      case '<' => {
	ch.next
	if (ch.current=='=') {ch.next; current = LEQ; return} 
	else {current = LESS; return}
      }
      case _ => {
	current = new Unknown(ch.current);
	ch.next
      }
    }
  }

  next // initialize lexer to first token
}