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)
}
}