public string ConvertAst(Node headNode) { var builder = new StringBuilder(); ConvertNode(headNode, builder); return builder.ToString(); }
private void AddNodeText(Node node, StringBuilder builder, string prefix) { var prefixChar = 'a'; var colour = node.IsTerminal ? " fillcolor=\"gold\" style=\"filled\"" : ""; builder.AppendLine($" {prefix} [label=\"{node.Contents}\"{colour}]"); foreach (var child in node.Children)//.Where(child => !child.Contents.Contains("Null"))) { builder.AppendLine($" {prefix} -> {prefix}{prefixChar}"); AddNodeText(child, builder, prefix + prefixChar); prefixChar ++; } }
public string GenerateGraphString(Node headNode) { var builder = new StringBuilder("digraph G {"); builder.AppendLine(" a ;"); AddNodeText( node: headNode, builder: builder, prefix: "a"); builder.AppendLine("}"); return builder.ToString(); }
public Node AddChild(Node child) { _children.Add(child); return this; }
/// <summary> /// Adds the following grammar rules to the RDP parser passed into the function: /// /// base -> stmt /// stmt -> IF expr THEN stmt ELSE stmt /// | WHILE expr DO stmt /// | INPUT ID /// | ID ASSIGN expr /// | WRITE expr /// | BEGIN stmtlist END /// stmtlist -> stmt SEMICOLON stmtlist' /// stmtlist' -> stmt SEMICOLON stmtlist' /// | . /// expr -> term expr' /// expr' -> addop term expr' /// | . /// term -> factor term' /// term' -> mulop factor term' /// | . /// factor -> LPAR expr RPAR /// | ID /// | NUM /// | SUB NUM /// addop -> ADD | SUB /// mulop -> MUL | DIV /// </summary> /// <param name="parser">Parser that should accept the rules</param> public static void AddRdpRules(RecursiveDescentParser parser) { // base -> stmt parser.AddRule( name: "base", decider: rdp => true, evaluator: rdp => rdp.InvokeRule("stmt")); // stmt -> IF expr THEN stmt ELSE stmt parser.AddRule( // if name: "stmt", decider: rdp => rdp.TryMatch(TokenType.If), evaluator: rdp => { var node = new Node {Contents = "IF Statement", Type = NodeType.If}; node.AddChild(rdp.TryConsumeToken(TokenType.If)); node.AddChild(rdp.InvokeRule("expr")); node.AddChild(rdp.TryConsumeToken(TokenType.Then)); node.AddChild(rdp.InvokeRule("stmt")); node.AddChild(rdp.TryConsumeToken(TokenType.Else)); node.AddChild(rdp.InvokeRule("stmt")); return node; }); // stmt -> WHILE expr DO stmt parser.AddRule( // while name: "stmt", decider: rdp => rdp.TryMatch(TokenType.While), evaluator: rdp => { var node = new Node {Contents = "WHILE Statement", Type = NodeType.While}; node.AddChild(rdp.TryConsumeToken(TokenType.While)); node.AddChild(rdp.InvokeRule("expr")); node.AddChild(rdp.TryConsumeToken(TokenType.Do)); node.AddChild(rdp.InvokeRule("stmt")); return node; }); // stmt -> INPUT ID parser.AddRule( // input name: "stmt", decider: rpd => rpd.TryMatch(TokenType.Input), evaluator: rdp => { var node = new Node {Contents = "INPUT Statement", Type = NodeType.Input}; node.AddChild(rdp.TryConsumeToken(TokenType.Input)); node.AddChild(rdp.TryConsumeToken(TokenType.Id)); return node; }); // stmt -> ID ASS expr parser.AddRule( // assign name: "stmt", decider: rdp => rdp.TryMatch(TokenType.Id), evaluator: rdp => { var node = new Node {Contents = "Assign Statement", Type = NodeType.Assign}; node.AddChild(rdp.TryConsumeToken(TokenType.Id)); node.AddChild(rdp.TryConsumeToken(TokenType.Assign)); node.AddChild(rdp.InvokeRule("expr")); return node; }); // stmt -> WRITE expr parser.AddRule( // write name: "stmt", decider: rdp => rdp.TryMatch(TokenType.Write), evaluator: rdp => { var node = new Node {Contents = "WRITE Statement", Type = NodeType.Write}; node.AddChild(rdp.TryConsumeToken(TokenType.Write)); node.AddChild(rdp.InvokeRule("expr")); return node; }); // stmt -> BEGIN stmtlist END parser.AddRule( // begin name: "stmt", decider: rdp => rdp.TryMatch(TokenType.Begin), evaluator: rdp => { var node = new Node {Contents = "BEGIN Statement", Type = NodeType.Begin}; node.AddChild(rdp.TryConsumeToken(TokenType.Begin)); node.AddChild(rdp.InvokeRule("stmtlist")); node.AddChild(rdp.TryConsumeToken(TokenType.End)); return node; }); // stmtlist -> stmt SEMICOLON stmtlist' parser.AddRule( name: "stmtlist", decider: rdp => rdp.TryMatch("stmt"), evaluator: rdp => { var node = new Node {Contents = "Statement List", Type = NodeType.StatementList}; node.AddChild(rdp.InvokeRule("stmt")); node.AddChild(rdp.TryConsumeToken(TokenType.Semicolon)); node.AddChild(rdp.InvokeRule("stmtlist'")); return node; }); // Transformed rule // stmtlist' -> stmt SEMICOLON stmtlist' parser.AddRule( name: "stmtlist'", decider: rdp => rdp.TryMatch("stmt"), evaluator: rdp => { var node = new Node {Contents = "Statement List", Type = NodeType.MoreStatements}; node.AddChild(rdp.InvokeRule("stmt")); node.AddChild(rdp.TryConsumeToken(TokenType.Semicolon)); node.AddChild(rdp.InvokeRule("stmtlist'")); return node; }); // Transformed rule // stmtlist' -> . parser.AddRule( name: "stmtlist'", decider: rdp => !rdp.TryMatch("stmt"), evaluator: rdp => new Node {Contents = "Null Statement", Type = NodeType.Null}); // expr -> term expr' parser.AddRule( name: "expr", decider: rdp => rdp.TryMatch("term"), evaluator: rdp => { var node = new Node {Contents = "Expr Statement", Type = NodeType.Expression}; node.AddChild(rdp.InvokeRule("term")); node.AddChild(rdp.InvokeRule("expr'")); return node; }); // Transformed rule // expr' -> addop term expr' parser.AddRule( name: "expr'", decider: rdp => rdp.TryMatch("addop"), evaluator: rdp => { var node = new Node {Contents = "Expr' Statement", Type = NodeType.MoreExpression}; node.AddChild(rdp.InvokeRule("addop")); node.AddChild(rdp.InvokeRule("term")); node.AddChild(rdp.InvokeRule("expr'")); return node; }); // Transformed rule // expr' -> . parser.AddRule( name: "expr'", decider: rdp => !rdp.TryMatch("addop"), evaluator: rdp => new Node {Contents = "Null expr", Type = NodeType.Null}); // term -> factor term' parser.AddRule( name: "term", decider: rdp => rdp.TryMatch("factor"), evaluator: rdp => { var node = new Node {Contents = "Term statement", Type = NodeType.Term}; node.AddChild(rdp.InvokeRule("factor")); node.AddChild(rdp.InvokeRule("term'")); return node; }); // Transformed rule // term' -> mulop factor term' parser.AddRule( name: "term'", decider: rdp => rdp.TryMatch("mulop"), evaluator: rdp => { var node = new Node {Contents = "Term' statement", Type = NodeType.MoreTerms}; node.AddChild(rdp.InvokeRule("mulop")); node.AddChild(rdp.InvokeRule("factor")); node.AddChild(rdp.InvokeRule("term'")); return node; }); // Transformed rule // term' -> . parser.AddRule( name: "term'", decider: rdp => !rdp.TryMatch("mulop"), evaluator: rdp => new Node {Contents = "Null term", Type = NodeType.Null}); // mulop -> MUL parser.AddRule( name: "mulop", decider: rdp => rdp.TryMatch(TokenType.Mul), evaluator: rdp => rdp.TryConsumeToken(TokenType.Mul)); // mulop -> DIV parser.AddRule( name: "mulop", decider: rdp => rdp.TryMatch(TokenType.Div), evaluator: rdp => rdp.TryConsumeToken(TokenType.Div)); // addop -> ADD parser.AddRule( name: "addop", decider: rdp => rdp.TryMatch(TokenType.Add), evaluator: rdp => rdp.TryConsumeToken(TokenType.Add)); // addop -> SUB parser.AddRule( name: "addop", decider: rdp => rdp.TryMatch(TokenType.Sub), evaluator: rdp => rdp.TryConsumeToken(TokenType.Sub)); // factor -> LPAR expr RPAR parser.AddRule( name: "factor", decider: rdp => rdp.TryMatch(TokenType.LPar), evaluator: rdp => { var node = new Node {Contents = "Factor statement", Type = NodeType.Factor}; node.AddChild(rdp.TryConsumeToken(TokenType.LPar)); node.AddChild(rdp.InvokeRule("expr")); node.AddChild(rdp.TryConsumeToken(TokenType.RPar)); return node; }); // factor -> ID parser.AddRule( name: "factor", decider: rdp => rdp.TryMatch(TokenType.Id), evaluator: rdp => { var node = new Node {Contents = "Factor statement", Type = NodeType.Factor}; node.AddChild(rdp.TryConsumeToken(TokenType.Id)); return node; }); // factor -> NUM parser.AddRule( name: "factor", decider: rdp => rdp.TryMatch(TokenType.Num), evaluator: rdp => { var node = new Node {Contents = "Factor statement", Type = NodeType.Factor}; node.AddChild(rdp.TryConsumeToken(TokenType.Num)); return node; }); // factor -> SUB NUM parser.AddRule( name: "factor", decider: rdp => rdp.TryMatch(TokenType.Sub), evaluator: rdp => { var node = new Node {Contents = "Factor statement", Type = NodeType.Factor}; node.AddChild(rdp.TryConsumeToken(TokenType.Sub)); node.AddChild(rdp.TryConsumeToken(TokenType.Num)); return node; }); }
private void ProcessFactor(Node node, StringBuilder builder) { switch (node.Children.Count) { case 3: ConvertNode(node.Children[1], builder); break; case 2: // [-] [num] builder.AppendLine($" cPUSH -{node.Children[1].Data}"); break; default: builder.AppendLine(node.Children[0].TerminalTokenType == TokenType.Id ? $" rPUSH {node.Children[0].Data}" : $" cPUSH {node.Children[0].Data}"); break; } }
private void ConvertNode(Node node, StringBuilder builder) { switch (node.Type) { case NodeType.If: ProcessIf(node, builder); break; case NodeType.Begin: // of the form [BEGIN] [statement list] [END] ConvertNode(node.Children[1], builder); break; case NodeType.While: ProcessWhile(node, builder); break; case NodeType.Factor: ProcessFactor(node, builder); break; case NodeType.Expression: case NodeType.Term: // has the form [term] [expr'] ConvertNode(node.Children[0], builder); ConvertNode(node.Children[1], builder); break; case NodeType.StatementList: // has the form [stmt] [;] [stmtlist'] ConvertNode(node.Children[0], builder); ConvertNode(node.Children[2], builder); break; case NodeType.MoreExpression: // of the form [addop] [term] [moreexpr] ConvertNode(node.Children[2], builder); // more expr ConvertNode(node.Children[1], builder); // term builder.AppendLine( // postfix the add/sub node.Children[0].TerminalTokenType == TokenType.Add ? " OP2 +" : " OP2 -"); break; case NodeType.MoreTerms: // of the form [mulop] [factor] [moreterm] ConvertNode(node.Children[2], builder); // more expr ConvertNode(node.Children[1], builder); // term builder.AppendLine( // postfix the add/sub node.Children[0].TerminalTokenType == TokenType.Mul ? " OP2 *" : " OP2 /"); // has the form break; case NodeType.Terminal: // may not be needed break; case NodeType.Null: // nothing to add break; case NodeType.Input: // of the form [INPUT] [ID] builder.AppendLine($" READ {node.Children[1].Data}"); break; case NodeType.Assign: // has the form [ID] [:=] [Expr] ConvertNode(node.Children[2], builder); builder.AppendLine($" LOAD {node.Children[0].Data}"); break; case NodeType.Write: // of the form [WRITE] [expr] ConvertNode(node.Children[1], builder); builder.AppendLine(" PRINT"); break; case NodeType.MoreStatements: // of the form [stmt] [;] [morestmt] ConvertNode(node.Children[0], builder); ConvertNode(node.Children[2], builder); break; default: throw new ArgumentOutOfRangeException(); } }
private void ProcessWhile(Node headNode, StringBuilder builder) { // of the form [WHILE] [expr] [DO] [stmt] var headLabel = GenerateLabel(); var exitLabel = GenerateLabel(); // make labels first builder.AppendLine($"{headLabel}:"); ConvertNode(headNode.Children[1], builder); // eval expr // decision branch builder.AppendLine($" cJUMP {exitLabel}"); ConvertNode(headNode.Children[3], builder); // do statement // skip back, and add follow label builder.AppendLine($" JUMP {headLabel}"); builder.AppendLine($"{exitLabel}:"); }
private void ProcessIf(Node headNode, StringBuilder builder) { // of the form [IF] [expr] [THEN] [stmt] [ELSE] [stmt] var elseLabel = GenerateLabel(); var skipLabel = GenerateLabel(); ConvertNode(headNode.Children[1], builder); builder.AppendLine($" cJUMP {elseLabel}"); ConvertNode(headNode.Children[3], builder); builder.AppendLine($" JUMP {skipLabel}"); builder.AppendLine($"{elseLabel}:"); ConvertNode(headNode.Children[5], builder); builder.AppendLine($"{skipLabel}:"); }
/// <summary> /// Generates the graphviz representation of the AST /// </summary> /// <param name="parentNode">root node of the AST</param> private static void GenerateGraphvis(Node parentNode) { Console.WriteLine("Writing to graphviz format..."); File.WriteAllText("graphviz.dot", new GraphVizGenerator().GenerateGraphString(parentNode)); }
/// <summary> /// Generates the stack machine code defined in the assignment description /// </summary> /// <param name="parentNode">Root node of the AST</param> private static void GenerateCompiledCode(Node parentNode) { Console.WriteLine("Writing compiled code..."); File.WriteAllText("StackCode.txt", new StackMachineConverter().ConvertAst(parentNode)); }