编译原理学习笔记(四)预测分析器(1)

来源:互联网 发布:刚开淘宝店铺怎么装修 编辑:程序博客网 时间:2024/04/27 17:03

词法分析是为语法分析做准备的,词法分析器提取词法单元后返回给语法分析器用以进行语法分析。龙书在第四章中详细阐述了语法分析的部分。语法分析有自顶向下和自底向上两类方法,自顶向下是由产生式不断推导,直到匹配了整个输入串的过程,自底向上是通过字符串归约直到得到开始符的过程。今天主要学习了自顶向下的预测分析法。

预测分析法是递归下降分析法的一种特例,因此要先说到递归下降分析法。
递归下降分析由一组过程组成,每个非终结符有一个对应的过程。从开始符起执行过程,直到过程体扫描了整个输入串。
例如如下简单产生式:
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);};

目前只建立了架构,实现部分的代码尚未测试,留作下次补全。

0 0
原创粉丝点击