2014-09-13 62 views
0

我在做野牛/ flex的分析器。Bison/Flex,減少/減少,不同生產中的標識符

這是我的代碼部分:

enter image description here

我要實現的任務生產,使該標識符既可以是boolean_expr或EXPR,它的類型將通過符號表進行檢查。 所以它允許這樣的:

int a = 1; 
boolean b = true; 
if(b) ... 

但是,降低/減少,如果我包括這兩個長期和boolean_expr標識,任何解決方案來解決這個問題呢?

回答

2

本質上,你要做的是在你的語法中注入語義規則(類型信息)。這是可能的,但這並不容易。更重要的是,這並不是一個好主意。如果語法和語義描述得很好,幾乎總是最好的。

完全相同,因爲呈現的語法是毫不含糊的,並且LALR(1)。但是,後一個特徵很脆弱,並且在完成語法時難以維護它。

例如,您不包括你的賦值語法在你的問題,但它會

assignment: identifier '=' expr 
      | identifier '=' boolean_expr 
      ; 

不同於所示的語法部分的剩餘部分,即生產是不明確的,因爲:

x = y 

不知道任何關於y,y可以減少到termboolean_expr

一個可能更有趣的例子是在語法中增加了括號。這樣做的最顯而易見的方法是添加兩種生產:

term: '(' expr ')' 

boolean_expr: '(' boolean_expr ')' 

產生的語法也不含糊,但它不再是LALR(1)。考慮以下兩個聲明:

boolean x = (y) < 7 
boolean x = (y) 

在第一個,y必須是int使得(y)可以減少到一個term;在第二個y必須是boolean,以便(y)可以減少到boolean_expr。沒有歧義;一旦看到(或不看)<,就完全清楚要選擇哪種減少。但是<不是超前記號,而實際上它可能是從y任意遙遠:

boolean x = ((((((((((((((((((((((y... 

所以產生明確的語法不是LALR(k)任何k。你可以解決這個問題


的一種方法是通過給符號表掃描訪問在詞彙層面注入類型的信息。然後,掃描器可以在符號表中查找已掃描的標識符標記,並使用符號表中的信息來決定三種標記類型之一(或更多,如果您有更多數據類型):undefined_variable,integer_variableboolean_variable。然後,你將有,例如:

declaration: "int" undefined_variable '=' expr 
      | "boolean" undefined_variable '=' boolean_expr 
      ; 

term: integer_variable 
    | ... 
    ; 

boolean_expr: boolean_variable 
      | ... 
      ; 

,將工作,但它應該是顯而易見的,這是不可伸縮的:每次添加一個類型的時候,你就必須擴大雙方的語法和詞彙的描述,因爲現在的語義不僅與語法混淆,甚至還與詞法分析混雜在一起。一旦你讓語義脫離盒子,它往往會污染一切。

有些語言的確是最方便的解決方案:例如,如果區分typedef名稱和標識符名稱,C語法分析就容易得多,因此您可以判斷(t)*x是演員還是乘法演員。 (但是對於C++來說這並不容易,C++有更復雜的名稱查找規則,並且爲了找到正確的解析也需要語義分析。)

但是,老實說,我會建議你做而不是使用C語言 - 更少的C++ - 作爲如何設計語言的模型。編譯器難以解析的語言對於人類來說也很難解析。該"most vexing parse"仍然是痛苦的正規來源C++新人,甚至有時會絆倒了比較有經驗的程序員:

class X { 
    public: 
    X(int n = 0) : data_is_available_(n) {} 
    operator bool() const { return data_is_available_; } 
    // ... 
    private: 
    bool data_is_available_; 
    // ... 
}; 

X my_x_object(); 
// ... 
if (!x) { 
    // This code is unreachable. Can you see why? 
} 

總之,你最好用可以被解析成一個AST沒有任何語言語義信息。一旦解析器產生了AST,就可以在單獨的通道中進行語義分析,其中一個將檢查類型約束。這是最乾淨的解決方案。沒有顯式類型,語法略有簡化,因爲expr現在可以是任何expr

expr:  conjunction | expr "or" conjunction ; 
conjunction: comparison | conjunction "and" comparison ; 
comparison: product  | product '<' product ; 
product:  factor  | product '*' factor ; 
factor:  term  | factor '+' term ; 
term:  identifier 
    |  constant 
    |  '(' expr ')' 
    ; 

每個動作在上面會簡單地創建一個新的AST節點並設置$$到新節點。在解析結束時,AST會走過來驗證所有expr都具有正確的類型。

如果這看起來對您的項目來說太過分了,那麼您可以在還原操作中進行語義檢查,從而有效地將AST漫步與解析混合在一起。這對於立即評估似乎很方便,但它也需要在解析器的語義類型中包含顯式類型信息,這會增加不必要的開銷(並且,正如前面提到的,讓語義干擾解析器的不妥當)。在這種情況下,每個動作都會看起來像這樣:

expr : expr '+' expr { CheckArithmeticCompatibility($1, $3); 
         $$ = NewArithmeticNode('+', $1, $3); 
        } 
+0

謝謝。其實我對野牛來說很新,2周前開始學習它。我有一個符號表來檢查類型,但我不明白它是如何幫助解決這個問題。在進行語義檢查之前,必須將標識符減少到expr,對嗎?同樣,如果標識符的類型是布爾型的,標識符必須簡化爲boolean_expr。我只做了一個簡單的解析器,只有3種類型:整型,布爾型和點型,所以我認爲如果爲每種類型的變量添加一個前綴,比如一個以「i_」開頭的整數變量,這可能很容易? – user2716189 2014-09-13 08:34:47

+0

@ user2716189:由於您的詞法分析器只能查看符號表中的變量類型(假設它可以訪問符號表),因此不需要在變量前加上前綴。未聲明的變量只能用作聲明的目標,並且聲明的變量不能重新聲明(這是一個假設,但似乎是合理的),所以掃描器可以爲三種情況返回三種標記類型之一。我試圖在答案中更加明確。 – rici 2014-09-13 18:04:27