第1章 LR解析過程

趙年峰發表於2013-08-12

LR解析過程

1.1 LR解析過程

這篇章節主要幫你瞭解LR分析的過程。

關於詞法分析部分我們採用ply的lexer進行分析,文法分析部分我們自己來完成。

LR中的技術名詞

首先我們瞭解一下文法分析中涉及到的技術名詞。

歸約 Reduction (complexity) : 在自底向上語法分析器中,利用A->ß的語法分析樹上邊緣種的ß替代A,從而縮減語法樹上邊緣的做法。


移動 shift

從左像右移動,每移動一步,呼叫GOTO,然後使用Closure計算。


state 為分析器的狀態。分析器每做一個動作都會從一個狀態轉換到另一個狀態。每個狀態包括shift和redure


Action 和 GOTO 表中包含了 分為Shift和redure兩種動作,shift是移進動作,redure是歸約動作,在ply中Action表包含了所有終結符的動作集合,GOTO表裡麵包含了所有非終結符的動作集合。

解析過程

下面是解析過程的圖記。

image

image

由圖可以看出LR的歸約過程是從左向右讀取,從右向左歸約。這一節我們先簡單瞭解這些,下面一節會深入一點講訴歸約的BNF如何構建更適合LALR解析器.

參考書籍和內容

  • ”LR parser - From Wikipedia, the free encyclopedia” http://en.wikipedia.org/wiki/LR_parser
  • "編譯器設計(第2版) 人民郵電出版社"

    1.2 LR編寫grammar中的問題和解決方法

上一節,簡單敘述了LR解析過程(使用ply),下面說一下關於BNF的典型衝突如何在LR中解決。

這章節主要講解如何避免Conflicts

Conflicts一般分為2種錯誤

shift/reduce錯誤

這種錯誤是因為 分析器在這種情況下不知道是歸約還是移進導致的。

redure/redure錯誤

這種錯誤是因為,解析器在解析棧中規則時發現有多個規則可以進行歸約。rejected rule 會指出跟哪個rule衝突

我們首先舉個例子。

例子1

文法如下: 這是一個簡單的解析

"<><><><>"

"<>"

""

class classnam {}

def p_start(p):
    '''
        start : typeArguments
    '''

def p_typeArguments(p):
    '''
        typeArguments : typeArgument
        | typeArguments typeArgument
    '''

def p_typeArgument(p):
    '''
        typeArgument : LESS MORE
        | empty
    '''

def p_empty( p ):
    '''empty : '''

這是產生的錯誤。

WARNING: 
WARNING: Conflicts:
WARNING: 
WARNING: shift/reduce conflict for LESS in state 0 resolved as shift
WARNING: shift/reduce conflict for LESS in state 2 resolved as shift
WARNING: reduce/reduce conflict in state 2 resolved using rule (start -> typeArguments)
WARNING: rejected rule (empty -> <empty>) in state 2

那麼下面看看如何來解決這個問題。

def p_start(p):
    '''
        start : typeArguments
        | empty
    '''

def p_typeArguments(p):
    '''
        typeArguments : typeArgument
        | typeArguments typeArgument
    '''

def p_typeArgument(p):
    '''
        typeArgument : LESS MORE
    '''

def p_empty( p ):
    '''empty : '''

這個問題出現在

def p_typeArgument(p):
    '''
        typeArgument : LESS MORE
        | empty
    '''

身上,因為empty比較特殊可以告訴yacc可以reduce,因為結束了。而LESS還需要shift.

而在 typeArgument 身上也出現了一個問題就是到底 typeArgument是redure成empty還是 LESS MORE形式。

例子2

def p_start(p):
    '''
        start : typeArguments
    '''

def p_typeArguments(p):
    '''
        typeArguments : typeArgument
        | typeArguments typeArgument
    '''

def p_typeArgument(p):
    '''
        typeArgument : LESS MORE
        | list
    '''

def p_list(p):
    '''
        list : LESS MORE
    '''

def p_empty( p ):
    '''empty : '''

這個問題比較是比較典型的redure衝突

出現在 typeArgument和list的衝突上。

WARNING: 
WARNING: Conflicts:
WARNING: 
WARNING: reduce/reduce conflict in state 7 resolved using rule (typeArgument -> LESS MORE)
WARNING: rejected rule (list -> LESS MORE) in state 7
WARNING: Rule (list -> LESS MORE) is never reduced

這種情況就是因為在一個規則樹中出現了兩個同樣的規則在同一個裡面。

例子3

這個例子算是一個較為經典的shift/redure的問題。

expression : expression PLUS expression
       | expression MINUS expression
       | expression TIMES expression
       | expression DIVIDE expression
       | LPAREN expression RPAREN
       | NUMBER

如果我們不只用優先順序來定義,那麼我們可以如下方法解決優先順序別的問題:

def p_start(p):
    '''
        start : expression
    '''

def p_expression(p):
    '''
        expression : multExpression
        | expression PLUS multExpression
        | expression MINUS multExpression
    '''

def p_multExpression(p):
    '''
        multExpression : subExpression
        | multExpression TIMES subExpression
        | multExpression DIVIDE subExpression
    '''

def p_subExpression(p):
    '''
        subExpression :  LPAREN expression RPAREN
        | primary
    '''

def p_primary(p):
    '''
        primary : NUMBER
    '''

在java中也可以使用如上方法來定義一個expression來完成整個expression樹的解析過程。因為過於複雜,所以這裡不就寫了,有興趣的可以看java 7 lanaguage

上面3個例子都比較典型,基本能把大部分書寫LR文法的時候遇到的問題解決掉。

下一章,我們講解一下LR的演算法是如何構建的,並寫一個稍微簡單些解析器和表構建演算法來學習一下。

1.0 LR技術簡介

1.0 LR技術簡介

相關文章