I want to make a little domain specific language in C# for a parser generator. Suppose, as an example, that I used it to make a simple calculator. I want to be able to do something like this (but in C#):
TERM ::= NUMBER PLUS TERM { (a,b) => a+b }
So there are two kinds of symbols, ones that have a value (like NUMBER & TERM) and others that don't (like PLUS). I can define those using two classes:
class Symbol {}
class Symbol<T> {}
Symbol PLUS;
Symbol<double> NUMBER, TERM;
How can I define the production rules in a type safe manner?
I could have a function like this:
Production Prod<T1,T2,TR>(Symbol<TR> head, Symbol<T1> s1, Symbol s2, Symbol<T2> s3, Func<T1,T2,TR> action);
// Declare the grammar:
var productions = new Production[] { Prod(TERM, NUMBER, PLUS, TERM, (a,b) => a+b), ... };
But I would have to have 2n+1 such functions (where n is the maximum number of symbols in a production rule), one for each possible sequence of Symbol
's and Symbol<T>
's.
[Edit]
I could have something like this:
class Void {}
Symbol<Void> PLUS;
Production Prod<T1,T2,T3,TR>(Symbol<TR> head, Symbol<T1> s1, Symbol<T2> s2, Symbol<T3> s3, Func<T1,T2,T3,TR> action);
var rule = Prod(TERM,NUMBER,PLUS,TERM,(a,b,c) => a+c); // no _ for b in C# 6.
That would only require n such functions.
[/Edit]
I've also considered using operator overloading, but they can't have generics, so something like this is not going to work:
var rule = TERM | NUMBER | PLUS | NUMBER | (a,b) => a+b;
I suppose I could use generic methods. Like this:
var rule = TERM.P(NUMBER).P(PLUS).P(NUMBER).A((a,b) => a+b);
But that's kind of ugly. Are there some other tricks that might work and not look so horrible?
Note: I don't need to keep the types in the Production
class, I'm fine having object
at that stage. I would just like to avoid manually adding types to the semantic actions and/or having dummy placeholder arguments for non-value symbols.
Note 2: It needs to work with C# 6 and dynamic
won't work for me.
[Edit]: And I can't really use external libraries. I've already implemented an Earley parser, so this is just about expressing the grammar conveniently for it.