前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用antlr解析odata filter条件表达式

用antlr解析odata filter条件表达式

作者头像
Bruce Li
发布2020-03-18 12:40:40
3.1K1
发布2020-03-18 12:40:40
举报
文章被收录于专栏:天马行空布鲁斯

这篇文章分享如何用antlr解析odata filter条件表达式。

我最早接触antlr,是在刚开始工作后不久,那次需要用antlr实现一个功能:把gemfire的OQL(object query language)翻译成SQL语句,以便进行数据库操作。其实,简单讲,antlr就是一个非常方便的词法分析和语法分析的类库,基于这个类库,可以很容易的实现很多场景,比如计算器算术表达式的解析、各种编程语言的解析等。

印象很深刻的记得,大学编译原理的课程里面就有类似的两个练习,一个是实现计算器算术表达式的解析,一个是实现C-语言(C语言的简化版)的解析,当时肯定是需要自己手动实现,不能借助这些类库,那如何做的呢?一个很关键的点是状态机,在真正开始实现功能之前,需要根据具体问题的需求画一个状态机(个人觉得和状态图有些类似,或者说是状态图的一种形式),用状态机来描述哪些字符连一起可以构成哪种token,基于这个状态机就可以很方便的实现词法解析。其实,状态机在很多其它地方也有用途,比如:订单的状态变化,其实就可以用状态机来定义。

除了上面提到的场景,还有两个我们平时经常碰到的场景:json解析和html在线编辑器,它们都可以用antlr来实现。

具体odata filter条件表达式的定义可以参考odata官方文档,这里为了描述问题方便,简化基本规则如下:

  • 最小的表达式符合模式 key operator value
  • 表达式和表达式可以用逻辑运算符连接成一个新的表达式 expression AND expression
  • 表达式的前后可以加括号以提高优先级 (expression OR expression) AND expression

根据上面的规则,下面列举几个例子:

  1. $filter=Name eq 'John' //查询所有name等于John的人
  2. $filter=Name eq 'John' AND age ge 30 //查询所有name等于John并且年龄大于等于30岁的人
  3. $filter=(firstName eq 'John' OR firstName eq 'Bill') AND lastName eq 'Smith' //查询所有名为John或Bill,姓为Smith的人

那么,如何解析上面定义的规则呢?

首先,有一种方案:利用关键字(比如eq, AND等)来split这个filter string,在比较简单的情况下也许这个方案可行,但是如果有表达式嵌套的情况(上面第三个例子),直接split string就会有些显得力不从心。

其实,我们可以看到odata filter条件表达式和计算器的算术表达式有些类似,它们都是非常典型的词法分析和语法分析案例,所以同样可以采用antlr来解析。

如果大家以前没有接触过antlr,网上有很多关于它的资料,大家可以自行网上搜索(包括antlr官网https://www.antlr.org/)。下面仅分享一些我使用antlr(antlr 4)解析odata filter条件表达式的经验总结:

  • antlr的简单使用流程:定义grammar->生成对应语言(比如c#)的词法和语法分析代码->实现自己的Visitor遍历抽象语法树AST(abstract syntax tree)。
  • 词法定义规则须大写打头,语法定义规则须小写打头。
  • 从antlr 4.7开始,提供了对所有unicode的支持。关于这个,举一个实际的例子:由于.NET里面的正则表达式\w可以match很多国家的字符(具体有哪些,see https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions#WordCharacter),如果我们需要odata filter条件表达式里面的key也支持\w可以match的字符,怎么做呢?那得益于antlr对Unicode的支持,可以像下面这样定义key: fragment VALID_ID_CHAR : [\p{General_Category=Other_Letter}\p{General_Category=Lowercase_Letter}\p{General_Category=Uppercase_Letter}\p{General_Category=Titlecase_Letter}\p{General_Category=Modifier_Letter}\p{General_Category=Nonspacing_Mark}\p{General_Category=Connector_Punctuation}\p{General_Category=Decimal_Number}] ;
  • 从antlr 4.5开始,c#的runtime换成了Antlr4.Runtime.Standard;之前的版本是用Sam Harwell提供的一个Runtime。参考https://github.com/antlr/antlr4/tree/master/runtime/CSharp。
  • Intellij的antlr的插件提供了实时preview的功能,非常方便调试;VS的插件则没有这功能。
  • 关于odata filter条件表达式的示例grammar文件,可以参考https://github.com/huazailmh/ODataFilterParser。

References

Antlr basics:

  • https://github.com/antlr/antlrcs
  • https://github.com/antlr/antlr4
  • https://github.com/tunnelvisionlabs/antlr4cs
  • https://github.com/antlr/grammars-v4

Unicode support:

  • https://github.com/antlr/antlr4/blob/master/doc/unicode.md
  • https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md#lexer-rule-elements
  • http://unicode.org/reports/tr44/#General_Category_Values
  • https://www.compart.com/en/unicode/category
  • https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions#WordCharacter
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 天马行空布鲁斯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档