看这篇之前,对antlr没有了解的可以自行百度。
用过antlr语法解析的都会遇到需要写listener和 visitor的时候,看我之前写过的这篇
松仁普:antlr解析语法树的使用zhuanlan.zhihu.com如果有一套DSL语言让你不用写很多listener和visitor你会用吗?
举个例子:你在查找一个SQL中的表名的时候:
select * from A join B on A.xx = B.xx where A.yy = yy
用listener可以轻松搞定,但是需要写一大段代码。
如果提供了一个工具类 ComplexQuery,调用方法 query("tableName")就返回了你想要的,你觉得是不是很好用。 这里"tableName"不是随意的字符串,而是q4语法文件中定义的节点。
这里我们用facebook的开源项目presto中的sql.q4文件为例,因为大而全,这个东西自己写会累死,而且考虑不周全。
姑且认为它是个sql完全体了,目前的大大小小sql它都能语法推导。
我们举一个简单的栗子,客官看下面这段SQL:
select
此时有个需求给到了你,“取出表B_t中的b,c字段”,很多童鞋看到这里,可能想到了用正则,但是正则匹配难度会比较大,本人正则比较菜,这里不过多讨论了。
如果了解过antlr,写过listener、visitor程序也觉得该需求好实现,相比于正则肯定是简单容易的多。
如果不信,我们需求再变态点,正则规则会越来越复杂。
比如说,我们这里的SQL还是比较简单的,现在加上where条件.
select
我随便举得这个栗子,不要在意sql不好看,这里我如果要取的 b,c字段是来自子查询中要有限制条件“e=0 且 不能 有 g=1 ” 的。 显然我们肉眼可见这里是无解的。
但是你用正则怎么写呢? 如果你能写出来,可以留言评论,我会再升级个变态3.0,总归能让你写的发狂。。
这里举这种栗子是为了引出下面的基于listener和visitor代码之上的DSL查找器。
主体代码就是三行
ComplexQuery
说明:
dsl 的值是下面插件第二行输入框的内容 “querySpecification:not( tableName:text(A_t)) d”
sql 的值是 第一行输入的内容 ,即SQL本尊
set 返回时一个ParseTree集合 ,即语法树节点集合
以上插件本质上就是上面三行代码实现的,这里为了直观看到效果,所以开发了IDEA的插件,方便直观看到节点选择跟随DSL的变化。还是比较实用的哈,觉得我这段不错的可以在github给star哦。(写文章真心不容易呀)
antlr_query的git地址:
prs1022/antlr_querygithub.comantlr语法高亮插件的git地址:
prs1022/a_antlr_plugingithub.com如果不想下载插件源码,直接安装这个插件,这里我发个百度云下载链接:
链接:https://pan.baidu.com/s/1E_Sg7NyScdzhJZkxNhAdLg
提取码:vux4
目前支持的查找指令如下:
- :is() 为以is之前查询的节点为根、按照括号内的查询语句进行后续查找 查找结果不为空时保留:is之前的筛选到的节点
- > 为相邻子节点查找
- :not()__ 与 :is() 相反
- :text(reg)__ 为节点 文本内容的匹配筛选 可以应用正则匹配
- 空格 为全部子孙节点的查找
- * 为任意节点都可以匹配的一个占位符
- + 相邻后继节点
- ~ 所有后继兄弟节点
eq(index) 根据下标获取还未实现
工作学习中有特殊需要可以自行添加,需要改下g4文件添加新语法支持
写在最后:
该项目不是我一人所开发,主要贡献者是前同事项目经理,也是我导师,还有很多待完善的地方。在此也对我导师表示感谢。
当初为什么要做这件事呢? 因为日常开发中写了太多listener和visitor,而熟悉的人也知道很多代码都是为了查找节点。
js中获取一个dom节点的API是这样的“getDocumentById(xxxx)” ,而用过jquery的都知道(当然现在jquery也不流行了),jquery中提供了丰富的选择器API,类似 $(‘p’)获取<p>元素,复杂点$("div#intro .head") 获取 id="intro" 的 <div> 元素中的所有 class="head" 的元素。
此时,大佬PM就想到antlr解析的语法树也是节点树,可以类比jquery的实现思路,我们也定义一套DSL语言,基于antlr语法文件之上的一套语法文件,这样可以帮开发人员省略大量listener,visitor查找节点的代码。
想到就去做,无奈当时的我听了是一脸懵逼,不过这个idea我是赞同的,只是实现起来由于我不理解没能完成。后面PM写了主体代码我才理解。时隔一年,我将它整理一下写成文章,放到github上也算开源了,供有识之士一起学习研究。感兴趣一起讨论的可以联系我。