编译原理语义分析详解
编译原理实验报告 语义分析
.编译原理课程实验报告实验3:语义分析. .. ...要求:对如下工作进行展开描核心数据结构的设(1Nod本程序使用了两个新的实体类,分别I的属性I是标识符里面也包含了该标识符在本程序中存储的地址和长度等信息I下/privat String name;/基本类privat String type/起始地privatin offset/长in length privat/该数组各维的长publi List<Integer> arr_length得/变量的维度可以根arr_length.size(的属性如下Nod是语法生成树的节点Nod节点/privat String nam父节/privat Node fathe/子节publi List<Node>son/属Map<String,String>attribut publi使用哈希表这是因为各个节点的属性不是统一的的类型是哈Ma其atrribut以方便地创建、使用属性主要功能函数说(2因为语义分析过程与语法分析同步进此次试验的语义分析部分并没有新的功能函数文法符号匹配的过程中,插入语义的,代码都是在第二次第二次实验的基础上,LL(l的功能发作代码。
所以,只有句法分析函analysis(List<Token> token_list了变化,添加了语义分析的功能,其他函数功能基本与实验二相同(3程序核心部分的程序流程图..是否否是否是3-1图四、实验结果及分析得分..要求:对实验结果进行描述和分析,基本内容包括针对一测试程序输出其语义分析结果输出针对此测试程序对应的语义错误报告输出针对此测试程序经过语义分析后的符号表对实验结果进行分析注:其中的测试样例需先用已编写的词法分析程序进行处理测试程序void main () {double d;int a[2][3];d = 0;a[0][1] = 2;if (d == 0) {a[0][1] = d;}else {a[1][1] = 0;}while (a[0][1]<3) {++a[0][1];}}分析结果以及错误报告:. .4-1 图标识符表:4-2图实验结果分析:. ..。
编译原理语义分析实验报告
实验3 语义分析实验报告一、实验目的二、通过上机实习, 加深对语法制导翻译原理的理解, 掌握将语法分析所识别的语法成分变换为中间代码的语义翻译方法。
三、实验要求四、采用递归下降语法制导翻译法, 对算术表达式、赋值语句进行语义分析并生成四元式序列。
五、算法思想1.设置语义过程。
(1)emit(char *result,char *ag1,char *op,char *ag2)该函数的功能是生成一个三地址语句送到四元式表中。
四元式表的结构如下:struct{ char result[8];char ag1[8];char op[8];char ag2[8];}quad[20];(2) char *newtemp()该函数回送一个新的临时变量名, 临时变量名产生的顺序为T1, T2, …char *newtemp(void){ char *p;char m[8];p=(char *)malloc(8);k++;itoa(k,m,10);strcpy(p+1,m);p[0]=’t’;return(p);}六、 2.函数lrparser 在原来语法分析的基础上插入相应的语义动作: 将输入串翻译成四元式序列。
在实验中我们只对表达式、赋值语句进行翻译。
源程序代码:#include<stdio.h>#include<string.h>#include<iostream.h>#include<stdlib.h>struct{char result[12];char ag1[12];char op[12];char ag2[12];}quad;char prog[80],token[12];char ch;int syn,p,m=0,n,sum=0,kk; //p是缓冲区prog的指针, m是token的指针char *rwtab[6]={"begin","if","then","while","do","end"};void scaner();char *factor(void);char *term(void);char *expression(void);int yucu();void emit(char *result,char *ag1,char *op,char *ag2);char *newtemp();int statement();int k=0;void emit(char *result,char *ag1,char *op,char *ag2){strcpy(quad.result,result);strcpy(quad.ag1,ag1);strcpy(quad.op,op);strcpy(quad.ag2,ag2);cout<<quad.result<<"="<<quad.ag1<<quad.op<<quad.ag2<<endl;}char *newtemp(){char *p;char m[12];p=(char *)malloc(12);k++;itoa(k,m,10);strcpy(p+1,m);p[0]='t';return (p);}void scaner(){for(n=0;n<8;n++) token[n]=NULL;ch=prog[p++];while(ch==' '){ch=prog[p];p++;}if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){m=0;while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){token[m++]=ch;ch=prog[p++];}token[m++]='\0';p--;syn=10;for(n=0;n<6;n++)if(strcmp(token,rwtab[n])==0){syn=n+1;break;}}else if((ch>='0'&&ch<='9')){{sum=0;while((ch>='0'&&ch<='9')){sum=sum*10+ch-'0';ch=prog[p++];}}p--;syn=11;if(sum>32767)syn=-1;}else switch(ch){case'<':m=0;token[m++]=ch;ch=prog[p++];if(ch=='>'){syn=21;token[m++]=ch;}else if(ch=='='){syn=22;token[m++]=ch;}else{syn=23;p--;}break;case'>':m=0;token[m++]=ch;ch=prog[p++];if(ch=='='){syn=24;token[m++]=ch;}else{syn=20;p--;}break;case':':m=0;token[m++]=ch;ch=prog[p++];if(ch=='='){syn=18;token[m++]=ch;}else{syn=17;p--;}break;case'*':syn=13;token[0]=ch;break; case'/':syn=14;token[0]=ch;break; case'+':syn=15;token[0]=ch;break; case'-':syn=16;token[0]=ch;break; case'=':syn=25;token[0]=ch;break; case';':syn=26;token[0]=ch;break; case'(':syn=27;token[0]=ch;break; case')':syn=28;token[0]=ch;break; case'#':syn=0;token[0]=ch;break; default: syn=-1;break;}}int lrparser(){//cout<<"调用lrparser"<<endl;int schain=0;kk=0;if(syn==1){scaner();schain=yucu();if(syn==6){scaner();if(syn==0 && (kk==0))cout<<"success!"<<endl;}else{if(kk!=1)cout<<"缺end!"<<endl;kk=1;}}else{cout<<"缺begin!"<<endl;kk=1;}return(schain);}int yucu(){// cout<<"调用yucu"<<endl;int schain=0;schain=statement();while(syn==26){scaner();schain=statement();}return(schain);}int statement(){//cout<<"调用statement"<<endl;char *eplace,*tt;eplace=(char *)malloc(12);tt=(char *)malloc(12);int schain=0;switch(syn){case 10:strcpy(tt,token);scaner();if(syn==18){scaner();strcpy(eplace,expression());emit(tt,eplace,"","");schain=0;}else{cout<<"缺少赋值符!"<<endl;kk=1;}return(schain);break;}return(schain);}char *expression(void){char *tp,*ep2,*eplace,*tt;tp=(char *)malloc(12);ep2=(char *)malloc(12);eplace=(char *)malloc(12);tt =(char *)malloc(12);strcpy(eplace,term ()); //调用term分析产生表达式计算的第一项eplacewhile((syn==15)||(syn==16)){if(syn==15)strcpy(tt,"+");else strcpy(tt,"-");scaner();strcpy(ep2,term()); //调用term分析产生表达式计算的第二项ep2strcpy(tp,newtemp()); //调用newtemp产生临时变量tp存储计算结果emit(tp,eplace,tt,ep2); //生成四元式送入四元式表strcpy(eplace,tp);}return(eplace);}char *term(void){// cout<<"调用term"<<endl;char *tp,*ep2,*eplace,*tt;tp=(char *)malloc(12);ep2=(char *)malloc(12);eplace=(char *)malloc(12);tt=(char *)malloc(12);strcpy(eplace,factor());while((syn==13)||(syn==14)){if(syn==13)strcpy(tt,"*");else strcpy(tt,"/");scaner();strcpy(ep2,factor()); //调用factor分析产生表达式计算的第二项ep2strcpy(tp,newtemp()); //调用newtemp产生临时变量tp存储计算结果emit(tp,eplace,tt,ep2); //生成四元式送入四元式表strcpy(eplace,tp);}return(eplace);}char *factor(void){char *fplace;fplace=(char *)malloc(12);strcpy(fplace,"");if(syn==10){strcpy(fplace,token); //将标识符token的值赋给fplacescaner();}else if(syn==11){itoa(sum,fplace,10);scaner();}else if(syn==27){scaner();fplace=expression(); //调用expression分析返回表达式的值if(syn==28)scaner();else{cout<<"缺)错误!"<<endl;kk=1;}}else{cout<<"缺(错误!"<<endl;kk=1;}return(fplace);}void main(){p=0;cout<<"**********语义分析程序**********"<<endl;cout<<"Please input string:"<<endl;do{cin.get(ch);prog[p++]=ch;}while(ch!='#');p=0;scaner();lrparser();}七、结果验证1、给定源程序begin a:=2+3*4; x:=(a+b)/c end#输出结果2、源程序begin a:=9; x:=2*3-1; b:=(a+x)/2 end#输出结果八、收获(体会)与建议通过此次实验, 让我了解到如何设计、编制并调试语义分析程序, 加深了对语法制导翻译原理的理解, 掌握了将语法分析所识别的语法成分变换为中间代码的语义翻译方法。
《编译原理教程》第四章语义分析和中间代码生成
控制流分析和数据流分析案例
总结词
控制流分析和数据流分析是编译器设计中两种重要的 语义分析技术。
详细描述
在控制流分析案例中,我们以一个具有条件语句和循环 的程序为例,分析其控制流图(Control Flow Graph, CFG)。CFG是一个有向图,用于表示程序中各个基本块 之间的控制流程关系。通过CFG,编译器可以检测到潜 在的程序错误,如死代码和无限循环。在数据流分析案 例中,我们使用数据流方程来跟踪程序中变量的值在执 行过程中的变化。我们以一个简单的程序为例,该程序 包含一个变量在函数调用后被修改的情况。通过数据流 分析,我们可以确定变量的最新值,以便在后续的语义 分析中使用。
定义
三地址代码是一种中间代码形式,它由一系列的三元组操作数和 操作符组成。
特点
三地址代码具有高度规范化,易于分析和优化,且易于转换成目 标代码。
常见形式
常见的三地址代码有三种基本形式,即加法、减法和赋值。
循环优化
定义
循环优化是指在编译过程中,对循环结构进行优化, 以提高目标代码的执行效率。
常见方法
将源程序分解成一个个的词素或标记。
语法分析
根据语言的语法规则,将词素或标记组合成一个个的语句或表达式。
语义分析
对语法分析得到的语句或表达式进行语义检查,确保其语义正确。
中间代码生成
基于语义分析的结果,生成中间代码。
02
语义分析技术
类型检查
类型检查是编译过程中对源代码进行语义分析的重要环节,其主要目的是 确保源代码பைடு நூலகம்类型安全。
常见的循环优化方法包括循环展开、循环合并、循环 嵌套等。
优化效果
通过循环优化,可以减少循环的次数,提高程序的执 行效率。
编译原理课程设计之第六章 语义分析
mcy
2
语义分析的任务:
计算各类语法成分的语义信息(属性信息),一般将收集
的语义信息存放到相应的信息表中,在编译程序中符号 表是用来存放源程序中标示符相关属性(语义)信息的一 种信息表。 静态语义检查
类型检查:指类型相容问题的检查,如果操作符作用于不相容的操作数, 则编译器应该报错。 上下文有关问题的检查:当某个对象出现时,要求它必须在前面的某个 适当位置已经出现过。 唯一性检查:有时,要求某个对象只能被定义一次。 控制流检查:引起控制流从某个结构中跳转出来的语句,必须能够决定 控制流转向的目标地址。
(1)所采用的语法分析方法
(2)属性的计算次序。 语法分析方法都要求从左向右处理输入程 序,等价于要求属性能通过从左向右遍历 分析树进行赋值。
mcy
44
定义属性a1,...,ak 的一个属性文法是L-属性(Lattributed)文法,如果满足
mcy
37
num→digit
num.val=digit.val digit.base=num.base digit.val=0 digit.val=1 digit.val=7
digit→0 digit→1 ...... digit→7
mcy
38
digit→8
digit→9
if digit.base=8 then digit.val=error else digit.val= 8 if digit.base=8 then digit.val= error else digit.val 9
mcy
29
文法规则
语义规则 var-list.dtype = type.dtype type.dtype = integer type.dtype = real
编译原理中的词法分析与语法分析原理解析
编译原理中的词法分析与语法分析原理解析编译原理是计算机科学中的重要课程,它研究的是如何将源程序翻译成目标程序的过程。
而词法分析和语法分析则是编译过程中的两个重要阶段,它们负责将源程序转换成抽象语法树,为接下来的语义分析和代码生成阶段做准备。
本文将从词法分析和语法分析的原理、方法和实现技术角度进行详细解析,以期对读者有所帮助。
一、词法分析的原理1.词法分析的定义词法分析(Lexical Analysis)是编译过程中的第一个阶段,它负责将源程序中的字符流转换成标记流的过程。
源程序中的字符流是没有结构的,而编程语言是有一定结构的,因此需要通过词法分析将源程序中的字符流转换成有意义的标记流,以便之后的语法分析和语义分析的进行。
在词法分析的过程中,会将源程序中的字符划分成一系列的标记(Token),每个标记都包含了一定的语义信息,比如关键字、标识符、常量等等。
2.词法分析的原理词法分析的原理主要是通过有限状态自动机(Finite State Automaton,FSA)来实现的。
有限状态自动机是一个数学模型,它描述了一个自动机可以处于的所有可能的状态以及状态之间的转移关系。
在词法分析过程中,会将源程序中的字符逐个读取,并根据当前的状态和字符的输入来确定下一个状态。
最终,当字符读取完毕时,自动机会处于某一状态,这个状态就代表了当前的标记。
3.词法分析的实现技术词法分析的实现技术主要有两种,一种是手工实现,另一种是使用词法分析器生成工具。
手工实现词法分析器的过程通常需要编写一系列的正则表达式来描述不同类型的标记,并通过有限状态自动机来实现这些正则表达式的匹配过程。
这个过程需要大量的人力和时间,而且容易出错。
而使用词法分析器生成工具则可以自动生成词法分析器的代码,开发者只需要定义好源程序中的各种标记,然后通过这些工具自动生成对应的词法分析器。
常见的词法分析器生成工具有Lex和Flex等。
二、语法分析的原理1.语法分析的定义语法分析(Syntax Analysis)是编译过程中的第二个阶段,它负责将词法分析得到的标记流转换成抽象语法树的过程。
程序设计语言与编译原理_第九章语义分析和中间代码生成
语义检查和语义处理 核心是生成相应的中间代码
程序设计语言与编译
三、语义值
在描述语义动作时,需要赋予每个文法符号以各种不 同的“值”,这些值统称为“语义值”.如,“类型”, “种属”,“地址”或“代码”等。通常用 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();
符合变量声明的语法、语义 符合函数声明的语法、语义 符合赋值语句的语法、不符合语义
得较为容易,但语义分析不像词法分析和
语法分析那样可以分别用正规文法和上下
文无关文法描述。
由于语义是上下文有关的,因此语
义的形式化描述是非常困难的,目前较为
常见的是用属性文法作为描述程序语言语
编译原理 第9章 语义分析和代码生成(Modified)
9.2 中间代码
精品课件
9.2 中间代码
波兰后缀表示除可用来表示表达式类的语言结构以外, 也能够通过操作符的扩充来表示其他的语言结构。
例(P171) 条 件 语 句 : if <expr> then <stmt1> else <stmt2> 可转换成波兰后缀表示:
<expr><label1>BZ<stmt1><label2>BR<stmt2 > 注意:在该波兰表示中,引入了BZ和BR操作符。
精品课件
9.2 中间代码
波兰后缀表示的特点: 操作符位于操作数之后。
例(P170) 算术表达式:F*3.1416*R*(H+R)
波兰后缀表示: F3.1416*R*HR+* 赋值表达式:S=F*3.1416/R*(H+R)
波兰后缀表示: SF3.1416*R/HR+*=
精品课件
9.2 中间代码
精品课件
9.2 中间代码
使用中间代码的优点: ➢不考虑机器的特性,使生成的中间代码较为简单。 ➢生成中间代码的编译程序移植性好,只需为该中 间代码开发一个解释器或者将中间代码翻译为目标 机指令就能在目标机上运行。 ➢在中间代码上更便于做优化处理。
常见的中间代码: ➢(1)波兰后缀表示 ➢(2)N-元表示
三元式 四元式
9.2 中间代码
用一个抽象机的汇编语言作为TEST编译器的目标语 言。TEST机的指令仅能作为TEST语言的目标。TEST 机的模拟程序直接从一个文件中读取汇编代码并执行 它,因此避免了由汇编语言翻译为机器代码的过程。 但是,这个模拟程序并非是一个真正的汇编程序,它 没有符号地址或标号。
编译原理课件-语义分析
22/94
Wensheng Li BUPT @ 2008
符号表的逻辑结构
sort nil head area a x readarray exchange quicksort
exchange head area
–控制流检查
• 检查控制语句是否使控制转移到一个合法的位置。
–唯一性检查
• 一个标识符在同一程序块中必须而且只能被说明一次 • CASE语句中用于匹配选择表达式的常量必须各不相同 • 枚举类型定义中的各元素不允许重复
–关联名字的检查
5/94
Wensheng Li BUPT @ 2008
类型检查
由类型检查程序完成 检验结构的类型是否和它的上下文所期望的一致, 如:
countx_totalb_loop
14/94
目标地址
指示运行时变量值存放的相对位置 对于静态存储分配的语言(如Fortran),目标地 址按连续的顺序分配,从0开始到m(m是分配给一 个程序的数据区的最大值)。 对于块结构的语言(如Pascal),通常采用二元地 址<BL,NO>
–BL:块的嵌套深度,用于确定分配给声明变量的块的数 据区的基址。
–编译程序在处理声明语句时调用两种操作
• 检索:查重、确定新表目的位置 • 插入:建立新的表目
–在程序中引用变量名时,调用检索操作
• 查找信息,进行语义分析、代码生成 • 可以发现未定义的名字
允许变量隐式声明的语言:
–标识符的每次出现都按首次出现处理 –检索:
• 已经声明,进行类型检查,... • 首次出现,插入操作,从其作用推测出该变量的全部属性
编译原理第6章-语义分析
Compiler Construction Principles & Implementation Techniques
College of Computer Science & Technology
6.2 符号表
• 什么是符号表 • 标识符的内部表示 • 类型的内部表示 • 值的内部表示 • 符号表的组织
-7-
College of Computer Science & Technology
基于Tokenlist的语义分析
int x,y; int f();
($int, -) ($id, x) ($comma, -) ($id, y) ($semi,-) ($int, -) ($id, f) ($lparen, -) ($rparen,-) ($semi,-)
• Off:当前函数是形参函数时,为其分配的地址偏移;
• Param: 指向当前函数的形式参数列表的指针;
• Code: 指向函数对应的目标代码的起始地址;
• Size: 目标代码的大小;
• Forward: 该声明为函数原型时取值 true,否则取值为false;
• 例:int f(int x, float*y, int inc()) { … }
6.1 语义分析概述
• 语法 vs.语义 • 静态语义 vs. 动态语义 • 语义错误 • 语义分析的一般过程
Compiler Construction Principles & Implementation Techniques
-2-
College of Computer Science & Technology
i : (0, 0) p: (0, 1) x : (0, 12) q: (0,…) main: (0,…)
编译原理精华总结8_语义分析与中间代码
例如:x := y op z 表示为 (op, y, z, x) x := y
三元式的指示器(编号)。为了防止优化后的重 新编址,在三元式基础上增加了一个存放结果的 单元,这就形成了四元式,是一种最常用的形式。
语法制导翻译是目前最常用的
语义分析技术
10
5.2 语法制导翻译
基本思想
在语法分析过程中,每当使用一条产生式进行推导
或归约,就执行该产生式所对应的语义动作,完成 相应的翻译工作。 语法制导翻译就是把语言的一些属性附加到代表语 言结构的文法符号上,这些属性值是由附加到文法
产生式的“语义规则”中计算的,也就是为每个产
5
5.1 语义分析概述
属性的引入
将语言结构的语义以属性(attribute)的形式赋予
代表此结构的文法符号, 在语法分析推导或归约的每一步骤中,通过语义 规则实现对属性的计算,以达到对语义的处理。
6ห้องสมุดไป่ตู้
5.1 语义分析概述
属性的引入
计算属性的值并把它和语言结构联系起来的过 程称作属性的绑定。属性绑定发生在编译或运 行过程的时刻叫做绑定时刻。不同属性的绑定 时刻不同,对于不同的语言,甚至同样的属性
生式配备翻译子程序,即语义子程序。 语法制导翻译法不论对自上而下分析或自下而上分 析都适用。
11
5.2 语法制导翻译
语法制导定义(属性文法)是上下文无关文法的推广,包含一个上下文无 关文法和一系列语义规则,这些语义规则附在文法的每个产生式上。其形 式定义: A→P都有与之相关联的一套语义规则,规则形式为: b = f(c1,c2,…,ck),f是一个函数,若:
编译原理compiler6语义分析
◆语法制导定义是对上下文无关文法的推广
◆属性
综合属性 继承属性
◆依赖图 语义规则建立了属性之间的依赖关系,这
些关系可以用图来表示,这样的图称为依赖图。
4
6.1.1 语法制导定义的形式
在一个语法制导定义中,A→P都有与之相关
联的一套语义规则,规则形式为
b:= f(c1,c2,…,ck),
f是一个函数,而且
终极符则只能有综合属性,而不能有继承属性。
非终结符既可有综合属性也可有继承属性
1
图6.1 ห้องสมุดไป่ตู้法制导翻译的概观
语
输 入
分
依
义 规
符
析
赖
则
号 串
树
图
的 计
算
一般来说,语义翻译可按图6.1 的流程处理。 实际上,编译中语义翻译的实现并不是按图6.1 的流 程处理的;而是随语法分析的进展,识别出一个语法 结构,就对它的语义进行分析和翻译。
19
6.2.2 建立表达式的语法树
建立表达式的语法树使用的函数
1. mknode(op,left,right) 建立一个运算符号结点,标 号是op,两个域left和right指向运算分量结点的指针。
2.mkleaf(id,entry) 建立一个标识符结点,由标号id标 识,一个域entry指向标识符符号表中相应的项。
val[ntop]:=val[top-2]*val[top]
val[ntop]:=val[top-1]
29
表6.5 输入串3*5+4n的语义分析过程
输入 state val
3*5+4n -
-
*5+4n 3
3
编译原理_第6章__语义分析和中间代码生成
非终结符T有一个综合属性type,其值为 int或float。语义规则L.in=T.type表示L.in的属性 值由相应说明语句指定的类型T.type决定;属 性L.in被确定后将随语法树的逐步生成而传递 到下边的有关结点使用,这种结点属性称为继 承属性。由此可见,标识符的类型可以通过继 承属性的复写规则来传递。 例如,对输入串int a,b,根据上述的语义 规则,可在其生成的语法树中看到用“→”表 示的属性传递情况,如图6–3所示。
直接生成目标代码 直接生成机器语言或汇编语言形式的目标 代码的优点是编译时间短且无需中间代码到目 标代码的翻译。 生成中间代码 生成中间代码的优点是使编译结构在逻辑 上更为简单明确,特别是使目标代码的优化比 较容易实现。
语义分析时语义检查的分类:
动态语义检查
需要生成相应的目标代码,它是在运行时进行的;
例如,简单算术表达式求值的属性文法如下: 规则 语义规则 (1) S→E print (E.val) (2) E→E(1)+T E.val=E(1).val+T.val (3) E→T E.val=T.val (4) T→T(1)*F T.val=T(1).val*F.val (5) T→T(1) T.val=T(1).val (6) F→(E) F.val=E.val (7) F→i F.val=i.lexval
6.1 概
述
6.1.1 语义分析的概念 一个源程序经过词法分析、语法分析之后,表 明该源程序在书写上是正确的,并且符合程序语言 所规定的语法。但是语法分析并未对程序内部的逻 辑含义加以分析,因此编译程序接下来的工作是语 义分析,即审查每个语法成分的静态语义。如果静 态语义正确,则生成与该语言成分等效的中间代码, 或者直接生成目标代码。
程序设计语言与编译-编译原理_语义分析和中间代码生成
A→i:=E { P=entry(); If(P!=0) gen ( :=, E.place, _, P) Else error(); }
程序设计语言与编译
E→E1 op E2
{ E.place:=newtemp;
gen(op,E1.place,E2.place,E.place) }
E →-E1
(:=,t3,_,A)
四元式出现顺序和表达式计值顺序一致; 四元式之间的联系通过临时变量来实现。
程序设计语言与编译
第二节 简单赋值语句的翻译
一、语义变量及过程
X.a
文法符X相应属性a,如,E.place
E.place:表示E所代表的变量在符号表的入口地址。
newtemp 语义函数,每调用一次产生一个新的临时变量。
a:=-b*(c+d)
E →i
a:=-E1*(c+d) a:=E2*(c+d) a:=E2*(E3+d) a:=E2*(E3+ E4) a:=E2*(E5) a:=E2*E6 a:=E7
A
E →-E1 E →i E →i E→E1 op E2 E →(E1) E→E1 op E2 A→i:=E
(@,b,_,t1)
一致性检查: (1)表达式中操作数是否保持类型一致; (2)赋值语句的左右两边是否类型一致; (3)形、实参数类型是否一致; (4)数组元素与数组说明是否一致。
越界检查:数组下标是否越界;子界类型是否越界等等。
语义处理: 对说明语句:登记信息; 对可执行语句:生成中间代码。
程序设计语言与编译 二. 语法制导翻译
gen(opi,E1.place,E2,place,t); E.type:=integer end else if E1.type=real
编译原理-语法分析
自顶向下的语法分析方法简单直观,易于实现,但可能存在 左递归和回溯的问题。
自底向上的语法分析
01
自底向上的语法分析方法从源代码中的每个符号出发
,逐步归约到文法的起始符号。
02
该方法通常采用LR(0)、SLR(1)、LALR(1)等算法进行
实现。
03
自底向上的语法分析方法可以避免回溯问题,但需要
• 随着人工智能和机器学习技术的不断发展,可以利用这些技术来辅助语法分析 过程,提高语法分析的准确性和效率。例如,可以使用机器学习算法来自动识 别和处理语法规则和歧义问题。
• 另外,随着软件工程和代码质量的重视程度不断提高,对编译器和语法分析器 的要求也越来越高。未来的研究需要更加注重编译器和语法分析器的可维护性 和可扩展性,以满足不断变化的软件需求。
词法分析的算法
自底向上算法
自底向上算法是从源代码的左向右进行扫描,并从下到上构建语法结构。常见 的自底向上算法有预测分析法和移进-规约法。
自顶向下算法
自顶向下算法是从语法结构的顶层开始,向下进行推导,直到找到与源代码相 匹配的语法结构。常见的自顶向下算法有规范分析法和贪婪分析法。
语法分析概述
语法分析是编译过程的核心环节,其任务是将源代码分解成一系列的语法 结构,以便后续的语义分析和代码生成。
自底向上的算法,通过构建归 约表进行移进和规约操作。
LALR(1)算法
扩展的LR(0)算法,能够处理 更广泛的文法,生成更小的归 约表。
03
语义分析
语义分析概述
01
Байду номын сангаас02
03
语义分析是编译过程的 一个阶段,它是在语法
分析之后进行的。
语义分析的主要任务是 检查源代码的语义是否 正确,例如变量是否已 经声明,类型是否匹配
编译原理-第5章-语义分析
类型分析
作用:把类型表示转换成类型的内部表示
分析过程:读Token序列,识别出各种类型, 返回类型内部表示的地址
array [ 1 .. 10 ] of integer
arrKind … low=1 tp1=intPtr … up=10 tp2=intPtr IndexPtr= (1,subTy, intPtr , 1 ,10) … … ElemPtr=intPtr size=(up-low+1) * sizeof (int)
NameType
形式:id (类型标识符) 处理思想:
查符号表 无声明错 typekind ? TypePtr 为Ptr的值 Forward:=0
EnumType
形式:(a0,… ,an) 处理思想:
生成a0,……an的符号表EntryList: (ai,Ptr,consKind,i),Ptr需回填
int x; …… x(a,b);
语义分析概述
任务
进行语义检查和构造标识符的符号表
语义检查包括类型检查和一般的语义检查
类型检查:运算分量的类型是否相容、赋值语句 左右部的类型是否相容、形参和实参的类型是否 相容、函数说明中函数类型和返回值的类型是否 相容等;
一般的语义检查:V[E]、V.id、V↑、y+f(a,b)、 使用性标识符有否声明、定义性标识符有否重复 声明、标号有否重复声明和重复定位错误等;
生成内部表示: Ptr:=★(enumSize,enumTy,EntryList)
回填EntryList中的Ptr值 Forward:=0
SubRangeType
形式:c1..c2 处理思想:
从C1 求出其内部类型地址Ptr1和值N1; 从C2 求出其内部类型地址Ptr2和值N2; 检查Ptr1=Ptr2,N1 N2; Ptr:=★(subSize,subTy,Ptr1,N1,N2) Forward := 0
编译原理实验三 语义分析
编译原理实验三语法分析并进行语义分析输入:经过词法分析后形成的token[]和tokenstring[]输出:检查有无语法错误,形成语法树,并检查是否符合语义。
样例程序已经能对变量声明填符号表、进行类型检查。
文法:stmt_seq -->statement ; stmt_seq | statementstatement-->decl_stmt | assign_stmtdecl_stmt-->type var_listtype-->int |floatvar_list-->id , var_list | idassign_stmt--> id := expexp-->exp + term | exp - term |termterm--> term * factor | term * factor | factorfactor-->id | num | ( exp )要求掌握理解程序设计方法。
样例程序#include<stdio.h>#include<ctype.h>typedef enum {MINUS,PLUS,TIMES,OVER,LPAREN,RPAREN,SEMI,ASSIGN,NUM,ID,INT,FLOAT,COM MA,DOLLAR} tokentype;/*记号*/typedef enum {stmtk,expk} nodekind;typedef enum {ifk,assignk,declk} stmtkind;typedef enum {opk,constk,idk} expkind;typedef enum {integer,real} exptype;typedef struct treenode{ struct treenode * child[3];struct treenode * sibling;nodekind nodek;exptype dtype ;union { stmtkind stmt; expkind exp;} kind;union { tokentype op;int val;char * name; } attr;} treenode;typedef struct bucket{char * name; exptype dtype; struct bucket * next;} bucket;bucket * hashtable[211];/*tokentype token[6]={ID,ASSIGN,NUM,PLUS,NUM,INT,FLOAT,COMMA,DOLLAR};char tokenstring[6][30]={"ab",":=","12","+","5","$"};*/tokentype token[]={INT,ID,COMMA,ID,SEMI, ID,ASSIGN,NUM,PLUS,NUM,TIMES,NUM,SEMI,ID,ASSIGN,NUM,DOLLAR};chartokenstring[][30]={"int","ab",",","xy",";","ab",":=","12","+","5","*","3",";","xy",":=","34","$"}; int wordindex=0; /*以上两个数组的索引*/treenode * decl();treenode * factor();treenode * term();treenode * exp();treenode * assign_stmt();treenode * stmt_seq();pretraverse(treenode *);int hash ( char * );void st_insert( char * ,exptype);int st_lookup ( char * );void buildsymtab(treenode * );void setnodetype(treenode * );main(){treenode * t;t=stmt_seq(); /*语法分析建语法树*/buildsymtab(t); /*建符号表*/pretraverse(t); /*遍历语法树*/setnodetype(t); /*类型检查设置*/}treenode * stmt_seq(){treenode * t;treenode * p;if(( token[wordindex]==INT)||(token[wordindex]==FLOA T)){t=decl(); }if(token[wordindex]==ID) t=assign_stmt();p=t;while( (token[wordindex]==SEMI)&& (token[wordindex]!=DOLLAR)){treenode * q;wordindex++;q=assign_stmt();p->sibling=q;p=q;}return t;}treenode * assign_stmt(){treenode * t=(treenode *)malloc(sizeof(treenode));if(token[wordindex]==ID){t->nodek=stmtk;t->kind.stmt=assignk;t->=tokenstring[wordindex];wordindex++;}else {printf("error");exit(1);}if(token[wordindex]==ASSIGN)wordindex++;else {printf("error");exit(1);}t->child[0]=exp();t->child[1]=NULL;t->child[2]=NULL;t->sibling=NULL;return t;}treenode * exp(){treenode * t;t=term();while((token[wordindex]==PLUS)||(token[wordindex]==MINUS)) {treenode * p=(treenode *)malloc(sizeof(treenode));p->nodek=expk;p->kind.exp=opk;p->attr.op=token[wordindex];p->child[0]=t;t=p;wordindex++;t->child[1]=term();t->child[2]=NULL;t->sibling=NULL;}return t;}treenode * term(){treenode * t=factor();while((token[wordindex]==TIMES)||(token[wordindex]==OVER)) {treenode * p=(treenode *)malloc(sizeof(treenode));p->nodek=expk;p->kind.exp=opk;p->attr.op=token[wordindex];p->child[0]=t;t=p;wordindex++;t->child[1]=factor();t->child[2]=NULL;t->sibling=NULL;}return t;}treenode * factor(){treenode * t;switch(token[wordindex]){case LPAREN :wordindex++;t=exp();if(token[wordindex]==RPAREN)wordindex++;else {printf("error");exit(1);}break;case NUM :t=(treenode *)malloc(sizeof(treenode));t->nodek=expk;t->kind.exp=constk;t->attr.val=atoi(tokenstring[wordindex]);t->child[0]=NULL;t->child[1]=NULL;t->child[2]=NULL;t->sibling=NULL;wordindex++;break;case ID :t=(treenode *)malloc(sizeof(treenode));t->nodek=expk;t->kind.exp=idk;t->=tokenstring[wordindex];wordindex++;break;default:printf("error");}return t;}pretraverse(treenode * t){if (t!=NULL){ if (t->nodek==stmtk) printf("stmt-id:%s\n",t->);if (t->nodek==expk && t->kind.exp==idk) printf("exp-id:%s\n",t->);if (t->nodek==expk && t->kind.exp==opk) printf("exp-op:%d\n",t->attr.op);if (t->nodek==expk && t->kind.exp==constk) printf("exp-val:%d\n",t->attr.val);if(t->child[0]!=NULL) pretraverse(t->child[0]);if(t->child[1]!=NULL) pretraverse(t->child[1]);if(t->child[2]!=NULL) pretraverse(t->child[2]);if(t->sibling!=NULL) pretraverse(t->sibling);}}treenode * decl(){treenode * p,* q,* t1;treenode * t=(treenode *)malloc(sizeof(treenode));t->nodek=stmtk;t->kind.stmt=declk;t->=tokenstring[wordindex];t->child[1]=NULL;t->child[2]=NULL;wordindex++;if(token[wordindex]==ID){t1=(treenode *)malloc(sizeof(treenode));t->child[0]=t1;t1->nodek= expk;t1->kind.exp=idk;t1->=tokenstring[wordindex];t1->child[0]=NULL;t1->child[1]=NULL;t1->child[2]=NULL;t1->sibling=NULL;wordindex++;p=t1;while( token[wordindex]==COMMA){ wordindex++;q=(treenode *)malloc(sizeof(treenode));q->nodek= expk;q->kind.exp=idk;q->=tokenstring[wordindex];q->child[0]=NULL;q->child[1]=NULL;q->child[2]=NULL;q->sibling=NULL;p->sibling=q;wordindex++;p=q;}return t;}}int hash ( char * key ){ int temp = 0;int i = 0;while (key[i] != '\0'){ temp = ((temp << 4) + key[i]) % 211;++i;}return temp;}void st_insert( char * name,exptype datatype){ int h = hash(name);bucket * l = hashtable[h];while ((l != NULL) && (strcmp(name,l->name) != 0)) l = l->next;if (l == NULL) /* variable not yet in table */{ l = (bucket *) malloc(sizeof(bucket));l->name = name;l->dtype=datatype;l->next = NULL;l->next = hashtable[h];hashtable[h] = l; }}int st_lookup ( char * name ){ int h = hash(name);bucket * l = hashtable[h];while ((l != NULL) && (strcmp(name,l->name) != 0)) l = l->next;if (l == NULL) return -1;else return 1;}void buildsymtab(treenode * t){exptype datatype;treenode * q;if (t!=NULL){ if (t->nodek==stmtk && t->kind.stmt==declk){ if (strcmp(t->,"int")==0) datatype=integer;if(t->=="float") datatype=real;q=t->child[0];while(q!=NULL){ st_insert(q->,datatype);q=q->sibling;}}if(t->nodek!=stmtk && t->kind.stmt!=declk && t->child[0]!=NULL) buildsymtab(t->child[0]);if(t->child[1]!=NULL) buildsymtab(t->child[1]);if(t->child[2]!=NULL) buildsymtab(t->child[2]);if(t->sibling!=NULL) buildsymtab(t->sibling);}}void setnodetype(treenode * t){if(t!=NULL){if(t->child[0]!=NULL) setnodetype (t->child[0]);if(t->child[1]!=NULL) setnodetype (t->child[1]);if (t->nodek==expk && t->kind.exp==idk) {int h = hash(t->);bucket * l = hashtable[h];while ((l != NULL) && (strcmp(t->,l->name) != 0))l = l->next;if (l==NULL) {printf("no declare");exit(1);}else t->dtype=l->dtype;}if (t->nodek==expk && t->kind.exp==constk) t->dtype=integer;if (t->nodek==expk && t->kind.exp==opk) {if(t->child[1]==NULL) t->dtype=t->child[0]->dtype;elseif(t->child[0]->dtype==t->child[1]->dtype )t->dtype=t->child[0]->dtype;else {printf("type no match");exit(1);}} if (t->nodek==stmtk) t->dtype=t->child[0]->dtype;if(t->sibling!=NULL) setnodetype (t->sibling);}}查看主要内存变量:t-> t->attr.valt->child[0]-> t->child[0]->attr.valt->child[1]-> t->child[1]->attr.valt->sibling-> t->sibling->attr.val语法树样例:。
编译原理语义分析和中间代码生成
在语义分析阶段,编译器会检查源代 码中是否存在类型错误、未定义的变 量和函数、不符合控制流规则的语句
等。
语义分析的主要任务包括类型检查、 函数和变量的解析、控制流的检查等。
如果发现错误,编译器会报错并停止 编译过程;如果没有发现错误,编译 器将继续生成中间代码。
02 中间代码生成
中间代码生成概述
中间代码是源代码和目标代码之间的代 码形式,用于表示源程序的结构和语义
信息。
中间代码生成是编译过程的一个重要阶 中间代码生成可以提高编译器的灵活性
段,它把源代码转换成一种更接近于机 和可移植性,因为中间代码与具体的机
器语言的代码形式,以便进行后续的优 器语言无关,可以在不同的平台上使用
代码优化
编译器通过优化技术 对源代码进行优化, 以提高生成代码的执 行效率,减少运行时 间。
代码分析
编译原理还可以用于 代码分析,检查代码 的语法、语义和结构, 发现潜在的错误和漏 洞。
程序理解
编译原理可以帮助理 解程序的内部结构和 行为,为程序修改、 重构和优化提供支持。
编译原理的发展趋势
静态分析
在语法分析阶段,编译器根据 语言的语法规则将词素或标记 组合成一个个的语法结构,如 表达式、语句、程序等。
语义分析阶段是在语法分析的 基础上,对这些语法结构进行 语义检查和语义处理。
语义分析的例子
01
02
假设有以下C语言代码 ```c
03
int a = "hello";
04
```
05
在语义分析阶段,编译 器会发现变量a被赋值为 一个字符串字面量,而 字符串字面量是字符数 组类型的值,因此会报 错,因为整型变量不能 赋值为字符数组类型的 值。
编译原理(第2版)7-1语义出理概述
如GCC、Clang等,这些工具集成了完整的 编译器前端和后端,可以方便地实现语义分 析。
工具库
如ANTLR、Bison等,这些工具可以自动生成解析 器和词法分析器,然后在此基础上进行语义分析。
集成开发环境(IDE)
如Eclipse、Visual Studio等,这些IDE提供 了丰富的编译和调试工具,可以方便地实现 语义分析。
3
语义等价
判断两个程序是否在语义上等价,即具有相同的 行为。
语义优化
死代码消除
01
识别并删除程序中不会执行的代码。
常量折叠
02
将常量表达式的结果在编译时计算出来,避免在运行时进行计
算。
公共子表达式消除
03
消除重复计算的表达式,减少程序中的冗余计算。
04
语义分析的实现
语义分析的实现方法
基于抽象语法树(Abstract Syntax Tr…
中间表示通常采用三地址代码 (Three-Address Code,TAC) 的形式,是一种类似于汇编语言
的低级语言。
中间表示可以方便地进行各种 优化操作,如常量折叠、死代 码消除等。
中间表示还可以方便地转换为 目标机器代码,提高编译器的 可移植性和可维护性。
03
语义分析的关键技术
类型检查
静态类型检查
语义分析的展望
• 人工智能技术的应用:随着人工智能技术的发展,一些新的算法和工具可以应 用于语义分析中。例如,深度学习模型可以用于自动构建抽象语法树(AST) 和进行语义歧义消解。
• 静态分析的改进:静态分析是一种在编译时检测源代码中错误的技术。随着静 态分析技术的不断改进,它将在语义分析中发挥更大的作用。例如,更精确的 类型推断和更强大的错误检测能力可以提高编译器的质量。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
machunyan
西北工业大学软件与微电子学院
7
5.1 属性和属性文法
至今没有形式化的系统来描述语义,但 存在一种属性文法,将语义信息和程序 设计语言的语法结构联系起来。 补充说明合法程序的规格说明
machunyan
西北工业大学软件与微电子学院
8
5.1 属性和属性文法(续)
每个属性文法是一个三元式: A=(G, V, F) G是一个上下文无关文法; V是一个属性的有限集合; F是一个与属性有关的语义规则的有限集 合。
2. 静态语义检查举例:
类型检查:指类型相容问题的检查,如果操作符作用于不 相容的操作数,则编译器应该报错。 上下文有关问题的检查:当某个对象出现时,要求它必须 在前面的某个适当位置已经出现过。 唯一性检查:要求某个对象只能被定义一次。 控制流检查:引起控制流从某个结构中跳转出来的语句, 必须能够决定控制流转向的目标地址。 Break和continue语句是否在循环结构中? 对于一个方法调用,实际参数的类型和实际参数的个数是 否与方法的声明的参数特征相符? 数组下标引用是否超出范围? 数组下标是否是整数?
machunyan 西北工业大学软件与微电子学院 11
5.1 属性和属性文法(续)
属性a1,...,ak的属性文法是文法所有产生式的语 义规则的集合。一般将属性文法写成表格形式, 每个产生式用相应语义规则列出,如下所示:
文法规则
产生式1 ... 产生式n
语义规则
相关的属性等式 ... 相关的属性等式
machunyan
西北工业大学软件与微电子学院
12
5.1 属性和属性文法(续)
属性文法的作用? 根据已求得的各产生式的语义规则,遍历 语法分析的结果---语法树或分析树,计 算任意句子的推导过程中各文法符号对应 的属性值(例如:变量的数据类型、表达式的值、存 储器中变量的位置、程序的目标代码、或数的有效位数等), 根据属性值分析相关语义,或者将属性值 存储在符号表中,以供编译的后续阶段使 用。
machunyan
西北工业大学软件与微电子学院
9
5.1 属性和属性文法(续)
V: 每个文法符号(终结符号或非终结符 号)都有一个属性集(语义信息)。 如果X是一个文法符号,与X关联的属 性a的值记作X.a。
文法符号关联的属性可以代表
2018/10/15
变量的数据类型 表达式的值 存储器中变量的位置 程序的中间或目标代码片段 数的有效位数 ……等
西北工业大学软件与微电子学院
17
5.1 属性和属性文法(续)
属性文法的求解方法: 给出一个句子最左推导对应的分析树,而 且该句子的推导过程中基本涵盖各种语法 规则(即产生式规则)的运用。 根据该句子的分析树和已知文法符号的属 性值,概括出各节点属性值的计算规则, 将该计算规则作为节点对应的产生式的语 义规则,最后得到属性文法。
西北工业大学软件与微电子学院 machunyan 10
5.1 属性和属性文法(续)
F: 每个产生式都有一个与文法符号属性相关的 语义规则集合。对于上下文无关文法中的任一产 生式X0→X1X2...Xn,其语义规则定义格式如下: Xi.aj=fij(X0.a1,...,X0.ak,X1.a1,..., X1.ak,...,Xn.a1,...,Xn.ak) 其中,a1,...,ak是与各文法关联的属性集合;fij是 一个数学函数,表示文法符合Xi的第j个属性aj 是如何计算得到的。 所以,产生式的语义规则是产生式中相关文法符 号属性值的等式。
machunyan
西北工业大学软件与微电子学院
2
编译器逻辑结构的组成 常数表 符号表 错误处理器
源 代 码
词 法 分 析 程 序
语 法 分 析 程 序
语 义 分 析 程 序
中 间 代 码 生 成
代 码 优 化 程 序
目 标 代 码 生 成
目 标 代 码
machunyan
西北工业大学软件与微电子学院
machunyan
西北工业大学软件与微电子学院
16
文法规则 digit → 0 digit → 1 digit → 2 digit → 3 . . .
语义规则 digit.val = 0 digit.val = 1 digit.val = 2 digit.val = 3 . . .
machunyan
3
第5章 语义分析(续)
语义分析的任务:
1. 计算各类语法成分的语义信息(属性信息), 一般将收集的语义信息存放到相应的信息 表中,在编译程序中符号表是用来存放源 程序中标示符相关属性(语义)信息的一种 信息表。
machunyan
西北工业大学软件与微电子学院
4
第5章 语义分析(续)
语义分析的任务:
课程内容 第1章 概论 第2章 词法分析 第3章上下文无关文法 第4章语法分析 第5章语义分析 第6章运行时环境 第7章代码生成
2018/10/15
西北工业大学软件与微电子学院 machunyan
1
第5章 语义分析
程序设计语言的语义分为静态语义和动态语 义两种。 静态语义是指在编译阶段能够检查的语义; 动态语义是指在目标程序运行阶段能够检 查的语义。
machunyan 西北工业大学软件与微电子学院 13
5.1 属性和属性文法(续)
例5.1:求解下述无符号数文法的val(十进制 值)属性的属性文法。 number→number digit |digit digit→0|1|2|3|4| 5 |6|7|8|9 文法定义的合法的句子举例: 345对应的分析树
2018/10/15 西北工业大学软件与微电子学院 machunyan 5
第5章 语义分析(续)
2018/10/15
西北工业大学软件与微电子学院 machunyan
6
第5章 语义分析
5.1 属性和属性文法 5.2 符号表 5.3 数据类型和类型检查
文法符号语 义信息的计 算技术 语义分析 的两个主 要方面
machunyan
西北工业大学软件与微电子学院
14
数345对应 的分析树
machunyan
西北工业大学软件与微电子学院
15
文法规则 number1→number2 digit
语义规则 number1.val = number2.val*10 + digit.val
number→digit
number.val =digit.val