簡介
Spark SQL、Presto --> Antlr4 SQL 解析器
Flink SQL --> Apache Calcite(通過JavaCC 實現)
Spark SQL如何進行語法解析:
Spark SQL 最終是轉換為RDD調用代碼, 然后被Spark Core 執行
Antlr4起的作用就是將SQL語句解析為未解析的邏輯計划
具體流程如下:
對於語法分析樹有兩種遍歷機制:
- Listener:
我們可以自行實現ParseTreeListener來填充自己的邏輯, 每條規則都對應接口enter () 和exit() 方法
不需要顯示遍歷訪問子節點
- vistors:
顯示訪問每個子節點, 每條規則對應接口中visit () 方法
Antlr4應用案例
問題: 實現識別包裹在花括號或者嵌套的花括號中的整數 {1,2,3} 和 {1,{2,3}}
實現:
1. 配置Antlr 運行環境
OS X
$ cd /usr/local/lib
$ sudo curl -O https://www.antlr.org/download/antlr-4.7.2-complete.jar
$ export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
$ alias antlr4='java -jar /usr/local/lib/antlr-4.7.2-complete.jar'
$ alias grun='java org.antlr.v4.gui.TestRig'
- 定義g4 語法文件
/** Grammars always start with a grammar header. This grammar is called
* ArrayInit and must match the filename: ArrayInit.g4
*/
grammar ArrayInit;
/** A rule called init that matches comma-separated values between {...}. */
init : '{' value (',' value)* '}' ; // must match at least one value
/** A value can be either a nested array/struct or a simple integer (INT) */
value : init
| INT
;
// parser rules start with lowercase letters, lexer rules with uppercase
INT : [0-9]+ ; // Define token INT as one or more digits
WS : [ \t\r\n]+ -> skip ; // Define whitespace rule, toss it out
grammars 關鍵字必須與 .g4 文件同名, 如果一個語法文件太大可以拆分成多個文件,相互依賴就是依賴 import + 關鍵字 文件名 語句
語法分析器的規則以小寫字母開頭( init和value)
詞法分析器的規則以大小字母開頭(INT和WS)
- 執行
antlr4 ArrayInit.g4
生成下列文件:
- ArrayInitLexer: 詞法解析器類識別我們語法中的文法規則和詞法規則
- ArrayInitParser: 語法解析器類
- ArrayInit.tokens: ANTLR會給每個我們定義的詞法符號指定一個數字形式的類型
- ArrayInitListener,ArrayInitBaseListener:監聽器類
- 我們實現將{1,2,3 } 識別解析成字符串 “123”, 自定義監聽器:
class ShortToUnicodeString extends ArrayInitBaseListener{
/**
* {@inheritDoc }
*
* <p>The default implementation does nothing.</p>
*/
override def enterInit(ctx: ArrayInitParser.InitContext): Unit = {
print('"')
}
/**
* {@inheritDoc }
*
* <p>The default implementation does nothing.</p>
*/
override def exitInit(ctx: ArrayInitParser.InitContext): Unit = {
print('"')
}
/**
* {@inheritDoc }
*
* <p>The default implementation does nothing.</p>
*/
override def enterValue(ctx: ArrayInitParser.ValueContext): Unit = {
val value = Integer.valueOf(ctx.INT().getText)
print(value)
}
}
將監聽器配置到分析樹上面:
object Translate {
def main(args: Array[String]): Unit = {
val input = new ANTLRInputStream("{1,2,3}")
//新建詞法分析器
val lexer = new ArrayInitLexer(input)
//新建詞法緩沖區,用於存儲分析器生成的詞法符號
val token = new CommonTokenStream(lexer)
//新建語法分析器用於處理詞法緩沖區中的內容
val parser = new ArrayInitParser(token)
//針對規則開始語法分析
val tree = parser.init(); // begin parsing at init rule
val walker = new ParseTreeWalker()
//遍歷解析期間創建的樹,觸發回調
walker.walk(new ShortToUnicodeString, tree)
println()
}
}
- 執行結果:
總結
本篇主要講解了Antlr4解析器以及Spark SQL 的解析流程, 介紹了Antlr4抽象樹兩種遍歷機制:listener 和visitor, 同時實現了一個簡單的語法通過 Antlr4 listener方式遍歷解析的案例.
下篇會介紹visitor 模式的案例以及實現一些語法並且會轉換為Spark RDD去執行.