編譯原理學習筆記(四)預測分析器(1)

詞法分析是爲語法分析做準備的,詞法分析器提取詞法單元后返回給語法分析器用以進行語法分析。龍書在第四章中詳細闡述了語法分析的部分。語法分析有自頂向下和自底向上兩類方法,自頂向下是由產生式不斷推導,直到匹配了整個輸入串的過程,自底向上是通過字符串歸約直到得到開始符的過程。今天主要學習了自頂向下的預測分析法。

預測分析法是遞歸下降分析法的一種特例,因此要先說到遞歸下降分析法。
遞歸下降分析由一組過程組成,每個非終結符有一個對應的過程。從開始符起執行過程,直到過程體掃描了整個輸入串。
例如如下簡單產生式:
S -> b
b->num + b| num
匹配2+3+5的過程就是:
從開始符S推導,推導得b,再由b第一個推導得到num+b,左邊是num屬於終結符(詞法單元),右邊可以繼續推導,再次推導,嘗試num+b發現無法匹配,因此選擇另一個匹配num。自此輸入串已掃描完畢。
其中的一個問題就在於,對於輸入的串,應該選擇哪一個產生式,這裏涉及一個文法:LL(k)文法。第一個L表示從左往右掃描輸入串,第二個L代表產生式從左往右推導,k表示在進行產生式選擇時,需要往前看至少k個字符。比如上面的匹配,只需往前看一個字符,如果是’+’則可以做出選擇。所以上面的文法是LL(1)文法。遞歸下降分析法應用於LL(1)文法則稱爲預測分析法。

依然用之前的的NC程序爲例子,這裏構造了一個語法表,描述產生式:
//配置文件grammar.txt
start \I
id \O\INT
addrs \G|\M|\X|\Y|\Z|\S|\T|\F
instruct #addrs\NUM#instruct|$
stmt \N\INT#instruct\END
end \I
program #start#id#stmt+#end

每行兩個字符串,第一個爲產生式名(非終結符),第二個爲其推導。\X表示以X爲名的詞法單元,#X表示以X爲名的非終結符。$表示空字符。
這裏依然使用類似於詞法分析部分的思想,構造一個語法單元,建立一個語法表存儲語法單元的類型和其他信息。


//語法單元(非終結符/詞法單元/空標記)
struct syn_unit
{
    string token;
    lex_token*lex;
    enum term_type
    {
        LEX,
        NONTERMINATOR,
        EMPTY
    };
    int id;
    term_type type;
    syn_unit() :token("$"), type(EMPTY){ ; };
    syn_unit(string s, lex_token* l, int i, term_type t):token(s),lex(l),id(i),type(t){
        ;
    }
    bool operator==(const syn_unit& els){
        return els.token == token && els.type==type;
    }
    bool operator!=(const syn_unit& els){
        return els.token != token && els.type != type;
    }
};

語法單元中存儲標記token和類型:詞法單元、非終結符、空符。詞法單元相當於終結符,非終結符可以繼續推導,空符一般是結束符。
爲了描述產生式,構造類型如下:

//產生式
struct production{
    //格式:非終結符-> |{詞法單元 ∪ 非終結符}
    string token;
    syn_unit src;
    vector<vector<syn_unit>> dst;
    set<lex_token*>FIRST;
    bool set_down;//是否已設置好了FIRST集合
    map<lex_token*,int> drivetable;//驅動表

public:
    production(string,string,lex_parser*);
    void set_first(map<string, production*>&mesh);

};

src,dst表示src->dst這個產生式,dst中包含語法單元串。FIRST集合是實現預測分析的一個基礎,它存儲着選擇這個產生式的判斷依據。(比如上面那個簡單的例子,S選擇b的依據是num,b二選一的依據是’+’)。FIRST集合構造方法見龍書140頁。粗略理解就是一個串的可能開頭字符的集合。
驅動表是預測分析法的核心(當然還有別的實現方法,不限於表驅動),對於一個當前推導出的非終結符,以及輸入的詞法單元,選擇其應選的產生式。表驅動構造算法見143頁。

語法分析部分架構:

//語法分析
class nc_parser{

    map<string, production*>mesh_table;//映射表
    lex_parser*lex;
public:
    nc_parser(string filename,lex_parser*);//先有詞法表 纔有語法表
    void initial();//初始化:建立完整的FIRST集合,標識id等
    void translate(string code,string);
};

目前只建立了架構,實現部分的代碼尚未測試,留作下次補全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章