在詞彙層面解決這一問題的難度在於很多語言甚至不嘗試的理由。解析器可以很容易地區分一個前綴爲+或-的運算符和具有相同拼寫的二進制中綴運算符,並且除了一個角落案例(參見下文)之外,-2
之間沒有真正的語義區別,被視爲前綴減號後跟一個整數常量,並將-2
視爲單個標記。在解析器中,如果不希望操作符出現在AST中,則可以使用常量摺疊來計算常量子表達式。
只有在詞法掃描過程中,通過維護詞法狀態才能區分中綴詞和前綴詞,這有效地將部分解析算法複製到詞法掃描器中的手工構建狀態機中。在普通算術表達式的情況下,它只是解析算法的一小部分,但即使如此也不美觀並且使得詞法分析器/解析器組合的正確性的驗證複雜化。
離開了操作者優先級和結合,這是不相關這裏,語法爲一個算術表達式可以減少到類似於下面:
expr: term
| expr INFIX term
term: CONSTANT | VARIABLE
| '(' expr ')'
| PREFIX term
(即省略後綴運算符,包括函數調用和陣列下標,但原理不受此影響。)
從該語法中,很容易推導出FIRST,LAST和FOLLOW集合。term
只能與一個終端啓動,並且expr
只能與term
開始,所以它們都具有相同的第一組:
FIRST(expr) = FIRST(term) = { (, PREFIX, CONSTANT, VARIABLE }
通過類似的推理,的term
和expr
最後集也是相同的:
LAST(expr) = LAST(term) = {), CONSTANT, VARIABLE }
最後,FOLLOW集對於非終端,基於該一個term
只出現在一個expr
的端部的觀察,和expr
或者是在輸入結束時,或出現在語法其次是一個終端:
FOLLOW(term) = FOLLOW(expr) = {), INFIX, $ }
(其中$
是結束輸入的標記)
所有這讓我們計算FOLLOW集終端,使用所述觀察到的每個終端A在LAST的非終端V在FOLLOW(V)中只能跟隨一個終端。 (在某些語法可能高估FOLLOW集,但它並沒有在這種情況下,無所謂。)這終於給了我們:
terminal can be followed by
-------- ------------------
INFIX PREFIX, (, CONSTANT, VARIABLE
( PREFIX, (, CONSTANT, VARIABLE
PREFIX PREFIX, (, CONSTANT, VARIABLE
) INFIX,), $
CONSTANT INFIX,), $
VARIABLE INFIX,), $
總之,前綴和中綴從未在同樣的背景下發生的。如果前一個標記是INFIX,PREFIX或((或沒有以前的標記),那麼操作符必須是PREFIX。否則,操作符必須是INFIX。我們可以使用兩個啓動條件在flex中實現:一個用於。我們可能會看到一個常數,而其他的,我們不能合法地看到一個恆定的情況下,第一個是初始狀態的情況下
這轉化成以下小彎曲說明:
%x FINAL
%%
<INITIAL>[-+]?[[:digit:]]+ {
BEGIN(FINAL); return CONSTANT;
}
<INITIAL>[[:alpha:]][[:alnum:]]* {
BEGIN(FINAL); return VARIABLE;
}
<INITIAL>[(] return yytext[0];
<INITIAL>[-] return PREFIX_MINUS;
<INITIAL>[+] return PREFIX_PLUS;
<FINAL>[)] return yytext[0];
<FINAL>[-+*/] BEGIN(INITIAL); return yytext[0];
<*>. /* Signal invalid token */
當然這並不完整,它遺漏了yylval
的設置以及不屬於表達式的輸入的處理(包括換行符和oth呃空白)。
雖然這種解決方案的工作原理,很容易看出爲什麼離開問題解析器是首選:
%%
[-+*/()] return yytext[0];
[[:digit:]]+ return CONSTANT;
[[:alpha:]][[:alnum:]]* return VARIABLE;
但有一個需要小心處理一個角落的情況。在一個Ñ位2的補碼錶示,可以表示-2 Ñ但它不可能代表2 Ñ,由於最大正數是2 Ñ − 1.如果有符號整數作爲表達式推遲到解析器,則至關重要的是允許整數2,即使它不適合所使用的有符號整數類型也是如此。
這可以通過使用無符號整數類型將整數值傳遞給解析器來實現,這意味着解析器將需要檢測溢出條件。
碰巧,這是而不是 C如何處理這種情況,這導致了一個有趣的異常。在C中(如上所述),整數常量不包含符號; -2
是兩個令牌。但是整數常量包含一個(隱式)類型,它在十進制整數的情況下是最小的帶符號的整數類型,它可以保存常量的值。由於一元否定保留了類型,因此結果是在32位機器上-2147483648
的類型爲long
(或long long
),即使它可表示爲int
。這偶爾會造成混淆。
不錯的答案... :) –