Antlr4入门(三)如何编写语法文件
本章我們將會學習詞法及語法規則,以及四種抽象的計算機語言模式。因為ANTLR的語法規則跟正則表達式是很類似的,所以還是推薦先閱讀下正則表達式的相關內容,這樣在編寫語法文件時可以事半功倍。
一、四種語言模式
雖然在過去的50年里人們發明了許許多多的編程語言,但是,相對而言,基本的語言模式種類并不多。之所以如此,是因為人們在設計編程語言的時候,傾向于將它們設計成與腦海中的自然語言相類似。我們希望符號按照有效的順序排列,并且符號之間擁有著特定的依賴關系。舉個例子,{(}) 就是不符合語法的,因為符號的順序不對。
單詞之間的順序和依賴性約束是來自于自然語言的,基本上可以總結成四種抽象的計算機語言模式。
二、通配符(更多見正則表達式)
常用的通配符如下所示:
下面通過識別一些常見的詞法符號來學習下通配符的用法:
1. 關鍵字、運算符和標點符號:對于關鍵字、運算符和標點符號,我們無須聲明詞法規則,只需在語法規則中直接使用單引號將他們括起來即可,比如 'while'、'+'。
2. 標識符:一個基本的標識符就是一個由大小寫字母組成的字符序列。需要注意的是,下面的ID規則也能夠匹配關鍵字(比如‘while’)等,上章中我們查看了Parser代碼,知道ANTLR是如何處理這種歧義性的——選擇所有匹配的備選分支中的第一條。因此,ID標識符應該放在關鍵字等定義之后。
// 匹配一個或者多個大小寫字母 ID : [a-zA-Z]+;3. 整數:整數是包括正數和負數的不以零開頭的數字。
// 匹配一個整數 INTEGER : '-'?[1-9][0-9]*| '0';4. 浮點數:一個浮點數以一列數字為開頭,后面跟著一個小數點,然后是可選的小數部分。浮點數的另外一個格式是,以小數點開頭,后面是一串數字。基于以上定義,我們可以得到以下詞法規則
FLOAT : DIGIT+ '.' DIGIT* // 1.39、3.14159等| '.' DIGIT+ // .12 (表示0.12);fragment DIGIT : [0-9]; // 匹配單個數字這里我們使用了一條輔助規則DIGIT,將一條規則聲明為fragment可以告訴ANTLR,該規則本身不是一個詞法符號,它只會被其他的詞法規則使用。這意味著在語法規則中不能引用它。這也是一條片段規則(fragment rule)。
5. 字符串常量:一個字符串就是兩個雙引號之間的任意字符序列。
// 匹配"……"之間的任意文本 STRING : '"' .*? '"';點號通配符(.)匹配任意的單個字符,.* 表示匹配零個或多個字符組成的任意字符序列。顯然,這是個貪婪匹配,它會一直匹配到文件結束,為解決這個問題,ANTLR通過標準正則表達式的標記(?后綴)提供了對非貪婪匹配子規則(nongreedy subrule)的支持。
非貪婪匹配的基本含義是:獲取一些字符,直到發現匹配后續子規則的字符為止。更準確的描述是,在保證整個父規則完成匹配的前提下,非貪婪的子規則匹配數量最少的字符。
回到我們的字符串常量定義中來,這里的定義其實并不完善,因為它不允許其中出現雙引號。為了解決這個問題,很多語言都定義了以 \ 開頭的轉義序列,因此我們可以使用 \" 來對字符串中的雙引號進行轉義。
STRING : '"' (ESC|.)*? '"'; // 表示\" 或者 \\ fragment ESC : '\\"' | '\\\\';其中,ANTLR語法本身需要對轉義字符 \ 進行轉義,因此我們需要 \\ 來表示單個反斜杠字符。
6. 注釋和空白字符:對于注釋和空白字符,大多數情況下對于語法分析器是無用的(Python是一個例外,它的換行符表示一條命令的終止,特定數量的縮進指明嵌套的層級),因此我們可以使用ANTLR的skip指令來通知詞法分析器將它們丟棄。
// 單行注釋(以//開頭,換行結束) LINE_COMMENT : '//' .*? '\r'?'\n' -> skip; // 多行注釋(/* */包裹的所有字符) COMMENT : '/*' .*? '*/' -> skip;詞法分析器可以接受許多 -> 操作符之后的指令,skip只是其中之一。例如,如果我們需要在語法分析器中對注釋做一定處理,我們可以使用channel指令將某些詞法符號送入一個“隱藏的通道”并輸送給語法分析器。
大多數編程語言將空白符看成是詞法符號間的分隔符,并將他們忽略。
// 匹配一個或者多個空白字符并將他們丟棄 WS : [ \t\r\n]+ -> skip;至此,我們已經學會了通配符的用法和如何編寫常見的詞法規則,下面我們將學習如何編寫語法規則。
三、語法
語法(grammar)包含了一系列描述語言結構的規則。這些規則不僅包括描述語法結構的規則,也包括描述標識符和整數之類的詞匯符號(詞法符號Token)的規則,即包含詞法規則和語法規則。注意:語法分析器的規則必須以小寫字母開頭,詞法分析器的規則必須以大寫字母開頭。
1. 語法文件聲明
語法由一個為該語法命名的頭部定義和一系列可以互相引用的語言規則組成。grammar關鍵字用于語法文件命名,需要注意的是,命名須與文件名一致。
2. 語法導入
前兩章的例子中,我們都是將詞法規則和語法規則放在一個語法文件中,然而一個優雅的寫法是將詞法規則和語法規則進行拆分。lexer grammar關鍵字用于聲明一個詞法規則文件。如下是一個通用的詞法規則文件定義。
// 通用的詞法規則,注意是 lexer grammar lexer grammar CommonLexerRules; // 匹配標識符(+表示匹配一次或者多次) ID : [a-zA-Z]+; // 匹配整數 INT : [0-9]+; // 匹配換行符(?表示匹配零次或者一次) NEWLINE : '\r'?'\n'; // 丟棄空白字符 WS : [ \t]+ -> skip;然后我們只需要import關鍵字,就可以輕松的將詞法規則進行導入。如下是一個計算器的語法文件。
grammar LibExpr; // 引入 CommonLexerRules.g4 中全部的詞法規則 import CommonLexerRules;prog : stat+; 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| 'clear' # clear;// 為上訴語法中使用的算術符命名 MUL : '*'; DIV : '/'; ADD : '+'; SUB : '-';3. 備選分支命名(標簽)
如果備選分支上面沒有標簽,ANTLR就只會為每條規則生成一個方法(監聽器和訪問器中的方法,用于對不同的輸入進行不同的操作)。為備選分支添加一個標簽,我們只需要在備選分支的右側,以?#?開頭,后面跟上任意的標識符即可。如上所示。需要注意的是,為一個規則的備選分支添加標簽,要么全部添加,要么全部不添加。
4. 優先級
在第二章中我們講述了ANTLR是如何處理歧義性語句(二義性文法)的:選擇所有匹配的備選分支中的第一條。即ANTLR通過優先選擇位置靠前的備選分支來解決歧義性問題,這也隱式地允許我們指定運算符優先級。例如,在上訴的例子中,乘除的優先級會比加減高。因此,ANTLR在解決1+2*3的歧義問題時,會優先處理乘法。
5. 結合性
默認情況下,ANTLR是左結合的,即將運算符從左到右地進行結合。但是有些情況下,比如指數運算符是從右向左結合的。1^2^3應該是3^(2^1)而不是(3^2)^1。我們可以使用assoc來手動指定結合性。
expr : expr '^' <assoc=right> expr // ^ 是右結合的| INT;注意,在ANTLR4.2之后,<assoc=right>需要放在備選分支的最左側,否則會收到警告。
expr : <assoc=right> expr '^' expr // ^ 是右結合的| INT;6. 詞法分析器與語法分析器的界限
由于ANTLR的詞法規則可以使用遞歸,因此從技術角度上看,詞法分析器可以和語法分析器一樣強大。這意味著我們甚至可以在詞法分析器中匹配語法結構。或者,在另一個極端,我們可以把字符當作詞法符號,然后使用語法分析器去分析整個字符流(這種被稱為無掃描的語法分析器scannerless parser)。因此,我們需要去界定詞法分析器和語法分析器具體需要處理的界限。
- 在詞法分析器中匹配并丟棄任何語法分析器無須知曉的東西。例如,需要在詞法分析器中識別和扔掉像空格和注釋諸如此類的東西。否則,語法分析器必須經常查看是否有空格或注釋在詞法符號之間。
- 在詞法分析器中匹配諸如標志符、關鍵字、字符串和數字這樣的常用記號。語法分析器比詞法分析器有更多的開銷,因此我們不必讓語法分析器承受把數字放在一起識別成整數的負擔。
- 將語法分析器不需要區分的詞法結構歸為同一個詞法符號類型。例如,如果我們的應用把整數和浮點數當作同一事物對待,那就把它們合并成詞法符號類型NUMBER。
- 將任何語法分析器可以以相同方式處理的實體歸為一類。例如,如果語法分析器不在乎XML標簽里的內容,詞法分析器可以把尖括號中的所有東西合并成一個單獨的名為TAG的詞法符號類型。
- 另一方面,如果語法分析器需要把一種類型的文本拆開處理,那么詞法分析器就應該將它的各個組成部分作為獨立的詞法符號輸送給語法分析器。例如,如果語法分析器需要處理IP地址中的元素,那么詞法分析器應該將IP的各個組成部分(整數和點)作為獨立的詞法符號送入語法分析器。
后記
本章我們學習了如何編寫語法文件,但是單獨的語法并沒有用處,而與其相關的語法分析器僅能告訴我們輸入的語句是否遵循該語言的規范。為了構建一個語言類應用程序,這是不夠的,我們還需要相應的“動作”去執行語法規則。而這就是下一章的內容——監聽器和訪問器。
?
總結
以上是生活随笔為你收集整理的Antlr4入门(三)如何编写语法文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里巴巴总裁马云语录
- 下一篇: 驾校计算机岗位管理制度,驾校计算机管理制