經過昨天的實作後,我們建立了加減乘除四個規則,讓計算機可以做兩個正整數的四則運算了。
然而,當規則變多之後,Yacc是怎麼進行處理,讓字串組合匹配到正確的規則呢?
今天,我們便來看看Yacc是如何一步步進行規則匹配的。
在Yacc裡面會有一個空的暫存器(buffer),資料結構為stack,用來放文本所產生的token。
接下來,Yacc會進行兩個主要操作:"Shift" 和 "Reduce”
我們來看看以下的例子吧!
以下是Yacc的語法規則:
func:
VAR '=' expr { printf("%s = %d", $1, $3); }
;
expr:
NUMBER '+' NUMBER { $$ = $1 + $3; }
| NUMBER '-' NUMBER { $$ = $1 - $3; }
| NUMBER '*' NUMBER { $$ = $1 * $3; }
| NUMBER '/' NUMBER { $$ = $1 / $3; }
;
接著,我們給Parser輸入一個文本:
X = 3 + 2
經過lex的token標記後,會變成這樣:
VAR '=' NUMBER '+' NUMBER
至此, Yacc便開始進行規則匹配了。
由於目前buffer內沒東西,所以shift第一個token VAR
VAR
由於沒有匹配到對應的規則,所以繼續shift第二個token '='
VAR '='
由於還是沒有匹配到對應的規則,所以繼續shift第三個token NUMBER
VAR '=' NUMBER
由於還是沒有匹配到對應的規則,所以繼續shift第四個token '+'
VAR '=' NUMBER '+'
由於還是沒有匹配到對應的規則,所以繼續shift第五個token NUMBER
VAR '=' NUMBER '+' NUMBER
此時,序列”NUMBER '+' NUMBER”可以匹配到規則”expr”,因此進行reduce
首先,先將匹配的字串組從stack中移出。
進行化簡後,得到一個非終端符號expr,並進行對應的操作(計算 3+2=5,然後存在變數expr中)
最後,將expr放回buffer內
VAR '=' expr
此時,序列”VAR '+' expr”可以匹配到規則”func”,因此進行reduce
首先,先將匹配的字串組從stack中移出。
進行化簡後,得到一個非終端符號func,並進行對應的操作(印出結果文字: X = 5)
最後,將func放回buffer內
func
最終,buffer中只剩下一個 "func",它代表整個語法樹的根。我們便完成了整個文本的規則匹配。
我們今天終於了解Yacc是怎麼做規則匹配的。如此一來,再複雜字串組結構都能藉由一步步化簡,最終匹配到規則並進行相對應的操作。