编译原理第4讲

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

间接码表 (1) (2) (3) (1) (4)
OP ARG1 ARG2
(1)
+
A
B
(2)
*
(1)
C
(3)
:=
X
(2)
(4)

D
(1)
(5)
:=
Y
(4)
(5)
CompilerPrinciples
22
❖ 有了间接码表后,一方面相同的三元式 就无须重复填进表中,节约了空间;另一方 面,当在代码优化过程中需要调整运算顺序 时,只需重新安排间接码表,无须改动三元 式。
词法分析器扫描到标识符i时送回(种别 码,i值),语法分析器把i放入语义变量 i.LEXCAL中,这时就可以调用ENTRY过程:
CompilerPrinciples
20
FUNCTION ENTRY(NAME) BEGIN
ENTRY:=LOOKUP(NAME); IF ENTRY=NULL
THEN ENTRY:=FILLSYM(NAME); //对FORTRAN等 ERROR; //对ALGOL,PASCAL等
CompilerPrinciples
4
§1.语法制导翻译回顾
一、什么是语法制导翻译? 所谓语法制导翻译是指:在语法分析过程
中,每当一个产生式获得匹配(自上而下分 析)或用于规约(自下而上分析)时,就执 行相应于该产生式的一个称为语义子程序的 翻译子程序来完成既定翻译任务。
CompilerPrinciples
◇ 需要指出的是:从单词符号序列的源程 序到某种形式的中间代码,不像词法分析和 语法分析那样有一套形式化的理论和算法 (如自动机等);中间代码产生目前仍处于 试验阶段,尽管已经有了一些形式语义系统, 但大都没有得到公共认可。这就是说,语义 的形式化要远比语法的形式化困难得多。
下面我们首先回顾一下上一讲介绍的语 法制导翻译法。
(3)条件表达式:if e then x else y ,我们可以 把if-then-else看成运算符¥,于是有exy¥。¥是 一个三目运算符。
CompilerPrinciples
11
3.语法制导生成后缀式
一般表达式翻译为后缀式是很容易的。下表给出 了其语义规则描述:
产生式
语义规则
E→E1 op E2 E→(E1) E→i
例: a+b → ab+
a*(b+c) → abc+*
-a+b*c → a@bc*+
CompilerPrinciples
8
一个表达式的后缀式可以如下定义:
(1)如果E是一个变量或常量,则E的后 缀式是E自身。
(2)如果E是E1 op E2形式的表达式,这 里op是任何二元操作符,则E的后缀式为 E1'E2'op,这里E1'和E2'分别为E1和E2的后缀式。
17
对于一目运算符,我们可以规定只用ARG1,而 多目运算符可以用若干条相继的三元式来表示。
OP ARG1 ARG2
例2:-x+y*z (1) @ x
--
(2) * y
z
(3) + (1) (2)
例3:赋值语句的三元式表示
OP ARG1 ARG2
A:=x+y*z (1) * y
z
(2) + x
(1)
(3)如果E是(E1)形式的表达式,则E1的 后缀式就是E的后缀式。
CompilerPrinciples
9
1.特点:不难看出,
(1)左、右扫描后缀式所得的表达式分解唯一;
(2)运算分量与原表达式在顺序上完全一致;
(3)计值方便:使用栈很容易实现。从左至右扫描后 缀式,每遇到运算分量即进栈,每遇到一个k目运算 符,就把它作用于栈顶的k个项,并用运算结果来代 替这k个项。当把后缀式扫描完了,则栈顶就是表达 式的值。
END
由于三元式的计算过程跟先后顺序密切相关,这 就给优化带来麻烦,所以一般不直接使用三元式。
CompilerPrinciples
21
4.间接三元式
在三元式的基础上附加一张指示器表─间接码表,按 运算的先后顺序列出有关三元式在三元式表中的位置。这 种表示方法称为间接三元式。
例: 语句X:=(A+B)*C; Y:=D↑(A+B)的间接三元式
例:A+B*(C-D)-E/F↑G的四元式为:
OP ARG1 ARG2 RESULT
①- C D
T1
② * B T1 T2
③ + A T2 T3
④↑ F G
T4
⑤ / E T4 T5
⑥ - T3 T5 T6
CompilerPrinciples
24
赋值语句A:=-B*(C+D)
OP (1) @
ARG1 ARG2
(3) := A
(2)
CompilerPrinciples
18
3.语法制导产生三元式
(1) E→E1 op E2 我们用E.val表示一个指 示器,它或指向有关符号表的某项,或指向 三元式表的某项,于是其语义子程序为:
{E.val:=TRIP(OP,E1.val,E2.val)} 这儿TRIP(OP,ARG1,ARG2)是一个语义过 程,它产生(OP,ARG1,ARG2)并放入三元式表 中,返回三元式在表中的位置。
第四讲 语义分析和 中间代码产生
语法制导翻译回顾 中间语言 几种常用语句的翻译 符号表
◇ 前面我们讨论了词法分析、语法分析、属性文 法和语法制导翻译,接下来我们来讨论编译程序工 作工程的第三个阶段:语义分析和中间代码产生。
◇ 紧接在词法分析和语法分析之后,编译程序要 做的工作是进行静态语义检查和翻译。静态语义检 查通常包括:
c
语法树
c DAG
CompilerPrinciples
14
2.再来看A*B+C*D的树、后缀式与三元式
+
*
*
ABC D
OP ARG1 ARG2
(1) *
A
B
(2) *
C
D
(3) + (1) (2)
后缀式:AB*CD*+
不难看出,后缀式实际上是抽象语法树的线性 表示形式(后序表示);
树是三元式的翻版,每个三元式对应一棵 (二叉)子树,最后的三元式对应树根。
§2.几种常用的中间语言
一、逆波兰表示法
波兰表示是波兰逻辑学家J·卢卡西维奇于 1929年提出的一种既不须考虑优先关系、又不用括 号的一种表示表达式的方法(前缀式),如 a+b→+ab。在计算机出现以后,这种表示方法显出 了巨大的优越性。后人为了纪念他,就用其祖国名 字来命名这种表示方法。现在我们要介绍的刚好是 另一种波兰表示形式,称为后缀式,即运算符在后。
❖ 当然这样一来,语义规则中应添加产生 间接码表的动作,并且向三元式表中每填入 一个三元式前,必须先查看一下此式是否已 经在表中出现过。
CompilerPrinciples
23
四、四元式
一个四元式是一个带有四个域的记录结构: op,arg1,arg2及result。它实际上就是一条三地址 的指令。
CompilerPrinciples
6
3. 语义子程序的描述:
(1)给每个文法符号赋以某方面的值: “类型”、“种属”、“地址”或“代码” 等;
(2)在一个产生式中同一文法符号出现 多次通过加上角标来区别;
(3)对应每个产生式的语义子程序写在 该产生式的后面。
CompilerPrinciples
7
{E.val:=UNARY(@E1.val)}
(4)E→i
与NODE相似,只
{E.val:=LEAF(i)} 不过是单枝。
建立以i.LEXCAL为标 志的叶结,回送的是 结点i的地址。
CompilerPrinciples
16
三、三元式
1.三元式由三个部分组成:
算符:OP
第一运算分量:ARG1
第二运算分量:ARG2
(1)便于进行与机器无关的代码优化;
(2)使编译程序改变目标机更容易;
(3)使编译程序的结构在逻辑上更为简单明确, 以中间语言为界面,编译前端和后端的接口更清晰。
静态语义检查和中间代码产生在编译程序中的位 置如图所示:
语法分析器
CompilerPrinciples
静态检查器 中间代码产生器
优化器 3
两者所不同的是,在一个DAG中代表公共子表 达式的结点具有多个父结点,而在一棵抽象语法树 中公共子表达式被表示为重复的子树。如下例:
CompilerPrinciples
13
a:=b*-c+b*-c的图表示法
assign
assign
a
+
*
*
b uminus b uminus
a
+
* b uminus
c
CompilerPrinciples
26
1.简单说明句:用一个基本字来定义一串名字,其语 法描述一般为:
2.各种语句都可表示成一组三元式
例1:
OP ARG1 ARG2
x+y*z
(1) * y
z
指示器----指
向(1)在表
(2) +
x
(1) 格中的位置
这儿实际实现时x,y,z也都是指示器,指向这些变
量在符号表中的位置。算符OP一般用整数编码,而
且可以加进一些诸如类型之类的信息。
CompilerPrinciples
E.code:=E1.code‖E2.code‖op E.code:=E1.code E.code:=i
其他语句可以类推。
CompilerPrinciples
12
二、图表示法
这里要介绍的图表示包括DAG与我们前面介绍 过的抽象语法树。
1. 无循环有向图(DAG)
DAG与抽象语法树基本上一样,对表达式中的 每个子表达式,DAG中都有一个结点。一个内部结 点表示一个操作符,它的孩子表示操作数。
Compil来自百度文库rPrinciples
15
3.树表示的翻译法:
例: 产生式
语义动作
(1)E→E1 op E2 {E.val:=NODE(op,E1.val,E2.val)}
(2)E→(E1) (3)E→-E1
建立一棵新树,以OP为根, 以E1.val,E2.val为左右枝。 返回指向树根的指针。
{E.val:=E1.val}
式之间的联系与三元式不同(通过指示器),它是通过临
时变量,所以更动四元式很容易,这就为优化提供了方便,
Comp因iler为Prin不cip牵les 扯到改变指示器的问题。
25
§3.某些语句的四元式及翻译
一、说明语句的翻译 程序语言中的说明语句都是给编译程序
提供信息的,诸如类型、维数、每维的界种 类等,因此一般不生成目标,只是在编译时 把有关信息填入相应表格即可。下面我们就 来看看对说明语句如何翻译─也就是语义子 程序的描述。
1、类型检查:如果操作符作用于不相容的操作 数,编译程序必须报错;
2、控制流检查:控制流语句必须使控制流转移 到合法的地方;
3、一致性检查:某些对象不允许重复定义等;
4、相关名字检查:有时,同一名字必须出现两 次或多次;
CompilerPrinciples
2
◇ 虽然源程序可以直接翻译为目标语言代码,但是 通常编译程序还是采用了独立于机器的、复杂性介 于源语言与机器语言之间的中间语言。这样做的好 处是:
CompilerPrinciples
19
(2)E→i {E.val:=Entry(i)}
这儿ENTRY是一个语义过程,它查找i在 符号表中的位置。若用LOOKUP(NAME)函数表 示对NAME查找符号表,找到则返回表项位置, 找不到则返回NULL;另用FILLSYM(NAME)表示 在符号表中开辟一新项,并将新项入口作为 返回值。 于是可如下实现:
只要遵从在运算量之后紧跟它们的运算符这条规 则,就可以把后缀式扩展到更大范围。
例如:
(1)函数调用F(x1,x2…xn)可表示为: 函数运算符─多目
x1x2…xn F FUN
运算符
(2) V=e的逆波兰表示: V' e' =
例如: S=2*3.14159*R → S 2 3.14159 * R * =
5
1. 既定翻译任务: ⅰ.改变编译程序某些变量的值;
ⅱ.查填表格;
ⅲ.诊察与报告错误信息; ⅳ.产生某种形式的中间代码;
2.语义子程序(语义动作): 指出相应产生式所产生的符号串的语义以及按
照该语义生成某种中间代码的基本动作。这种翻 译方法直观上说就是为每个产生式配上一个语义 程序,在执行语法分析的同时执行语义程序。
B
--
RESULT T1
❖ 规定凡只有一个 运算量时只用 ARG1。
(2) +
C
D
(3) * T1 T2
T2
❖ 以上的Ti都是临时
T3
变量,其处理方
(4) := T3 --
A
法可以像用户自
定义变量一样进
填倒顺序也可
符号表,也可以
直接用某种整数
从以上例子可以看出,四元式出现的 码表示。
顺序和表达式计值顺序一样,但四元
由此可以看出,表达式的编译过程实际上就是产生逆波兰
表示的过程,甚至有的计算机把整个运算器做成一个后进先 出的运算体系(如美国Burroughs公司的一些机器)。逆波 兰表示并不是只用于表达式的翻译,我们可以很容易把它推 广,用它来表示整个语句乃至整个程序。
CompilerPrinciples
10
2.后缀式的推广
相关文档
最新文档