C# Parsing Library:一个模仿Boost.Spirit的LL解析器库

    C# Parsing Library 是一个LL解析器产生框架,可以在C#中模拟ENBF文法定义。设计思路来自于Boost.Spirit,一个C++解析器框架。

 
一)Parser 基本概念

a) 文法定义举例:P ::= a b    C#用法:P = a + b    序列
b) 文法定义举例:P ::= a | b  C#用法:P = a | b    选择
c) 文法定义举例:P ::= a *    C#用法:P = a.Star   0..n次匹配
d) 文法定义举例:P ::= a +    C#用法:P = a.Plus   1..n次匹配
e) 文法定义举例:P ::= a ?    C#用法:P = a.Opt    0..1次匹配

  P为Parser类型,是解析器的抽象基类,它定义了一个抽象的Parse方法:
      bool Parse(Scanner scanner);

   Scanner类主要存储一个字符串输入,及一个光标位置,光标随着解析的进行向前移动。
 
   例子:一个整数解析器, 定义为一个可选的符号后面跟若干数字:
 
Parser signed = (Parser.Lit('+'| '-').Opt;
Parser p 
= (signed + Parser.DigitChar.Plus).Lexeme;
bool success = p.Parse(new Scanner("-123"));

   其中,Lit表示常量,Lexeme表示为词法分析,即不忽略空格。


二)ParserRef

一个常用的四则运算表达式文法:
    group      ::= '(' expression ')'
    factor     ::= integer | group
    term       ::= factor (('*' factor) | ('/' factor))*
    expression ::= term (('+' term) | ('-' term))*

用下面的方法是错误的:

Parser group; //  Parser 是抽象类,无法 new
Parser factor;
factor 
= Parser.Int | group; // 错误! group没有初始化!

但是使用ParserRef 就可以了:

ParserRef group = new ParserRef();
ParserRef factor 
= new ParserRef();
factor.Parser 
= Parser.Int | group; 


完整的定义如下:

ParserRef group = new ParserRef();
ParserRef factor 
= new ParserRef();
ParserRef term 
= new ParserRef();
ParserRef expression 
= new ParserRef();


group.Parser 
= '(' + expression + ')';

factor.Parser 
= Parser.Int
              
| group;

term.Parser     
= factor + 
                  ( (
'*' + factor)
                  
| ('/' + factor)
                  ).Star;

expression.Parser 
= term + 
                    ( (
'+' + term)
                    
| ('-' + term)
                    ).Star;



三)Rule和语义支持

    和 spirit一样,通过对[]的重载,实现对语义的支持。一般的parser的Action类型为Action<string>, 即 void Action(string s)。s为该parser匹配的内容。如果要支持上下文, 就要使用Rule了. Rule带有一个模板参数T,表示属性类型。Action类型为Func<T,T,T> 即 T Action(T lhs, T rhs)。对于以下的简单规则:

       LeftRule := RightRule [ Action(lhs, rhs) ]

其语义为:LeftRule.Attribute = Action(LeftRule.Attribute, RightRule.Attribute).

   上面的四则运算示例可修改如下:




Grammar
<int> grammar  = new Grammar<int>();
Rule
<int> group = new Rule<int>(grammar);
Rule
<int> factor = new Rule<int>(grammar);
Rule
<int> term = new Rule<int>(grammar);
Rule
<int> expression  = new Rule<int>(grammar);
Rule
<int> start = new Rule<int>(grammar);
grammar.Start 
= start;



group.Parser 
= '(' + expression [ (lhs, rhs) => rhs ] + ')';

factor.Parser 
= Parser.IntValue [ v => grammar.Ret(v) ]  // (#1)
              | group [ (lhs, rhs) => rhs ];

term.Parser 
= factor [ (lhs, rhs) => rhs ] + 
              ( (
'*' + factor [ (lhs, rhs) => lhs * rhs ])
              
| ('/' + factor [ (lhs, rhs) => lhs / rhs ])
              ).Star;

expression.Parser 
= term [ (lhs, rhs) => rhs ] + 
                    ( (
'+' + term [ (lhs, rhs) => lhs + rhs ])
                    
| ('-' + term [ (lhs, rhs) => lhs - rhs ])
                    ).Star;


start.Parser 
= expression [ (lhs, rhs) => rhs ] + Parser.End;




int result;
bool success = grammar.Parse("10 + 20 + 30 * (40 + 50)"out result);
if (success) Console.WriteLine(result);

说明:
      #1:
对于一般的Parser,语义动作中并不能有返回值,因为它不知道属性的确切类型,要支持属性,必须使用 Grammar.Ret().

     在我自己实现以前,大致搜了一下,在CodeProject上有一个类似的实现,也是模仿Boost.Spirit,不过它的语义处理采用C#的事件机制,用起来极不方便。这个项目我刚刚把它发布在google code 上面,项目主页:http://code.google.com/p/csparsing/。当然它还远远不够成熟。

posted on 2009-08-21 19:20  livin1984  阅读(925)  评论(2编辑  收藏  举报

导航