一、一个编译器的结构
我们可以吧编译器看作是一个黑盒子,它可以把源程序映射为在语义上等价的目标程序。这个映射过程我们我们可以先分为两个部分:分析部分和综合部分。
(1)分析部分:把源程序分解为多个组成要素,并在这些要素之上加上语法结构。然后,使用这个语法结构来创建一个中间表示。而且,分析部分会收集有关源程序的信息,并把信息放在一个符号表的数据结构中。符号表与中间表示形式一起被传送给综合部分。
符号表中的信息会被语义分析和代码生成步骤使用
(2)综合部分:根据中间表示和符号表中的信息来构造用户期待的目标程序。分析部分经常被称为编译器的前端,而综合部分被称为后端。
1、编译器的各个步骤
(1)词法分析(lexical analysis)(named:扫描(scanning)):词法分析器读入源程序的字符流,并且将他们组织成为有意义的词素序列。对每个词素词法分析器都会产生如下的词法单元作为输出:<token-name,attribute-value>
a、token-name:语法分析步骤使用的抽象符号
b、attribute-value:指向符号表中关于这个词法单元的条目。
c、步骤:假设一个源程序包含如下赋值语句:position = initial + rate *60
1)position被映射成词法单元<id,1>,其中id是标识符,1是指向符号表中对应的条目。一个标识符对应的 符号表条目存放该标识符有关的信息,比如他的名字和类型。
2)赋值语句=,被映射成<=>。因为这个词法单元不需要属性值,所以省略了第二个量。可以使用assign这样的抽象符号与词素本身作为抽象符号的名字。
3)initial语素被映射成词法单元<id,2>,与1)类似
4)+被映射成<+>
5)rate被映射成<id,3>, *被映射成<*>,60被映射成<60>.(从技术上讲我们应该建立一个形如<number ,4>的词法单元)。
经过1)到5)的词法分析之后,赋值语句被表示成<id,1><=><id,2><+><id,3><*><60>
(2)语法分析
编译器第二个步骤被称为语法分析,也叫解析。语法分析器是使用由词法分析器生成的各个词法单元来创建树形的中间表示。该中间表示给出了词法分析生成的词法单元流的语法结构。一个常用的表示方法是语法树,树中的每个内部节点表示一个运算,而该节点的子节点表示该运算的分量。
(3)语义分析
作用:语义分析器使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致
类型检查:语义分析的一个重要部分,编译器检查每个运算符是否具有匹配的运算分量。比如数组下标为整数,如果用浮点数,编译器就报告错误。比如自动类型转换,编译器各个步骤的第二幅图中,语义分析器的输出中有一个关于inttofloat的额外节点。是应为别的变量都为浮点数,而60为整数,语义分析器将其类型自动转换。
(4)中间代码生成
在词法,语法,语义分析完毕之后,许多编译器会生成一个明确的低级的语言或类机器语言的中间表示。我们可以吧这个表示看作是某个抽象机器的程序。
性质:a、易于生成;b、能够被轻松地翻译为目标机器上的语言。
输出:在后边我们将使用一种称为三地址代码(见本人三地址代码随笔)的中间表示形式。中间代码生成器的输出是如下的三地址代码序列
t1 = inttofloat(60)
t2 = id3 * t1
t3 = id2 + t2
id1 = t3
(5)代码优化:视图改进中间代码,以便生成更好的目标代码。
t1 = id3 *60.0
id1 = id2 + t1.
(6)代码生成:以源程序的中间形式作为输入,并把它映射到目标语言。