编译原理第七章
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
• 四元式中操作分量或运算结果的地址表示可以分 为三大类:标号类、数值类和地址类。
标号类的语义信息是相应的标号值,包括过 程 ⁄ 函数体的入口标号; 数值类的语义信息就是该数据值;
地址类的语义信息由三个部分组成:层数、 偏移和访问方式(分为直接访问方式和间接访 问方式)。变参变量(指针变量)以及代表复 杂变量地址的临时变量属于间接访问方式。
Sem[top] ,并把运算结果的类型和FORM压入Sem栈。 码,如ω为 + ,则ω* 为ADDI或ADDF。
PROCEDURE GenCode(op)
Begin L:=Sem[top-1]; R:=Sem[top]; IF L.typ ≠R.typ THEN BEGIN new_dir (tempArg) IF L.typ=integer THEN BEGIN generate(FLOAT, L.FORM,-, tempArg) L:=(real, tempArg) END
First集 E Es T Ts C, id , ( ε, +, C, id , ( ε, *, / C, id , (
Follow集 #, ) #, ) #, ),+,#, ),+,#, ),+,-,*,/
(4)Es → - T Es (5)T → P Ts
P
(6)Ts→
(7)Ts → * P Ts (8)Ts → / P (9)P → C (10)P → id Ts
SemElem=record typ:ElemType ; FORM:FORM_Record end; SemStack=array[1..Max] of SemElem; var Sem:SemStack ; top:integer ;
• 对语义栈Sem的操作有
1. push(x):将x的类型和FORM(x)压入 Sem栈;X为标识符或常量。
在产生中间代中,用于类型检查和类型转换; 在目标代码生成阶段,当对名字进行地址分 配时,符号表是地址分配的依据。
• 如果符号表一直保存到目标代码生成阶段,则在四 元式中标识符的地址可用其在符号表中的地址表 示。 • 如果符号表不保存到目标代码生成阶段,则需要 把目标代码生成阶段对名字进行地址分配所需要 的相关信息放到中间代码中,这些信息包括标识 符抽象地址(层数、偏移)以及直接 ⁄ 间接访问 等信息,具体地说这些信息应放在四元式中的操作 分量或运算结果的地址表示中。
⑸ 下标表达式的类型是否为所允许的类型?
⑹ 函数说明中的函数类型与返回值的类型是否一致?
repeat 类型检查的语法单位 while(表达式)do 语句
类型相容性检查项目 类型检查的语法单位 for 变量:=初值 to 终值 do 语句
⑴ 各种条件表达式的类型是否是 pascal语言:if语句,while语 布尔类型? 句,repeat语句; do while(表达式) 语句; C语言:if语句 ,while语句,do语句; while语句,for while (表达式); 语句。 for ( [表达式 1]; [表达式 2 ]; [表达式3] )语句; ⑵ 运算符的运算分量是否相容? 表达式 ⑶ 赋值语句的左、右部类型是否 相容? ⑷ 实参与形参的类型是否相容? ⑸ 下标表达式的类型是否为所允 许的类型? ⑹ 函数说明中的函数类型与返回 值的类型是否一致? 赋值语句 过程调用语句、函数调用语句 下标变量 赋值语句/return语句
DataAddr=RECORD DataLevel:0..7; DataOffset :0..OffsetRange; DataMode: (dir,indir) END;
FORM_Record=RECORD case form:ArgForm of LabelForm:(Label:0…999); ValueForm:(Value:DataValue); AddrForm: (Addr:DataAddr); END: 操作分量或运算结果的地址表示的三大类:标号类、 数值类和地址类。
•表达式的四元式中间代码的实例
例如:表达式 E ≡ a * ( 3.5 + i * b ),假设 a、b 为实型变 量,i 为整型变量,则 E 生成的四元式中间代码如下: ( FLOAT,(i.level,i.off,dir) ,-, (-1,t1,dir)) ( MULTF,(-1,t1,dir), (b.level,b.off,dir) , (-1,t2,dir) ) ( ADDF , 3.5 , (-1,t2,dir) , (-1,t3,dir) ) ( MULTF ,(a.level,a.off,dir) , (-1,t3,dir) , (-1,t4,dir) ) ( FLOAT ,•尽管在实际的四元式中间代码中各运算分量或运算结 i , ─ , t1 ) ( MULTF 果都应该是地址表示,但为了简便起见,本章在生成的 , t1 , b , t2 ) 四元式中间代码中,我们只写变量名或临时变量名,而 ( ADDF , 3.5 , t2 , t3 ) 不是用地址形式。 ( MULTF , a , t3 , t4 )
2. pop(n):从Sem的栈顶删除n个元素。
7.4.3 常用的语义子程序
• 申请临时单元
new_dir(t): 申请一个临时变量t,且t是直接寻址;
new_indir(t):申请一个临时变量t,且t是间接寻址。
• 存放中间代码子程序Generate
该子程序功能是将一条四元式中间代码存放到 中间代码区中,其格式如下:
参数只给出操作码ω即可。
此处讨论的表达式涉及的操作码有加法(ADDI, ADDF)、减法(SUBI, SUBF)、乘法(MULTI, MULTF)、除法(DIVI, DIVF)和类型转换
(FLOABiblioteka Baidu),并且运算分量类型只考虑整型和
实型的情况。
GenCode(ω )的主要工作
(1) 若Sem[top–1].typ ≠ Sem[top].typ , 且Sem [top–1].typ = integer,则ω运算结果类型确定为real型, 并且为左分量产生类型转换代码; (FLOAT, [top–1].FORM,-, tempArg) (2) 若Sem[top–1].typ ≠ Sem[top].typ , 且Sem[top].typ = integer,则ω结果运算类型确定为real型,并 且为右分量产生类型转换代码; (FLOAT, [top].FORM,-, tempArg) (3) 若Sem[top–1].typ = Sem[top].typ ,则ω运算结果类型确定为 Sem[top-1].typ ,不产生类型转换代码;
First集 E Es C, id , ( ε, +, -
Follow集 # ,) # ,) +,- ,# ,) +,- ,# ,) *,/ ,+,- ,# ,)
T
Ts P
C, id , (
ε, *, / C, id , (
•每一个产生式的predict集
产生式 (1 ) E → T Es (2)Es → (3)Es → + T Es Predict集 C, id , ( #, ) + - C, id , (
Generate (ω , left , right , result )
•产生一条中间代码子程序GenCode(ω) 该子程序主要用于表达式中间代码生成,其功 能是产生一条四元式中间代码。 当执行语义动作子程序GenCode时,四元式操 作码的左、右运算分量的类型和FORM已经在语
义栈Sem的次栈顶和栈顶,因此,该子程序的
语句; until(表达式)
二、类型转换
• 由于一些语言允许运算分量的类型不同,所以在 检查运算符分量类型是否相容的同时,可能还需 要对某一运算分量做类型转换。
7.4 中间代码生成中的几个问题 7.4.1 语义信息的获取和保存
• 符号表在编译的不同阶段都要用到
在语义分析中,符号表所登记的内容将用于 语义检查;
• 本教材中我们采用了在中间代码生成时进行类型
检查的方法,而不是在目标代码生成时进行类型
检查,所以在中间代码的地址表示中不必包含类 型信息。 • 尽管在实际的四元式中间代码中各运算分量或运 算结果都应该是地址表示,但为了简便起见,本 章在生成的四元式中间代码中,我们只写变量名 或临时变量名,而不是用地址形式。
Es
T Ts P 9 10 11 5 5 5
2
3
4
2
(4)Es → - T Es (5)T → P Ts (6)Ts→ (7)Ts → * P Ts
#, ),+,* / C Id
(11)P → ( E )
(
•简单表达式的LL(1)分析表
C E 1 id ( 1 1 ) + * / #
产生式 (1 ) E → T Es (2)Es → (3)Es → + T Es Predict集 C, id , ( #, ) + - C, id , ( #, ),+,* /
•简单算术表达式的LL(1)文法定义
( 0) E → E#
(1) (2) (3) (4) (5 ) (6) (7) (8) (9 ) (10) (11)
E → TEs Es → Es → +TEs Es → -TEs T → PTs Ts→ Ts → *PTs Ts → /PTs P → C P → id P → (E)
7.4.2 语义栈Sem及其操作
• 中间代码生成尤其是变量和表达式的处理过程中要 用到语义栈Sem ,该栈主要用于存放运算分量和运 算结果的类型和地址表示 。假设语义栈 Sem 用数 组实现,具体定义如下:
type ArgForm=(LabelForm,ValueForm,AddrForm); ValueKind=(IntKind,RealKind,StringKind); DataValue=RECORD CASE Kind:ValueKind OF IntKind: (IntValue:integer); RealKind: ( RealValue:real); StringKind: (StringValue:string); END;
PROCEDURE GenCode(op)
ELSE
BEGIN
generate(FLOAT, R.FORM,-, tempArg) R:=(real, tempArg)
END
END new-dir (tempArg1); generate(op*, L.FORM, R.FORM, tempArg1); POP(2); PUSH((L.typ, tempArg1)) End;
GenCode(ω )的主要工作(续) (4) 产生(类型转换后)的中间代码:
(ω* , Sem[top–1].FORM /tempArg, Sem[top].FORM/tempArg , t );
(5) 从语义栈Sem中删除左、右运算分量Sem[top–1] 和 其中ω*表示ω的相应整型或实型的中间代码操作
• 关于临时变量的地址表示:
1. 临时变量没有层数概念,因此对临时变量的 层数可以取任意一个负数,如取-1;
2. 在中间代码生成阶段尽管产生大量的临时变 量,但经过优化后,只有少部分临时变量能保 留下来,这样在中间代码生成阶段还不能确定 临时变量的Offset值,因此临时变量的地址表 示中的Offset暂时取为临时变量的编号。
7.3 类型检查和类型转换
一、类型检查
• 类型检查是静态语义分析的重要工作之一,但为方便起 见,我们把类型检查的工作也放在中间代码生成时进行。 • 类型检查主要有以下几种: ⑴ 各种条件表达式的类型是否是布尔类型?
⑵ 运算符的运算分量是否相容?
⑶ 赋值语句的左、右部类型是否相容? ⑷ 实参与形参的类型是否相容?
7.5 表达式的中间代码生成
• 表达式的中间代码就是正确计算表达式值的四元 式序列。 • 表达式中的变量可以是简单、复杂变量(数组变 量,结构体成员变量等),还可以是函数调用, 而表达式中的运算符也可以包括算术运算符,布 尔运算符等。
• 本小节将给出简单算术表达式的中间代码生成的 LL(1)语法制导方法。