簡單編譯器之語法分析


OK,書接上文,今次這篇博客是准備說語法分析。

其實詞法分析和語法分析可以說是一體的。詞法分析用於分析輸入的單詞,將其一一分門別類。語法分析分析已經分門別類好的單詞,看其組成的句子是否符合語言的文法。

首先,先確定一個簡單的文法:

S -> if ( M ) { N }
S -> N
M - > P == P
p -> id | digit
N -> int id ; | int id = num ; | Q;
Q - > id = E
E - > E + T | T
T - > id | digit| ( E )

這是一個非常簡單的java風格文法,大家一眼就能看出來它的意思了。這段文法會匹配如下的句子:

if(1 == 1)
{
    int p = 1;
}

就如從正則表達式可以構造出詞法分析器一樣,也存在語法分析器的生成器之類的工具,可以用來構造預測分析器。相關的算法有LL(1)、LR(1)等。本文涉及的文法比較簡單,符合LL(1)文法,不需要用到LR(1)這樣強大而復雜的推理。因而本文准備用遞歸下降算法來構造預測分析器。

 

LL(1)文法代表從左到右分析、最左推導和超前查看一個符號。為了確認一個文法是否是LL(1)文法需要構造LL(1)分析表,而為了構造LL(1)分析表又需要計算文法的FIRST和FOLLOW集合(詳細的算法可以自行百度,這里就不列出)。

記得第一次看LL(1)描述時看的頭暈眼花,雲里霧里的。后來自己干脆動手寫程序時,就突然一下子明白了,為了要有這些步驟,這些步驟到底用來做什么的?

直接用例子來說明,以S -> if ( M ) { N }、S -> N 來說,我們要如何寫匹配這段文法的程序了?

1.獲取下一個字符t;

2.根據t,判斷是走if ( M ) { N }這條路了,還是走N這條路;

3.如果t == 'if',而N的首字符沒有'if'的情況,那就妥妥的走if ( M ) { N },反之也一樣;

4.如果N -> if blabla,那么我就傻眼了,到底應該怎么判斷了?

 

如果真出現4中的情況,那么這個文法就不是LL(1)文法。因為從左到右只超前讀一個字符無法推導出一個唯一的結果。

事實上,無論是消除左遞歸、計算FIRST集合、計算FOLLOW集合都是為了確認當前文法在出現分岔路時,每個分支的首個終結字符不相同

 

本文的文法比較簡單,直接消除左遞歸后得到滿足LL(1)的文法:

S -> if (M) {N}
S -> N
M -> P == P
P -> id | digit
N -> N1 N11 | E
N1 -> int id
N11 -> = num | null
E -> TE1
E1 -> + T | T
T -> id | digit | (E)

然后直接根據這個就可以得到下面的程序啦:

def S():
    #下一個字符,用於預測
    token = seeToPretict();
    if(token == 'if'):
        eat('if');
        eat('(');
        M();
        eat(')');
        eat('{');
        N();
        eat('}');
        pass
    elif(token == 'id' or token == 'digit'):
        N();
    else :
        #報錯
        Syntaxerror();
    print 'complete';
    pass
        
def M():
    P();
    eat('==');
    P();
    pass

def P():
    #下一個字符
    token = getToken();
    if(token != 'id' and token != 'digit'):
        syntaxerror();
    pass

def N():
    token = seeToPretict();
    if(token == 'int'):
        N1();
        N11();
        eat(';');
    elif(token == 'id' or token == 'digit'):
        E();
        eat(';');
    else:
        syntaxerror();
    pass
    
def N1():
    eat('int');
    eat('id');

def N11():
    token = seeToPretict();
    if(token == '='):
        eat('=');
        eat('digit');
    pass
    
def E():
    T();
    E1();
    
def E1():
    token = seeToPretict();
    if(token == '+'):
        eat('+');
        T();
    elif(token == 'id' or token == 'digit'):
        T();
    else:
        syntaxerror();

def T():
    token = getToken();
    if(token != 'id' and token != 'digit'):
        syntaxerror();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM