開源語法分析器--ANTLR


  序言
有的時候,我還真是懷疑過上本科時候學的那些原理課究竟是不是在浪費時間。比方學完操作系統原理之后我們並不能自己動手實現一個操作系統;學完數據庫原理我們也不能弄出個像樣的DBMS出來;相同,學完編譯原理之后我們好像就僅僅能看着一大堆符號,表和下推自己主動機發呆,然后帶着極其虔誠的心向從事編譯器實現工作前輩致敬,先前些許對某些編譯器小Bug不滿的心情從此不翼而飛。
早在數年前我做一個有關DBMS的模擬試驗的時候,當中就有一部分要求對SQL語言的WHERE語句進行編譯,提取出實用的形式,並保證運算符(AND,OR和括號)各自的優先級。當時我全然是手工用編譯原理中講到的知識作了一個小編譯器,歷時數周,筋疲力盡。而我近期的一個項目要求將一個自己定義的查詢語言(簡化的改動版SQL)進行編譯並以查詢包的形式發送給傳感器網絡。這時我學乖了,不再什么都從輪子造起了 J,開始的時候本來想用YACC/LEX作為底層編譯器,但由於整個應用程序是純Java的,基於可移植性考慮,也由於不想和下面推自己主動機為原理的YACC/LEX生成的一大堆整數表打交道,我選擇了還有一個開源的LL(K)語法/詞法分析器—ANTLR。


1     ANTLR簡單介紹
ANTLR— A,其前身是PCCTS,它為包含Java,C++,C#在內的語言提供了一個通過語法描寫敘述來自己主動構造自己定義語言的識別器(recognizer),編譯器(parser)和解釋器(translator)的框架。ANTLR能夠通過斷言(Predicate)解決識別沖突;支持動作(Action)和返回值(Return Value)來;更棒的是,它能夠依據輸入自己主動生成語法樹並可視化的顯示出來(這一點我將在以下的樣例中演示)。由此,計算機語言的翻譯變成了一項普通的任務—在這之前YACC/LEX顯得過於學院派,而以LL(k)為基礎的ANTLR盡管在效率上還略有不足,可是經過近些年來的升級改動,使得ANTLR足以應付現存的絕大多數應用。感謝Terence Parr博士和他的同事們十幾年來的出色工作,他們為編譯理論的基礎和語言工具的構造做了大量基礎性工作,也直接導致了俄ANTLR的產生。nother Tool for Language Recognition
詞法分析器又稱為Scanner,Lexical analyser和Tokenizer。程序設計語言通常由keyword和嚴格定義的語法結構組成。編譯的終於目的是將程序設計語言的高層指令翻譯成物力機器或虛擬機能夠運行的指令。此法分析器的工作是分析量化那些本來毫無意義的字符流,將他們翻譯成離散的字符組(也就是一個一個的Token)括keyword,標識符,符號(symbols)和操作符供語法分析器使用。
編譯器又稱為Syntactical analyser。在分析字符流的時候,Lexer不關心所生成的單個Token的語法意義及其與上下文之間的關系,而這就是Parser的工作。語法分析器將收到的Tokens組織起來,並轉換成為目標語言語法定義所同意的序列。
不管是Lexer還是Parser都是一種識別器,Lexer是字符序列識別器而Parser是Token序列識別器。他們在本質上是相似的東西,而僅僅是在分工上有所不同而已。
ANTLR將上述兩者結合起來,它同意我們定義識別字符流的詞法規則和用於解釋Token流的詞法分析規則。然后,ANTLR將依據用戶提供的語法文件自己主動生成對應的詞法/語法分析器。用戶能夠利用他們將輸入的文本進行編譯,並轉換成其它形式(如AST—Abstract Syntax Tree,抽象的語法樹)。
2     ANTLR的使用
http://www.antlr.org/下載最新版本號的ANTLR開發包和源代碼(比如版本號2.7.5)。將antlr-2.7.5.jar所在文件夾配置到你的環境變量中,寫好語法文件(比如SensorSQL.g),執行命令“java antlr.Tool SensorSQL.g”就能夠獲得自己主動生成語法/詞法分析器。
2.2 ANTLR語法文件解析
以下我們對圖中所描寫敘述的ANTLR語法文件做一些具體的分析。為了更好的使用ANTLR,你還能夠下載ANTLR的Eclipse插件來幫助你完畢工作。
1.         header域:全部出如今這里的部分,都會出如今由ANTLR編譯之后生成的Java文件的最頂部。在本例中你能夠將包名和其它信息放到這一區域中,生成的結果如由面相應代碼部分所看到的。
2.         你在這一部分所提供的內容對於文件里的每一個語法都是唯一的。這一區域的內容將出如今實際的類定義之前。也就是說,兩個import僅屬於類CalcParser,而不屬於在同一個文件里定義的其它類(如CalcLexer)
3.         這里是語法定義部分,你相同能夠將它看成是類定義。
4.         在Option域中,你能夠為你的語法提供可選項。比如是否建立缺省的抽象語法樹,指定LL(K)中的參數k的值(缺省為1)等等,更具體的參數請參閱ANTLR自帶的手冊。
5.         Token部分用來聲明那些在詞法分析器中沒有被聲明的“想象的”token。這些信息通經常使用在TreeParser中指定“想象”的節點。
6.         這是還有一個Action區,ANTLR將會忠實地將這一區域內的信息放置到類的定義其中,相當於類的成員方法,主要為用戶提供一種在Parser種定制可擴展方法的途徑。
2.3 ANTLR規則(RULE)解析
在ANTLR的語法文件里,一個規則的定義是與一個由ANTLR生成的Java源文件相相應的。
1,2,3,4:正如你所示那樣,我們能夠在一個規則定義中作與一個函數等價的全部事情。我們能夠為規則指定參數(像上面的int a),制定返回值(int c),甚至拋出一個異常。從右半面我們能夠清楚地看到,全部在規則中定義的內容都被忠實而准確的翻譯到Java源文件的對應位置。
5:這一可選的部分為我們提供了指定某些可選參數的能力。比如圖中所看到的代表告訴ANTLR在生成代碼的時候不要生成缺省的錯誤處理部分,這部分將由用戶自己負責。
7:在異常處理部分,我們能夠指定自己定義的異常處理方法。像這里就不過打印錯誤棧信息。
3     ANTLR語法實例—SensorSQL
SensorSQL是一個自己定義的簡化版SQL語言,鑒於篇幅所限,它所支持的語法定義這里就不具體列出了,我僅僅是給出查詢的演示樣例:
通常,編譯一個查詢的目的是要把它轉化成某種被查詢設備能夠理解的形式。通常的做法有兩種,一種是像在上一節中提到的那樣,寫好具體的語法規則,在ANTLR生成對應的Java文件之后,就能夠直接使用其執行結果。這種樣例有非常多,當中最典型的就是對於算數表達式的解析了。對於形如1+2-3*4/5^6這種表達式,僅僅要寫好語法規則,就能夠在解析過程中直接得到運算結果:首先ANTLR將其編譯成逆波蘭結構-- ( - ( + 1 2 ) ( / ( * 3 4 ) ( ^ 5 6 ) ) ) ;在生成語法樹的過程中,同步計算表達式的值,即相似於2.3節中看到的表達式計算。結果例如以下:
只是這樣作有一個缺點,就是在非常多情況下,你可能並不知道要用什么樣的方法來處理。所以當真正要開始寫處理代碼的時候,就要受限於已有的Parser/Lexer中的代碼。一旦要有所改動,就要又一次編譯語法文件,生成新的Java代碼,不勝繁瑣。並且,一旦處理過程有誤,就要重復調試改動Antlr生成的代。自己主動生成的代碼嘛,結構着實也不怎么樣,調試的時候也麻煩。所以假設效率同意的話,就沒有必要讓Antlr作額外的工作,干脆就專心於做他的語法分析也就是了,其它的工作等到生成語法樹之后再怎么遍歷或者折騰都能夠嘛 J
上圖就是我剛才演示的SensorSQL語法分析之后產生的結果。在產生這個結果之后,我須要將每個語法元素翻譯成字節序列打包發送給傳感器網絡。這時候,為了保證Where語句中的優先級,你就能夠依照ANTLR文檔中關於生成語法樹的一章,生成相似於我這種結構,然后僅僅需前序遍歷這顆語法樹的Where部分就能夠達到目的,至於其它部分,順序遍歷一遍就好了。

