编译原理 第七章———语义分析和中间代码的产生

一、中间语言

1 语法树 

语法树,有向非循环图和后缀式表示源程序的自然层次结构

2 后缀式

中缀式: a:=b*-c+b*-c
后缀式: a b c - * b c - * + =

3 三地址代码表示

一般形式    x:=y op z 

4 语法制导翻译生成三地址代码  

需要用到的量:

(1)E.place表示存放E值的名字。 

(2)E.code表示对E求值的三地址语句序列。 

(3)   newtemp是个函数,对它的调用将产生   一个新的临时变量。

5 三地址代码的具体实现

(1)四元式  op, arg1, arg2, result
(2)三元式 op, arg1, arg2
(3)间接三元式  间接码表+三元式表

二、说明语句

    说明语句的翻译:对每个局部名字在符号表中建立相应的表项,填写有关的信息。如:类型、嵌套深度、相对地址、内情向量等。
    相对地址:相对静态数据区基址或活动数据中局部数据区基址的一个偏移值。

过程中的说明语句 :

一个过程中的所有说明语句作为一个类集来处理。一个全程变量Offset来记录下 一个数据在符号表中的相对地址。

三、赋值语句的翻译

赋值语句的翻译:

表达式的成分可以是整型量、实型量、数组 元素和记录。

1、符号表中的名字

名字可以理解为指向符号表中相应该名字表项的指针。

例:
emit(E.place‘:=’E1.place‘+’E2.place) 或emit(= , E1.place,E2.place, E.place)


2、数组元素地址分配(复杂赋值语句)

数组元素地址的计算公式

①一维数组的数组元素计算公式

A[i]的地址:

base+(i-low )* w=bace-low*w +  i*w

常量部分+变量部分:bace-low*w +  i*w 

②二维数组

A[i1,i2]的地址:

base+((i1 一low1)* n2+i2 一low2)*w)= base-((low1 *n2)+low2)*w + ((i1*n2)+i2)* w

令c= ((low1 *n2)+low2)*w    则常量部分=a[low1,low2]-c 

③多维数组A[i1,i2,...,ik] 的地址的计算:

常量部分c=((...((low1*n2+low2)*n3+low3)...)*nk+lowk) * w 

