介绍C# Parsing Library

开发 后端
本文介绍了C# Parsing Library,即一个模仿Boost.Spirit的LL解析器库,可以在C#中模拟ENBF文法定义。

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

一)C# Parsing Library: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); 
  • 1.

Scanner类主要存储一个字符串输入,及一个光标位置,光标随着解析的进行向前移动。

例子:一个整数解析器, 定义为一个可选的符号后面跟若干数字:

Parser signed = (Parser.Lit('+') | '-').Opt;  
Parser p = (signed + Parser.DigitChar.Plus).Lexeme;  
bool success = p.Parse(new Scanner("-123")); 
  • 1.
  • 2.
  • 3.

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

二)C# Parsing Library:ParserRef

一个常用的四则运算表达式文法:

group      ::= '(' expression ')' 
factor     ::= integer | group  
term       ::= factor (('*' factor) | ('/' factor))*  
expression ::= term (('+' term) | ('-' term))* 
  • 1.
  • 2.
  • 3.
  • 4.

用下面的方法是错误的:

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

但是使用ParserRef 就可以了:

ParserRef group = new ParserRef();  
ParserRef factor = new ParserRef();  
factor.Parser = Parser.Int | group;  
  • 1.
  • 2.
  • 3.

完整的定义如下:

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; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

三)C# Parsing Library: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) ] 
  • 1.

其语义为: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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

说明:

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

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

【编辑推荐】

  1. 简单易懂的C#.NET多线程应用
  2. C#注册表是如何操作的
  3. C#扩展方法:对扩展进行分组管理
  4. Visual C# 3.0新特性的总结
  5. 网站安全性:C#防SQL注入代码的实现方法

 

责任编辑:book05 来源: cnblogs
相关推荐

2009-08-12 18:35:36

C# ArrayLis

2009-08-25 10:24:29

C# delegate

2009-08-10 16:30:56

C# BitmapDa

2009-08-17 16:47:51

C# Anonymou

2009-07-31 14:15:38

C# 构造函数

2009-08-04 08:48:44

C#内置特性

2009-09-03 15:57:11

C# SystemMe

2009-08-12 09:41:28

C# Director

2009-08-12 15:34:40

C# DBNull

2009-08-12 15:43:02

操作C# Datase

2009-08-18 16:45:40

C# Raw Sock

2009-09-17 18:14:05

C#动态数组

2009-09-07 15:40:06

2009-08-25 13:38:35

C# Timer组件

2009-08-13 17:36:54

编译C#代码

2009-08-26 11:30:16

C# Arraylis

2009-08-06 18:15:13

C# SQL Serv

2009-08-18 17:17:05

C#局部类型

2009-08-07 16:10:20

C#调用API

2009-08-20 16:25:59

C# 匿名方法
点赞
收藏

51CTO技术栈公众号