4     ANTLR Studio
有了前面的基礎之后,我們就能夠開始真正的工作了。只是用“記事本或Editplus+命令行”或者干脆寫個ANT腳本也不是不能夠,可是總認為在集成化IDE滿天飛的時代用這個方式有點過於原始,幸好Placid System 為我們提供了一個Eclipse插件來使我們有機會直接走出原始社會。下載地址為: http://www.placidsystems.com/,眼下最新版本號是1.1.0。唯一令人遺憾的是這個插件盡管功能非常完好,卻是要收費的,否則僅僅有11天的試用期。
4.1 ANTLR Studio插件的安裝
Eclipse下插件的安裝自不必多說,要注意的是從Placid System站點上提供的license文件,下載之后它的名字為license.lic.txt,要把它的后綴名.txt去掉,然后放到ECLIPSE_DIR/plugins/AntlrStudio_x.x.x 文件夾(這里 x.x.x 是版本, 比如 - 1.1.0)。成功安裝之后在Eclipse的工具欄上會出現一個詞法分析器的導航button:
當右鍵單點擊你的project時,你會發現控制是否使用ANTLR Studio的開關:
當打開一個文法文件之后,能夠看到例如以下界面:
在右面的大綱窗體,列有全部Parser和Lexer的元素,能夠看到Protected Token(比如Number)和其它普通的Token是不一樣的;在左面,不同的區域是用不同的顏色塊加亮來區分的。
4.2 功能介紹
ANTLR Studio在Eclipse Help提供了比較詳盡的文檔描寫敘述,所以這里我僅僅介紹一些1.1.0版本號的新功能。
l         全然支持ANTLR 2.7.6,並支持將之前的project自己主動升級到1.1.0版本號。
l         Syntax Diagram View,能夠方便的查看所輸入的語法結構。
l         改進了Debug功能,能夠調試比較大的文法文件。而在這之前,假設一個文法文件非常大的話,ANTLR Studio就會拋出異常。
l         支持自己主動的代碼補全功能,提供一個ANTLR文檔的比較全面的提示信息(例如以下所看到的)。
4.2.1   語法圖表視圖(Syntax Diagram View)
Window->Show View->Other 中選擇顯示這個視圖之后,你就能夠使用這個非常酷的功能了
利用這個視圖,你能夠非常easy的看到你定義語法的語法結構,比如,我的SELECT語句定義例如以下
你僅僅須要將光標標放到selectStatement規則的任何位置,就能夠在Syntax Diagram View中看到:
於是完整的語法結構清晰的顯示在了我們面前。這時你僅僅須要將光標放到脫字符號(^)上面(注:脫字符號用於指明在生成語法樹的時候,脫字符號所在的SubRule要作為樹或子樹的根節點):就會看到:
 
相應的SubRule被加亮成粉紅色,而假設你的光標放到的位置是一個Token的話就會變成淡藍色,簡直太酷了。

4.2.2   增強的Debug功能
想要啟動或關閉ANTLR Studio的Debug功能,須要完畢下面步驟:
l         在project中啟用/取消ANTLR Studio
l         右鍵單擊project,打開“屬性”中的 ANTLR Studio 選項卡。
l         選擇/取消'Enable debugging in grammar files'
       做完這些后,我們就能夠痛快的使用其Debug功能了。與調試其它Java文件一樣,我們能夠在語法文件的任何位置插入斷點:
當程序執行至斷點之后,我們相同能夠像調試普通應用程序一樣使用諸如“跳過”,“繼續”等Java應用程序的Debug方式來進行,十分的方便和順手。

在這篇文章里,我沒有介紹 ANTLR 的各種使用或語言細節,我覺得這些東西網絡上有大把中 / 英文的資料能夠參考,我所關注的是一個大的方向和其最核心的內容。對於那些不想深入了解 ANTLR 實現原理或不想研究其代碼而僅僅想盡快讓它在自己的project中起作用的人來說,依舊足夠了,祝各位 ANTLR 之旅愉快 像我一樣 J  


免責聲明!

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



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