2016-01-20 38 views
1

我有一個flex文件,根據輸入文件返回我的令牌。我目前設置爲當我在flex中看到一個帶符號的int時返回一個令牌INT,並且當我看到一個加法或減法的時候,我會設置一個令牌ADD或SUB。Flex:匹配int整數與加/減

我有一個符號整數定義的宏彎曲

signedint = [+-]?[0-9]+ 

時,Flex看到這種模式,它返回一個int令牌。

但是,我遇到了能夠區分帶符號的整數與加法或減法的問題。例如,

5 + 2 

返回3個標記:INT,ADD,INT。這可以。

當空間缺失,但是,我遇到了麻煩:

5+2 
5 +2 

無論是哪種只返回兩個記號(2個整數),因爲彎曲看到5作爲signedint(正確),並返回它,然後看到'+2',並看到另一個有符號的int(它不會返回任何空格)。

關於如何區分signed int和add/subtraction的想法?

回答

3

在詞彙層面解決這一問題的難度在於很多語言甚至不嘗試的理由。解析器可以很容易地區分一個前綴爲+-的運算符和具有相同拼寫的二進制中綴運算符,並且除了一個角落案例(參見下文)之外,-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 } 

通過類似的推理,的termexpr最後集也是相同的:

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。這偶爾會造成混淆。

+0

不錯的答案... :) –

-1
signedint = \s*[+-]?\s*[0-9]+\s* 

這應該工作