Working with Prism and the Blacklight Controls


I recently set out to build a WPF application using the Composite Client Application Guidance (lovingly known as “Prism”) code base. Additionally, I wanted to make use of the Blacklight controls – specifically, the DragDockPanelHost and DragDockPanel controls – in the application. I soon discovered that these two code bases didn’t work together “right out of the box”. I found a problem, and a solution, and this post describes both the issue and its resolution.

The architecture promoted by Prism is that of an application made up of an executable shell and a set of module assemblies. In this pattern, each of the application views are implemented in their own module assembly, which is loaded by the shell at runtime. So far, so good.

Since I was interested in using the DragDockPanelHost and DragDockPanel controls in my WPF application, my approach was to put the DragDockPanelHost in my shell application, and a DragDockPanel in each of my Prism modules. With this design, and after assigning a Prism region name to the DragDockPanelHost in the shell application, Prism would do its thing and load each of the DragDockPanels from each module and add them into my DragDockPanelHost region at runtime.

You may be asking yourself, “And how did that work out for you?” To quote Stewie on Family Guy, “Not well, Brian. Not well.”

My first mistake was in assuming that Prism regions worked with any kind of container control, and, therefore, putting a region name on my DragDockPanelHost XAML element would be enough. It wasn’t. Prism, by default, only supports regions defined on containers derived from one of the following classes:

  • System.Windows.Controls.Primitives.Selector
  • System.Windows.Controls.ItemsControl
  • System.Windows.Controls.ContentControl

The DragDockPanelHost class is derived from Canvas, which explains the lack of love that I got from Prism.

Fortunately, Prism has a way to add support for other containers defined as regions through the RegionAdapterBase class. In my solution, I wrote a class, derived from RegionAdapterBase, that defined a region adapter for the Blacklight DragDockPanelHost. The class looks like this:

   1:      public class DragDockPanelHostRegionAdapter : RegionAdapterBase<DragDockPanelHost>
   2:      {
   3:          public DragDockPanelHostRegionAdapter(IRegionBehaviorFactory BehaviorFactory) : base(BehaviorFactory)
   4:          {
   5:          }
   6:   
   7:          protected override void Adapt(IRegion region, DragDockPanelHost regionTarget)
   8:          {
   9:              region.Views.CollectionChanged += (s, e) =>
  10:                  {
  11:                      if (e.Action == NotifyCollectionChangedAction.Add)
  12:                      {
  13:                          foreach (FrameworkElement CurrentElement in e.NewItems)
  14:                          {
  15:                              UserControl CurrentElementAsUserControl = CurrentElement as UserControl;
  16:                              DragDockPanel PanelToAdd = CurrentElementAsUserControl.Content as DragDockPanel;
  17:                              CurrentElementAsUserControl.Content = null;
  18:                              regionTarget.AddPanel(PanelToAdd);
  19:                          }
  20:                      }
  21:                      else if (e.Action == NotifyCollectionChangedAction.Remove)
  22:                      {
  23:                          foreach (FrameworkElement CurrentElement in e.OldItems)
  24:                              regionTarget.Children.Remove(CurrentElement);
  25:                      }
  26:                  };
  27:          }
  28:   
  29:          protected override IRegion CreateRegion()
  30:          {
  31:              return new AllActiveRegion();
  32:          }
  33:      }

The generic type used in the RegionAdapterBase class is the type of the class for which you need Prism to support as a region. The body of the effort is in the Adapt() method, which is called when controls are added or removed from the container.

This class needs to be used in the shell’s Prism bootstrapper through an overloaded method called ConfigureRegionAdapterMappings():

   1:      internal class Bootstrapper : UnityBootstrapper
   2:      {
   3:          protected override DependencyObject CreateShell()
   4:          {
   5:              // ... snip
   6:          }
   7:   
   8:          protected override IModuleCatalog GetModuleCatalog()
   9:          {
  10:              // ... snip
  11:          }
  12:   
  13:          protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
  14:          {
  15:              RegionAdapterMappings Mappings = base.ConfigureRegionAdapterMappings();
  16:              Mappings.RegisterMapping(typeof(DragDockPanelHost), Container.Resolve<DragDockPanelHostRegionAdapter>());
  17:              return Mappings;
  18:          }
  19:      }

 

The region adapter is registered with Prism in the RegisterMapping() method of the RegionAdapterMappings class.

