package Compiler; import java.util.ArrayList; import java.util.List; import Compiler.Statement.BlockStatement; public class Parser { private final List tokens; private int currentToken = 0; Parser(List tokens){ this.tokens=tokens; } List parse(){ List statements = new ArrayList<>(); try{ while (!checkEOF()){ statements.add(statement()); } }catch (Error e){ return null; } return statements; } private Statement statement(){ if(checkToken(TokenType.INT)||checkToken(TokenType.REAL)||checkToken(TokenType.STRING)){ return declaration(); } else if (matchAndAdvance(TokenType.PRINT)){ return printStatement(); }else if (matchAndAdvance(TokenType.IF)){ return ifStatement(); }else if (matchAndAdvance(TokenType.DO)){ return doStatement(); } return expressionStatement(); } //Clean up and reduce code mess private Statement declaration(){ if (matchAndAdvance(TokenType.INT)){ matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); return new Statement.VariableDeclaration(varName,"int"); } else if (matchAndAdvance(TokenType.REAL)){ matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); return new Statement.VariableDeclaration(varName,"real"); //Could be improved significatly when verifiying length is a positive integer } else if (matchAndAdvance(TokenType.STRING)){ matchOrError(TokenType.LEFT_PAREN, "Length of string must be defined"); matchOrError(TokenType.LEN, "Length of string must be defined"); matchOrError(TokenType.EQUALS, "Length of string must be defined"); Expression length = expression(); if(!(length instanceof Expression.Literal)){ throw error(getCurrentToken(),"String length must be a number"); } if(!((Expression.Literal)length).type.equals("int")){ throw error(getCurrentToken(),"String length must be a integer"); } if((int)((Expression.Literal)length).value.value<1){ throw error(getCurrentToken(),"String length must be greater then 0"); } matchOrError(TokenType.RIGHT_PAREN, "Length of string must be defined"); matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); return new Statement.StringDeclaration(varName,length); } return null; } private BlockStatement blockStatement(){ List statements = new ArrayList<>(); while(!matchAndAdvance(TokenType.END)&&!checkToken(TokenType.ELSE)){ if(checkEOF()){ throw error(getCurrentToken(),"end missing from block"); } statements.add(statement()); } return new Statement.BlockStatement(statements); } private Statement printStatement(){ matchOrError(TokenType.STAR, "Syntax, print *, item1, item2..."); List exprList = new ArrayList<>(); while(matchAndAdvance(TokenType.COMMA)){ if(checkEOF()){ throw error(getCurrentToken(),"reached end of file"); } Expression expr = expression(); exprList.add(expr); } return new Statement.PrintStatement(exprList); } //Could be cleaned up to handle else statements better private Statement ifStatement(){ Expression condition = expression(); if(matchOrError(TokenType.THEN, "then expected after if statement")){ Statement.BlockStatement ifBlock = blockStatement(); Statement.BlockStatement elseBlock=null; if(matchAndAdvance(TokenType.ELSE)){ elseBlock=blockStatement(); } matchOrError(TokenType.IF, "If statements end with if"); Statement ifstatement = new Statement.IfStatement(condition, ifBlock,elseBlock); return ifstatement; } return null; } private Statement doStatement(){ if(matchAndAdvance(TokenType.WHILE)){ return whileStatement(); } Expression variable =expression(); matchOrError(TokenType.EQUALS, "'=' missing"); Expression start = expression(); matchOrError(TokenType.COMMA, " use ',' between values"); Expression stop = expression(); Expression step = null; if(matchAndAdvance(TokenType.COMMA)){ step = expression(); } Statement.BlockStatement codeBlock = blockStatement(); matchOrError(TokenType.DO, "Do statements end with do"); return new Statement.DoStatement(variable, start, stop, step,codeBlock); } private Statement whileStatement(){ matchOrError(TokenType.LEFT_PAREN, " missing '(' for do statement condition"); Expression condition = expression(); matchOrError(TokenType.RIGHT_PAREN, " missing ')' for do condition"); Statement.BlockStatement codeBlock = blockStatement(); matchOrError(TokenType.DO, "Do while statements end with do"); return new Statement.DoWhileStatement(condition,codeBlock); } private Statement expressionStatement(){ Expression expression = assignment(); return new Statement.ExpressionStatement(expression); } private Expression assignment(){ Expression variable = expression(); if (matchAndAdvance(TokenType.EQUALS)){ Expression assignedvalue = expression(); if (variable instanceof Expression.Variable){ return new Expression.AssignmentExpression(((Expression.Variable)variable).name,assignedvalue); } throw error(getCurrentToken(),"Left of assignment must be a variable"); } return variable; } private Expression expression(){ Expression createdExpression = equality(); return createdExpression; } private Expression equality(){ Expression createdExpression = logical(); while (matchAndAdvance(TokenType.EQUALITY)){ Token op = getPreviousToken(); Expression right = logical(); createdExpression = new Expression.Binary(createdExpression, op, right); } return createdExpression; } private Expression logical(){ if(matchAndAdvance(TokenType.NOT)){ Token op = getPreviousToken(); Expression right = comparison(); Expression createdExpression = new Expression.Singular(op, right); return createdExpression; } Expression createdExpression = comparison(); while (matchAndAdvance(TokenType.AND)||matchAndAdvance(TokenType.OR)){ Token op = getPreviousToken(); Expression right = comparison(); createdExpression = new Expression.Binary(createdExpression, op, right); } return createdExpression; } private Expression comparison(){ Expression createdExpression = term(); while (matchAndAdvance(TokenType.GREATER)||matchAndAdvance(TokenType.LESS)||matchAndAdvance(TokenType.GREATER_EQUAL)||matchAndAdvance(TokenType.LESS_EQUAL)){ Token op = getPreviousToken(); Expression right = term(); createdExpression = new Expression.Binary(createdExpression, op, right); } return createdExpression; } private Expression term(){ Expression createdExpression = factor(); while (matchAndAdvance(TokenType.PLUS)||matchAndAdvance(TokenType.MINUS)){ Token op = getPreviousToken(); Expression right = factor(); createdExpression = new Expression.Binary(createdExpression, op, right); } return createdExpression; } private Expression factor(){ Expression createdExpression = primary(); while (matchAndAdvance(TokenType.STAR)||matchAndAdvance(TokenType.SLASH)){ Token op = getPreviousToken(); Expression right = primary(); createdExpression = new Expression.Binary(createdExpression, op, right); } return createdExpression; } private Expression primary(){ if (matchAndAdvance(TokenType.NUMBER)){ if(getPreviousToken().value instanceof Integer){ return new Expression.Literal(getPreviousToken(),"int"); } else if(getPreviousToken().value instanceof Double){ return new Expression.Literal(getPreviousToken(),"double"); } } if (matchAndAdvance(TokenType.STRING)){ return new Expression.Literal(getPreviousToken(), "string"); } if (matchAndAdvance(TokenType.IDENTIFIER)) { return new Expression.Variable(getPreviousToken()); } if (matchAndAdvance(TokenType.LEFT_PAREN)){ Expression expr = expression(); matchOrError(TokenType.RIGHT_PAREN,"Expected ')'"); return new Expression.BracketedExpression(expr); } throw error(getCurrentToken(),"Unknown syntax error"); } private void advanceToken(){ if (!checkEOF()) { currentToken++; }; } private boolean matchAndAdvance(TokenType type){ if (checkToken(type)) { advanceToken(); return true; } return false; } private boolean matchOrError(TokenType type,String errorMessage){ if (matchAndAdvance(type)){ return true; } throw error(getCurrentToken(),errorMessage); } private boolean checkToken(TokenType type){ if (checkEOF()) return false; return getCurrentToken().type == type; } private boolean checkEOF(){ return tokens.get(currentToken).type==TokenType.EOF; } private Token getCurrentToken(){ return tokens.get(currentToken); } private Token getPreviousToken(){ return tokens.get(currentToken - 1); } private Error error(Token token, String message){ Language.displayError(token ,message); return new Error(); } }