Trino SQL 解析引擎 Antlr4

2022-05-12 00:00:00 分析 规则 生成 语法 词法

众所周知,Trino 原生支持 SQL,可以大大降低数据分析的难度,方便大家上手数据分析工作。但是 Trino 如何识别用户输入的 SQL,并且能正确的解析呢?今天来简单的看看 Trino SQL 解析引擎 -- Antlr4。网络上关于 Trino/Presto 的文章是真的少,之前搜索 Antlr4 Presto 出来的文章比较少,这次搜索 Anltr4 Spark,就有很多资源出来,看来 Spark 的热度还是高(Spark SQL 也是使用的 Anltr4 做解析)

声明一下,本文虽然是自己原创,但是借鉴了网络上的的内容,主要是做知识的归纳总结,如有雷同,纯属我借鉴~


ANTLR(全名:ANotherTool forLanguageRecognition)是基于LL(*)算法实现的语法解析器生成器(parser generator),用Java语言编写,使用自上而下(top-down)的递归下降LL剖析器方法。由旧金山大学的Terence Parr博士等人于1989年开始发展。(好家伙,比我还大)

上面的描述来自维基百科,可以看到 Antlr 可以生成语法解析器,好像也并没有展示它有多厉害。实际上,它的功能十分强大。

你只需要定义一份语法规则的文件,Antlr 会自动的根据你定义的语法规则,帮你进行词法分析语法分析,并且生成一颗语法树,可以通过 Listener 方式以及 Visitor 模式进行语法树的遍历。

Antlr 的语法分析分为两个阶段

  • 个阶段是将字符聚集为单词或者符号的过程称为词法分析,可以实现这一过程的程序叫词法分析器
  • 第二个阶段是语法分析输入的词法符号来识别语句结构,生成语法树

前面的 LEXER 就是词法分析,例子中的 sp = 100; 被分词成四部分(4 个 token),sp、‘=’、100、‘;’,这个过程就是词法分析,然后根据定义的语法规则,后面的 PARSER 就是语法分析,会帮你生成了一个对应的语法树。这一切只需要你提供一份语法规则以及一个输入语句。

至于这个语法规则怎么写,就要遵循 Antlr4 的语法了,对于上面的这个图,我一直没有找到对应的语法规则,就自己写了个简易的,大概结构是我这个样子,大家注意看树的根以及各叶子节点的命名与我下图中规则对应的命名方式。可以通过右击 stat 选择 Test Rule stat,查看语法树的生成,从左边输入语句,右边会产生对应的语法树。

这个只是让大家可以直观的了解到 Antlr 可以做什么,但其实它还在幕后做了很多事情。接下来通过一个计算器的例子,让我们用代码去操纵 Antlr,完成我们想要的功能。

这个例子的目的就是支持简单的四则运算,例子从网上找来的,但是可以清楚的知道使用 Antlr 实现我们需求的过程,由于在 Trino 中使用的是 Visitor 模式,所以这个例子也是使用了 Visitor 模式对语法树进行遍历。

grammar Test; // 规则需要命名为 Test.g4

prog:   stat+ ;  // 右击 prog -> Generate ANTLR Recognizer

stat:   expr NEWLINE                # printExpr
    |   ID '=' expr NEWLINE         # assign
    |   NEWLINE                     # blank
    ;

expr:   expr op=('*'|'/') expr      # MulDiv
    |   expr op=('+'|'-') expr      # AddSub
    |   INT                         # int
    |   ID                          # id
    |   '(' expr ')'                # parens
    ;


MUL :   '*' ; // assigns token name to '*' used above in grammar
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
ID  :   [a-zA-Z]+ ;      // match identifiers
INT :   [0-9]+ ;         // match integers
NEWLINE:'\r'? '\n' ;     // return newlines to parser (is end-statement   signal)
WS  :   [ \t]+ -> skip ; // toss out whitespace

相关文章