/// <summary> /// Gets expression AST if operation is plus, minus, equal, less_than or logical_and /// If not, then return term /// </summary> /// <returns></returns> private AST Expression() { AST node = this.Term(); while (this.currentToken.type == TokenType.PLUS || this.currentToken.type == TokenType.MINUS || this.currentToken.type == TokenType.EQUAL || this.currentToken.type == TokenType.LESS_THAN || this.currentToken.type == TokenType.LOGICAL_AND) { Token token = this.currentToken; if (token.type == TokenType.PLUS) { this.ConsumeToken(TokenType.PLUS); } else if (token.type == TokenType.MINUS) { this.ConsumeToken(TokenType.MINUS); } else if (token.type == TokenType.EQUAL) { this.ConsumeToken(TokenType.EQUAL); } else if (token.type == TokenType.LESS_THAN) { this.ConsumeToken(TokenType.LESS_THAN); } else if (token.type == TokenType.LOGICAL_AND) { this.ConsumeToken(TokenType.LOGICAL_AND); } node = new BinaryOperationAST(node, token, this.Term()); } return(node); }
/// <summary> /// Returns a binary operation AST if operation is multiply or divide, if not, then return factor /// </summary> /// <returns>AST</returns> private AST Term() { AST node = this.Factor(); while (this.currentToken.type == TokenType.MUL || this.currentToken.type == TokenType.DIV) { Token token = this.currentToken; if (token.type == TokenType.MUL) { this.ConsumeToken(TokenType.MUL); } else if (token.type == TokenType.DIV) { this.ConsumeToken(TokenType.DIV); } node = new BinaryOperationAST(node, token, this.Factor()); } return(node); }
/// <summary> /// Checks semantic errors of a binary operation /// If the types don't match, then add error /// </summary> /// <param name="ast">AST</param> /// <returns>Result</returns> private object VisitBinaryOperationAST(BinaryOperationAST ast) { object component1 = this.VisitNode(ast.left); object component2 = this.VisitNode(ast.right); if (component1 is string && component2 is string) { string c1 = component1 as string; string c2 = component2 as string; if (ast.token.type == TokenType.PLUS) { return(c1 + c2); } else if (ast.token.type == TokenType.EQUAL) { return(c1.Equals(c2)); } else if (ast.token.type == TokenType.LESS_THAN) { return(c1.Length < c2.Length); } else { errors.Add(new UnsupportedOperationError(ast.token, "string")); } } else if (component1 is int && component2 is int) { // I have classified the division by zero as a runtime error // So in the semantic analysis it is ignored and reported at the runtime // This is to make the division of the two phases more clear try { if (ast.token.type == TokenType.PLUS) { return((int)component1 + (int)component2); } else if (ast.token.type == TokenType.MINUS) { return((int)component1 - (int)component2); } else if (ast.token.type == TokenType.MUL) { return((int)component1 * (int)component2); } else if (ast.token.type == TokenType.DIV) { return((int)component1 / (int)component2); } else if (ast.token.type == TokenType.EQUAL) { return((int)component1 == (int)component2); } else if (ast.token.type == TokenType.LESS_THAN) { return((int)component1 < (int)component2); } else { errors.Add(new UnsupportedOperationError(ast.token, "int")); } } catch (DivideByZeroException) { return(0); } catch (OverflowException) { return(0); } } else if (component1 is bool && component2 is bool) { if (ast.token.type == TokenType.EQUAL) { return((bool)component1 == (bool)component2); } else if (ast.token.type == TokenType.LOGICAL_AND) { return((bool)component1 & (bool)component2); } else if (ast.token.type == TokenType.LESS_THAN) { if ((bool)component1 == false && (bool)component2 == true) { return(true); } else { return(false); } } else { errors.Add(new UnsupportedOperationError(ast.token, "bool")); } } else if (component1 is null || component2 is null) { }
/// <summary> /// Traverses BinaryOperationAST /// </summary> /// <param name="ast"></param> /// <returns>BinaryOperationAST</returns> private object VisitBinaryOperationAST(BinaryOperationAST ast) { object component1 = this.VisitNode(ast.left); object component2 = this.VisitNode(ast.right); if (component1 is string && component2 is string) { string c1 = component1 as string; string c2 = component2 as string; if (ast.token.type == TokenType.EQUAL) { return(c1.Equals(c2)); } else if (ast.token.type == TokenType.LESS_THAN) { return(c1.Length < c2.Length); } else { return(c1 + c2); } } else if (component1 is bool && component2 is bool) { if (ast.token.type == TokenType.EQUAL) { return((bool)component1 == (bool)component2); } else if (ast.token.type == TokenType.LOGICAL_AND) { return((bool)component1 & (bool)component2); } else // less than { if ((bool)component1 == false && (bool)component2 == true) { return(true); } else { return(false); } } } else { try { if (ast.token.type == TokenType.PLUS) { return((int)component1 + (int)component2); } else if (ast.token.type == TokenType.MINUS) { return((int)component1 - (int)component2); } else if (ast.token.type == TokenType.MUL) { return((int)component1 * (int)component2); } else if (ast.token.type == TokenType.EQUAL) { return((int)component1 == (int)component2); } else if (ast.token.type == TokenType.LESS_THAN) { return((int)component1 < (int)component2); } else { return((int)component1 / (int)component2); } } catch (DivideByZeroException) { throw new RuntimeError(ast.token, "Attempted to divide by zero").Exception(); } catch (OverflowException) { throw new RuntimeError(ast.token, "Integer overflow").Exception(); } } }