// Parser.java // Parser is a class to represent a recursive descent parser for the PL/0 // programming language, described in Algorithms + Data Structures = Programs // by Niklaus Wirth, Prentice-Hall, 1976. The parser also constructs a symbol // table as it processes declarations and a syntax tree representation of // all executable constructions. At the end of each block, these are output. public class Parser { protected PL0Lexer lexer; // lexical analyzer protected Token token; // current token public Parser () throws java.io.IOException { lexer = new PL0Lexer (System . in); getToken (); } private void getToken () throws java.io.IOException { token = lexer . nextToken (); } // program parses PL/0 programs. public SyntaxTree program (SymbolTable env) throws java.io.IOException { SyntaxTree programTree = block (env); if (token . symbol () != TokenClass . PERIOD) ErrorMessage . print (lexer . position (), ". expected"); return programTree; } // block parses blocks. Both a local symbol table and syntax tree will be // created for the block. public SyntaxTree block (SymbolTable env) throws java.io.IOException { if (token . symbol () == TokenClass . CONST) { getToken (); constDeclaration (env); while (token . symbol () == TokenClass . COMMA) { getToken (); constDeclaration (env); } if (token . symbol () == TokenClass . SEMICOLON) getToken (); else ErrorMessage . print (lexer . position (), ", or ; expected"); } if (token . symbol () == TokenClass . VAR) { getToken (); varDeclaration (env); while (token . symbol () == TokenClass . COMMA) { getToken (); varDeclaration (env); } if (token . symbol () == TokenClass . SEMICOLON) getToken (); else ErrorMessage . print (lexer . position (), ", or ; expected"); } SymbolTable procEnv = new SymbolTable (); while (token . symbol () == TokenClass . PROC) { getToken (); String procId = null; if (token . symbol () == TokenClass . ID) { procId = token . lexeme (); getToken (); } else ErrorMessage . print (lexer . position (), "Id expected"); if (token . symbol () == TokenClass . SEMICOLON) getToken (); else ErrorMessage . print (lexer . position (), "; expected"); SymbolTable blockEnv = new SymbolTable (); SyntaxTree blockTree = block (blockEnv); if (token . symbol () == TokenClass . SEMICOLON) getToken (); else ErrorMessage . print (lexer . position (), "; expected"); env . enterProc (procId, blockEnv, blockTree); } SyntaxTree statementTree = statement (env); return statementTree; } // constDeclaration parses constant declarations, entering the identifiers // into the symbol table as constants. public void constDeclaration (SymbolTable env) throws java.io.IOException { String id; if (token . symbol () == TokenClass . ID) { id = token . lexeme (); getToken (); if (token . symbol () == TokenClass . EQ) { getToken (); if (token . symbol () == TokenClass . INTEGER) { env . enterConst (id, Integer . parseInt (token . lexeme ())); getToken (); } else ErrorMessage . print (lexer . position (), "Integer expected"); } else ErrorMessage . print (lexer . position (), "= expected"); } else ErrorMessage . print (lexer . position (), "Id expected"); } // varDeclaration parses variable declarations, entering the identifiers // into the symbol table as variables. public void varDeclaration (SymbolTable env) throws java.io.IOException { if (token . symbol () == TokenClass . ID) { env . enterVar (token . lexeme ()); getToken (); } else ErrorMessage . print (lexer . position (), "Id expected"); } // statement parses and constructs a syntax tree for statements. public SyntaxTree statement (SymbolTable env) throws java.io.IOException { SyntaxTree conditionTree, expressionTree, idTree; SyntaxTree statementTree, statement2Tree; switch (token . symbol ()) { case ID : idTree = new SyntaxTree ("id", new SyntaxTree (token . lexeme ())); getToken (); if (token . symbol () == TokenClass . ASSIGN) getToken (); else ErrorMessage . print (lexer . position (), ":= expected"); expressionTree = expression (env); statementTree = new SyntaxTree (":=", idTree, expressionTree); break; case CALL : getToken (); if (token . symbol () != TokenClass . ID) ErrorMessage . print (lexer . position (), "Id expected"); else { idTree = new SyntaxTree (token . lexeme ()); statementTree = new SyntaxTree ("call", idTree); getToken (); break; } case IF : getToken (); conditionTree = condition (env); if (token . symbol () == TokenClass . THEN) getToken (); else ErrorMessage . print (lexer . position (), "then expected"); statementTree = statement (env); statementTree = new SyntaxTree ("if-then", conditionTree, statementTree); break; case BEGIN : getToken (); statementTree = statement (env); while (token . symbol () == TokenClass . SEMICOLON) { getToken (); statement2Tree = statement (env); statementTree = new SyntaxTree (";", statementTree, statement2Tree); } if (token . symbol () == TokenClass . END) getToken (); else ErrorMessage . print (lexer . position (), "end or ; expected"); break; case WHILE : getToken (); conditionTree = condition (env); if (token . symbol () == TokenClass . DO) getToken (); else ErrorMessage . print (lexer . position (), "do expected"); statementTree = statement (env); statementTree = new SyntaxTree ("while-do", conditionTree, statementTree); break; default : statementTree = new SyntaxTree ("nil"); } return statementTree; } // condition parses and constructs a syntax tree for conditions. public SyntaxTree condition (SymbolTable env) throws java.io.IOException { String op = null; SyntaxTree conditionTree, expression1Tree, expression2Tree; if (token . symbol () == TokenClass . ODD) { getToken (); expression1Tree = expression (env); conditionTree = new SyntaxTree ("odd", expression1Tree); } else { expression1Tree = expression (env); switch (token . symbol ()) { case EQ : op = "="; break; case LT : op = "<"; break; case GT : op = ">"; break; case NE : op = "<>"; break; case LE : op = "<="; break; case GE : op = ">="; break; default : ErrorMessage . print (lexer . position (), "Relational operator expected"); } getToken (); expression2Tree = expression (env); conditionTree = new SyntaxTree (op, expression1Tree, expression2Tree); } return conditionTree; } // expression parses and constructs a syntax tree for expressions. public SyntaxTree expression (SymbolTable env) throws java.io.IOException { String op; SyntaxTree expressionTree, termTree; if (token . symbol () == TokenClass . PLUS || token . symbol () == TokenClass . MINUS) { if (token . symbol () == TokenClass . PLUS) op = "+"; else op = "-"; getToken (); termTree = term (env); expressionTree = new SyntaxTree (op, termTree); } else expressionTree = term (env); while (token . symbol () == TokenClass . PLUS || token . symbol () == TokenClass . MINUS) { if (token . symbol () == TokenClass . PLUS) op = "+"; else op = "-"; getToken (); termTree = term (env); expressionTree = new SyntaxTree (op, expressionTree, termTree); } return expressionTree; } // term parses and constructs a syntax tree for terms. public SyntaxTree term (SymbolTable env) throws java.io.IOException { String op; SyntaxTree factorTree, termTree; termTree = factor (env); while (token . symbol () == TokenClass . TIMES || token . symbol () == TokenClass . SLASH) { if (token . symbol () == TokenClass . TIMES) op = "*"; else op = "/"; getToken (); factorTree = factor (env); termTree = new SyntaxTree (op, termTree, factorTree); } return termTree; } // factor parses and constructs a syntax tree for factors. public SyntaxTree factor (SymbolTable env) throws java.io.IOException { SyntaxTree factorTree = null, idTree, intTree; if (token . symbol () == TokenClass . ID) { factorTree = new SyntaxTree ("id", new SyntaxTree (token . lexeme ())); getToken (); } else if (token . symbol () == TokenClass . INTEGER) { factorTree = new SyntaxTree ("int", new SyntaxTree (token . lexeme ())); getToken (); } else if (token . symbol () == TokenClass . LPAREN) { getToken (); factorTree = expression (env); if (token . symbol () == TokenClass . RPAREN) getToken (); else ErrorMessage . print (lexer . position (), "Missing )"); } else ErrorMessage . print (lexer . position (), "Unrecognizable symbol"); return factorTree; } }