The Definitive ANTLR 4 Reference (2nd Edition) - Chapter 1

Chapter 1 - Meet ANTLR

本書Part I中,我們的目標是對ANTLR的能力有一個整體的認知,並嘗試探索語言應用框架。一旦我們有了整體的概念,我們接下來就會在Part II 中通過大量的真實案例系統學習ANTLR。本書開始,我們要先安裝ANTLR,然後嘗試製作一個簡單的“hello world”的grammer。

1.1 安裝ANTLR

ANTLR是用Java編寫的,所以在開始使用之前需要有完整的Java環境。ANTLR需要Java version 1.6或者更高。本章內容的的實時更新可見ANTLR的Github主頁

爲什麼這本書使用命令行工具?

在整本書中,我們會使用命令行(shell)運行ANTLR以及構建我們的應用。由於編程人員會使用不同的操作系統以及開發環境,操作系統的shell是我們唯一共同使用的“interface”。使用shell能夠使得語言應用開發和構建過程的每一步變得更加明確。本書中我會一一直使用Mac OS X系統的Shell,不過使用的命令應該在Unix及Unix-like的shell中也能使用,在Windows系統中可能有一些細微的不同。

安裝ANTLR本身其實僅僅就是下載最新的jar包,當前的jar包(這是本書寫時最新的jar包,建議讀者也下同樣的包)是antlr-4.0complete.jar,將其下載並放到合適位置。這個jar包包含了運行ANTLR tool以及編譯和執行由ANTLR生成的recognizers所需要的runtime library。在命令行中,ANTLR tool能夠將識別grammar定義的語言的語句,將grammars轉換成programs。比如,給定一個JSON的grammar,ANTLR tool能夠使用ANTLR runtime library中的相關支持類,生成一個能夠解析JSON輸入流的程序。

這個jar包也包含了兩個支持庫:一個複雜樹佈局庫(sophisticated tree layout library),以及一個StringTemplate。後者是用於生成代碼和其它結構化文本的模板引擎。在version 4.0中,ANTLR仍然是在ANTLR v3中寫的,所以這個jar包也包含了之前版本的ANTLR。