With this, I can now define a user control in my Prism module that defines a DragDockPanel:

   1:  <UserControl x:Class="Narrative.Units.View"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:blacklight="clr-namespace:Blacklight.Controls;assembly=Blacklight.WPFControls">
   5:      <blacklight:DragDockPanel Header="Title" />
   6:  </UserControl>

 

Then I can, in my Prism shell, define a DragDockPanelHost with a region:

   1:  <Window x:Class="App.MainWindow"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:blacklight="clr-namespace:Blacklight.Controls;assembly=Blacklight.WPFControls"
   5:      xmlns:regions="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation"
   6:      Title="App Title" Height="300" Width="300" x:Name="Main">
   7:      <Grid x:Name="MainGrid">
   8:          <Grid.RowDefinitions>
   9:              <RowDefinition Height="auto"/>
  10:              <RowDefinition Height="auto"/>
  11:              <RowDefinition Height="*"/>
  12:          </Grid.RowDefinitions>
  13:          <blacklight:DragDockPanelHost Background="Khaki" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Grid.Row="2" x:Name="PanelHost" Margin="15" regions:RegionManager.RegionName="PanelHostRegion" />
  14:      </Grid>
  15:  </Window>

 

This solved the issue, and I can now have my DragDockPanelHost in a Prism executable shell and a DragDockPanel user control in a Prism module.

author: Jeff Ferguson | posted @ Tuesday, June 30, 2009 6:58 PM | Feedback (1)

Slides and Code for Lexical Analysis Talk


Last evening, I presented my “Inside Lexical Analysis” talk to the Twin Cities Languages User Group (TCLUG). I have posted both the slide deck and code samples that used during the talk; feel free to grab a copy if you wish. If you have any questions or comments about the material, feel free to get in touch with me and let me know (you can find me on Twitter here, or leave a comment on this blog post).

Many thanks to TCLUG coordinator (and friend and colleague) Jason Bock for arranging the event and allowing me the opportunity to speak!

author: Jeff Ferguson | posted @ Friday, April 10, 2009 7:42 AM | Feedback (2)

Expression Evaluation Coming to MGrammar?


Consider the following simple calculator authored in yacc:

%{  
    #include <stdio.h>   
    int yylex(void);   
    void yyerror(char *);   
%}   
  
%token INTEGER   
  
%%   
   
program:   
        program expr '\n'         { printf("%d\n", $2); }   
        |    
        ;   
  
expr:   
        INTEGER   
        | expr '+' expr           { $$ = $1 + $3; }   
        | expr '-' expr           { $$ = $1 - $3; }   
        ;   
  
%%   
  
void yyerror(char *s) {   
    fprintf(stderr, "%s\n", s);   
}   
  
int main(void) {   
    yyparse();   
    return 0;   
}   

A notable feature of this grammar is that the syntax definitions are augmented with C code statements that are executed when the syntax is discovered in the input stream. This code is able to interpret the input and, if necessary, perform a calculation and send the result of the calculation into the abstract syntax tree (AST). Consider, for example, the following fragment from the yacc grammar above:

expr '+' expr           { $$ = $1 + $3; } 

The yacc engine will assign temporary variables to matching input as follows:

  • $1: the first expression
  • $2: the ‘+’ token
  • $3: the second expression

An additional variable, called $$, contains the results of the parsed input. In this fragment, the equivalent of an anonymous delegate includes C code to perform the addition operation on the contents of $1 and $3 and place the results in $$. With this, some work can be done “in place” to not only construct, but also to evaluate, the AST.

This background information is necessary to understand the following: MGrammar has no such capability today. Anonymous methods cannot be attached to MGrammar syntax productions, and “in place” interpretations of the AST in this manner is not possible.

However, this doesn’t mean that such a feature will never appear in MGrammar. In discussing this issue in the MSDN Forums for “Oslo”, Microsoft’s Paul Vick made this comment:

I thought I'd interject that we're considering ways to evaluate expressions directly in grammars. It's a pretty useful feature, and we'd love to provide the full M expression capabilities on the RHS of a grammar production. So stay tuned!

Disclaimer:This does not represent an official commitment from Microsoft. Don’t read too much into this. It might happen. It might not. Don’t storm Paul’s office with torches and pitchforks if it doesn’t appear. But Microsoft is thinking about it, and that’s all we can ask.

author: Jeff Ferguson | posted @ Wednesday, April 01, 2009 7:47 AM | Feedback (3)

