上一節(jié),我們通過(guò)Antlr快速的落地實(shí)現(xiàn)了Token的解析,這一節(jié)我們還是基于Antlr來(lái)實(shí)現(xiàn)語(yǔ)法的解析。
語(yǔ)法分析相對(duì)來(lái)講就復(fù)雜多了,我們需要對(duì)Token進(jìn)行推導(dǎo)與組合,生成我們想要表達(dá)的式子。
我們先來(lái)看一個(gè)簡(jiǎn)單的例子:
a + b * c
這個(gè)看上去很簡(jiǎn)單的表達(dá)式,我們最終想要得到的結(jié)果是b * c 再與 a相加,而不是a + b再乘以 c。
這里就涉及到一個(gè)優(yōu)先級(jí)的問(wèn)題,Antlr支持通過(guò)右邊產(chǎn)生式的順序來(lái)定義優(yōu)先級(jí)。
語(yǔ)法規(guī)則是由上下文無(wú)關(guān)文法表示的,而上下文無(wú)關(guān)文法是由一組替換規(guī)則(又叫產(chǎn)生式)組成的,比如算術(shù)表達(dá)式的文法規(guī)則可以表達(dá)成下面這種形式:
add -> mul | add + mul
mul -> pri | mul * pri
pri -> Id | Num | Operator
pri表示基礎(chǔ)表達(dá)式,他可以推導(dǎo)成Id(標(biāo)識(shí)符),Num(數(shù)字), Operator(操作符)
mul表示可以推導(dǎo)成一個(gè)基礎(chǔ)表達(dá)式或者是mul乘以pri
add表示可以推導(dǎo)成一個(gè)mul或者add 加上 mul
按這個(gè)規(guī)則我們來(lái)推導(dǎo)一下a + b * c.
優(yōu)先級(jí)高的后推導(dǎo),優(yōu)先級(jí)低的先推薦。
嘗試將這個(gè)式子推薦成add,發(fā)現(xiàn)剛好符合要求add+mul
add+mul 推導(dǎo)成mul+mul
再推導(dǎo)成pri+mul
再推導(dǎo)成pri+mul*pri
再推導(dǎo)成pri+pri*pri
最后推導(dǎo)成pri+pri*pri
以上的推導(dǎo)是建立在你有一定的編譯器前端認(rèn)識(shí)的基礎(chǔ)之上,在這之前你需要知道推導(dǎo)的左遞歸與結(jié)合性的問(wèn)題。
Antlr已經(jīng)幫我們處理好了左遞歸,我們可以放心的按左遞歸的規(guī)則來(lái)書(shū)寫(xiě)。
至于結(jié)合性的問(wèn)題,正如我上面提到的,a + b * c 不能推導(dǎo)成a + b 再乘以c,我們可能通過(guò)Antlr規(guī)則產(chǎn)生式的順序來(lái)確保結(jié)合性的正確。
上面說(shuō)了很多都比較空洞, 接下來(lái)我們通過(guò)Antlr來(lái)實(shí)現(xiàn)我們的語(yǔ)法分析
grammar FlexDSLScript;
import FlexDSLLexer;
/// 表達(dá)式,按右邊產(chǎn)生式的順序來(lái)依次優(yōu)先推導(dǎo)
expression:
primary
| dot = '.' expression
| expression dot = '.' expression
| '(' expression ')'
| FOR Id IN Id
| expression postfix = ('++' | '--')
| prefix = ('++' | '--') expression
| expression bop = ('*' | '/' | '%') expression
| expression bop = ('+' | '-') expression
| expression bop = ('<' | '<=' | '>' | '>=') expression
| expression bop = ('==' | '!=') expression
| expression bop = ('&&' | '||') expression
| expression bop = '?' expression bop = ':' expression;
primary:
Id
| StringLiteral
| IntLiteral
| DoubleLiteral
| TF = (True | False);
首先我們需要導(dǎo)入語(yǔ)法分析規(guī)則
我們定義了最基礎(chǔ)的表達(dá)未
primary, 他可以推導(dǎo)出Id(標(biāo)簽符: 變量名稱),
StringLiteral(字符串字面量),
IntLiteral(整形字面量),
DoubleLiteral(浮點(diǎn)書(shū)字面量),
TF(true|false)
expression 通過(guò)順序定義了推導(dǎo)邏輯,優(yōu)先級(jí)高的寫(xiě)在前面,優(yōu)先級(jí)低的寫(xiě)后面 寫(xiě)好規(guī)則文件之后我們來(lái)編譯一下
antlr4 FlexDSLScript.g4
javac *.java
編譯完成后運(yùn)行
grun FlexDSLScript expression -gui
在終端里輸入這個(gè)表達(dá)式,然后按Alt+D(mac), Windows應(yīng)該是Control+D,輸入一下結(jié)束符,
接下來(lái)java彈出一個(gè)對(duì)話框,直觀的展示了解析后的AST
通過(guò)遍歷這個(gè)AST我們就可以得到這個(gè)表達(dá)式最后的結(jié)果了。
下一節(jié)我們來(lái)實(shí)現(xiàn)語(yǔ)義分析,也就是對(duì)AST的遍歷求運(yùn)算。
-
語(yǔ)法
+關(guān)注
關(guān)注
0文章
44瀏覽量
10126 -
ANTLR
+關(guān)注
關(guān)注
0文章
3瀏覽量
5822
發(fā)布評(píng)論請(qǐng)先 登錄
Stanford編譯原理詳解


關(guān)于antlr詞法分析器的使用
Linux內(nèi)核中GNU C擴(kuò)展的一些常用C語(yǔ)言語(yǔ)法分析
一個(gè)高效的語(yǔ)法分析器生成工具
YACC在ATLAS語(yǔ)言語(yǔ)法分析中的沖突消解研究
編譯原理實(shí)踐環(huán)節(jié)模擬試題
借助Lex和Yacc進(jìn)行詞法語(yǔ)法分析
基于ANTLR的試卷識(shí)別和導(dǎo)入系統(tǒng)

Java程序的工作原理是怎樣的
開(kāi)源L2C編譯器前端語(yǔ)法分析器及驗(yàn)證過(guò)程
自頂向下的語(yǔ)法分析器—采用遞歸下降方法

評(píng)論