ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Antlr词法分析之技巧——保留空白符

2022-09-09 16:01:39  阅读:143  来源: 互联网

标签:ab name Antlr 词法 user test 空白符 id select


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有