Error 8013150A Importing TFS Work Item Templates with TFS Power Tools


I just ran across this error while using the Team Foundation Server Power Tools to import a work item template XML document into a TFS instance:

Error 0x8013150A: An error relating to security occurred.

So it’s a security issue, right? No.

As it turns out, I had edited the XML file in Notepad and added values to a field definition with the ALLOWEDVALUES rule. In my over-zealous copy-and-paste efforts, I had added a few of the allowable values in more than one place:

<FIELD reportable="dimension" type="String" name="MyName" refname="MyProject.MyName">
  <ALLOWEDVALUES>
    <LISTITEM value="Value1" />
    <LISTITEM value="Value2" />
    <LISTITEM value="Value3" />
    <LISTITEM value="Value1" />
    <!-- more values -->
  </ALLOWEDVALUES>
</FIELD>

The attempt to import this file into the TFS Process Editor Power Tool failed with the 8013150A error. After playing with security rights for a while, to no avail, I decided to double check my edits. I found and removed the duplicate entries in Notepad and tried again to load the file into TFS Process Editor Power Tool. Success!

Just thought that I would document the issue in case someone else runs across the same problem. The moral of the story is this: an error message of “an error relating to security occurred” does not necessarily mean that you have a security issue with your file. Check the contents of the file and make sure that is valid with regards to both syntax and semantics. In particular, do not list an item in an ALLOWEDVALUES list more than once … or you’ll get a security error … which makes perfect sense … I guess …

author: Jeff Ferguson | posted @ Monday, March 30, 2009 9:30 AM | Feedback (0)

Interpreting Mathematical Operators in MGrammar


Suppose that you have a need for a domain specific language that supports mathematical expressions. Specifically, you want to support the following:

  • unsigned integers
  • signed integers
  • addition expressions
  • subtraction expressions

Your first attempt at such a grammar using MGrammar might look like this:

module JeffFerguson.BlogPosts
{
    
    language MathOps
    {
        
        // ignore whitespace
        
        syntax LF = "\u000A";
        syntax CR = "\u000D";
        syntax Space = "\u0020";
        interleave Whitespace = LF | CR | Space;
        
        // operators
        
        token PlusOperator = "+";
        token MinusOperator = "-";
        token StatementEnd = ";";
        
        // numerics
        
        token UnaryOperator = PlusOperator | MinusOperator;
        token Digit = "0".."9";
        token WholeNumber = UnaryOperator? Digit+;
        
        // expressions
        
        syntax AdditionExpression = t1:WholeNumber PlusOperator t2:WholeNumber =>Add[t1,t2];
        syntax SubtractionExpression = t1:WholeNumber MinusOperator t2:WholeNumber => Subtract[t1,t2];
        syntax MathematicalExpression =
            WholeNumber
            | AdditionExpression
            | SubtractionExpression;
                             
        // language syntax
        syntax Statement = e:MathematicalExpression StatementEnd => Statement[valuesof(e)];
        syntax Main = Statement*;
    }
}

There is a subtle problem with this grammar. Suppose that you offer the following input to the grammar:

1;
+2;
-3;
4+5;

This input will give you errors:

