什么是编译器?
编译器是一种程序
核心功能是把源代码翻译成目标代码
解释器和编译器的区别:
解释器是一条一条的解释执行源语言。比如php,postscritp,javascript就是典型的解释性语言。
编译器是把源代码整个编译成目标代码,执行时不在需要编译器,直接在支持目标代码的平台上运行,这样执行效率比解释执行快很多。比如C语言代码被编译成二进制代码(exe程序),在windows平台上执行。
读入源语言后,解释器和编译器都要进行词法分析、语法分析和语义分析,之后,二者开始有所分别。解释器在语义分析后选择了直接执行语句;编译器在语义分析后选择将将语义存储成某一种中间语言,之后通过不同的后端翻译成不同的机器语言(可执行程序)。
编译器结构
编译器由多个阶段组成,每个阶段处理不同的问题
程序语言的语法描述
文法
描述语言的语法结构的形式规则
以英语来进行类比:
文法定义:
文法G 定义为四元组(VT ,VN,P,S)
VN :非终结符集合(非空)
VT :终结符集合(非空), 且VT,VN相交为空集
S :文法的开始符号(识别符号), S属于VN
P :产生式集合(规则集合),有限
开始符S至少必须在某个产生式的左部出现一次
例如:
定义只含+,*的算术表达式的文法
G=<{i,+,*,(,)}, {E},E,P>,其中P由下列产生式组成;
E —>i
E —>E+E
E —>E*E
E —>(E)
巴科斯范式(BNF)
“—>”也可以用“::=”表示,这种表示叫做巴科斯范式
因为“—>”无法用ASCII表示
几点约定:
1. P—>α1 , P—>α2 ,P—>α3 ……P—>αn
可缩写为P —> α1| α2|……| αn
| 表示“或”
2. 表示一个文法时,通常只要给出开始符号和产生式
3. 大写字母开头为非终结符,小写字母开头为终结符
句型:是由终结符和非终结符组成
句子:只含终结符
给出文法推语言
例子1:
(i*i+i)是文法G(E): E—> i | E+E |E*E|(E) 的一个句子
证明:
E =>(E)
=>(E+E)
=>(E*E+E)
=>(i*E+E)
=>(i*i+E)
=>(i*i+i)
例子2:
文法G1(A):A —> c | Ab,G1(A)的语言
A=>c
A=>Ab
=>cb
A=>Ab
=>Abb
=>Abbbb
=>Abb….bb
=>cbb….bb
给出语言推文法
例子:
给出产生语言为{a^mb^n|1<=n<=m<=2n}的文法
G(S):
S —> ab | aab
S —> aSb | aaSb
先从最简单的往上分析
语法树
用一张图表示一个句型的推导,称为语法树
语法树与二义性
如果一个文法存在某个句子对应两颗不同的语法树,则说这个文法是二义的。
如:
语言的二义性:一个语言是二义的,如果对它不存在无二义性的文法
可能存在G和G’,一个为二义的,一个为无二义的。但L(G)=L(G’)
二义性问题是不可判定问题,即不存在一个算法,它能在有限步骤内,确切的判定一个文法是否是二义的
可以找到一组无二义文法的充分必要条件
二义文法:G(E): E—> i | E+E |E*E|(E)(一个句子有两颗树)
无二义文法:
G(E): E—> T | E+T
T —> F | T*F
F —> (E) | i
表达式 — > 项|表达式+项
项 —> 因子|项*因子
因子 —> (表达式)| i
改进后,它画出的语法树是唯一的
文法的四种类型:
1. 0型文法(短语文法)
产生式的形式:α→β
α∈(VN∪VT)* ,且至少含一个非终结符 ,β∈(VN∪VT)*
对产生式没有任何限制
例如:A0→A0 , A1→B
2. 1型文法(上下文有关文法):
产生式形如:α→β
其中, |α|<=|β|, 仅仅 S→ε除外
产生式的形式描述:xUy —> xuy
(其中,x、y、u∈(VN∪VT)*,β≠ε,A∈VN)
有U出现时,必须关注是否有x,y。有x,y时才能将U用u代替
例如:0A0→011000 1A1→101011
3. 2型文法(上下文无关文法):
对任一产生式α→β,都有α∈VN,β∈(VN∪VT)*
产生式的形式描述:A→β (A∈VN)
即β取代A时,与A所处的上下文无关。
例如:G[S]:S→01 S→0S1
4. 3型文法(正规文法)
每个产生式均为 “A→aB”或“A→a” —— 右线性
“A→Ba”或“A→a” —— 左线性
其中,A、B∈VN,a∈VT*
产生的语言称“正规语言”
例如:G[S]: S→0A | 0
A→1B | B
B→1 | 0
左边式子都是非终结符最有可能是2型,3型文法
对于现今程序设计语言,在编译程序中,仍然采用上下文无关文法来描述其语言结构
一. 词法分析
词法分析的任务:
从左至右逐个字符地对源程序进行扫描,产生一个个单词符号
对于词法分析器的要求:
功能:
输入源程序,输出单词符号
单词符号的种类:
1. 基本字:begin,repeat…
2. 标识符:表示各种名字:如变量名,数组名和过程名
3. 常数:各种类型的常数
4. 运算符:+,-,*,/
5. 界符:逗号,分号,括号和空白
输出单词符号的表现形式:
一个二元组(单词种别,单词自身的值)
单词种别通常用整数编码表示:
若一个种别只有一个单词符号,则种别的编码就代表该单词符号,假定基本字,运算符和界符都是一符一种
若一个种别符有多个单词符号,则对于每个单词符号,给出种别编码和自身的值
标识符单列一种,标识符自身的值表示成按机器字节划分的内部码
常数按类型分钟,常数的值表示成标准的二进制方式
例子:
FORTRAN程序
IF (5.EQ.M) GOTO 100
输出单词符号
逻辑 IF (34,-)
左括号 (2,-)
整常数 (20,‘5’的二进制)
等号(.EQ.) (6,-)
标识符 (26,‘M’)
右括号 (16,-)
GOTO (30,-)
标号 (19,‘100’的二进制)
C程序
while(i>=j) i--;
输出单词符号(二元组)
<while , ->
<( , ->
<id , 指向i的符号表项的指针>
<>= , ->
<id , 指向j的符号表项的指针>
<) , ->
<id , 指向i的符号表项的指针>
<– , ->
< ; , ->
词法分析器在编辑器中的地位:
词法分析器的结构
-
状态转换图
状态转换图是一张有限方向图
结点代表状态,用圆圈表示
状态之间用弧线连接,箭弧上的标记(字符)代表射出结状态下可能出现的输入字符或字符类
一张转换图只包含有限个状态,其中有一个为初态,至少要有一个终态
状态转换图可用于识别一定的字符串
若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字等于a,则称a为改状态图所识别(接受)
小例子:
假如下图是一个程序语言的全部语法规则
它的状态转换图可画为:
状态转换图的实现:
思想:每个状态结点对应一小段程序
具体方法:
1. 对不含回路的分叉结点,可用一个CASE语句或一组IF -THEN-ELSE语句实现
对含回路的状态结点,可对应一段由WHILE结构和IF语句构成的程序
终态结点表示识别出某种单词符号,对应语句
RETURN (C , VAL)
C为单词种别 VAL为单词自身值
全局变量与过程
1. ch字符变量,存放最新读入的源程序字符
2. strToken 字符数组,存放构成单词符号的字符串
3. GetChar 子程序过程,把下一个字符读入到ch中
4. GetBC 子程序过程,跳过空白符,直至ch中读入一非空白符
5. Concat 子程序,把ch中的字符连接到strToken
6. IsLetter和IsDisgital布尔函数,判断ch中字符是否为字母和数字
7. Reserve整型函数,对于strToken中的字符串查找保留字表,若他是保留字则给出它的编码,否则回送0
8. Retract子程序,把搜索指针回调一个字符位置
9. InsertId 整型函数,将strToken中的标识符插入符号表,返回符号表指针
10. InsertConst 整型函数过程,将strToken中的常数插入常数表,返回常数表指针
正规表达式与有限自动机(DFA)
正规集和正规表达式:
正规集可以用正规表达式表示
正规表达式是表示正规集的一种方法
一个字集合是正规集当且仅当它能用正规式表示
正规式和正规集的递归定义
确定有限自动机
DFA可以表示为状态转换图:
假定DFA M含有m个状态和n个输入字符
这个图含有m个状态结点,每个结点顶多含有n条箭弧射出,且每条箭弧用不同的输入字符来表示
DFA M所识别的字的全体记为L(M)
选A 从初态出发可以到达终态的这条通路上所有的字符构成了一个字,而
A直接到终态,中间无字符就是空字
而B代表Φ
非确定有限自动机(NFA)
将 DFA转换为NFA
小例子:运用以上三条规则
弧上的标记不允许有ε,要消除ε
最后一步:
从一个状态出发,识别若干个ε后能到达的状态,都在这个集合里
对1做ε的闭包,得到{1,2},而J为I中的某个状态出发进过a弧而到达的状态的集合
1 —> 5,4
2 —> 3
{1,2}识别a后到达的状态集{3,4,5}
再对{5,4,3}做ε的闭包,得到{5,4,3,6,2,7,8}
第一行第一列就是X的ε闭包
这个表转换为状态集之间的转换关系
例如:
第一行:X的闭包{X,5,1},输入a状态转换到{5,3},再对{5,3}做新的闭包得{5,3,1},Ib同理得{5,4,1},{5,3,1}和{5,4,1}在列中未出现,因此加入到列中。以此类推
初态就是包含X的集合{X,5,1}
终态就是包含Y的集合
将每个集合看做一个状态,给一个新的编号