LARA

Recursive Descent Parser for While Language

/* A very simple version of while language:
 
program ::= statements EOF
statements = statement*
statement ::= print | assign | while | block
write ::= "println" "(" var ")" ";"
assign ::= var "=" expr ";"
while ::= "while" "(" expr ")" statement
block ::= "{" statements "}"
expr ::= expr "+" expr | expr "-" expr | expr "*" expr | 
         "(" expr ")" | var | intconst 
*/
 
class Parser(lex : Lexer) {
  // program ::= statement* EOF
  def parseProgram = {
    parseStatements
    ensure(EOF)
  }
 
  // statements = statement*
  def parseStatements = {
    while (firstTokenOfStatement) {
      parseStatement
    }
  }
 
  def firstTokenOfStatement : Boolean = {
    lex.current match {
      case PRINTLN => true
      case ID(_) => true
      case WHILE => true
      case OBRACE => true
      case _ => false
    }
  }
 
  // statement ::= print | assign | if | while | block
  def parseStatement : Unit = {
    lex.current match {
      case PRINTLN => parseWrite
      case ID(_) => parseAssign
      case WHILE => parseWhile
      case OBRACE => parseBlock
      case _ => error("statement expected")
    }
  }
 
  // write ::= "println" "(" var ")" ";"
  def parseWrite = {
    eat(PRINTLN)
    eat(OPAREN)
    parseID
    eat(CPAREN)
    eat(SEMICOL)
  }
 
  // assign ::= var "=" expr ";"
  def parseAssign = {
    parseID
    eat(AssignEQ)
    parseExpr
    eat(SEMICOL)
  }
 
  // while ::= "while" "(" expr ")" statement
  def parseWhile = {
    eat(WHILE)
    eat(OPAREN)
    parseExpr
    eat(CPAREN)
    parseStatement
  }
 
  // block ::= "{" statements "}"
  def parseBlock = {
    eat(OBRACE)
    parseStatements
    eat(CBRACE)
  }
 
  /* Original rule:
     expr ::= expr "+" expr | expr "-" expr | expr "*" expr | 
              "(" expr ")" | var | intconst 
 
     Modified rules:
       expr ::= term ("+" term)*
       term ::= factor ("*" factor)*
       factor ::= "(" expr ")" | var | intconst
  */
 
  // expr ::= term ("+" term)*
  def parseExpr : Unit = { 
    parseTerm
    while ((lex.current==PLUS)|(lex.current==MINUS)) {
      lex.next
      parseTerm
    }
  }
 
  // term ::= factor ("*" factor)*
  def parseTerm = {
    parseFactor
    while (lex.current==MUL) {
      lex.next
      parseFactor
    }
  }
 
  // factor ::= "(" expr ")" | var | intconst
  def parseFactor = {
    lex.current match {
      case OPAREN => { lex.next; parseExpr; eat(CPAREN) }
      case ID(_) => lex.next
      case IntConst(_) => lex.next
      case _ => error("(, ID, or IntConst expected")
    }
  }
 
  def error(s : String) = { println(s); exit(-1); }
  def eat(t : Token) = {
    if (lex.current==t) lex.next
    else error("Expected token " + t + " found token " + lex.current)
  }
  def ensure(t : Token) = {
    if (lex.current==t) {}
    else error("Expected token " + t + " found token " + lex.current)
  }
  def parseID = {
    lex.current match {
      case ID(_) => lex.next
      case _ => error("identifier expected")
    }
  }
 
  parseProgram // start non-terminal
}
 
object ParserTest {
  def main(args : Array[String]) = {
    val lex = new Lexer(new CharStream(args(0)))
    new Parser(lex)
  }
}