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