[file://untitled1/#4,2-4,4] error 5007: Token JeffFerguson.BlogPosts.MathOps.WholeNumber with text "+5" unexpected. 
[file://untitled1/#4,2-4,4] message 5011: Assuming an insertion of value ';' to continue parsing. Could also have inserted '-', '+'.

The problem is with the “4+5” statement. The issue here is that the “+” symbol is being interpreted as the unary operator for the “5” symbol, and MGrammar is attempting to see the “4” and the “+5” as separate symbols, which is not what was intended. Rather, the input most likely meant to offer the “4+5” (as in “9”, for those of you not up on your math) as an additive operator.

The issue has to do with the fact that MGrammar’s lexical analysis engine runs before the grammatical parser, just as lex runs before yacc. This means that token operations in an MGrammar definition are interpreted before the syntax definitions. Since the unary operator is defined as part of a token definition, then that binding will take precedence over the addition syntax.

This can be fixed by specifying the unary operator as a syntax definition with a slight change to the grammar:

module JeffFerguson.BlogPosts
{
    
    language MathOps
    {
        
        // ignore whitespace
        
        syntax LF = "\u000A";
        syntax CR = "\u000D";
        syntax Space = "\u0020";
        interleave Whitespace = LF | CR | Space;
        
        // operators
        
        token PlusOperator = "+";
        token MinusOperator = "-";
        token StatementEnd = ";";
        
        // numerics
        
        token UnaryOperator = PlusOperator | MinusOperator;
        token Digit = "0".."9";
        syntax WholeNumber =
            n:Digit+ => UnsignedNumber[n]
            | s:UnaryOperator n:Digit+ => SignedNumber[s, valuesof(n)]
            ;
        
        // expressions
        
        syntax AdditionExpression = t1:WholeNumber PlusOperator t2:WholeNumber =>Add[t1,t2];
        syntax SubtractionExpression = t1:WholeNumber MinusOperator t2:WholeNumber => Subtract[t1,t2];
        syntax MathematicalExpression =
            WholeNumber
            | AdditionExpression
            | SubtractionExpression;
                             
        // language syntax
        syntax Statement = e:MathematicalExpression StatementEnd => Statement[valuesof(e)];
        syntax Main = Statement*;
    }
}

The abstract syntax tree (AST) for the input now looks like this:

Main[
  [
    Statement[
      UnsignedNumber[
        [
          "1"
        ]
      ]
    ],
    Statement[
      SignedNumber[
        "+",
        "2"
      ]
    ],
    Statement[
      SignedNumber[
        "-",
        "3"
      ]
    ],
    Statement[
      Add[
        UnsignedNumber[
          [
            "4"
          ]
        ],
        UnsignedNumber[
          [
            "5"
          ]
        ]
      ]
    ]
  ]
]

All better.

author: Jeff Ferguson | posted @ Tuesday, March 24, 2009 9:23 AM | Feedback (1)

Defining String Literals in MGrammar


Consider a language authored in MGrammar that recognizes a keyword named “keyword” as well as string literals:

module MyModule
{
    language MyLanguage
    {
        // ignore whitespace
        
        syntax LF = "\u000A";
        syntax CR = "\u000D";
        syntax Space = "\u0020";
        interleave Whitespace = LF | CR | Space;
        
        // string literals
        token Quote = "\"";
        token StringLiteral = Quote a:any* Quote => StringLiteral[a];
        token Keyword = "keyword";
        
        // statements
        token Semicolon = ";";
        syntax Statement = s:(Keyword | StringLiteral) Semicolon => Statement[s];
        syntax Main = Statement*;
    }
}

Now consider some sample input:

keyword;
"this is a string";
keyword;

Given this sample input, you would expect the abstract syntax tree (AST) for this input to define two “keyword" statements and one string literal. This is, in fact, exactly what happens:

Main[
  [
    Statement[
      [
        "keyword"
      ]
    ],
    Statement[
      [
        StringLiteral[
          "this is a string"
        ]
      ]
    ],
    Statement[
      [
        "keyword"
      ]
    ]
  ]
]

But what happens when two string literals appear in the input, as in the following example?

keyword;
"this is a string";
keyword;
"this is another string";
keyword;

Given this sample input, you would expect the abstract syntax tree (AST) for this input to define two “keyword" statements and two string literals. This is, in fact, not what happens. The AST you’ll get back looks like this:

Main[
  [
    Statement[
      [
        "keyword"
      ]
    ],
    Statement[
      [
        StringLiteral[
          "this is a string\";\r\nkeyword;\r\n\"this is another string"
        ]
      ]
    ],
    Statement[
      [
        "keyword"
      ]
    ]
  ]
]

The problem lies in the grammar’s definition of a string literal. The definition states that a string literal starts with a quotation mark and finishes with a quotation mark. The input parsing engine for M, however, is as greedy as possible, swallowing as much input as possible until the syntax definition can no longer be satisfied. In this case, it finds the very last quotation mark in the input and uses that entire text as the string literal. This is not what we want.

The workaround for this is to reduce our definition of what should appear between the quotation marks from the any keyword, which states that any character can appear, to a definition which states that any character but a quotation mark can appear. The definition now looks like this:

token StringLiteral = Quote a:(any - Quote)* Quote => StringLiteral[a];

The AST for the input now looks as we expect:

Main[
  [
    Statement[
      [
        "keyword"
      ]
    ],
    Statement[
      [
        StringLiteral[
          "this is a string"
        ]
      ]
    ],
    Statement[
      [
        "keyword"
      ]
    ],
    Statement[
      [
        StringLiteral[
          "this is another string"
        ]
      ]
    ],
    Statement[
      [
        "keyword"
      ]
    ]
  ]
]
Much better!

author: Jeff Ferguson | posted @ Saturday, March 21, 2009 7:03 PM | Feedback (0)

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!

author: Jeff Ferguson | posted @ Wednesday, March 18, 2009 8:06 AM | Feedback (0)

Adding Line Slopes and Print Statements to Calculatix Using MGrammar


I have added, thanks to MGrammar, a few new constructs to my fledgling Calculatix language:

  • unary operators, used primarily so that I can specify negative whole numbers
  • support for string literals
  • the slopeof expression, allowing for the calculation of the slope of a line
  • the show statement, allowing for the output of an expression’s value to the console

This allows me to write input such as this:

line LineA (4, -3) (2, 5);
show "The slope of LineA is:";
show slopeof LineA;

The MGrammar for this language now looks like this:

   1: module JeffFerguson.Calculatix.Grammars
   2: {
   3:     language Calculus
   4:     {
   5:         
   6:         // ignore whitespace
   7:         
   8:         syntax LF = "\u000A";
   9:         syntax CR = "\u000D";
  10:         syntax Space = "\u0020";
  11:         interleave Whitespace = LF | CR | Space;
  12:         
  13:         // punctuation
  14:         token Comma = ',';
  15:         
  16:         // operators
  17:         token UnaryOperator = "+" | "-";
  18:         token Quote = "\"";
  19:  
  20:         // keywords
  21:         token LineKeyword = "line";
  22:         token ShowKeyword = "show";
  23:         token SlopeKeyword = "slopeof";
  24:         
  25:          // numbers
  26:         token Digit = "0".."9";
  27:         token WholeNumber = UnaryOperator? Digit+;
  28:  
  29:  
  30:         // strings
  31:         token StringLiteral = Quote a:any* Quote => StringLiteral[a];
  32:         
  33:         // identifiers
  34:         token Uppercase = "A".."Z";
  35:         token Lowercase = "a".."z";
  36:         token Alphabetic = Uppercase | Lowercase;
  37:         token Identifier = Alphabetic (Alphabetic | Digit)*;
  38:                         
  39:         // expressions
  40:         syntax SlopeExpression = SlopeKeyword i:Identifier => Slope[i];
  41:         syntax Expression =
  42:             SlopeExpression
  43:             | StringLiteral
  44:             ;
  45:                     
  46:         // points
  47:         token XCoordinate = WholeNumber;
  48:         token YCoordinate = WholeNumber;
  49:         syntax Point = "(" x:XCoordinate "," y:YCoordinate ")" =>Point[x,y];
  50:         
  51:         // lines
  52:         syntax LineStatement = LineKeyword i:Identifier p1:Point p2:Point => Line[i, p1, p2];
  53:         syntax SlopeStatement = SlopeKeyword i:Identifier => Slope[i];
  54:         
  55:         // expression display
  56:         syntax ShowStatement = ShowKeyword e:Expression => Show[valuesof(e)];
  57:         
  58:         // statements
  59:         syntax StatementExpression =
  60:             LineStatement
  61:             | ShowStatement;
  62:                         
  63:         // language syntax
  64:         syntax Statement = e:StatementExpression ";" =>Statement[valuesof(e)];
  65:         syntax Main = Statement*;
  66:     }
  67: }

This input shown above produces a graph that looks like this:

Main[
  [
    Statement[
      Line[
        "LineA",
        Point[
          "4",
          "-3"
        ],
        Point[
          "2",
          "5"
        ]
      ]
    ],
    Statement[
      Show[
        StringLiteral[
          "The slope of LineA is:"
        ]
      ]
    ],
    Statement[
      Show[
        Slope[
          "LineA"
        ]
      ]
    ]
  ]
]

It is worth noting that I have used two terms here - expression and statement – and it is probably important to discuss the difference (at least according to how I have defined them). A statement is an action. When a statement is encountered, an action is taken: perhaps an object is created in the .NET code that processes the source input, or information is displayed to the user. When an expression is encountered, an evaluation is made: perhaps a calculation is performed, or some memory is reserved for a value. Expressions do nothing, however, until used in a statement. The current grammar defines two types expressions:

  • string literals
  • slope expressions via the slopeof keyword

Since the show statement is defined as accepting an expression, either expression type can be used in the show statement, and, as you can see, the source input uses both of those types.

It’s also important to note what the grammar doesn’t do:

  • Identifier Validation: The intent of the slopeof expression is that the identifier used in the expression was previously used to define a line. The grammar doesn’t check that, nor can it, nor should it. That has to be checked by the .NET code that is processing the input. This is an example of semantics versus syntax. MGrammar will handle the syntax, but you need to handle the semantics. In this example, the code that checks to ensure that the identifier used in the slopeof expression actually belongs to a line will be code that I have to write in the .NET processor.
  • Calculation: The grammar doesn’t calculate the actual slope of the line. Again, that’s code that must be left to the .NET processor.

Next up – processing this input in C#. With any luck, I will be able to show the slope’s calculation on the console.

author: Jeff Ferguson | posted @ Tuesday, March 17, 2009 11:40 AM | Feedback (0)

Processing MGrammar Graphs in C#


In this post, I began a discussion of a simple language defined in MGrammar that allows the specification of two-dimensional lines. Through this language, I can write input files that look like this:

   1: line LineA (4,3) (2,5); 
   2: line LineB (5,6) (5,1);

Now what? I’ve got a grammar, and an input language … but how is the input language processed? As it turns out, the abstract syntax tree (AST) graph that MGrammar generates from the input source can be read from a .NET  language. In this post, I will illustrate how this is done.

The goal for this example is to take the source text and turn it into .NET objects. To accomplish this, I defined a Point class to contain the point data:

   1: internal class Point
   2: {
   3:     public int X { get; internal set; }
   4:     public int Y { get; internal set; }
   5: }

I also defined a Line class to contain the line data:

   1: internal class Line
   2: {
   3:     public string Name { get; internal set; }
   4:     public Point Point1 { get; internal set; }
   5:     public Point Point2 { get; internal set; }
   6: }

I then defined a private list to contain the data for the lines that I read in from the source language:

   1: private List<Line> thisLines;

My code, which is configured as a simple console application, begins as follows:

   1: string ImageFileName = @"..\..\..\Grammar\JeffFerguson.Calculatix.mgx";
   2: string LanguageName = "JeffFerguson.Calculatix.Grammars.Calculus";
   3:  
   4: try
   5: {
   6:     if (args.Length != 1)
   7:     {
   8:         Console.WriteLine("usage: lines [input]");
   9:         return;
  10:     }
  11:     thisLines = new List<Line>();
  12:     StreamReader InputReader = File.OpenText(args[0]);
  13:     DynamicParser Lang = DynamicParser.LoadFromMgx(ImageFileName, LanguageName);
  14:     if (Lang == null)
  15:         throw new NullReferenceException("LoadFromMgx() returned null");
  16:     object Root = Lang.Parse<object>(null, InputReader, ErrorReporter.Standard);
  17:     EnumerateGraph(new GraphBuilder(), Root);
  18:     ShowLines();
  19: }
  20: catch (Exception e)
  21: {
  22:     Console.WriteLine("*** EXCEPTION: {0}", e.Message);
  23: }
  24: finally
  25: {
  26:     Console.WriteLine();
  27:     Console.WriteLine("Press [Enter].");
  28:     Console.ReadLine();
  29: }

The Olso-related keys here are:

  1. the call to DynamicParser.LoadFromMgx(), which loads the compiled MGrammar code into the process
  2. the call to Lang.Parse(), which processes the source language and produces the AST graph

Once this is done, I call a private method called EnumerateGraph() which navigates the graph that the call to Parse() created. That implementation looks like this:

   1: private void EnumerateGraph(GraphBuilder builder, object node)
   2: {
   3:     Identifier Label = builder.GetLabel(node) as Identifier;
   4:     if (Label.Text.Equals("Line") == true)
   5:         ProcessLine(builder, node);
   6:     else
   7:     {
   8:         foreach (object ChildNode in builder.GetSequenceElements(node))
   9:         {
  10:             if((ChildNode is string) == false)
  11:                 EnumerateGraph(builder, ChildNode);
  12:         }
  13:     }
  14: }

Remember, the goal here is to read in the source input and create a collection of Line objects that represent the input. This code is moving down the tree, looking for nodes labeled “Line”. If the node is found, then another private method called ProcessLine() is called. If the current graph node does not have a label of “Line”, then the code checks siblings, and, through recursion, its children.

The implementation of ProcessLine() is pretty straightforward:

   1: private void ProcessLine(GraphBuilder builder, object LineNode)
   2: {
   3:     if (builder.GetSequenceCount(LineNode) != 3)
   4:         throw new NotSupportedException("Line does not have three children");
   5:     Line NewLine = new Line();
   6:     NewLine.Name = builder.GetSequenceElementAt(LineNode, 0) as string;
   7:     NewLine.Point1 = ProcessPoint(builder, builder.GetSequenceElementAt(LineNode, 1));
   8:     NewLine.Point2 = ProcessPoint(builder, builder.GetSequenceElementAt(LineNode, 2));
   9:     thisLines.Add(NewLine);
  10: }

Remember that the Line node should have three elements:

  • the line identifier
  • the starting point for the line
  • the ending point for the line

Recall that this part of the graph looks like this:

 

image

The implementation of ProcessLine() creates a new Line object and populates it with the values stored in the graph. The points are read in by another helper method called ProcessPoint(), which looks very similar:

   1: private Point ProcessPoint(GraphBuilder builder, object PointNode)
   2: {
   3:     if (builder.GetSequenceCount(PointNode) != 2)
   4:         throw new NotSupportedException("Point does not have two children");
   5:     Point NewPoint = new Point();
   6:     NewPoint.X = Convert.ToInt32(builder.GetSequenceElementAt(PointNode, 0) as string);
   7:     NewPoint.Y = Convert.ToInt32(builder.GetSequenceElementAt(PointNode, 1) as string);
   8:     return NewPoint;
   9: }

Once all of this is done, control returns to the caller, and a private method called ShowLines() displays the .NET Line objects created from the tree traversal out to the console. This couldn’t be easier:

   1: private void ShowLines()
   2: {
   3:     foreach (Line CurrentLine in thisLines)
   4:         Console.WriteLine("The line named {0} runs from ({1}, {2}) to ({3}, {4}).", CurrentLine.Name, CurrentLine.Point1.X, CurrentLine.Point1.Y, CurrentLine.Point2.X, CurrentLine.Point2.Y);
   5: }

This results in the following output being sent to the console:

The line named LineA runs from (4, 3) to (2, 5).
The line named LineB runs from (5, 6) to (5, 1).

Press [Enter].

And, there you have it: a language defined in MGrammar with an input source file processed by C# to produce results.

author: Jeff Ferguson | posted @ Monday, March 16, 2009 3:06 PM | Feedback (2)

Optimizing Parse Trees Through MGrammar Productions


In my last post, I presented a simple MGrammar definition for a specification of two-dimensional lines. My specification of a Point entity, which, in my language, looks like this:

(4, 3)

is implemented in MGrammar using the following specifications:

// numbers
token Digit = "0".."9";
token WholeNumber = Digit+;

// points
token XCoordinate = WholeNumber;
token YCoordinate = WholeNumber;
syntax Point = "(" x:XCoordinate "," y:YCoordinate ")" => Point[x,y];

What is the “=>” symbol, and what is all of that after the symbol? It’s actually not needed, and you could get by with this:

syntax Point = "(" XCoordinate "," YCoordinate ")";

Let’s take a look as to why this “=>” symbol can be handy.

Remember that running input through MGrammar produces a graph of the input. My last post showed the entire parse tree for the two lines of input I described in that post. The syntax of Point contains five elements:

  • the open parenthesis
  • an integer
  • a comma
  • an integer
  • the close parenthesis

The parse tree for an input of (4, 3) would, by default, look like this (note that, for brevity, a complete parse tree is not shown):

 

image

 

There is nothing wrong with that, but it is more information than the code processing the input really needs. The only two elements of the point that really matter are the two integers. The punctuation can be stripped out, since it is not adding any value. the “=>” symbol defines a production that defines how the parse tree should actually be generated if the default is not needed.

Let’s take a look once again at the definition of Point:

syntax Point = "(" x:XCoordinate "," y:YCoordinate ")" => Point[x,y];

There are a few things to note here:

  • the XCoordinate token is prefaced by a “local name” of x
  • the YCoordinate token is prefaced by a “local name” of y

Those local names are used in the right hand side of the production, which specifies what should actually appear in the parse tree. In our case, we really only want the two integers, and we specify the comma delimited items that should appear in the parse tree within square brackets. The items are specified using these “local names”. The production in this case directs the parse tree to contain only x and y and everything else can be left out. That will leave us with a parse tree like this:

 

image

 

This gives the .NET code that is actually processing the input that much less to wade through when examining the input. Handy.

author: Jeff Ferguson | posted @ Sunday, March 15, 2009 5:21 PM | Feedback (0)