Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.5k views
in Technique[技术] by (71.8m points)

Type-safe Backus-Naur-Form DSL with semantic actions in C#

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
等待大神解答

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...