2012-12-28 74 views
3

我正在開發一個領域特定的語言。部分語言完全像C表達式解析語義,如精度和符號。如何處理用於兩件事情的相同符號檸檬解析器

我正在使用檸檬分析器。我遇到了一個同樣的問題,用於兩種不同的事情,我無法分辨詞法分析器中的差異。 &符號(&)符號用於「按位」和「地址」。

起初我認爲這是一個微不足道的問題,直到我意識到它們沒有相同的關聯性。

我該如何給出兩個不同的關聯性?我應該只使用AMP(如&符號),並使地址和位和規則使用AMP,或者我應該使用不同的標記(例如ADDRESSOF和BITWISE_AND)。如果我使用單獨的符號,我該如何知道詞法分析器中的哪一個(它不能不知道,而不是解析器本身!)。

+0

+1爲了補償疼痛,因爲你必須親手寫這個。 – 2012-12-28 21:40:47

+0

我不確定從哪裏開始。我應該嘗試在語法樹級別解析它,還是應該嘗試在解析器中檢測它(例如,通過窺視最近的令牌流)。 – doug65536

+0

在解析器中。 AST必須是明確的。解析器是什麼數學和邏輯。 – 2012-12-28 21:57:09

回答

3

如果你打算寫規則明確地說,對於每個「優先級」級別使用不同的非終端,那麼根本不需要聲明優先級,而且您也不應該這樣做。

檸檬和所有yacc衍生物一樣,使用優先聲明來消除模糊語法中的歧義。特定的模糊語法是這樣的:

expression: expression '+' expression 
      | expression '*' expression 
      | '&' expression 
      | ... etc, etc. 

在這種情況下,每一個替代方案都會導致轉換減少衝突。如果您的解析器發電機沒有優先規則,或者你想成爲精確,你必須寫,作爲一個明確的語法(這是你做了什麼):

term: ID | NUMBER | '(' expression ')' ; 
postfix_expr:  term | term '[' expression '] | ... ; 
unary_expr:   postfix_expr | '&' unary_expr | '*' unary_expr | ... ; 
multiplicative_expr: unary_expr | multiplicative_expr '*' postfix_expr | ... ; 
additive_expr:  multiplicative_expr | additive_expr '+' multiplicative_expr | ... ; 
... 
assignment_expr:  conditional_expr | unary_expr '=' assignment_expr | ...; 
expression:   assignment_expr ; 
[1] 

注意,明確語法甚至顯示了左結合(上面的乘法和加法)和右結合(賦值,儘管有點奇怪,見下文)。所以確實沒有含糊之處。

現在,優先聲明(%left,%right等)是只有用於消除歧義。如果沒有歧義,則聲明忽略。解析器生成器甚至不檢查它們是否反映語法。 (實際上,許多語法不能表示爲這種優先關係。)

因此,如果語法是明確的,那麼包含優先聲明是一個非常糟糕的主意。他們可能是完全錯誤的,並誤導任何讀過語法的人。改變它們不會影響語言解析的方式,這可能會誤導任何想編輯語法的人。

至少有一些問題,最好是使用具有優先規則的模糊語法,還是使用明確的語法。在類似於C的語言中,其語法不能用簡單的優先級列表表示,因此使用明確的語法可能會更好。然而,明確的語法有更多的狀態,並且可能會使語法分析稍微慢一些,除非語法分析器生成器能夠優化單位減少量(上述語法中的所有第一個替代方法,其中每個表達式類型可能只是前一個表達式類型,但不影響AST;這些生產中的每一個都需要減少,儘管它大部分是空操作,並且許多解析器生成器會插入一些代碼。)

原因C不能簡單地表示爲優先關係恰好是賦值運算符。試想一下:

a = 4 + b = c + 4; 

這並不是因爲assignment-expression分析,賦值運算符可以在左側僅適用於unary-expression。這並不反映+=之間可能的數字優先順序。 [2]

如果+較高優先級的比=,表達式將作爲解析:

a = ((4 + b) = (c + 4)); 

並且如果+較低優先級,這將解析爲

(a = 4) + (b = (c + 4)); 

[1]我剛剛意識到我遺漏了cast_expression,但我不能投入重演;你明白了)

[2]描述固定。

+0

自從我生成解析器(並且它是flex/bison)以來已經有數年了。謝謝,這是一個很好的回覆。 – doug65536

+0

GCC拒絕'a = 4 + b = c + 4;',錯誤值爲左值,作爲賦值的左操作數。你必須明確地引入圓括號才能將賦值賦予「b」(最低限度:a = 4 +(b = c + 4);')。 –

+0

@JonathanLeffler:非常正確(並且規則在C++中是不同的)。 – rici

0

後來我意識到在解引用(*)和乘法(*)之間有相同的含義。

檸檬提供了一種方法來爲規則指定一個精度,使用週期後的方括號中的關聯聲明(%左/右/非關聯)中使用的名稱。

我沒有驗證這正常工作,但我認爲你可以做到這一點(注意:在方括號中接近尾聲的東西):

. 
. 
. 

%left COMMA. 
%right QUESTION ASSIGN 
    ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN 
    LSH_ASSIGN RSH_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN. 
%left LOGICAL_OR. 
%left LOGICAL_AND. 
%left BITWISE_OR. 
%left BITWISE_XOR. 
%left BITWISE_AND. 
%left EQ NE. 
%left LT LE GT GE. 
%left LSHIFT RSHIFT. 
%left PLUS MINUS. 
%left TIMES DIVIDE MOD. 
//%left MEMBER_INDIRECT ->* .* 
%right INCREMENT DECREMENT CALL INDEX DOT INDIRECT ADDRESSOF DEREFERENCE. 

. 
. 
. 

multiplicative_expr ::= cast_expr. 
multiplicative_expr(A) ::= multiplicative_expr(B) STAR cast_expr(C). [TIMES] 
    { A = Node_2_Op(Op_Mul, B, C); } 
. 
. 
. 
unary_expr(A) ::= STAR unary_expr(B). [DEREFERENCE] 
    { A = Node_1_Op(Op_Dereference, B); }