语义分析和中间代码产生
程序设计语言编译原理第三版第7章

N→ Є
D →id: T { enter(top(tblptr), , T.type, top(offset)); top(offset):= top(offset) +T.width } 24
§7.2
说明语句
2.含嵌套说明的翻译模式:
(1)语义规则中的操作: Mktable (previous): 创建一张新符号表,并返回指向新表的一个指针; Enter (table, name, type, offset):
(2)置相对地址为当前offset之值, (3)使offset加上该名字所表示的数据对象的域宽。
20
§7.2 说明语句
3. 相应的翻译模式:
PD { offset:=0 } { enter (, T.type,offset);
DD;D
Did:T
offset:=offset+t.width } Tinteger
(0) (1) (2) (3) (4) (5)
(2) (3) (4)
16
§7.1 中间语言
4.间接三元式:便于代码优化处理
方法:间接码表+三元式表
按运算的先后顺序列出有关三元式在三元表中的位置 例: 语句X:=(A+B)*C;Y:=D↑(A+B)的间接三元式表示如下所示: 间接代码 三元式表 (1) (2) (3) (1) (4) (5)
30
已归约串
PLACE
输入串
语义动作
# #X # X:= # X:= # X:= # X:= # X:=
X:= -B*(C+D)# X := -B*(C+D)# X_ -B*(C+D)# X__ B*(C+D)# -B X__B *(C+D)# -E X__B *(C+D)# { E.place:=p=<B>} E1 X_T1 *(C+D)# {E1.place:=newtemp=T1; 生成四元式(1) } … … … … # X:=E*(C X_T1__C +D)# # X:=E*(E1 X_T1__C +D)# { E1.place:=p=<C> } … … … …
程序编译的四个步骤

程序编译的四个步骤程序编译是将高级语言编写的程序翻译成机器语言的过程。
编译器是用来进行编译的工具,它可以将源代码转换为可执行的机器码,从而能够被计算机直接执行。
程序编译通常包括四个主要步骤:词法分析、语法分析、语义分析和代码生成。
1.词法分析词法分析是程序编译的第一步,也是一个很关键的步骤。
在词法分析中,编译器会将源代码分解为一个个的词法单元。
词法单元是程序的最小语法单位,可以是关键字、标识符、运算符、常量等等。
编译器会根据事先定义好的语法规则,将源代码中的字符序列解析成词法单元序列,并且给每个词法单元加上相应的标记,以便后面的步骤进行处理。
2.语法分析语法分析是程序编译的第二步。
在语法分析中,编译器会根据词法分析得到的词法单元序列,构建语法树或抽象语法树。
语法树是一个树状的数据结构,它表示程序的语法结构。
编译器会根据文法规则和词法单元的组合规则,对词法单元序列进行检查,并将其组织成语法树或抽象语法树。
语法树或抽象语法树是编译器进行后续处理的基础,它描述了程序的语法结构,方便后续步骤对程序进行分析和优化。
3.语义分析语义分析是程序编译的第三步。
在语义分析中,编译器会对语法树或抽象语法树进行分析,进行语义检查和语义推导。
语义是指程序中传达的意义和规则,它描述了程序如何运行和产生结果。
编译器会根据语义规则检查程序是否存在语义错误,并进行类型检查和类型推导。
如果程序存在语义错误,则编译器会输出错误信息,提示开发人员进行修正。
另外,编译器还会进行一些语义转换和优化,例如将高级语言中的循环结构转换为汇编语言中的跳转指令。
4.代码生成代码生成是程序编译的最后一步。
在代码生成中,编译器会根据语义分析得到的语法树或抽象语法树,生成目标代码或机器代码。
目标代码是特定平台上的中间代码表示,它与具体的机器相关性较低。
机器代码是目标机器上可以直接执行的二进制代码。
编译器会将目标代码或机器代码生成为对应的输出文件,例如可执行文件、动态链接库或静态链接库。
第8章+语义分析和中间代码生成

