package InterpreterAlpha; import java.util.ArrayList; // a node on the Abstract Syntax Tree class AST { } class BinOp extends AST { AST left; AST right; Token op; BinOp(AST left, Token op, AST right) { this.left = left; this.op = op; this.right = right; } } class UnaryOp extends AST { Token op; AST expr; UnaryOp(Token op, AST expr) { this.op = op; this.expr = expr; } } class Num extends AST { Token token; String value; Num(Token token) { this.token = token; this.value = token.value; } } class Compound extends AST { ArrayList children; Compound() { this.children = new ArrayList(); } } class Assign extends AST { Var left; Token op; AST right; Assign(Var left, Token op, AST right) { this.left = left; this.op = op; this.right = right; } } class Var extends AST { Token token; String value; // variable's name Var(Token token) { this.token = token; this.value = token.value; } } class ForStatement extends AST { ArrayList statements; Var variable; ForStatement(ArrayList statements, Var variable) { this.statements = statements; this.variable = variable; } } // empty operator class NoOp extends AST { } class Program extends AST { String name; Block block; Program(String name, Block block) { this.name = name; this.block = block; } } class Block extends AST { ArrayList declaration; Compound compound_statement; Block(ArrayList declaration, Compound compound_statement) { this.declaration = declaration; this.compound_statement = compound_statement; } } class VarDecl extends AST { Var var_node; Type type_node; VarDecl(Var var_node, Type type_node) { this.var_node = var_node; this.type_node = type_node; } } // an integer or a real number class Type extends AST { Token token; String value; Type(Token token) { this.token = token; this.value = token.value; } } class Parser { Lexer lexer; Token current_token; Parser(Lexer lexer) throws Exception { this.lexer = lexer; this.current_token = this.lexer.get_next_token(); } void error() throws Exception { throw new Exception("Invalid syntax!"); } void eat(TokenType token_type) throws Exception { if (this.current_token.type == token_type) { this.current_token = this.lexer.get_next_token(); } else { this.error(); } } // program: PROGRAM variable SEMI block DOT Program program() throws Exception { this.eat(TokenType.PROGRAM); Var var_node = this.variable(); String program_name = var_node.value; this.eat(TokenType.SEMI); Block block_node = this.block(); this.eat(TokenType.DOT); return new Program(program_name, block_node); } // block: declarations compound_statement Block block() throws Exception { ArrayList declaration_nodes = this.declarations(); Compound compound_statement_node = this.compound_statement(); return new Block(declaration_nodes, compound_statement_node); } // declarations: VAR (variable_declaration SEMI)* // | empty ArrayList declarations() throws Exception { ArrayList declarations = new ArrayList(); if (this.current_token.type == TokenType.VAR) { this.eat(TokenType.VAR); while (this.current_token.type == TokenType.ID) { ArrayList var_decl = this.variable_declaration(); declarations.addAll(var_decl); this.eat(TokenType.SEMI); } } return declarations; } // variable_declaration: ID (COMMA ID)* COLON type_spec ArrayList variable_declaration() throws Exception { ArrayList var_nodes = new ArrayList(); var_nodes.add(new Var(this.current_token)); this.eat(TokenType.ID); while (this.current_token.type == TokenType.COMMA) { this.eat(TokenType.COMMA); var_nodes.add(new Var(this.current_token)); this.eat(TokenType.ID); } this.eat(TokenType.COLON); Type type_node = this.type_spec(); ArrayList var_declarations = new ArrayList(); for (Var var_node : var_nodes) var_declarations.add(new VarDecl(var_node, type_node)); return var_declarations; } // type_spec: INTEGER | REAL Type type_spec() throws Exception { Token token = this.current_token; if (this.current_token.type == TokenType.INTEGER) this.eat(TokenType.INTEGER); else this.eat(TokenType.REAL); return new Type(token); } // compound_statement: BEGIN statement_list END Compound compound_statement() throws Exception { this.eat(TokenType.BEGIN); ArrayList nodes = this.statement_list(); this.eat(TokenType.END); Compound root = new Compound(); root.children.addAll(nodes); return root; } // statement_list: statement // | statement SEMI statement_list ArrayList statement_list() throws Exception { AST node = this.statement(); ArrayList results = new ArrayList(); results.add(node); while (this.current_token.type == TokenType.SEMI) { this.eat(TokenType.SEMI); results.add(this.statement()); } if (this.current_token.type == TokenType.ID) this.error(); return results; } // statement: compound_statement // | for_statement // | assign_statement // | empty AST statement() throws Exception { AST node; if (this.current_token.type == TokenType.BEGIN) node = this.compound_statement(); else if (this.current_token.type == TokenType.ID) node = this.assign_statement(); else if (this.current_token.type == TokenType.LOOP) node = this.for_statement(); else node = this.empty(); return node; } // for_statement: LOOP ID DO statement_list END ForStatement for_statement() throws Exception { this.eat(TokenType.LOOP); Var variable = this.variable(); this.eat(TokenType.DO); ArrayList statements = this.statement_list(); this.eat(TokenType.END); return new ForStatement(statements, variable); } // assign_statement: variable ASSIGN expr Assign assign_statement() throws Exception { Var left = this.variable(); Token token = this.current_token; this.eat(TokenType.ASSIGN); AST right = this.expr(); return new Assign(left, token, right); } // variable: ID Var variable() throws Exception { Var node = new Var(this.current_token); this.eat(TokenType.ID); return node; } NoOp empty() { return new NoOp(); } // factor: PLUS factor // | MINUS factor // | INTEGER_CONST // | REAL_CONST // | LPAREN expr RPAREN // | variable AST factor() throws Exception { Token token = this.current_token; switch (token.type) { case PLUS: this.eat(TokenType.PLUS); return new UnaryOp(token, this.factor()); case MINUS: this.eat(TokenType.MINUS); return new UnaryOp(token, this.factor()); case INTEGER_CONST: this.eat(TokenType.INTEGER_CONST); return new Num(token); case REAL_CONST: this.eat(TokenType.REAL_CONST); return new Num(token); case LPAREN: this.eat(TokenType.LPAREN); AST node = this.expr(); this.eat(TokenType.RPAREN); return node; default: return this.variable(); } } // term: factor ((MUL | DIV) factor)* AST term() throws Exception { AST node = this.factor(); while (this.current_token.type == TokenType.FLOAT_DIV || this.current_token.type == TokenType.INTEGER_DIV || this.current_token.type == TokenType.MUL) { Token token = this.current_token; if (token.type == TokenType.MUL) { this.eat(TokenType.MUL); } else if (token.type == TokenType.INTEGER_DIV) this.eat(TokenType.INTEGER_DIV); else this.eat(TokenType.FLOAT_DIV); node = new BinOp(node, token, this.factor()); } return node; } // expr: term ((PLUS | MINUS) term)* AST expr() throws Exception { AST node = this.term(); while (this.current_token.type == TokenType.MINUS || this.current_token.type == TokenType.PLUS) { Token token = this.current_token; if (token.type == TokenType.MINUS) { this.eat(TokenType.MINUS); } else this.eat(TokenType.PLUS); node = new BinOp(node, token, this.term()); } return node; } AST parse() throws Exception { AST node = this.program(); if (this.current_token.type != TokenType.EOF) this.error(); return node; } }