Antlr词法分析之技巧——保留空白符
作者:互联网
Antlr是一个功能非常强大的编译器前端工具。
之前我们都把关注点放在他的语法分析上,其实它在词法分析方面也有很多强大的功能。
比方我们有一个SQL,但有些子查询没有写库名,我们想给他补充上库名,将SQL重新打印出来。
这个看似简单,其实也要经历词法分析、语法分析,将所有表名符号识别出来。
insert overwrite table test_user.user_info select ab.id,ab.name,c.dept from (select a.id,b.name from (select id,name,sex,age from test_user.date_user)a join (select id,name from d_user)b on a.id=b.id)ab JOIN (select eid,name,dept from employee)c on ab.id=c.eid
如果走正常词法分析、语法分析的路子。
public class InsertDbListenerTest { static String sql = "insert overwrite table test_user.user_info \n" + "select ab.id,ab.name,c.dept from \n" + "(select a.id,b.name from \n" + "(select id,name,sex,age from test_user.date_user)a \n" + "join\n" + "(select id,name from d_user)b \n" + "on a.id=b.id)ab \n" + "JOIN \n" + "(select eid,name,dept from employee)c \n" + "on ab.id=c.eid"; public static void main(String[] args) { System.out.println(sql); CharStream input = CharStreams.fromString(sql); HqlLexer hqlLexer = new HqlLexer(input); CommonTokenStream tokenStream = new CommonTokenStream(hqlLexer); HqlParser hqlParser = new HqlParser(tokenStream); HqlParser.ProgramContext tree = hqlParser.program(); // InsertDbListener listener = new InsertDbListener(tokenStream); ParseTreeWalker walker = new ParseTreeWalker(); walker.walk(listener, tree); System.out.println(listener.rewriter.getText()); } } public class InsertDbListener extends HqlBaseListener { TokenStreamRewriter rewriter; public InsertDbListener(TokenStream tokens) { this.rewriter = new TokenStreamRewriter(tokens); } @Override public void enterTable_name(HqlParser.Table_nameContext ctx) { String tb="test."; if(!ctx.getText().contains(".")){ rewriter.insertBefore(ctx.start,tb); } } }
我们启用了一个特别的listener,使用TokenStreamRewriter功能,在每个没有库名的表名前面加上库名,得到的输出结果是
insertoverwritetabletest_user.user_infoselectab.id,ab.name,c.deptfrom(selecta.id,b.namefrom(selectid,name,sex,agefromtest_user.date_user)ajoin(selectid,namefromtest.d_user)bona.id=b.id)abJOIN(selecteid,name,deptfromtest.employee)conab.id=c.eid
仔细一看,库名的确是补上了,但是既没有空格,也没有换行,结果简直没法看!
这是为什么?
我们知道,一般在词法分析中,空白符会被识别出来,然后丢弃,不会进入语法分析器。
这个是可以理解的,毕竟空白符跟语法也没有半点关系。
但有些时候,就比如我们这个需求里,我们需要保留空白符、甚至注释什么的,那该怎么做呢?
其实非常简单,只需要修改一行语法文件
将语法文件中的
WS : L_BLANK+ -> skip ;
修改为
WS : L_BLANK+ -> channel(HIDDEN) ;
即可。这个改动的意义是将词法分析匹配到的空白符写入一个隐藏通道,不传递给语法文件。但也没有丢弃。
重新生成antlr的词法分析和语法分析文件,我们自己写的代码一点都不用改。
再执行一遍,输出结果
insert overwrite table test_user.user_info select ab.id,ab.name,c.dept from (select a.id,b.name from (select id,name,sex,age from test_user.date_user)a join (select id,name from test.d_user)b on a.id=b.id)ab JOIN (select eid,name,dept from test.employee)c on ab.id=c.eid
完美!库名补充上了。换行符都在,空格也都在,正是我们想要的结果。
标签:ab,name,Antlr,词法,user,test,空白符,id,select 来源: https://www.cnblogs.com/wangbin2188/p/16673129.html