Creating a DSL Parser in C#
I was listening to the Herding Code podcast with Louis DeJardin on the Spark View Engine and Louis mentioned an old blog entry he'd made on parsing DSLs. It immediately caught my interest as I've been looking for a better way to do this for a while, to use with my Card Game Language, and I went and looked it up.
The reason I've been looking for another way of parsing the Card Game Language is that it relies on the generation of code files from a grammar file which is not very flexible when you want to make small changes to the language. This new approach, however, works all in C# code which seems to be much easier to work with - of cause there is more to running the code than just parsing it, but I'll have to look into that later.
To illustrate how the code works, take a look at the following code:
- public class PhoneGrammar : Grammar
- {
- public PhoneGrammar()
- {
- var number =
- Rep(Ch(char.IsNumber))
- .Build(hit => new string(hit.ToArray()));
- var localNumber =
- number.If(hit=>hit.Length == 3)
- .And(Ch('-'))
- .And(number.If(hit=>hit.Length == 4))
- .Build(hit => new PhoneNumber
- {
- Prefix = hit.Left.Left,
- Body = hit.Down
- });
- var longDistanceNumber =
- Ch('(')
- .And(number.If(hit=>hit.Length == 3))
- .And(Ch(')'))
- .And(localNumber)
- .Build(hit => new PhoneNumber
- {
- AreaCode = hit.Left.Left.Down,
- Prefix = hit.Down.Prefix,
- Body = hit.Down.Body
- });
- ParsePhoneNumber = localNumber.Or(longDistanceNumber);
- var phoneNumberIgnoringWhitespace =
- Rep(Ch(' '))
- .And(ParsePhoneNumber)
- .And(Rep(Ch(' ')))
- .Build(hit => hit.Left.Down);
- ParsePhoneNumberList = Rep(phoneNumberIgnoringWhitespace);
- }
- public ParseAction<PhoneNumber> ParsePhoneNumber;
- public ParseAction<IList<PhoneNumber>> ParsePhoneNumberList;
- }
The code defines a grammar for parsing phone numbers. The way the grammar is constructed is by building up the patterns describing a valid phone number, starting from the definition of a number and then adding structure to the formatting of the number based on whether the phone number is long distance. Each pattern maps the matched string to the desired object it represents. In the blog post Louis also demonstrates how a grammar can be created to parse XML code. This is actually the basis for the parsing code found in the Spark View Engine which uses XML notation.
I find this approach very interesting and I'm looking forward to seeing how well it will work as a replacement for the Grammatica library I'm currently using. Now I just need to find that extra spare time it requires. If you find this stuff interesting you should definitely go check of Louis' blog post.
Comments