其他分享
首页 > 其他分享> > Antlr词法分析之技巧——保留空白符

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