的二维数组,一结点一行,行号表示结点的位置。 【例】 a=b*c+b*d
1 2 a = 1 3 4 7 6 5 8 9
3
4 5 6 7 8 9
第8章 语义分析和中间代码生成
b
* c + b * d
8.2 中间代码
8.2.3 三地址码
1. 三地址码 (i)x := y op z (1) (2) (3) (4) t1:=b*c t2:=b*d t3:=t1+t2 a:=t3
(c,real)填入符号表,D.t =real
10
D
#
结束
8.3 自底向上语法制导翻译
第8章 语义分析和中间代码生成
8.3.2 说明语句的翻译
2. 数组说明的翻译
数组内情向量:存放数组的相关信息。
内情向量的地址存于符号表中。
数组引用的关键问题 —— 数组元素的地址计算
第8章 语义分析和中间代码生成
语义: E.val=T1.val+T2.val
E (E.val= T1.val+T2.val=12)
T1(T1.val=5)
第8章 语义分析和中间代码生成
T2(T1.val=7)
8.1 语义分析
8.1.2 语义的描述
(2)继承属性 一个文法符号N在产生式的右边出现,若它的t属性由该产生 式的左部非终结符或右部的其他符号的属性决定,则N.t为N的继 承属性。
第8章 语义分析和中间代码生成
8.1 语义分析
8.1.2 语义的描述
例:变量说明语句的文法 D→TL T→real | int L→id | L1, id T.type由real或int决定; 标识符表类型L.in由T.type确定, 且依次传给表中的每一个标识符。
编译原理实验报告

编译原理实验报告一、实验目的编译原理是计算机科学中的重要学科,它涉及到将高级编程语言转换为计算机能够理解和执行的机器语言。
本次实验的目的是通过实际操作和编程实践,深入理解编译原理中的词法分析、语法分析、语义分析以及中间代码生成等关键环节,提高我们对编译过程的认识和编程能力。
二、实验环境本次实验使用的编程语言为C++,开发环境为Visual Studio 2019。
此外,还使用了一些相关的编译工具和调试工具,如 GDB 等。
三、实验内容(一)词法分析器的实现词法分析是编译过程的第一步,其任务是将输入的源程序分解为一个个单词符号。
在本次实验中,我们使用有限自动机的理论来设计和实现词法分析器。
首先,定义了各种单词符号的类别,如标识符、关键字、常量、运算符等。
然后,根据这些类别设计了相应的状态转换图,并将其转换为代码实现。
在实现过程中,使用了正则表达式来匹配输入字符串中的单词符号。
对于标识符和常量等需要进一步处理的单词符号,使用了相应的规则进行解析和转换。
(二)语法分析器的实现语法分析是编译过程的核心环节之一,其任务是根据给定的语法规则,分析输入的单词符号序列是否符合语法结构。
在本次实验中,我们使用了递归下降的语法分析方法。
首先,根据实验要求定义了语法规则,并将其转换为相应的递归函数。
在递归函数中,通过对输入单词符号的判断和处理,逐步分析语法结构。
为了处理语法错误,在分析过程中添加了错误检测和处理机制。
当遇到不符合语法规则的输入时,能够输出相应的错误信息,并尝试进行恢复。
(三)语义分析及中间代码生成语义分析的目的是对语法分析得到的语法树进行语义检查和语义处理,生成中间代码。
在本次实验中,我们使用了三地址码作为中间代码的表示形式。
在语义分析过程中,对变量的定义和使用、表达式的计算、控制流语句等进行了语义检查和处理。
对于符合语义规则的语法结构,生成相应的三地址码指令。
四、实验步骤(一)词法分析器的实现步骤1、定义单词符号的类别和对应的正则表达式。
第七章 语义分析和中间代码产生(第九周)