The StringTemplate Engine
StringTemplate 是一個用於生成源代碼,網頁,郵件或者任何格式化文本的Java模板引擎(with ports for C#, Python, Ruby, and Scala)。在多目標代碼生成器,多種網站外觀,以及國際化/本地化中尤爲擅長。jGuru.com這個網站的構建過程一直在使用它。StringTemplate也爲ANTLR v3和v4的code generators提供了大力的支持。更多信息可見About頁面。

你可以從ANTLR網站通過瀏覽器手動下載ANTLR,或者在命令行中使用curl工具抓取它。

cd /usr/local/lib
curl -0 http://www.antlr.org/download/antlr-4.0-complete.jar

在Unix中,/usr/local/lib是存儲jar包的比較合適的目錄。在Windows系統中,沒有相關的建議,所以你可以將其簡單存儲在你的工程目錄中。大多數的開發環境希望你能將你的jar包放到你的應用工程的依賴列表中。對於ANTLR來說,不需要配置腳本和配置文件,你只需要確保Java能夠檢測到該jar包即可。

由於本書通篇都在使用命令行,所以或許你有必要通過設置CLASSPATH環境變量以方便命令調用。在Unix系統中,你可以在shell中執行一下的命令,或者將其放到開機自動執行的腳本中(.bash_profile等等):

export CLASSPATH=".:/usr/local/lib/antlr-4.0-complete.jar:$CLASSPATH"

設置好以後,可以通過直接運行不攜帶參數的ANTLR tool確保安裝是否成功。有以下兩種方式:

$ java -jar /usr/local/lib/antlr-4.0-complete.jar # launch org.antlr.v4.Tool
ANTLR Parser Generator Version 4.0
-o ___ specify output directory where all output is generated
-lib ___ specify location of .tokens files
...

$ java org.antlr.v4.Tool # launch org.antlr.v4.Tool
ANTLR Parser Generator Version 4.0
-o ___ specify output directory where all output is generated
-lib ___ specify location of .tokens files
...

每次運行ANTLR的時候都使用上面的命令無疑是很痛苦的,因此我們最好使用alias命令或者寫一個腳本。在本書中,我們使用alias命令定義antlr4:

$ alias antlr4='java -jar /usr/local/lib/antlr-4.0-complete.jar'

然後,就可以用antlr4代替上面的java命令了:

$ antlr4
ANTLR Parser Generator Version 4.0
-o ___ specify output directory where all output is generated
-lib ___ specify location of .tokens files
...

1.2 執行ANTLR以及測試Recognizers

下面是一個簡單的grammar,能夠理解像“hello world”這樣的語句:

grammar Hello; // Define a grammar called Hello
r : 'hello' ID ; // match keyword hello followed by an identifier
ID : [a-z]+ ; // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines, \r (Windows)

我們將這個grammar文件Hello.g4放到自己的目錄下,比如tmp/test。然後我們能在其中運行ANTLR,並且編譯結果。

$ cd /tmp/test
$ # copy-n-paste Hello.g4 or download the file into /tmp/test
$ antlr4 Hello.g4 # Generate parser and lexer using antlr4 alias from before
$ ls
Hello.g4 HelloLexer.java HelloParser.java
Hello.tokens HelloLexer.tokens
HelloBaseListener.java HelloListener.java
$ javac *.java # Compile ANTLR-generated code

對Hello.g4文件執行ANTLR tool會生成一個由HelloParser.javaHelloLexer.java生成可執行的recognizer,但是我們沒有一個main program來觸發對語言的識別(下一個chapter中我們會學習到什麼是parsers和lexers)。在構建實際應用之前,你可能會使用幾個不同的grammars。因此不對每一個新生成的grammar都生成一個main program是比較好的行爲。

這裏理解沒有man program,應該就是生成的java文件中,都沒有包含main方法的意思吧。

ANTLR在runtime library中提供了一個靈活的測試工具,叫TestRig。它能夠輸出很多和recognizer如何匹配輸入文本相關的有用的信息,TestTig使用Java的反射機制調用表一號的recognizers。和之前相似,此時最好用alias命令簡化以下TestRig的調用:

$ alias grun='java org.antlr.v4.runtime.misc.TestRig'

這個測試命令需要一個grammar名作爲參數,一個rule名,以及指明輸出相關的參數。在本例中,我們想輸出在recognition期間創建的tokens。tokens是像keyword “hello”和identifier “part”這樣的詞彙符號(vacabulary symbols):

➾ $ grun Hello r -tokens # start the TestRig on grammar Hello at rule r
➾ hello parrt # input for the recognizer that you type
➾ EOF # type ctrl-D on Unix or Ctrl+Z on Window
❮ [@0,0:4='hello',<1>,1:0] # these three lines are output from grun
[@1,6:10='parrt',<2>,1:6]
[@2,12:11='<EOF>',<-1>,2:0]

在輸入grun命令,回車進入新的一行後,計算機會靜默等待你的符合“hello parrt”類型的輸入。其後,需要輸入文件終止符來結束本次輸入,否則命令行會一直等待下去。一旦recognizer已經讀進來了所有的輸入,由於使用了-tokens參數,TestRig會打印出使用tokens的列表。

輸出的每一行都表示一個單獨的token被使用,並且會顯示我們對該token所知的所有信息。比如,[@1,6:10='parrt',<2>,1:6],按照順序其表示意義如下:

  • @1 —— 這是第二個token,token下標從0開始。
  • 6:10 —— 這個token是從第6個字符到第10個字符(字符下標衝0開始,包括6和10。)。
  • =’parrt’ —— 這個token的文本是parrt
  • <2> —— 這個token的type是2(ID)。
  • 1:6 —— 這個token出現在輸入文本的第一行(行數從第1行開始數),起始位置字符是6(起始位置字符下標從0開始,將tabs看作單個字符)。

我們也能以LISP風格的文本格式打印這個解析樹:

$ grun Hello r -tree
➾ hello parrt
➾ EOF
❮ (r hello parrt)

最方便查看一個grammar如何recognize輸入的方法是在可視化的解析樹中查看。使用-gui參數可以做到。命令是:

$ grun Hello r -gui

然後會生成下述的圖形框:
這裏寫圖片描述

不使用任何參數運行TestRig,會打印一些幫助信息:

$ grun
java org.antlr.v4.runtime.misc.TestRig GrammarName startRuleName
[-tokens] [-tree] [-gui] [-ps file.ps] [-encoding encodingname]
[-trace] [-diagnostics] [-SLL]
[input-filename(s)]
Use startRuleName='tokens' if GrammarName is a lexer grammar.
Omitting input-filename makes rig read from stdin.

在本書中,我們主要使用的參數及其簡單解釋如下:

  • -tokens —— 打印token流
  • -tree —— 以LISP風格打印解析樹
  • -gui —— 在對話框中顯示解析樹
  • -ps file.ps —— 以PostScript方式生成解析樹的可視化表現,並存儲在file.ps中。本chapter中解析樹的圖是由-ps生成的。
  • -encoding encodingname —— 指定輸入文件的編碼格式。
  • -trace —— 打印rule名以及當前的token,並且退出。
  • -diagnostics —— 在解析過程中打開診斷信息。這會爲不常見的場景生成詳細,比如會引起歧義的短語。
  • -SLL —— 使用一個更快,功能更弱的解析策略。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章