/// <summary> /// Sets the current node's value and it's left child /// </summary> public void Insert(String value, Node leftNode) { CurrentNode.Value = value; CurrentNode.Left = new Node(); CurrentNode.Right = new Node(); CurrentNode.Left = leftNode; CurrentNode = CurrentNode.Right; }
/// <summary> /// Returns a cloned node with all the same properties /// </summary> public Node Clone() { Node clone = new Node(); clone.Left = Left; clone.Right = Right; clone.Value = Value; clone.Attributes = Attributes; return clone; }
/// <summary> /// Given a node, deletes all the children /// </summary> public static void DeleteTree(Node Root) { if (Root.Left != null) DeleteTree(Root.Left); if (Root.Right != null) DeleteTree(Root.Right); Root = null; }
/// <summary> /// Outputs the parse tree in a readable format /// </summary> public static void DisplayTree(Node node, String inner) { if (node == null) return; Console.Write(inner + ">" + node.Value); String Attrib = ""; foreach (String Attribute in node.Attributes) if (Attribute != "") Attrib += " " + Attribute; if (Attrib != "" && node.Value != "STATEMENTS") Console.Write(" (Attributes: " + Attrib + ")"); Console.Write("\n"); DisplayTree(node.Left, inner + "-"); DisplayTree(node.Right, inner + "-"); }
/// <summary> /// Traverses the tree in post-order (bottom-up) and optimises any conditions /// </summary> private void OptimiseConditionalsOfNode(Node currentNode) { if (currentNode.Left != null) { OptimiseConditionalsOfNode(currentNode.Left); } if (currentNode.Right != null) { OptimiseConditionalsOfNode(currentNode.Right); } if (Parser.IsConditionalOperator(currentNode.Attributes[0])) { //If left node and right node are both values Int32 leftValue; Int32 rightValue; if (currentNode.Left != null && Int32.TryParse(currentNode.Left.Value, out leftValue) && currentNode.Right != null && Int32.TryParse(currentNode.Right.Value, out rightValue) && currentNode.Left.Attributes[1] == "VALUE" && currentNode.Right.Attributes[1] == "VALUE") { if (currentNode.Attributes[0] == "==") currentNode.Value = (leftValue == rightValue ? 1 : 0).ToString(); else if (currentNode.Attributes[0] == "<=") currentNode.Value = (leftValue <= rightValue ? 1 : 0).ToString(); else if (currentNode.Attributes[0] == ">=") currentNode.Value = (leftValue >= rightValue ? 1 : 0).ToString(); else currentNode.Value = (leftValue != rightValue ? 1 : 0).ToString(); currentNode.Attributes[1] = "VALUE"; currentNode.Attributes[0] = currentNode.Attributes[2] = ""; currentNode.Left = null; currentNode.Right = null; } } }
/// <summary> /// Parse a new statement /// </summary> private Node ParseStatement(Node node, ref Node currentNodeMarker) { //Call appropriate parse function according to first Token in the statement if (VariableTypes.Contains(GetCurrentToken().Value)) node = DeclareVariable(node); else if (GetCurrentToken().Value.Equals("INPUT")) node = ParseInputFunction(node); else if (GetCurrentToken().Value.Equals("PRINT")) node = ParsePrintFunction(node); else if (GetCurrentToken().Type.Equals(TokenType.Identifier) && Tokens[TokenIndex+1].Value.Equals("=")) node = AssignExpression(node); else if (GetCurrentToken().Value.Equals("IF")) node = ParseIfStatement(node, ref currentNodeMarker); else { Console.WriteLine("INVALID STATEMENT on line " + GetCurrentToken().Line + ": " + GetCurrentToken().Value); node = null; } return node; }
/// <summary> /// <PRINT> ::= 'PRINT', ( <IDENTIFIER> | <CONSTANT> ); /// </summary> private Node ParsePrintFunction(Node node) { node = new Node(); node.Value = "PRINT"; TokenIndex++; CheckIfOutOfTokens(); node.Attributes[0] = GetCurrentToken().Value; //Semantic part //Check for validity - argument should be variable or constant if (!GetCurrentToken().Type.Equals(TokenType.Identifier) && !GetCurrentToken().Type.Equals(TokenType.String) && !GetCurrentToken().Type.Equals(TokenType.Number)) Console.Write("Invalid argument for PRINT statement"); //And in case of variable, it must exist if (GetCurrentToken().Type.Equals(TokenType.Identifier) && !VariableExists(GetCurrentToken().Value)) Console.Write("Undeclared variable name used as argument in PRINT"); //Store some attributes to aid in code gen if (GetCurrentToken().Type.Equals(TokenType.Number) || GetVariableType(GetCurrentToken().Value).Equals(VariableType.Integer)) node.Attributes[1] = "INT"; else node.Attributes[1] = "STRING"; if (GetCurrentToken().Type.Equals(TokenType.Identifier)) node.Attributes[2] = "VARIABLE"; else node.Attributes[2] = "VALUE"; TokenIndex++; return node; }
/// <summary> /// <INPUT> ::= 'INPUT', <IDENTIFIER> /// </summary> private Node ParseInputFunction(Node node) { node = new Node(); node.Value = "INPUT"; TokenIndex++; CheckIfOutOfTokens(); node.Attributes[0] = GetCurrentToken().Value; //Semantic part if (!GetCurrentToken().Type.Equals(TokenType.Identifier)) Console.WriteLine("Invalid argument for input statement"); if (!VariableExists(GetCurrentToken().Value)) Console.WriteLine("Undeclared variable name used as argument in INPUT"); if (GetVariableType(GetCurrentToken().Value).Equals(VariableType.Integer)) node.Attributes[1] = "INT"; else node.Attributes[1] = "STRING"; TokenIndex++; return node; }
/// <summary> /// <IFSTATEMENT> := 'IF (' <CONDITION> ')' <STATEMENTS> 'END' /// If statements within if statements do not work /// </summary> private Node ParseIfStatement(Node node, ref Node currentNodeMarker) { node = new Node(); node.Value = "IF"; TokenIndex++; //Semantics if (!GetCurrentToken().Value.Equals("(")) Console.Write("Enclose the condition in (...)"); TokenIndex++; //Parse the condition in the left child of the IF node node.Left = ParseCondition(node.Left); //Semantics if (!GetCurrentToken().Value.Equals(")")) Console.Write("Enclose the condition in (...)"); //Skip the right bracket and new line TokenIndex++; TokenIndex++; //Create a root class node for the top of the IF tree Root root = new Root(); //Insert the IF node in the root root.Insert(node.Value, node.Left); //Mark the current node and set it currentNodeMarker = node.Right; Node currentNode = node.Right; //Parse statements until END is found while (!GetCurrentToken().Value.Equals("END")) { if (GetCurrentToken().Type.Equals(TokenType.EOL)) { TokenIndex++; } //Insert the statements in currentNode = ParseStatement(currentNode, ref currentNodeMarker); if (currentNode != null) { root.Insert("STATEMENTS", currentNode); currentNode = currentNode.Right; currentNodeMarker = currentNode; } TokenIndex++; } //The final right node will always be empty, so remove it root.NullifyEmptyChildren(); return (Node)root; }
/// <summary> /// Parse a facor in an expression /// EBNF: <factor> ::= <IDENTIFIER> | <CONSTANT> | ( ‘(’, <expression>, ‘)’ ) | ( '-', <factor> ); /// We need to have the node where the factor is to be held /// A the type so we know whether we are expecting an int or a string /// </summary> private Node ParseFactor(Node node, VariableType type) { //Check if we have any tokens left CheckIfOutOfTokens(); //First check for a parenthesised expression //( ‘(’, <expression>, ‘)’ ) if (GetCurrentToken().Value.Equals("(")) { //Skip the parenthesis TokenIndex++; //Get the expression node = ParseExpression(node, type); if (!GetCurrentToken().Value.Equals(")")) Console.WriteLine("Expected closing bracket"); //Skip the closing paren TokenIndex++; return node; } //If it's not a parenthesised expression //Check for a negative number //( '-', <factor> ) if (type.Equals(VariableType.Integer)) { if (GetCurrentToken().Value.Equals("-")) { TokenIndex++; node = ParseFactor(node, type); //Apply the negative sign to the parsed factor node.Value = "-" + node.Value; return node; } } //We have narrowed it down to either a <IDENTIFIER> or <CONSTANT> node = new Node(); //Store the value in the node node.Value = GetCurrentToken().Value; //Semantics - check if variable exists if (GetCurrentToken().Type.Equals(TokenType.Identifier) && !VariableExists(GetCurrentToken().Value)) Console.WriteLine("Undeclared variable"); //Give the node extra info to aid in code gen if (GetCurrentToken().Type.Equals(TokenType.Identifier)) node.Attributes[0] = "VARIABLE"; else node.Attributes[1] = "VALUE"; TokenIndex++; return node; }
/// <summary> /// To declare a variable /// In EBNF: /// <DECLARE> ::= ( 'INT' | 'STRING' | 'BOOL'), <IDENTIFIER>; /// </summary> private Node DeclareVariable(Node node) { node = new Node(); node.Value = "DECLARE"; node.Attributes[1] = GetCurrentToken().Value; TokenIndex++; CheckIfOutOfTokens(); node.Attributes[0] = GetCurrentToken().Value; //Semantics - check variable name is valid if (!GetCurrentToken().Type.Equals(TokenType.Identifier) || IsNewVariableNameInvalid(GetCurrentToken().Value)) Console.WriteLine("Invalid variable name used in declaration"); //Add the variable to the list for future reference var varInfo = new VariableInfo(); varInfo.Name = GetCurrentToken().Value; varInfo.Type = GetVariableFromValue(node.Attributes[1]); Variables.Add(varInfo); TokenIndex++; return node; }
/// <summary> /// To assign expression to a varibale /// In EBNF: /// <ASSIGN> ::= <IDENTIFIER>, '=', <EXPRESSION>; /// </summary> private Node AssignExpression(Node node) { node.Value = "ASSIGN"; node.Left = new Node(); node.Left.Value = GetCurrentToken().Value; //Skip through '=' symbol TokenIndex += 2; node.Right = ParseExpression(node.Right, GetVariableType(node.Left.Value)); //Semantics - checks variable exists if (!VariableExists(node.Left.Value)) Console.WriteLine("Undeclared var name used in assignment"); //Additional node information to aid code gen if (GetVariableType(node.Left.Value).Equals(VariableType.Integer)) node.Attributes[0] = "INT"; else if (GetVariableType(node.Left.Value).Equals(VariableType.Boolean)) node.Attributes[0] = "BOOL"; else node.Attributes[0] = "STRING"; return node; }
/// <summary> /// Inserts the final node in the parse tree /// </summary> public void InsertFinal(Node node) { CurrentNode.Value = "STATEMENTS"; CurrentNode.Left = node; }
public Root() { CurrentNode = this; }
/// <summary> /// Recursively searches through the child nodes /// </summary> public void NullifyEmptyChildren() { if (Left != null) { Left.NullifyEmptyChildren(); if (Left.IsEmpty()) Left = null; } if (Right != null) { Right.NullifyEmptyChildren(); if (Right.IsEmpty()) Right = null; } }
/// <summary> /// To parse a term in the epression /// In EBNF: <term> ::= <factor>, { ‘*’|’/’, <factor> }; /// </summary> private Node ParseTerm(Node node) { CheckIfOutOfTokens(); //Parse the first factor node = ParseFactor(node, VariableType.Integer); //Then the {...} //For each * or / parse another factor while (GetCurrentToken().Value.Equals("*") || GetCurrentToken().Value.Equals("/")) { Node clonedNode = node.Clone(); node.Left = clonedNode; node.Value = GetCurrentToken().Value; TokenIndex++; node.Right = ParseFactor(node.Right, VariableType.Integer); } return node; }
/// <summary> /// Parses all the tokens /// </summary> public void ParseProgram() { //Last Token must be EOL so add one in case Token eol = new Token(); eol.Line = Tokens[Tokens.Count - 1].Line + 1; eol.Type = TokenType.EOL; eol.Value = "\n"; Tokens.Add(eol); //Start at the first Token TokenIndex = 0; for (int line = 0; line < LineCount && GetCurrentToken() != null; line++) { Node currentNode = new Node(); Node currentNodeMarker; if (!GetCurrentToken().Type.Equals(TokenType.EOL)) { currentNodeMarker = null; if (line == LineCount - 1) { currentNode = ParseStatement(currentNode, ref currentNodeMarker); RootNode.InsertFinal(currentNode); if (currentNodeMarker != null) RootNode.CurrentNode = currentNodeMarker; } else { RootNode.Insert("STATEMENTS", ParseStatement(currentNode, ref currentNodeMarker)); if (currentNodeMarker != null) RootNode.CurrentNode = currentNodeMarker; } } TokenIndex++; } RootNode.NullifyEmptyChildren(); }
/// <summary> /// Parses a condition /// EBNF: <condition> ::= <expression> | <expression> '==' <expression> /// </summary> private Node ParseCondition(Node node) { node = new Node(); node.Value = "CONDITION"; //LHS of condition node.Left = ParseExpression(node, VariableType.Integer); //Check for anything that isn't a variable or value //If we don't find either, the condition has been parsed if (!GetCurrentToken().Type.Equals(TokenType.Identifier) && !GetCurrentToken().Type.Equals(TokenType.Number) && !IsConditionalOperator(GetCurrentToken().Value)) return node; //Get the equality comparators node.Attributes[0] = GetCurrentToken().Value; TokenIndex++; //Get the RHS of the condition node.Right = ParseExpression(node, VariableType.Integer); return node; }
/// <summary> /// Traverses the tree in post-order (bottom-up) and optimises any arithmetic /// </summary> private void OptimiseOperatorsAndValuesOfNode(Node currentNode) { if (currentNode.Left != null) { OptimiseOperatorsAndValuesOfNode(currentNode.Left); } if (currentNode.Right != null) { OptimiseOperatorsAndValuesOfNode(currentNode.Right); } if ("+-*/".Contains(currentNode.Value)) { //If left node and right node are both values Int32 leftValue; Int32 rightValue; if (currentNode.Left != null && Int32.TryParse(currentNode.Left.Value, out leftValue) && currentNode.Right != null && Int32.TryParse(currentNode.Right.Value, out rightValue) && currentNode.Left.Attributes[1] == "VALUE" && currentNode.Right.Attributes[1] == "VALUE") { if (currentNode.Value == "+") currentNode.Value = (leftValue + rightValue).ToString(); else if (currentNode.Value == "-") currentNode.Value = (leftValue - rightValue).ToString(); else if (currentNode.Value == "*") currentNode.Value = (leftValue * rightValue).ToString(); else currentNode.Value = (leftValue / rightValue).ToString(); currentNode.Attributes[1] = "VALUE"; currentNode.Attributes[0] = currentNode.Attributes[2] = ""; currentNode.Left = null; currentNode.Right = null; } } }
/// <summary> /// To parse the expression /// In EBNF: <expression> ::= <term>, { ‘+’|’-’, <term> }; /// </summary> private Node ParseExpression(Node node, VariableType type) { CheckIfOutOfTokens(); //A numeric expr can be made up of terms, but a string expr is made up of string factors only //(we can't multilply, divide or subtract strings) if (type.Equals(VariableType.Integer)) node = ParseTerm(node); else if (type.Equals(VariableType.Boolean)) node = ParseCondition(node); else node = ParseFactor(node, VariableType.String); //While a valid operator is found while (GetCurrentToken().Value.Equals("+") || (type.Equals(VariableType.Integer) && GetCurrentToken().Value.Equals("-"))) { if (node == null) node = new Node(); Node clonedNode = node.Clone(); node.Left = clonedNode; node.Value = GetCurrentToken().Value; TokenIndex++; if (type.Equals(VariableType.Integer)) node.Right = ParseTerm(node.Right); else node.Right = ParseFactor(node.Right, VariableType.String); } return node; }