Making MGrammar Graph Traversal Easier

Do you enjoy writing the code to navigate graphs produced by MGrammar, looking for the nodes that you need? Neither do I.

I have written a small helper class to make graph navigation just a little bit easier. The class, which I have called GraphNavigator, performs all of the navigation and calls delegates when interesting nodes are found. Here is an example usage:

   1: GraphNavigator Nav = new GraphNavigator("Grammar.mgx", "JeffFerguson.Calculatix.Grammars.Calculus");
   2: Nav.AddNodeProcessorDelegate("Line", LineNodeDelegate);
   3: Nav.AddNodeProcessorDelegate("Point", PointNodeDelegate);
   4: Nav.AddNodeProcessorDelegate("Show", ShowNodeDelegate);
   5: Nav.AddNodeProcessorDelegate("StringLiteral", StringLiteralNodeDelegate);
   6: Nav.AddNodeProcessorDelegate("Slope", SlopeNodeDelegate);
   7: Nav.Parse("SourceInputFile.txt");

The key here is in the AddNodeProcessorDelegate() method. It accepts two arguments:

  • the name of a node
  • a delegate to a method to be called when the named node is found during graph traversal. In the first example on line 2, the GraphNavigator is instructed to call the LineNodeDelegate() method when a node labeled “Line” is found in the tree.

The delegates, when called, are called with two arguments:

  • a reference to the GraphBuilder object used to build the graph
  • an object representing the discovered node

An example delegate implementation is as follows:

   1: private void LineNodeDelegate(GraphBuilder builder, object LineNode)
   2: {
   3:     Line NewLine = new Line();
   4:     NewLine.Name = builder.GetSequenceElementAt(LineNode, 0) as string;
   5:     NewLine.Point2 = thisValueStack.Pop() as Point;
   6:     NewLine.Point1 = thisValueStack.Pop() as Point;
   7:     thisLines.Add(NewLine);
   8: }

This seemed, at least to me, a bit easier than doing all of the navigation inline.

The implementation of the GraphNavigator class, which really isn’t all that complicated, is as follows:

   1: using System.Dataflow;
   2: using System.IO;
   3: using System.Collections.Generic;
   4:  
   5: namespace JeffFerguson.Oslo.MGrammar
   6: {
   7:     internal class GraphNavigator
   8:     {
   9:         internal delegate void NodeProcessorDelegate(GraphBuilder Builder, object Node);
  10:  
  11:         private DynamicParser thisParser;
  12:         private object thisGraphRoot;
  13:         private Dictionary<string, NodeProcessorDelegate> thisNodeDictionary;
  14:  
  15:         //-----------------------------------------------------------------------------------
  16:         //-----------------------------------------------------------------------------------
  17:         internal GraphNavigator(string MgxFile, string LanguageName)
  18:         {
  19:             thisParser = DynamicParser.LoadFromMgx(MgxFile, LanguageName);
  20:             thisNodeDictionary = new Dictionary<string, NodeProcessorDelegate>();
  21:         }
  22:  
  23:         //-----------------------------------------------------------------------------------
  24:         //-----------------------------------------------------------------------------------
  25:         internal void AddNodeProcessorDelegate(string NodeName, NodeProcessorDelegate NodeDelegate)
  26:         {
  27:             thisNodeDictionary.Add(NodeName, NodeDelegate);
  28:         }
  29:  
  30:         //-----------------------------------------------------------------------------------
  31:         //-----------------------------------------------------------------------------------
  32:         internal void Parse(string SourceFileName)
  33:         {
  34:             StreamReader SourceFileReader = File.OpenText(SourceFileName);
  35:             Parse(SourceFileReader);
  36:             SourceFileReader.Close();
  37:             SourceFileReader.Dispose();
  38:         }
  39:  
  40:         //-----------------------------------------------------------------------------------
  41:         //-----------------------------------------------------------------------------------
  42:         internal void Parse(StreamReader SourceStreamReader)
  43:         {
  44:             thisGraphRoot = thisParser.Parse<object>(null, SourceStreamReader, ErrorReporter.Standard);
  45:             EnumerateGraph(new GraphBuilder(), thisGraphRoot);
  46:         }
  47:  
  48:         //-----------------------------------------------------------------------------------
  49:         //-----------------------------------------------------------------------------------
  50:         private void EnumerateGraph(GraphBuilder builder, object node)
  51:         {
  52:             NodeProcessorDelegate NodeDelegate = null;
  53:             object Label = builder.GetLabel(node);
  54:  
  55:             if (Label != null)
  56:             {
  57:                 Identifier IdentifierLabel = Label as Identifier;
  58:                 if (IdentifierLabel != null)
  59:                     thisNodeDictionary.TryGetValue(IdentifierLabel.Text, out NodeDelegate);
  60:             }
  61:             foreach (object ChildNode in builder.GetSequenceElements(node))
  62:             {
  63:                 if ((ChildNode is string) == false)
  64:                     EnumerateGraph(builder, ChildNode);
  65:             }
  66:             if (NodeDelegate != null)
  67:                 NodeDelegate(builder, node);
  68:         }
  69:     }
  70: }

I hope you find it useful. Enjoy!

Twitter