变量部分v= ((...((i1*n2+i2)*n3+i3...)*nk+ik)*w 

所以a[i1,i2,…in]的地址=base-c+v


四、布尔表达式的翻译

布尔表达式: 用布尔运算符号(and,or,not)作用到布尔变量或关系表达式上而组成

布尔表达式的作用:
1. 用作计算逻辑值

        2. 用作控制流语句如if-then,if-then-else和while-do等之中的条件表达式

翻译布尔表达式的方法 

表示一个布尔表达式的值

   方法一:用数值表示真和假,从而对布尔表达式的求值可以象对算术表达式的求值那样一步一步地来计算

   方法二:另一种方法是根据布尔表达式的特点,采用了某种优化措施。

数值表示法:用1表示真,0表示假来实现布尔表达式的翻译。

布尔表达式的数值表示法的翻译模式

·emit用于将一个三地址语句输送到文件中

·Nextquat是一个计数器,指向下一个三地址语句在输出序列中的索引序号,也就是即将生成的三地址语句序号。

每执行一次emit后,nextquat自动加1


五、控制语句的翻译

控制流语句中的布尔表达式的翻译:
对于出现在条件语句  if E then s1 else s2中的布尔表达式E,其作用就是控制对S1和S2的选择。因此,作为条件的布尔表达式,把它设计成两个出口:E.true   和   E.false。考虑E的上下文,对于IF语句,E.true   指向S1,E.false指向S2;对于while语句E.true  指向循环的开始,E.false指向while 的下一语句。

基本思想:  假定E 形如a<b,则将生成如下的E的代码:

三地址表示:
 E.true: if a<b goto E.true (真出口)

 E.false: goto E.false     (假出口)                     


四元式表示:
E.true:(j<, a , b , E.true)   (真出口)

E.false: (j,   ,    , E.false )  (假出口)

用四元式实现三地址码,真假出口可表示为:

真出口:
(jnz,a,_,P)表示 if a goto P

(jrop,x,y,P)表示 if  x rop y  goto P

假出口:

(j,_,_,P)表示 goto P

回填

生成跳转语句时,将其E.true和E.false链成一个链表,记录在E.truelist和E.falselist中。等到转移目标确定以后,再将转移出口填入E.truelist和E.falselist中。

翻译模式中用到的三个函数:

①.makelist(i):创建一个仅包含i的新表,i  是四元式数组的一个索引(下标),或说 i是四元式代码序列的一个标号。

②.merge(p1,p2):连接由指针p1和p2指向的两个表并且返回一个指向连接后的表的指针。 Merg(p1,p2)= p1 p2=0;p1 p2≠0;

③.backpatch(p,t):把i作为目标标号回填到p所指向的表中的每一个转移指令中去。此处的“表”都是为“反填”所拉的链.


使用回填翻译控制流语句 
先记录要回填的转移指令地址,在适当的时候进行回填,以便赋值和布尔表达式的求值得到合适的连接,以完成程序的控制流程。

六、过程调用的处理

(1)把程序控制转移到子程序(过程段),执行完毕再返回。这个问题很好解决。

(2)传递实在参数。我们前面谈到过几种不同的参数传递方式(传名、传值、传地址),它们的语义动作也就有所区别。

七、感想

这一章主要是用来做题了,语义分析和中间代码生成。

编译程序要做的工作就是进行静态语义检查和翻译。在静态语义检查包括类型检查,控制流
检查,一致性检查,相关名字检查。
中间语言主要学习了三地址代码和逆波兰。逆波兰其实就是后缀表达式。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机科学中的一个重要分支,主要研究如何将高级语言转换为机语言的过程。其中,词法分析是编译中的一个重要组成部分,它负责将源代码中的字符序列转换为有意义的单词序列,也就是词法单元。 下面是一个简单的词法分析C语言代码示例: ```c #include <stdio.h> #include <ctype.h> #include <string.h> #define MAX_TOKEN_LEN 100 typedef enum { INT, FLOAT, IDENTIFIER, KEYWORD } TokenType; typedef struct { TokenType type; char value[MAX_TOKEN_LEN]; } Token; char *keywords[] = {"if", "else", "while", "for", "int", "float"}; int is_keyword(char *word) { int i; for (i = 0; i < 6; i++) { if (strcmp(word, keywords[i]) == 0) { return 1; } } return 0; } Token get_token() { Token token; char c = getchar(); while (isspace(c)) { c = getchar(); } if (isdigit(c)) { token.type = INT; int i = 0; while (isdigit(c)) { token.value[i++] = c; c = getchar(); } if (c == '.') { token.type = FLOAT; token.value[i++] = c; c = getchar(); while (isdigit(c)) { token.value[i++] = c; c = getchar(); } } token.value[i] = '\0'; ungetc(c, stdin); } else if (isalpha(c) || c == '_') { token.type = IDENTIFIER; int i = 0; while (isalnum(c) || c == '_') { token.value[i++] = c; c = getchar(); } token.value[i] = '\0'; ungetc(c, stdin); if (is_keyword(token.value)) { token.type = KEYWORD; } } else { token.type = c; } return token; } int main() { Token token; do { token = get_token(); switch (token.type) { case INT: printf("INT: %s\n", token.value); break; case FLOAT: printf("FLOAT: %s\n", token.value); break; case IDENTIFIER: printf("IDENTIFIER: %s\n", token.value); break; case KEYWORD: printf("KEYWORD: %s\n", token.value); break; default: printf("%c\n", token.type); break; } } while (token.type != EOF); return 0; } ``` 这个词法分析可以识别整数、浮点数、标识符和关键字。它通过一个`get_token()`函数来获取下一个词法单元,并根据单元的类型进行相应的处理。在`get_token()`函数中,它会读取输入流中的字符,根据字符的类型来判断当前单元的类型,并将单元的值存储在一个`Token`结构体中返回。在`main()`函数中,它会不断调用`get_token()`函数来获取下一个单元,并根据单元的类型进行相应的输出,直到读取到输入流的结尾。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值