Parsing the command line with MGrammar – part 2
In the first installment of this series we took a look at the basic grammar for parsing the command line with MGrammar. In this part I’ll show you how we can load in a compiled version of the MGrammar and parse the input (i.e. the command line) to produce a valid MGraph that we in turn can process in the backend code.
A quick reminder from part 1; the code is located here:
You can download the code either by using git or downloading it as an archive. Once you’ve done that, open the solution LarsW.CommandLineParser.sln in Visual Studio 2008.
Most likely you will be presented with the following dialog box, informing you that opening the solution (or more correct the LarsW.CommandLineParser C# project inside) can pose a security risk. The reason for this is that I’ve included the a MSBuild task for compiling MGrammar files (.mg) into .mgx is that included in the Oslo SDK. Select the “Load project normally” and press OK.
We can first take a look at the extra plumbing I’ve added to the project to get the .mg file to compile. Right-click the LarsW.CommandLineParser project in the Solution Explorer, and choose Unload Project. Next, right-click it again, and choose Edit LarsW.CommandLineparser.csproj. This should bring up the project file will be shown as raw XML in the editor window.
In the first <PropertyGroup> I’ve added seven lines that I borrowed from a project created with the “M” template. They basically set’s up the path to various M-specific tools and auxiliary files.
The only line of these that really matter and that I had to tweak in order to get this right is the <MgTarget> element. Out of the box this is set to Mgx, that instructs the Mg compiler to spit out the result of the compilation as a .mgx file. As we will see later, the value needs to be set to MgResource in order to get the DynamicParser to load the .mgx as a resource.
If you navigate to the end of the project file, I’ve also added an <Import> element that imports some MGrammar specific MSBuild tasks and the most important thing; in the last <ItemGroup> section I’ve changed the element type from <None> to <MgCompile> for the cmd.mg file.
Well, we’ve been mucking around in the MSBuild plumbing too long now, haven’t we? Right-click the project again and choose Reload Project. When the project has loaded up again, build to ensure that everything is fine and dandy. Even though I haven’t stated it before, it should be obvious that the project depends on the latest (as of now that is the January 2009 CTP Refresh) Oslo SDK.
The core component is the CommandLineProcessor class.
It loads up the language (the compiled version of the cmd.mg) with DynamicParser.LoadFromResource(). The reason why we had to specify MgxResource as the MgTarget earlier is that if we don’t, and add the compiled .mgx file as a plain resource, the .LoadFromResource() method won’t find it. As of now, it seems that it will only look for resources with the .resource extension.
We then pass in the command line with a StringReader instance to the .Parse<T>() method on the DynamicParser instance. Even though it’s not specified, the T has to be object or a type that implements System.Dataflow.ISourceInfo. The internal/inner Node classes in GraphBuilder is what that will be handed out per default, but you can also create your own GraphBuilder and produce nodes from your own domain model.
So, by calling parser.Parse<object>(null, commandLineReader, ErrorReporter.Standard) we will get an instance to the root of the Abstract Syntax Tree (AST) returned if the input matches the grammar. The AST is basically a representation of the MGraph.
The next step is to traverse the AST and act upon the different node types. The grammar for this project is quite trivial and is mostly done by the private ProcessParameter() method in the CommandLineProcessor class. I suggest that you take a look at it if you’re interested in doing something similar.
So, just create an instance of the CommandLineProcessor and pass in an instance of an arbitrary class that contains method that will handle the command line arguments. To specify that a method is a argument handler, decorate it with the CommandLineArgumentHandler attribute. It will take in three parameters; short form & long form of the argument keyword and a description. For now the description isn’t used for anything but the idea is that the command line processor can auto generate a usage screen for you (typically shown with –?).
That’s about it – if you find it useful or modify the code, please let me know. With git you can push me a change set and I will try to merge it if you’ve come up with a cool feature.