OData(02) - Irony构建OData文法,悲剧的Babelua (2)

在Irony框架的帮助下,我们可以忽略词法分析这个过程,这个由框架实现了,我们只需要关注文法规则就好,如上面例子所示,Irony构造规则的代码和原始BNF语言非常接近了,下面我们摘一段OData的ABNF原文给大家参考。

queryOptions = queryOption *( "&" queryOption ) queryOption = systemQueryOption / aliasAndValue / customQueryOption

由于这些因素,所以可以很快的构造出文法,例如我只花了一周时间就实现了OData主要及扩展的文法,但是当这些文法规则复杂到一定程度时,你会发现会出现很多冲突,这也是实际中实现一个文法的主要工作,解决文法冲突,这里列出几种常见的情况及解决方案。下面是OData的文法文档地址:

odata-abnf-construction-rules odata-aggregation-abnf 1. 文法冲突之空白字符

C#、Java中都是以空白字符做为默认的分词字符,不过在OData中是基于URL的,所以原文中声明空白字符、%20及%09都算空白字符,如下所示:

OWS = *( SP / HTAB / "%20" / "%09" ) ; "optional" whitespace RWS = 1*( SP / HTAB / "%20" / "%09" ) ; "required" whitespace BWS = OWS ; "bad" whitespace

这里我实践过,如果按原文的意思去构造会大大增加文法规则的复杂程度,Irony默认也是跳过空白字符。

2. 文法冲突之重复引用冲突

原始的文法是为比较准确的描述出OData的各个元素在文法中的出现位置及组合方式,总而言之如果你完全按原文的定义来写肯定无法实现,如下所示:

navigationProperty = entityNavigationProperty / entityColNavigationProperty entityNavigationProperty = odataIdentifier entityColNavigationProperty = odataIdentifier

上面这段需要合并成下面这一句话,因为人为意识可以知道navigationProperty这个非终结点代表这两种元素,不过对于机器而言navigationProperty的产生式中有两条路,而这两条路对代表一个终点,所以它不知道应该走哪条路,所以产生二义性。

navigationProperty = odataIdentifier

这只是一个复杂例子的缩影,实例中情况可能是这样的,这样必然会冲突。

A = B | C C = D E D = B 3. 文法冲突之重复元素

在文法定义中会有下面这种情况,这表示 queryOptions 是由一个 queryOption 或多个由 “&” 分割的 queryOption 元素组成的,这里Irony有两个很有用的函数 MakeStarRule 与 MakePlusRule 这两个分别对应了BNF中 * 和 1* 这两个操作符。

queryOptions = queryOption *( "&" queryOption ) queryOption = systemQueryOption / aliasAndValue / customQueryOption

这里有个陷阱,如果你声明一个 MakeStarRule 规则,在另一个地方引用它为空则会引发冲突如下代码所示,这时改成 MakePlusRule 即可,

NonTerminal A = new NonTerminal("A"); NonTerminal B = new NonTerminal("B"); Terminal id = new IdentifierTerminal("id") A.Rule = ToTerm("+") + B.Q(); B.Rule = MakeStarRule(B, ToTerm(","), id); this.Root = A;

对于可重复元素规则是发生冲突的比较多的地方,具体只能实际情况对待。

4. 文法冲突之强制移入或归约

文法冲突发生时通过规则自动决定不了接下来的行为是移入还是归约,这个时候可以调用方法强制执行ReduceHere,PreferShiftHere,ReduceIf,ShiftIf,不过还有更复杂的情况发生,例如C#语言中的终结符号 “<" , 这个符号可以表示小于或小于等于以及泛型声明的开始,这里框架根据优先预读的规则可以判断出小于及小于等于的冲突,不过对于泛型而言这时需要向后预读若干个字符如果发现成对的“>”出现则表示为泛型定义,否则为小于号,使用CustomActionHere 这个函数可以自定义代码干预解析行为。

5. 文法冲突之解决不了的冲突

其实理论上是不存在解决不了的冲突,可能是因为复杂度和代价的原因导致不值得去解决这个冲突,这里我们可以修改规则放宽要求而在语法分析解决可以通过,可以把这个验证工作放到语义分析阶段去做。

以上是我分享一些创建文法解决冲突的一些方式。

完整的OData文法实现

目前已经实现了整个 OData URL约定,并通过官方文档的所有示例测试,如下图所示。

OData(02) - Irony构建OData文法,悲剧的Babelua

以下为文法的实现代码,欢迎大家指正错误。

文法实现代码 https://github.com/CarefreeXT/OData/blob/master/src/Caredev.OData/Core/UriParser/ODataGrammar.cs 单元测试代码 https://github.com/CarefreeXT/OData/blob/master/test/Caredev.OData.Tests.AspNet/ODataGrammarTest.cs OData项目及进程

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyygwd.html