Antlr4 語法解析生成器(上)


簡介

Spark SQL、Presto --> Antlr4 SQL 解析器

Flink SQL --> Apache Calcite(通過JavaCC 實現)

Spark SQL如何進行語法解析:

Spark SQL 最終是轉換為RDD調用代碼, 然后被Spark Core 執行

Antlr4起的作用就是將SQL語句解析為未解析的邏輯計划

具體流程如下:

對於語法分析樹有兩種遍歷機制:

  1. Listener:

我們可以自行實現ParseTreeListener來填充自己的邏輯, 每條規則都對應接口enter () 和exit() 方法

不需要顯示遍歷訪問子節點

  1. 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' 
  1. 定義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)

  1. 執行 antlr4 ArrayInit.g4 生成下列文件:

  • ArrayInitLexer: 詞法解析器類識別我們語法中的文法規則和詞法規則
  • ArrayInitParser: 語法解析器類
  • ArrayInit.tokens: ANTLR會給每個我們定義的詞法符號指定一個數字形式的類型
  • ArrayInitListener,ArrayInitBaseListener:監聽器類
  1. 我們實現將{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()
    }
}
  1. 執行結果:

總結

本篇主要講解了Antlr4解析器以及Spark SQL 的解析流程, 介紹了Antlr4抽象樹兩種遍歷機制:listener 和visitor, 同時實現了一個簡單的語法通過 Antlr4 listener方式遍歷解析的案例.
下篇會介紹visitor 模式的案例以及實現一些語法並且會轉換為Spark RDD去執行.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM