import java.util.List; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; class Parser { public int currentTokenPosition = 0; public List<Token> tokens; public Map symbolTable = new HashMap(); public Parser() {} public Parser(List<Token> tokens) { this.tokens = tokens; } public List<Token> getTokens() { return tokens; } // ~~~~Language Constructs Parsing Methods Start~~~~ public RootNode Program() { MatchAndEat(TokenType.SCRIPT); return new RootNode(Block(), Util.CreateInlineFunctions(this)); } public BlockNode Block() { List<Node> statements = new LinkedList<Node>(); while ( CurrentToken().type != TokenType.END) { statements.add(Statement()); } MatchAndEat(TokenType.END); return new BlockNode(statements); } public Node Assignment() { Node node = null; String name = MatchAndEat(TokenType.KEYWORD).text; MatchAndEat(TokenType.ASSIGNMENT); if (CurrentToken().type == TokenType.LEFT_BRACKET) { node = ArrayDefinition(name); } else { Node value = Expression(); node = new AssignmentNode(name, value, this); } return node; } public Node While() { Node condition, body; MatchAndEat(TokenType.WHILE); condition = Expression(); body = Block(); return new WhileNode(condition, body); } public Node If() { Node condition=null, thenPart=null, elsePart=null; MatchAndEat(TokenType.IF); condition = Expression(); thenPart = Block(); if ( CurrentToken().type == TokenType.ELSE ) { MatchAndEat(TokenType.ELSE); if ( CurrentToken().type == TokenType.IF ) elsePart = If(); else elsePart = Block(); } return new IfNode(condition, thenPart, elsePart); } public Node ArrayDefinition(String name) { List<Node> elements = new ArrayList<Node>(); MatchAndEat(TokenType.LEFT_BRACKET); if (CurrentToken().type != TokenType.RIGHT_BRACKET) { elements.add(Expression()); while (CurrentToken().type != TokenType.RIGHT_BRACKET) { MatchAndEat(TokenType.COMMA); elements.add(Expression()); } } MatchAndEat(TokenType.RIGHT_BRACKET); return new AssignmentNode(name, new ArrayNode(elements), this); } public Node ArrayUpdate() { String arrayName = MatchAndEat(TokenType.KEYWORD).text; Node array = new VariableNode(arrayName, this); MatchAndEat(TokenType.LEFT_BRACKET); Node indexExpr = Expression(); MatchAndEat(TokenType.RIGHT_BRACKET); MatchAndEat(TokenType.ASSIGNMENT); Node rightSideExpr = Expression(); return new ArrayUpdateNode(array, indexExpr, rightSideExpr); } public Node Statement() { Node node = null; TokenType type = CurrentToken().type; if (IsAssignment()) { node = Assignment(); } else if (IsArrayAccess()) { node = ArrayUpdate(); } else if (IsWhile()) { node = While(); } else if (IsIfElse()) { node = If(); } else if (IsFunctionDef()) { node = FunctionDefinition(); } else if (IsFunctionCall()) { node = FunctionCall(); } else { Util.Writeln("Unknown language construct: " + CurrentToken().text + CurrentToken().type); System.exit(0); } return node; } // ~~~~Language Constructs Parsing Methods End~~~~ // ~~~~Function Parsing and Calling Methods Start~~~~ public Node FunctionDefinition() { MatchAndEat(TokenType.DEF); String functionName = MatchAndEat(TokenType.KEYWORD).text; MatchAndEat(TokenType.LEFT_PAREN); List<Parameter> parameters = FunctionDefParameters(); MatchAndEat(TokenType.RIGHT_PAREN); Node functionBody = Block(); Function function = new Function(functionName, functionBody, parameters); Node functionVariable = new AssignmentNode(functionName, function, this); return functionVariable; } public List FunctionDefParameters() { List<Parameter> parameters = null; if (CurrentToken().type == TokenType.KEYWORD) { parameters = new ArrayList(); parameters.add(new Parameter(MatchAndEat(TokenType.KEYWORD).text)); while (CurrentToken().type == TokenType.COMMA) { MatchAndEat(TokenType.COMMA); parameters.add(new Parameter(MatchAndEat(TokenType.KEYWORD).text)); } } return parameters; } public Node FunctionCall() { String functionName = MatchAndEat(TokenType.KEYWORD).text; Node calleeFunctionName = new VariableNode(functionName, this); MatchAndEat(TokenType.LEFT_PAREN); List<Parameter> actualParameters = FunctionCallParameters(); MatchAndEat(TokenType.RIGHT_PAREN); Node functionCallNode = new FunctionCallNode(calleeFunctionName, actualParameters, this); return functionCallNode; } public List FunctionCallParameters() { List<Parameter> actualParameters = null; Node expression = Expression(); if (expression != null) { actualParameters = new ArrayList(); actualParameters.add(new Parameter(expression)); while (CurrentToken().type == TokenType.COMMA) { MatchAndEat(TokenType.COMMA); actualParameters.add(new Parameter(Expression())); } } return actualParameters; } public Object ExecuteFunction(Function function, List boundParameters) { // Save the symbolTable Map<String, Object> savedSymbolTable = new HashMap<String, Object>(symbolTable); // Get bound parameters for (int index = 0; index < boundParameters.size(); index++) { BoundParameter param = (BoundParameter) boundParameters.get(index); setVariable(param.getName(), param.getValue()); } // Eval function Object ret = function.eval(); // Restore symbolTable symbolTable = savedSymbolTable; return ret; } // ~~~~Function Parsing and Calling Methods End~~~~ //~~~~Expression Parsing Methods Start~~~~ public Node Multiply() { MatchAndEat(TokenType.MULTIPLY); return Factor(); } public Node Divide() { MatchAndEat(TokenType.DIVIDE); return Factor(); } public Node Add() { MatchAndEat(TokenType.ADD); return Term(); } public Node Subtract() { MatchAndEat(TokenType.SUBTRACT); return Term(); } public Node Factor() { Node result = null; if (CurrentToken().type == TokenType.LEFT_PAREN) { MatchAndEat(TokenType.LEFT_PAREN); result = Expression(); MatchAndEat(TokenType.RIGHT_PAREN); } else if (IsNumber()) { Token token = MatchAndEat(TokenType.NUMBER); result = new NumberNode(new Integer(token.text).intValue()); } else if (IsString()) { Token token = MatchAndEat(TokenType.STRING); result = new StringNode(new String(token.text)); } else if (IsKeyWord()) { result = Variable(); } return result; } public Node Variable() { Node node = null; if (NextToken().type == TokenType.LEFT_PAREN) { node = FunctionCall(); } else { Token token = MatchAndEat(TokenType.KEYWORD); Node varNode = new VariableNode(token.text, this); if (CurrentToken().type == TokenType.LEFT_BRACKET) { MatchAndEat(TokenType.LEFT_BRACKET); Node key = Expression(); MatchAndEat(TokenType.RIGHT_BRACKET); return new LookupNode((VariableNode) varNode, key); } else return varNode; } return node; } public Node SignedFactor() { if (CurrentToken().type == TokenType.SUBTRACT) { MatchAndEat(TokenType.SUBTRACT); Node node = new NegOpNode(Factor()); return node; } return Factor(); } public Node Term() { Node node = SignedFactor(); while (IsMulOp(CurrentToken().type)) { switch(CurrentToken().type) { case MULTIPLY: node = new BinOpNode(TokenType.MULTIPLY, node, Multiply()); break; case DIVIDE: node = new BinOpNode(TokenType.DIVIDE, node, Divide()); break; } } return node; } public Node ArithmeticExpression() { Node node = Term(); while (IsAddOp(CurrentToken().type)) { switch(CurrentToken().type) { case ADD: node = new BinOpNode(TokenType.ADD, node, Add()); break; case SUBTRACT: node = new BinOpNode(TokenType.SUBTRACT, node, Subtract()); break; } } return node; } public Node Less(Node node) { MatchAndEat(TokenType.LESS); return new BinOpNode(TokenType.LESS, node, ArithmeticExpression()); } public Node Greater(Node node) { MatchAndEat(TokenType.GREATER); return new BinOpNode(TokenType.GREATER, node, ArithmeticExpression()); } public Node Equal(Node node) { MatchAndEat(TokenType.EQUAL); return new BinOpNode(TokenType.EQUAL, node, ArithmeticExpression()); } public Node NotEqual(Node node) { MatchAndEat(TokenType.NOTEQUAL); return new BinOpNode(TokenType.NOTEQUAL, node, ArithmeticExpression()); } public Node LessEqual(Node node) { MatchAndEat(TokenType.LESSEQUAL); return new BinOpNode(TokenType.LESSEQUAL, node, ArithmeticExpression()); } public Node GreaterEqual(Node node) { MatchAndEat(TokenType.GREATEREQUAL); return new BinOpNode(TokenType.GREATEREQUAL, node, ArithmeticExpression()); } public Node Relation() { Node node = ArithmeticExpression(); if (IsRelOp(CurrentToken().type)) { switch(CurrentToken().type) { case LESS: node = Less(node); break; case GREATER: node = Greater(node); break; case EQUAL: node = Equal(node); break; case NOTEQUAL: node = NotEqual(node); break; case LESSEQUAL: node = LessEqual(node); break; case GREATEREQUAL: node = GreaterEqual(node); break; } } return node; } public Node BooleanFactor() { return Relation(); } public Node NotFactor() { if (CurrentToken().type == TokenType.NOT) { MatchAndEat(TokenType.NOT); Node node = BooleanFactor(); return new NotOpNode(node); } return BooleanFactor(); } public Node BooleanTerm() { Node node = NotFactor(); while (CurrentToken().type == TokenType.AND) { MatchAndEat(TokenType.AND); node = new BinOpNode(TokenType.AND, node, NotFactor()); } return node; } public Node BooleanExpression() { Node node = BooleanTerm(); while (IsLogicalOp(CurrentToken().type)) { switch(CurrentToken().type) { case OR: MatchAndEat(TokenType.OR); node = new BinOpNode(TokenType.OR, node, BooleanTerm()); break; } } return node; } public Node Expression() { return BooleanExpression(); } public void PrettyPrint(List<Token> tokens) { int numberCount = 0; int opCount = 0; for (Token token: tokens) { if (token.type == TokenType.NUMBER) { Util.Writeln("Number....: " + token.text); numberCount++; } else { Util.Writeln("Operator..: " + token.type); opCount++; } } Util.Writeln("You have got " + numberCount + " different number and " + opCount +" operators."); } //~~~~Expression Parsing Methods End~~~~ //~~~~Recognizer Methods Start~~~~ public boolean IsMulOp(TokenType type) { return type == TokenType.MULTIPLY || type == TokenType.DIVIDE; } public boolean IsAddOp(TokenType type) { return type == TokenType.ADD || type == TokenType.SUBTRACT; } public boolean IsMultiDigitOp(TokenType type) { return type == TokenType.LESSEQUAL || type == TokenType.GREATEREQUAL; } public boolean IsRelOp(TokenType type) { boolean lgOps = type == TokenType.LESS || type == TokenType.GREATER; boolean eqOps = type == TokenType.EQUAL || type == TokenType.NOTEQUAL; return eqOps || lgOps || IsMultiDigitOp(type); } public boolean IsLogicalOp(TokenType type) { return type == TokenType.OR || type == TokenType.AND; } public boolean IsNumber() { return CurrentToken().type == TokenType.NUMBER; } public boolean IsString() { return CurrentToken().type == TokenType.STRING; } public boolean IsKeyWord() { return CurrentToken().type == TokenType.KEYWORD; } public boolean IsAssignment() { TokenType type = CurrentToken().type; return type == TokenType.KEYWORD && NextToken().type == TokenType.ASSIGNMENT; } public boolean IsWhile() { return CurrentToken().type == TokenType.WHILE; } public boolean IsIfElse() { TokenType type = CurrentToken().type; return type == TokenType.IF || type == TokenType.ELSE; } public boolean IsArrayAccess() { TokenType type = CurrentToken().type; return type == TokenType.KEYWORD && NextToken().type == TokenType.LEFT_BRACKET; } public boolean IsFunctionDef() { TokenType type = CurrentToken().type; return type == TokenType.DEF && NextToken().type == TokenType.KEYWORD; } public boolean IsFunctionCall() { TokenType type = CurrentToken().type; return type == TokenType.KEYWORD && NextToken().type == TokenType.LEFT_PAREN; } //~~~~Recognizer Methods End~~~~ //~~~~Token Manipulation Methods Start~~~~ public Token GetToken(int offset) { if (currentTokenPosition + offset >= tokens.size()) { return new Token("", TokenType.EOF); } return tokens.get(currentTokenPosition + offset); } public Token CurrentToken() { return GetToken(0); } public Token NextToken() { return GetToken(1); } public void EatToken(int offset) { currentTokenPosition = currentTokenPosition + offset; } public Token MatchAndEat(TokenType type) { Token token = CurrentToken(); if (CurrentToken().type != type) Util.Writeln("Saw " + token.type + " but " + type + " expected"); EatToken(1); return token; } //~~~~Token Maniplation Methods End~~~~ // ~~~~Symbol Table Methods Start~~~~ public Object setVariable(String name, Object value) { symbolTable.put(name, value); return value; } public Object getVariable(String name) { Object value = (Object) symbolTable.get(name); if (value != null) return value; return null; } // ~~~~Symbol Table Methods End~~~~ }