源语言 程序
Compiler Front End
中间语 言程序
Compiler Back End
目标语 言程序
内容线索
中间语言 说明语句
赋值语句的翻译
布尔表达式的翻译
控制语句的翻译
过程调用的处理
中间语言
常用的中间语言
后缀式,逆波兰表示 图表示
DAG 抽象语法树
三地址代码
7. 2 中间语言
1. 后缀式
后缀式表示法又称逆波兰表示法,是一种表示表达式的 方法,它把运算量(操作数)写在前面,算符写在后面 例:中缀表示: a+b,a+b*c,m=1,(a+b)*c, (a+b)*(c-d) 后缀表示: ab+, abc*+, m1=,ab+c*, ab+cd-* 特点: 运算分量的个数与先后次序不变; 运算符的个数不变, 但其出现顺序即为执行顺序 无括号;
一个表达式E的后缀式可如下定义:
(1)如果E是一个变量或常数,则E的后缀式是E 自身; (2)如果E是E1opE2形式的表达式,op是二元操 作符,则E的后缀式为E1’E2’op, E1’和E2‘分 别为E1和E2的后缀式; (3)如果E是(E1)形式的表达式,则E1的后缀式 就是E的后缀式
3*5+4
+
if_then_else
*
B S1 S2 3 5
4
建立表达式的抽象语法树
mknode (op,left,right) 建立一个运算符号结点, 标号是op,两个域left和right分别指向左子树和 右子树。 mkleaf (id,entry) 建立一个标识符结点,标号为 id,一个域entry指向标识符在符号表中的入口。
程序设计语言与编译原理_第九章语义分析和中间代码生成

语义检查和语义处理 核心是生成相应的中间代码
程序设计语言与编译
三、语义值
在描述语义动作时,需要赋予每个文法符号以各种不 同的“值”,这些值统称为“语义值”.如,“类型”, “种属”,“地址”或“代码”等。通常用 X.TYPE,X.CAT,X.VAL来表示这些值。
形如x:=y op z的赋值语句,op为二目算术
算符或逻辑算符;
赋值语句x:=op y,op为一元算符,如一元
减uminus, not, 移位及转换算符(如将定点 数转换为浮点数);
赋值语句x:=y; 无条件转移语句 goto L;
16
程序设计语言与编译
条件转移语句 if x relop y goto L 或 if a goto
动态语义检查
需要生成相应的目标代码,它是在运行时进行的;
例如:除零溢出错误。
静态语义检查
在编译时完成的,它涉及以下几个方面:
(1) 类型检查
(2) 控制流检查
(3) 一致性检查
程序设计语言与编译
(1) 类型检查
int x; float f(); x = f();
符合变量声明的语法、语义 符合函数声明的语法、语义 符合赋值语句的语法、不符合语义
得较为容易,但语义分析不像词法分析和
语法分析那样可以分别用正规文法和上下
文无关文法描述。
由于语义是上下文有关的,因此语
义的形式化描述是非常困难的,目前较为
常见的是用属性文法作为描述程序语言语
编译原理作业集-第七章(精选.)

编译原理作业集-第七章(精选.)第七章语义分析和中间代码产⽣本章要点1. 中间语⾔,各种常见中间语⾔形式;2. 说明语句、赋值语句、布尔表达式、控制语句等的翻译;3. 过程调⽤的处理;4. 类型检查;本章⽬标掌握和理解中间语⾔,各种常见中间语⾔形式;各种语句到中间语⾔的翻译;以及类型检查等内容。
本章重点1.中间代码的⼏种形式,它们之间的相互转换:四元式、三元式、逆波兰表⽰;3.赋值语句、算术表达式、布尔表达式的翻译及其中间代码格式;4.各种控制流语句的翻译及其中间代码格式;5.过程调⽤的中间代码格式;6.类型检查;本章难点1. 各种语句的翻译;2. 类型系统和类型检查;作业题⼀、单项选择题:1. 布尔表达式计算时可以采⽤某种优化措施,⽐如A and B⽤if-then-else可解释为_______。
a. if A then true else B;b. if A then B else false;c. if A then false else true;d. if A then true else false;2. 为了便于优化处理,三地址代码可以表⽰成________。
a. 三元式b. 四元式c. 后缀式d. 间接三元式3. 使⽤三元式是为了________:a. 便于代码优化处理b. 避免把临时变量填⼊符号表c. 节省存储代码的空间d. 提⾼访问代码的速度4. 表达式-a+b*(-c+d)的逆波兰式是________。
a. ab+-cd+-*;b. a-b+c-d+*;c. a-b+c-d+*;d. a-bc-d+*+;5. 赋值语句x:=-(a+b)/(c-d)-(a+b*c)的逆波兰式表⽰是_______。
a. xab+cd-/-bc*a+-:=;a. xab+/cd-bc*a+--:=;a. xab+-cd-/abc*+-:=;a. xab+cd-/abc*+--:=;6. 在⼀棵语法树中结点的继承属性和综合属性之间的相互依赖关系可以由________来描述。
第7章 语义分析与中间代码生成

