practice/java/BasicInterpreter/src/InterpreterAlpha/Parser.java

360 lines
9.5 KiB
Java

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<AST> children;
Compound() {
this.children = new ArrayList<AST>();
}
}
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<AST> statements;
Var variable;
ForStatement(ArrayList<AST> 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<VarDecl> declaration;
Compound compound_statement;
Block(ArrayList<VarDecl> 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<VarDecl> 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<VarDecl> declarations() throws Exception {
ArrayList<VarDecl> declarations = new ArrayList<VarDecl>();
if (this.current_token.type == TokenType.VAR) {
this.eat(TokenType.VAR);
while (this.current_token.type == TokenType.ID) {
ArrayList<VarDecl> var_decl = this.variable_declaration();
declarations.addAll(var_decl);
this.eat(TokenType.SEMI);
}
}
return declarations;
}
// variable_declaration: ID (COMMA ID)* COLON type_spec
ArrayList<VarDecl> variable_declaration() throws Exception {
ArrayList<Var> var_nodes = new ArrayList<Var>();
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<VarDecl> var_declarations = new ArrayList<VarDecl>();
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<AST> 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<AST> statement_list() throws Exception {
AST node = this.statement();
ArrayList<AST> results = new ArrayList<AST>();
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<AST> 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;
}
}