•
第6章小结
• 注释分析树和相应的依赖图是属性值的关联 关系和计算顺序的表达形式,语义关系可以 使用抽象语法树表示。 依据语法分析方法有自底向上的和自顶向下 的,语法制导翻译既可以按照自底向上的策 略进行,也可以按照自顶向下的策略进行。
•
2013-7-25
3
第7章 语义分析与中间代码生成
第6章小结
• • 语法分析中进行静态语义检查和中间代码生 成的技术称为语法制导翻译技术。 为了通过将语义属性关联到文法符号、将语 义规则关联到产生式,有效地将语法和语义 关联起来,人们引入了语法制导定义。没有 副作用的语法制导定义又称为属性文法。
2013-7-25
1
第6章小结
• 为相应的语法成分设置表示语义的属性,属 性的值是可以计算的,根据属性值计算的关 联关系,将其分成综合属性和继承属性,根 据属性文法中所含的属性将属性文法分成S属性文法和L-属性文法。 如果不仅将语义属性关联到文法符号、将语 义规则关联到产生式,而且还通过将语义动 作嵌入到产生式的适当位置来表达该语义动 作的执行时机,这就是翻译模式。翻译模式 给语义分析的实现提供了更好的支持。
2013-7-25
arg2 result t1 d t2 d t4 t2 t3 t4 t5 a
5 assign
图7.2(a) 图7.1中三地址码的四元式表示
11
三元式
• 为了节省临时变量的开销,有时也可以使用只有三个域的三 元式来表示三地址码。三元式的三个域分别称为op,arg1和 arg2,op,arg1和arg2的含义与四元式类似,区别只是arg1和 arg2可以是某个三元式的编号(图7.2(b)中用圆括号括起来的数 字),表示用该三元式的运算结果作为运算对象。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第七章语义分析和中间代码产生本章概述上一章我们介绍了属性文法和语法制导翻译,本章我们将把上章所介绍的方法和技术应用于语义分析和中间代码产生中。
主要学习内容:中间语言的形式——后缀式、图表示法、三地址代码,说明语句的语义分析,赋值语句的翻译,布尔表达式的翻译,控制语句的翻译,过程调用的处理,类型检查。
学习目标:熟悉几种中间语言的描述,掌握各种语句的翻译方法,会给出各种语句的语义规则和语义子程序。
学习重点和难点:如何把属性文法和语法制导翻译的方法和技术应用于语义分析和中间代码产生中,特别是表达式和控制语句的翻译。
7.1 中间语言7.1.1 中间语言虽然源程序可以直接翻译为目标语言代码。
但是许多编译程序都采用了独立于机器的、复杂性介于源语言和机器语言之间的中间语言。
常见的中间语言形式包括:后缀式,三地址代码(包括三元式,四元式,间接三元式),DAG图表示和抽象语法树等。
1. 后缀式后缀式表示法是波兰逻辑学家卢卡西维奇(Lukasiewicz)发明的一种表示表达式的方法,因此又称逆波兰表示法。
这种表示法是,把运算量(操作数)写在前面,把算符写在后面(后缀)。
例如,把a+b写成ab+,把a*b写成ab*。
2. 抽象语法树在语法树中去掉那些对翻译不必要的信息,从而获得更有效的源程序中间表示。
这种经变换后的语法树称之为抽象语法树(Abstract Syntax Tree)。
3. DAG与抽象语法树一样,对表达式中的每个子表达式,DAG(Directed Acyclic Graph)中都有一个结点。
一个内部结点代表一个操作符,它的孩子代表操作数。
两者不同的是,在一个DAG 中代表公共子表达式的结点具有多个父结点,而在一棵抽象语法树中公共子表达式被表示为重复的子树。
4. 三地址代码三地址代码是由下面一般形式的语句构成的序列:x:=y op z其中x,y,z为名字、常数或编译时产生的临时变量,op代表运算符号如定点运算符、浮点运算符、逻辑运算符等等。
每个语句的右边只能有一个运算符。
例如,源语言表达式x+y*z 可以被翻译为如下语句序列:T1:=y*zT2:=x+T1其中T1,T2为编译时产生的临时变量。
之所以称为三地址代码是因为每条语句通常包含三个地址,两个用来表示操作数,一个用来存放结果。
三地址语句类似于汇编语言代码。
语句可以带有符号标号,而且存在各种控制流语句。
符号标号代表存放中间代码的数组中三地址代码语句的下标。
下面是常用的三地址语句的种类:(1) 形如x:=y op z的赋值语句,其中op为二元算术算符或逻辑算符。
(2) 形如x:=op y的赋值语句,其中op为一元算符,如一元减uminus、逻辑非not、移位算符及转换算符(如将定点数转换成浮点数)。
(3) 形如x:=y的复制语句,它将y的值赋给x。
(4) 形如goto L的无条件转移语句,即下一条将被执行的语句是带标号L的三地址语句。
(5) 形如if x relop y goto L或if a goto L的条件转移语句。
第一种形式语句施用关系运算符号relop(如<, =, >, =等等)于x和y,若x与y满足关系relop,那么下面就执行带标号L的语句,否则下面就继续执行if语句之后的语句。
第二种形式的语句中,a为布尔变量或常量,若a为真,则执行带标号L的语句,否则执行后一条语句。
(6) 用于过程调用的语句param x和call p,n,以及返回语句return y。
源程序中的过程调用语句p(x1,x2,…,x n)通常产生如下的三地址代码:param x1param x2……param x ncall p,n其中n表示实参个数。
过程返回语句return y中y为过程返回的一个值。
(7) 形如x:=y[i]及x[i]:=y的索引赋值。
前者把相对于地址y的后面第i个单元里的值赋给x。
后者把y的值赋给相对于地址x后面的第i个单元。
(8) 形如x:=&y, x:=*y和*x:=y的地址和指针赋值。
其中第一个赋值语句把y的地址赋给x。
第二个赋值语句x:=*y执行的结果是把y所指示的地址单元里存放的内容赋给x。
第三个赋值语句*x:=y,将把x所指向的对象的右值赋为y的右值。
三地址语句可看成中间代码的一种抽象形式。
编译程序中,三地址代码语句的具体实现可以用记录表示,记录中包含表示运算符和操作数的域。
通常有三种表示方法:四元式、三元式、间接三元式。
1. 四元式一个四元式是一个带有四个域的记录结构,这四个域分别称为op, arg1, arg2及result。
域op包含一个代表运算符的内部码。
三地址语句x:=y op z可表示为:将y置于arg1域,z 置于arg2域,x置于result域,:=为算符。
带有一元运算符的语句如x:=-y或者x:=y的表示中不用arg2。
而象param这样的运算符仅使用arg1域。
条件和无条件转移语句将目标标号置于result域中。
2. 三元式为了避免把临时变量填入到符号表,我们可以通过计算这个临时变量值的语句的位置来引用这个临时变量。
这样表示三地址代码的记录只需三个域:op、arg1和arg2。
因为用了三个域,所以称之为三元式。
3. 间接三元式为了便于代码优化处理,有时不直接使用三元式表,而是另设一张指示器(称为间接码表),它将按运算的先后顺序列出有关三元式在三元表中的位置。
换句话说就是,我们用一张间接码表辅以三元式表的办法来表示中间代码。
这种表示法称为间接三元式。
7.2说明语句7.2.1 说明语句当考查一个过程或分程序的一系列说明语句时,便可为局部于该过程的名字分配存储空间。
对每个局部名字,我们都将在符号表中建立相应的表项,并填入有关的信息如类型、在存储器中的相对地址等。
相对地址是指对静态数据区基址或活动记录中局部数据区基址的一个偏移量。
当产生中间代码地址时,对目标机一些情况做到心中有数是有好处的。
例如,假定在一个以字节编址的目标机上,整数必须存放在4的倍数的地址单元,那么,计算地址时就应以4的倍数增加。
在C、Pascal及FORTRAN等语言的语法中,允许在一个过程中的所有说明语句作为一个组来处理,把它们安排在一所数据区中。
从而我们需要一个全程变量如offset来跟踪下一个可用的相对地址的位置。
允许嵌套过程的语言,对于每一个过程,其中局部名字的相对地址计算与前述一样。
而当遇到一个嵌入的过程说明时,则应当暂停包围此过程的外围过程说明语句的处理。
对于记录中的域名同样可以类似地给它建立一张符号表。
7.3 赋值语句的翻译7.3.1 赋值语句的翻译对于简单算术表达式及赋值语句,把它们翻译为三地址代码的翻译模式是比较好理解的,如下所示。
S→id:=E {p:=lookup();if p nil thenemit(p ‘:=’ E.place)else error}E→E1+E2{E.place:=newtemp;emit(E.place ‘:=’ E1.place ‘+’ E2.place)}E→E1*E2 {E.place:=newtemp;emit(E.place ‘:=’ E 1.place ‘*’ E 2.place)}E→E1{E.place:=newtemp;emit(E.place‘:=’ ‘uminus’E 1.place)}E→(E1) {E.place:=E1.place}E→id {p:=lookup();if p nil thenE.place:=pelse error }产生简单算术表达式及赋值语句三地址代码的翻译模式如果表达式和赋值句中包含数组元素的引用,则编译程序要根据数组元素的地址计算公式产生相应的代码。
为了便于翻译,我们把包含数组元素引用的表达式和赋值句的产生式改写成如下形式:(1) S→L:=E(2) E→E+E(3) E→(E)(4) E→L(5) L→Elist ](6) L→id(7) Elist→Elist, E(8) Elist→id [ E相应的翻译模式如下所示。
S→L:=E{if L.offset=null then /*L是简单变量*/emit(L.place ‘:=’ E.place)else emit(L.place ‘ [’ L.offset‘]’ ‘:=’ E.place)}E→E1 +E2{E.place:=newtemp;emit(E.place ‘:=’ E 1.place ‘+’ E 2.place)}E→(E1){E.place:=E1.place}E→L{if L.offset=null thenE.place:=L.placeelse beginE.place:=newtemp;emit(E.place ‘:=’ L.place ‘[’ L.offset ‘]’)end }L→Elist ]{L.place:=newtemp;emit(L.place ‘:=’ Elist.array ‘-’ C);{C的定义见(7.7)} L.offset:=newtemp;emit(L.offset ‘:=’ w ‘*’ Elist.place)}L→id{ L.place:=id.place; L.offset:=null }Elist→Elist1, E{t:=newtemp;m:=Elist1.ndim+1;emit(t ‘:=’ Elist1.place ‘*’ limit(Elist1.array,m));emit(t ‘:=’t ‘+’ E.place);Elist.array:= Elist1.array;Elist.place:=t;Elist.ndim:=m}Elist→id [ E{Elist.place:=E.place;Elist.ndim:=1;Elist.array:=id.place}含数组元素引用的表达式和赋值句的翻译模式7.4 布尔表达式的翻译7.4.1 布尔表达式的翻译在程序设计语言中,布尔表达式有两个基本的作用:一个是用作计算逻辑值;另一个,也是更多地是用作控制流语句如if-then、if-then-else和while-do等之中的条件表达式。
作为逻辑求值的布尔表达式的翻译与简单算术表达式的翻译类似,较易理解。
重点是作为条件控制的布尔表达式的翻译。
出现在条件语句if E then S1 else S2中的布尔表达式E,它的作用仅在于控制对S1和S2的选择。
只要能够完成这一使命,E的值就无须最终保留在某个临时单元之中。
因此,作为转移条件的布尔式E,我们可以赋予它两种“出口”。
一是“真”出口,出向S1;一是“假”出口,出向S2。
对于作为转移条件的布尔式,我们可以把它翻译为一串跳转指令。