精华内容
下载资源
问答
  • 编译程序的语义分析输出
    2021-05-20 17:31:05

    《编译原理语义分析程序设计》由会员分享,可在线阅读,更多相关《编译原理语义分析程序设计(9页珍藏版)》请在人人文库网上搜索。

    1、实验3 语义分析程序设计【实验目的】加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换为中间代码的语义翻译方法。【实验内容】采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。置初值调用scaner读下一个单词符号调用lrparser结束【设计思路】1、流程图输出四元式图2.1递归下降分析程序示意图2、源程序代码(1)scan.h /头文件-扫描程序#include#includechar prog80,token8;char ch;int syn,p,m,n,sum; /p是缓冲区prog的指针,m是token的指针char *rwtab6=begi。

    2、n,if,then,while,do,end;void scanner() /词法扫描程序memset(token,0,sizeof(token);/清空数组tokenm=0;while(ch= )+p;ch=progp; /读下一个字符;if(ch=a&ch=A&ch=a&ch=A&ch=0&ch=0&ch=0&ch)syn=21;+m;tokenm=ch;else if(ch=)syn=22;+m;tokenm=ch;elsesyn=20;break;case:token0=ch;+p;ch=progp;if(ch=)syn=24;token0=ch;elsesyn=23;break;ca。

    3、se:token0=ch;+p;ch=progp;if(ch=)syn=18;+m;tokenm=ch;+p;ch=progp;elsesyn=17;break;case+:syn=13;token0=ch;ch=prog+p;break;case-:syn=14;token0=ch;ch=prog+p;break;case*:syn=15;token0=ch;ch=prog+p;break;case/:syn=16;token0=ch;ch=prog+p;break;case=:syn=25;token0=ch;ch=prog+p;break;case;:syn=26;token0=ch;c。

    4、h=prog+p;break;case(:syn=27;token0=ch;ch=prog+p;break;case):syn=28;token0=ch;ch=prog+p;break;case#:syn=0; token0=ch;ch=prog+p;break;default:syn=-1;(2)yuyi.cpp / 语法分析主程序#include#includescan.hint kk=0,q=0,k=0;structchar result8;char agl8;char op8;char ag28;quad20;int lrparser();int yucu();int statemen。

    5、t();char * expression(void);char * term();char * factor();char * newtemp(void);void emit(char *result,char *agl,char *op,char *ag2);void main()kk=0;rewind(stdin);memset(prog,0,sizeof(prog);/清空数组progp=0;printf(n please input string:n);doch=getchar();progp+=ch;while(ch!=#);p=0;ch=prog0;scanner();lrpar。

    6、ser();for(int i=0;iq;+i)printf(%s=%s%s%sn,quadi.result,quadi.agl,quadi.op,quadi.ag2);int lrparser()int schain=0;kk=0;if(1=syn)scanner();schain=yucu();if(6=syn)scanner();if(0=syn|0=kk)printf(success!n);elseif(kk!=1) printf(缺end!);kk=1;elseprintf(缺begin!);kk=1;return schain;int yucu() /语句串分析函数int scha。

    7、in=0;schain=statement();while(26=syn)scanner();schain=statement();return schain;int statement()char tt8,eplace8;int schain=0;switch(syn)case 10:strcpy(tt,token);scanner();if(18=syn)scanner();strcpy(eplace,expression();emit(tt,eplace,);schain=0;else printf(缺赋值号!);kk=1;return schain;break;char * expre。

    8、ssion(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();while(13=syn)|(14=syn)strcpy(tt,token);scanner();strcpy(ep2,term();strcpy(tp,newtemp();emit(tp,eplace,tt,ep2);strcpy(eplace,tp);return eplace。

    9、;char *term(void)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(15=syn)|(16=syn)strcpy(tt,token);scanner();strcpy(ep2,factor();strcpy(tp,newtemp();emit(tp,eplace,tt,ep2);strcpy(eplace,tp);return epla。

    10、ce;char *factor(void)char *fplace;fplace=(char *)malloc(12);strcpy(fplace, );if(10=syn)strcpy(fplace,token);scanner();else if(11=syn)itoa(sum,fplace,10);scanner();else if(27=syn)scanner();fplace=expression();if(28=syn)scanner();elseprintf()错误!);kk=1;elseprintf(错误!);kk=1;return fplace;char * newtemp(void)char *p;char m8;p=(char *)malloc(8);k+;itoa(k,m,10);strcpy(p+1,m);p0=t;return p;void emit(char *result,char *agl,char *op,char *ag2) /生成三地址语句送到四元式表中strcpy(quadq.result,result);strcpy(quadq.agl,agl);strcpy(quadq.op,op);strcpy(quadq.ag2,ag2);q。

    更多相关内容
  • 一个简单的编辑器 编译原理课设 对简单的程序进行语义分析并将中间代码生成
  • 编译原理-语义分析

    2014-05-11 16:48:17
    实验报告内容要求:要给出所分析简单语言语法结构的词法说明、上下文无关文法描述,单词的种别编码方案,词法分析程序的主要算法思想,以及所采用的语法语义分析方法的算法思想的详细描述,测试结果与分析,实验总结...
  • 编译原理语义分析实验报告.doc
  • 编译原理词法分析,语义分析,抽象机实现。
  • 编译原理语义分析实验报告 如果需要配套的源代码可以在我上传的资源中找
  • 这是哈工大编译原理实验语义分析的实验指导书,自我感觉还是不错的
  • 采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 算法思想 1、设置语义过程。 (1)emit(char *result,char *ag1,char *op,char *ag2) 该函数的功能是生成一个三地址语句送...
  • 在实验一词法分析的基础上,以词法分析输出结果(单词串或者成为多元式序列)作为该语法语义分析器的输入,最后输出中间代码四元式序列,并计算出表达式最后的结果。(共8个上机学时,时间不够的请自己课下找时间补...
  • 语义分析 编译原理 北邮 大三 实验要求:编写语义分析程序,实现对算术表达式的类型检查和求值。要求所分析算术表达式由如下的文法产生。 实验要求:用自底向上的语法制导翻译技术实现对表达式的分析和翻译。 (1) ...
  • 你的程序需要对输入文件进行语义分析并检查错误进行输出。 (2)输入格式 一个包含源代码的文本文件,程序需要能够接收一个输入文件名作为参数。 (3)输出格式 要求通过标准输出打印程序的运行结果。对于那些没有...

    1 实验任务

    审查每一个语法结构的静态语义,即验证语法正确的结构是否有意义。此部分不再借助已有工具,需手写代码来完成。

    2 实验内容

    (1)实验要求
    你的程序需要对输入文件进行语义分析并检查错误进行输出。

    (2)输入格式
    一个包含源代码的文本文件,程序需要能够接收一个输入文件名作为参数。

    (3)输出格式
    要求通过标准输出打印程序的运行结果。对于那些没有语义错误的输入文件,你的程序不需要输出任何内容。对于那些存在语义错误的输入文件,你的程序应当输出相应的错误信息,这些信息包括错误类型、出错的行号以及说明文字,其格式为:
    Error type [错误类型] at Line [行号]: [说明文字].

    3 错误类型声明

    自定义错误类型如下(与C–重合的部分不再给出补充说明):

    1. 错误类型 1:变量在使用时未经定义。
    2. 错误类型 2:函数在调用时未经定义。
    3. 错误类型 3:变量出现重复定义,或变量与前面定义过的结构体名字重复。
    4. 错误类型 4:函数出现重复定义(即同样的函数名出现了不止一次定义)。
      说明:在测试此功能时,要注意PL/0不能将变量定义在begin、end内,只能在过程体外部定义。否则会引发过程相关的错误。
    5. 错误类型 5:赋值号两边的表达式类型不匹配。
      说明:改进的PL/0 类型只有常量、变量、过程类型。它们三者互相赋值会导致此错误,事实上,只有左部为变量类型才是合法的赋值。
      同时,常量只能由数字赋初值。
    6. 错误类型 6:操作数类型不匹配或操作数类型与操作符不匹配。
      说明:当因子为过程或其他非法值时,使得操作数不合法或表达式运算不合法。
    7. 错误类型 7:过程声明格式不正确(procedure后不为标识符)(原:return 语句的返回类型与函数定义的返回类型不匹配。)
      说明:PL/0仅有过程体,无返回值。故进行如上修改。
    8. 错误类型 8:read或write函数的调用不完整(括号缺失)(原:函数调用时实参与形参的数目或类型不匹配。)
      说明:PL/0调用过程无参数。但read和write需要参数,故进行如上修改。
    9. 错误类型 9:read函数的参数不是id(原:对非数组型变量使用“[…]”(数组访问)操作符。)
      说明:改进的PL/0无数组。
    10. 错误类型 10:调用函数格式非法(call后不为过程标识符)(原:对普通变量使用“(…)”或“()”(函数调用)操作符。)

    说明:PL/0与C–调用方式不同。
    前十个错误类型是对C–相关错误的进行一定修改,以下是结合书后代码新增的一些错误类型。有些错误设计后在实际测试时发现不合适就删除了,下面只给出保留部分的声明。

    1. 错误类型 11:缺少赋值符号或赋值符号不正确。
    2. 错误类型 12:一条语句定义(多个)常量或变量时漏掉了逗号或者分号/过程结束时漏了分号。
    3. 错误类型 14:数字过长。
    4. 错误类型 16:if后缺少then语句。
    5. 错误类型 18:while语句缺少do。
    6. 错误类型 19:地址超出上界。
    7. 错误类型 20:逻辑表达式不合法(逻辑符号不合法)。
    8. 错误类型 21:因子标识符为过程。
    9. 错误类型 22:应输入),表达式不完整。
    10. 错误类型 24:程序不以.结尾。

    4 文件结构与代码

    算法在处理上的词法分析思路与第一个实验一致(专栏实验1),而语法分析则**采用了递归下降(专栏实验2)**的方法,在此基础上做出改进并进行了出错处理。

    递归下降子程序包含了所有EBNF的描述,可以根据EBNF描述来写出递归下降子程序。对于错误处理的方法主要是通过在这些递归下降子程序中的相应位置插入error方法,在处理的时候检测是否有错误产生,若有错误则调用error函数进行出错提示和处理。

    4.1 代码结构

    在这里插入图片描述

    4.2 详细代码

    pl0.h:

    #include<sstream>
    
    # define norw 13 //关键字个数 
    # define txmax 100 //名字表容量 
    # define nmax 14 //number 的最大位数 
    # define al 10 //符号的最大长度
    # define levmax 3 //max depth of block nesting 最大允许过程嵌套声明层数[0,lexmax]
    enum symbol {
    	nul, ident, number, plus, minus,
    	times, slash, oddsym, eql, neq, //slash 斜线
    	lss, leq, gtr, geq, lparen, //leq :less than or equal to; gtr: great than; lparen:left parenthesis
    	rparen, comma, semicolon, period, becomes,//comma 逗号 semicolon 分号 period 句号 becomes 赋值号
    	beginsym, endsym, ifsym, thensym, whilesym,
    	writesym, readsym, dosym, callsym, constsym,
    	varsym, procsym,
    };
    #define symnum 32 
    enum object { //object 为三种标识符的类型
    	constant,
    	variable,
    	procedur,
    };
    FILE * fa1; //输出源文件及其各行对应的首地址
    FILE * fas; //输出名字表
    bool tableswitch; //显示名字表与否
    char ch; //获取字符的缓冲区 ,getch 使用
    enum symbol sym; //当前符号
    char id[al + 1]; //当前 ident
    int num; //当前 number 
    int cc, ll; //getch 使用的计数器 ,cc 表示当前字符 (ch)的位置
    int lineIndex; //当前行数
    char line[81]; //读取行缓冲区
    char a[al + 1]; //临时符号
    char word[norw][al]; //保留字
    enum symbol wsym[norw]; //保留字对应的符号值
    enum symbol ssym[256]; //单字符的符号值
    bool declbegsys[symnum]; //表示声明开始的符号集合 ,declaring begin symbol set 
    bool statbegsys[symnum]; //表示语句开始的符号集 , statement 
    bool facbegsys[symnum]; //表示因子开始的符号集合 ,factor 
    
    struct tablestruct//有用的属性是前几个,后面两个属性为生成中间代码
    {
    	char name[al]; // 名字 
    	enum object kind; // 类型仅三种
    	int val; //const数值
    	int level; // 所处层,const 不使用
    	int adr; //地址
    	int size; //需要分配的数据区空间
    };
    struct tablestruct table[txmax]; //名字表 
    FILE * fin; //fin 文本文件用于指向输入的源程序文件
    char fname[al];
    int err; //错误计数器
    #define getsymdo if(-1==getsym())return -1 
    #define getchdo if(-1==getch())return -1 
    #define expressiondo(a,b,c) if(-1==expression(a,b,c))return -1 
    #define factordo(a,b,c) if(-1==factor(a,b,c))return -1 
    #define termdo(a,b,c) if(-1==term(a,b,c))return -1 
    #define conditiondo(a,b,c) if(-1==condition(a,b,c))return -1 
    #define statementdo(a,b,c) if(-1==statement(a,b,c))return -1 
    #define constdeclarationdo(a,b,c) if(-1==constdeclaration(a,b,c))return -1 
    #define vardeclarationdo(a,b,c) if(-1==vardeclaration(a,b,c))return -1 
    void error(int n);
    int getsym();
    int getch();
    void init();
    //集合操作声明
    //int inset(int e, bool*s);
    int addset(bool*sr, bool*s1, bool*s2, int n);
    int subset(bool*sr, bool*s1, bool*s2, int n);
    int mulset(bool*sr, bool*s1, bool*s2, int n);
    //递归下降主要子程序声明
    int block(int lev, int tx, bool* fsys);
    int factor(bool* fsys, int* ptx, int lev);
    int term(bool*fsys, int*ptx, int lev);
    int condition(bool*fsys, int*ptx, int lev);
    int expression(bool*fsys, int*ptx, int lev);
    int statement(bool*fsys, int*ptx, int lev);
    int vardeclaration(int* ptx, int lev, int* pdx);
    int constdeclaration(int* ptx, int lev, int* pdx);
    int position(char* idt, int tx);
    void enter(enum object k, int* ptx, int lev, int* pdx);
    

    源.cpp:

    #include<stdio.h> 
    #include"pl0.h" 
    #include"string.h" 
    int main()
    {
    	bool nxtlev[symnum];
    	printf("Input pl/0 file ?");
    	scanf("%s", fname); 
    	fin = fopen(fname, "r"); 
    	if (fin)
    	{
    		fa1 = fopen("fa1.txt", "w");
    		fprintf(fa1, "Iput pl/0 file ?");
    		fprintf(fa1, "%s\n", fname);
    		init(); 
    		err = 0; //错误计数器置 0 
    		cc = lineIndex = ll = 0;
    		ch = ' ';
    		if (-1 != getsym())
    		{
    			fas = fopen("fas.tmp", "w");
    			addset(nxtlev, declbegsys, statbegsys, symnum);
    			nxtlev[period] = true;
    			if (-1 == block(0, 0, nxtlev)) //调用编译程序
    			{
    				fclose(fa1);
    				fclose(fas);
    				fclose(fin);
    				printf("\n");
    			}
    			fclose(fa1);
    			fclose(fas);
    			if (sym != period)
    			{
    				error(24);
    			}
    			if (err == 0) {
    				printf("\ncomplie sucess!\n");
    			}
    			else {
    				printf("\n%derrors,complie failed.\n",err);
    			}
    		}
    		fclose(fin);
    	}
    	else
    		printf("File doesn't exist! \n");
    	printf("\n");
    	getchar();
    	getchar();
    	return 0;
    }
    //初始化符号表等内容
    void init()
    {
    	int i;
    	for (i = 0; i <= 255; i++)
    	{
    		ssym[i] = nul; //ssym :单字符的符号值
    	}
    	ssym['+'] = plus;
    	ssym['-'] = minus;
    	ssym['*'] = times;
    	ssym['/'] = slash;
    	ssym['('] = lparen;
    	ssym[')'] = rparen;
    	ssym['='] = eql;
    	ssym[','] = comma;
    	ssym['.'] = period;
    	ssym['#'] = neq;
    	ssym[';'] = semicolon;
    	/*设置保留字名字 ,按照字母顺序 ,便于折半查找 */
    	strcpy(&(word[0][0]), "begin");
    	strcpy(&(word[1][0]), "call");
    	strcpy(&(word[2][0]), "const");
    	strcpy(&(word[3][0]), "do");
    	strcpy(&(word[4][0]), "end");
    	strcpy(&(word[5][0]), "if");
    	strcpy(&(word[6][0]), "odd");
    	strcpy(&(word[7][0]), "procedure");
    	strcpy(&(word[8][0]), "read");
    	strcpy(&(word[9][0]), "then");
    	strcpy(&(word[10][0]), "var");
    	strcpy(&(word[11][0]), "while");
    	strcpy(&(word[12][0]), "write");
    	/*设置保留字符号 */
    	wsym[0] = beginsym;
    	wsym[1] = callsym;
    	wsym[2] = constsym;
    	wsym[3] = dosym;
    	wsym[4] = endsym;
    	wsym[5] = ifsym;
    	wsym[6] = oddsym;
    	wsym[7] = procsym;
    	wsym[8] = readsym;
    	wsym[9] = thensym;
    	wsym[10] = varsym;
    	wsym[11] = whilesym;
    	wsym[12] = writesym;
    	/*设置符号集 */
    	for (i = 0; i < symnum; i++)
    	{
    		declbegsys[i] = false;
    		statbegsys[i] = false;
    		facbegsys[i] = false;
    	}
    	/*设置声明开始符号集 */
    	declbegsys[constsym] = true;
    	declbegsys[varsym] = true;
    	declbegsys[procsym] = true;
    	/*设置语句开始符号集 */
    	statbegsys[beginsym] = true;
    	statbegsys[callsym] = true;
    	statbegsys[ifsym] = true;
    	statbegsys[whilesym] = true;
    	/*设置因子开始符号集 */
    	facbegsys[ident] = true;
    	facbegsys[number] = true;
    	facbegsys[lparen] = true;
    }
    //集合运算部分
    int inset(int e, bool* s)
    {
    	return s[e];
    }
    int addset(bool* sr, bool* s1, bool* s2, int n)
    {
    	int i;
    	for (i = 0; i < n; i++)
    	{
    		sr[i] = s1[i] || s2[i];
    	}
    	return 0;
    }
    int subset(bool* sr, bool* s1, bool* s2, int n)
    {
    	int i;
    	for (i = 0; i < n; i++)
    	{
    		sr[i] = s1[i] && (!s2[i]);
    	}
    	return 0;
    }
    int mulset(bool* sr, bool* s1, bool* s2, int n)
    {
    	int i;
    	for (i = 0; i < n; i++)
    	{
    		sr[i] = s1[i] && s2[i];
    	}
    	return 0;
    }
    //出错处理,打印出错位置和错误描述
    void error(int n)
    {
    	char space[256];
    	memset(space, 0, sizeof(space));
    	space[cc - 1] = 0;// 出错时当前符号已经读完,所以 cc-1 
    	switch (n)
    	{
    		case 1:
    			sprintf(space,"Error type [%d] at Line [%d]: [变量%s在使用时未经定义].\n", n, lineIndex,id);
    			break;
    		case 2:
    			sprintf(space,"Error type [%d] at Line [%d]: [函数%s在调用时未经定义].\n", n, lineIndex,id);
    			break;
    		case 3:
    			sprintf(space,"Error type [%d] at Line [%d]: [变量%s出现重复定义,或变量与前面定义过的结构体名字重复].\n", n, lineIndex,id);
    			break;
    		case 4:
    			sprintf(space,"Error type [%d] at Line [%d]: [过程%s出现重复定义].\n", n, lineIndex,id);
    			break;
    		case 5:
    			sprintf(space,"Error type [%d] at Line [%d]: [赋值号两边的表达式类型不匹配].\n", n, lineIndex);
    			break;
    		case 6:
    			sprintf(space,"Error type [%d] at Line [%d]: [操作数类型不匹配或操作数类型与操作符不匹配].\n", n, lineIndex);
    			break;
    		case 7:
    			sprintf(space,"Error type [%d] at Line [%d]: [过程声明格式不正确(procedure后不为标识符)].\n", n, lineIndex);
    			break;
    		case 8:
    			sprintf(space,"Error type [%d] at Line [%d]: [read或write函数的调用不完整(括号缺失)].\n", n, lineIndex);
    			break;
    		case 9:
    			sprintf(space,"Error type [%d] at Line [%d]: [read函数的参数不是id].\n", n, lineIndex);
    			break;
    		case 10:
    			sprintf(space,"Error type [%d] at Line [%d]: [调用函数格式非法(call后不为过程标识符)].\n", n, lineIndex);
    			break;
    		case 11:
    			sprintf(space, "Error type [%d] at Line [%d]: [缺少赋值符号或赋值符号不正确].\n", n, lineIndex);//可能发生在语句定义、常变量定义与赋值时。
    			break;
    		case 12:
    			sprintf(space, "Error type [%d] at Line [%d]: [一条语句定义(多个)常量或变量时漏掉了逗号或者分号/过程结束时漏了分号].\n", n, lineIndex);// 语句结束时或连续定义时缺少终止符号(end, ; )
    			break;
    		case 14:
    			sprintf(space, "Error type [%d] at Line [%d]: [数字过长].\n", n, lineIndex);
    			break;
    		case 16:
    			sprintf(space, "Error type [%d] at Line [%d]: [if后缺少then语句].\n", n, lineIndex);
    			break;
    		case 18:
    			sprintf(space, "Error type [%d] at Line [%d]: [while语句缺少do].\n", n, lineIndex);
    			break;
    		case 19:
    			sprintf(space, "Error type [%d] at Line [%d]: [地址超出上界].\n", n, lineIndex);
    			break;
    		case 20:
    			sprintf(space, "Error type [%d] at Line [%d]: [逻辑表达式不合法(逻辑符号不合法)].\n", n, lineIndex);
    			break;
    		case 21:
    			sprintf(space, "Error type [%d] at Line [%d]: [因子标识符为过程].\n", n, lineIndex);
    			break;
    		case 22:
    			sprintf(space, "Error type [%d] at Line [%d]: [应输入),表达式不完整].\n", n, lineIndex);
    			break;
    		case 24:
    			sprintf(space, "Error type [%d] at Line [%d]: [不以.结尾].\n", n, lineIndex);
    			break;
    		default:
    			sprintf(space, "Error type [%d] at Line [%d]: [未知错误].\n", n, lineIndex);
    			break;
    	}
    	printf("%s", space);
    	err++;
    }
    //读取字符部分,先把字符存到缓冲区,然后进行读取,这里同时进行了行号的记录
    int getch()
    {
    	if (cc == ll)
    	{
    		if (feof(fin)) //读到文件尾
    		{
    			printf("program incomplete");
    			return -1;
    		}
    		ll = 0;
    		cc = 0;
    		printf("%d ", ++lineIndex);
    		fprintf(fa1, "%d ", lineIndex);
    		ch = ' ';
    		while (ch != 10)
    		{
    			if (EOF == fscanf(fin, "%c", &ch))
    			{
    				line[ll] = 0;
    				break;
    			}
    			printf("%c", ch);
    			fprintf(fa1, "%c", ch);
    			line[ll] = ch;
    			ll++;
    		}
    	}
    	ch = line[cc];
    	cc++;
    	return 0;
    }
    // 词法分析 获取一个词法单元
    int getsym()
    {
    	int i, j, k;
    	while (ch == ' ' || ch == 10 || ch == 9) //忽略空白部分
    	{
    		getchdo; 
    
    	}
    	if (ch >= 'a'&&ch <= 'z')
    	{
    		k = 0;
    		do {
    			if (k < al)
    			{
    				a[k] = ch;
    				k++;
    			}
    			getchdo;
    		} while (ch >= 'a'&&ch <= 'z' || ch >= '0'&&ch <= '9');
    		a[k] = 0;
    		strcpy(id, a);
    		i = 0;
    		j = norw - 1;
    		do {
    			k = (i + j) / 2;
    			if (strcmp(id, word[k]) <= 0)
    			{
    				j = k - 1;
    			}
    			if (strcmp(id, word[k]) >= 0)
    			{
    				i = k + 1;
    			}
    		} while (i <= j);
    		if (i - 1 > j)
    		{
    			sym = wsym[k];
    		}
    		else
    		{
    			sym = ident;
    		}
    	}
    	else
    	{
    		if (ch >= '0'&&ch <= '9')
    		{
    			k = 0;
    			num = 0;
    			sym = number;
    			do {
    				num = 10 * num + ch - '0';
    				k++;
    				getchdo;
    			} while (ch >= '0'&&ch <= '9'); /* 获取数字的值 */
    			k--;
    			if (k > nmax)
    			{
    				error(14);
    			}
    		}
    		else
    		{
    			if (ch == ':') // 检测赋值符号 
    			{
    				getchdo;
    				if (ch == '=')
    				{
    					sym = becomes;
    					getchdo;
    				}
    				else
    				{
    					sym = nul; // 不能识别的符号 
    				}
    			}
    			else
    			{
    				if (ch == '<') //检测小于或小于等于符号 
    				{
    					getchdo;
    					if (ch == '=')
    					{
    						sym = leq;
    						getchdo;
    					}
    					else
    					{
    						sym = lss;
    					}
    				}
    				else
    				{
    					if (ch == '>') // 大于或大于等于
    					{
    						getchdo;
    						if (ch == '=')
    						{
    							sym = geq;
    							getchdo;
    						}
    						else
    						{
    							sym = gtr;
    						}
    					}
    					else
    					{
    						sym = ssym[ch];// 按单字符号处理
    						if (sym != period) {
    
    							getchdo;
    						}
    					}
    				}
    			}
    		}
    	}
    	return 0;
    }
    
    
    //在名字表中加入一项
    void enter(enum object k, int *ptx, int lev, int *pdx)
    {
    	(*ptx)++;
    	strcpy(table[(*ptx)].name, id); //全局变量 id 中已存有当前名字的名字
    	table[(*ptx)].kind = k;
    	switch (k)
    	{
    		case constant: 
    			if (num >= 100000000000000)
    			{
    				error(19);
    				num = 0;
    			}
    			table[(*ptx)].val = num;
    			break;
    		case variable: /* 变量名字 */
    			table[(*ptx)].level = lev;
    			table[(*ptx)].adr = (*pdx);
    			(*pdx)++;
    			break; /* 过程名字 */
    		case procedur:
    			table[(*ptx)].level = lev;
    			break;
    	}
    }
    //查找ident在表中的位置
    int position(char * idt, int tx)
    {
    	int i;
    	strcpy(table[0].name, idt);
    	i = tx;
    	while (strcmp(table[i].name, idt) != 0)
    	{
    		i--;
    	}
    	return i;
    }
    #pragma region 递归下降
    //分程序
    int block(int lev, int tx, bool* fsys)
    {
    	int i;
    	int dx; /* 名字分配到的相对地址 */
    	int tx0; /*保留初始 tx*/
    	bool nxtlev[symnum]; /* 在下级函数的参数中,符号集合均为值参,但由于使用数组实现,传递进来的是指针, 为防止下级函数改变上级函数的集合,开辟新的空间传递给下级函数 */
    	dx = 3;
    	tx0 = tx; /* 记录本层名字的初始位置 */
    	if (lev > levmax)
    	{
    		error(15);
    	}
    	do {
    		if (sym == constsym) /* 收到常量声明符号,开始处理常量声明 */
    		{
    			getsymdo;
    			do {
    				constdeclarationdo(&tx, lev, &dx); /*dx 的值会被 constdeclaration 改变,使用指针 */
    				while (sym == comma)
    				{
    					getsymdo;
    					constdeclarationdo(&tx, lev, &dx);
    				}
    				if (sym == semicolon)
    				{
    					getsymdo;
    				}
    				else
    				{
    					error(12); /* 漏掉了逗号或者分号 */
    				}
    			} while (sym == ident);
    		}
    		if (sym == varsym)/* 收到变量声名符号,开始处理变量声名 */
    		{
    			getsymdo;
    			do {
    				vardeclarationdo(&tx, lev, &dx);
    				while (sym == comma)
    				{
    					getsymdo;
    					vardeclarationdo(&tx, lev, &dx);
    				}
    				if (sym == semicolon)
    				{
    					getsymdo;
    				}
    				else
    				{
    					error(12);
    				}
    			} while (sym == ident);
    		}
    		while (sym == procsym)/* 收到过程声名符号,开始处理过程声名 */
    		{
    			getsymdo;
    			if (sym == ident)
    			{
    				int i = position(id, dx);
    				if (i != 0)
    				{
    					if (table[i].kind == procedur)//过程重定义
    						error(4);
    				}
    				enter(procedur, &tx, lev, &dx);/* 记录过程名字 */
    				getsymdo;
    			}
    			else
    			{
    				error(7);/*procedure 后应为标识符 */
    				getsymdo;
    			}
    			if (sym == semicolon)
    			{
    				getsymdo;
    			}
    			else
    			{
    				error(12);/* 漏掉了分号 */
    			}
    			memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    			nxtlev[semicolon] = true;
    			if (-1 == block(lev + 1, tx, nxtlev))
    			{
    				return -1;/* 递归调用 */
    			}
    			if (sym == semicolon)
    			{
    				getsymdo;
    				memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);
    				nxtlev[ident] = true;
    				nxtlev[procsym] = true;
    			}
    			else
    			{
    				error(12); /* 漏掉了分号 */
    			}
    		}
    		memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);
    		nxtlev[ident] = true;
    		nxtlev[period] = true;
    	} while (inset(sym, declbegsys)); /* 直到没有声明符号 */
    	table[tx0].size = dx; /* 声明部分中每增加一条声明都会给dx 增加 1,声明部分已经结束 ,dx 就是当前过程数据的 size*/
    	if (tableswitch) /* 输出名字表 */
    	{
    		printf("TABLE:\n");
    		if (tx0 + 1 > tx)
    		{
    			printf("NULL\n");
    		}
    		for (i = tx0 + 1; i <= tx; i++)
    		{
    			switch (table[i].kind)
    			{
    			case constant:
    				printf("%d const %s", i, table[i].name);
    				printf("val=%d\n", table[i].val);
    				fprintf(fas, "%d const %s", i, table[i].name);
    				fprintf(fas, "val=%d\n", table[i].val);
    				break;
    			case variable:
    				printf("%d var%s", i, table[i].name);
    				printf("lev=%d addr=%d\n", table[i].level, table[i].adr);
    				fprintf(fas, "%d var %s", i, table[i].name);
    				fprintf(fas, "lev=%d addr=%d\n", table[i].level, table[i].adr);
    				break;
    			case procedur:
    				printf("%d proc%s", i, table[i].name);
    				printf("lev=%d addr=%d size = %d\n", table[i].level, table[i].adr, table[i].size);
    				fprintf(fas, "%d proc%s", i, table[i].name);
    				fprintf(fas, "lev=%d adr=%d size=%d \n", table[i].level, table[i].adr, table[i].size);
    				break;
    			}
    		}
    		printf("\n");
    	}
    	nxtlev[semicolon] = true;
    	nxtlev[endsym] = true;
    	statementdo(nxtlev, &tx, lev);
    	return 0;
    }
    
    //常量声明处理
    int constdeclaration(int * ptx, int lev, int * pdx)
    {
    	if (sym == ident)
    	{
    		if (position(id, *ptx) != 0) //重定义
    			error(3);
    		getsymdo;
    		if (sym == eql || sym == becomes)//为=或:=
    		{
    			if (sym == becomes)//重复写赋值符号
    			{
    				error(11);
    			}
    			getsymdo;
    			if (sym == number)//是常量说明,则向下
    			{
    				enter(constant, ptx, lev, pdx);
    				getsymdo;
    			}
    			else
    			{
    				error(5); //常量声明后面不为数字,导致错误5
    				getsymdo;
    			}
    		}
    		else
    		{
    			error(11); /* 常量说明标识后应是 =*/
    			getsymdo;
    		}
    	}
    	else
    	{
    		error(8); /*const 后应是标识 */
    	}
    	return 0;
    }
    //变量定义
    int vardeclaration(int * ptx, int lev, int * pdx)
    {
    	if (sym == ident)
    	{
    		if (position(id, *ptx) != 0) //重定义
    			error(3);
    		enter(variable, ptx, lev, pdx);// 填写名字表
    		getsymdo;
    	}
    	else
    		error(8);
    	return 0;
    }
    //语句
    int statement(bool* fsys, int * ptx, int lev)
    {
    	int i, cx1, cx2;
    	bool nxtlev[symnum];
    	if (sym == ident)
    	{
    		i = position(id, *ptx);
    		if (i == 0)//未定义
    		{
    			error(1);
    		}
    		else if (table[i].kind != variable)
    		{
    			error(5);//赋值号两边的表达式类型不匹配(左部)
    			i = 0;
    		}
    		getsymdo;
    		if (sym == becomes)
    		{
    			getsymdo;
    		}
    		else
    		{
    			error(11);
    		}
    		memcpy(nxtlev, fsys, sizeof(bool)* symnum);
    		expressiondo(nxtlev, ptx, lev);
    	}
    	else
    	{
    		if (sym == readsym)
    		{
    			getsymdo;
    			if (sym != lparen)
    			{
    				error(8);//缺失左括号
    			}
    			else
    			{
    
    				getsymdo;
    			}
    			do {
    				if (sym == ident)
    				{
    					i = position(id, *ptx);
    					if (i == 0)
    					{
    						error(1);//使用变量时未定义
    					}
    					//else if(table[i].kind!=)
    				}
    				else
    					error(9);//read函数的参数不为id
    				getsymdo;
    			} while (sym == comma); /* 一条 read 语句可读多个变量 */
    			if (sym != rparen)
    				error(8); //缺失右括号
    			else
    			{
    
    				getsymdo;
    			}
    		}
    		else
    		{
    			if (sym == writesym) /* 准备按照 write 语句处理,与 read 类似 */
    			{
    				getsymdo;
    				if (sym != lparen) 
    					error(8);//缺失左括号
    				else {
    
    					getsymdo;
    				}
    				do {
    					memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    					nxtlev[rparen] = true;
    					nxtlev[comma] = true; /* write 的后跟符号为) or,*/
    					expressiondo(nxtlev, ptx, lev);/* 调用表达式处理,此处与 read 不同, read 为给变量赋值 */
    				} while (sym == comma);
    				if (sym != rparen)
    					error(8);//缺失有括号
    				else {
    
    					getsymdo;
    				}
    			}
    			else
    			{
    				if (sym == callsym) /* 准备按照 call 语句处理 */
    				{
    					getsymdo;
    					if (sym != ident)
    					{
    						error(10); /*call 后应为标识符 */
    						getsymdo;
    					}
    					else
    					{
    						i = position(id, *ptx);
    						if (i == 0)
    							error(2); /* 过程未找到 */
    						else
    						{
    							if (table[i].kind != procedur)
    								error(10); /*call 后标识符应为过程 */
    							
    						}
    						getsymdo;
    					}
    				}
    				else
    				{
    					if (sym == ifsym) /* 准备按照 if 语句处理 */
    					{
    						getsymdo;
    						memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    						nxtlev[thensym] = true;
    						nxtlev[dosym] = true; /* 后跟符号为 then 或 do*/
    						conditiondo(nxtlev, ptx, lev); /*调用条件处理(逻辑运算)函数
    						*/
    						if (sym == thensym) {
    
    							getsymdo;
    						}
    						else
    						{
    							error(16); /*缺少 then*/
    							//getsymdo;
    						}
    						statementdo(fsys, ptx, lev); /* 处理 then 后的语句 */
    					}
    					else
    					{
    						if (sym == beginsym) /* 准备按照复合语句处理 */
    						{
    							getsymdo;
    							memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    							nxtlev[semicolon] = true;
    							nxtlev[endsym] = true;/* 后跟符号为分号或 end*/
    							/* 循环调用语句处理函数, 直到下一个符号不是语句开始符号或收到 end*/
    							statementdo(nxtlev, ptx, lev);
    							while (inset(sym, statbegsys) || sym == semicolon)
    							{
    								if (sym == semicolon) 
    								{
    
    									getsymdo;
    								}
    								else
    								{
    									error(12);/* 缺少分号 */
    									getsymdo;
    								}
    								statementdo(nxtlev, ptx, lev);
    							}
    							if (sym == endsym)
    							{
    
    								getsymdo;
    							}
    							else
    							{
    								error(12); /* 缺少 end 或分号 */
    								getsymdo;
    							}
    						}
    						else
    						{
    							if (sym == whilesym)/* 准备按照 while 语句处理 */
    							{
    								getsymdo;
    								memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    								nxtlev[dosym] = true;/* 后跟符号为 do*/
    								conditiondo(nxtlev, ptx, lev); /*调用条件处理 */
    								if (sym == dosym)
    								{
    									getsymdo;
    								}
    								else
    								{
    									error(18); /* 缺少 do*/
    									//return 0;
    								}
    								statementdo(fsys, ptx, lev); /* 循环体 */							
    							}
    						}
    					}
    				}
    			}
    		}
    	}
    	return 0;
    }
    //表达式
    int expression(bool*fsys, int*ptx, int lev)
    {
    	enum symbol addop; /* 用于保存正负号 */
    	bool nxtlev[symnum];
    	if (sym == plus || sym == minus) /* 开头的正负号,此时当前表达式被看作一个正的或负的项 */
    	{
    		addop = sym; /* 保存开头的正负号 */
    		getsymdo;
    		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    		nxtlev[plus] = true;
    		nxtlev[minus] = true;
    		termdo(nxtlev, ptx, lev); /* 处理项 */
    	}
    	else /* 此时表达式被看作项的加减 */
    	{
    		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    		nxtlev[plus] = true;
    		nxtlev[minus] = true;
    		termdo(nxtlev, ptx, lev); /* 处理项 */
    	}
    	while (sym == plus || sym == minus)
    	{
    		addop = sym;
    		getsymdo;
    		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    		nxtlev[plus] = true;
    		nxtlev[minus] = true;
    		termdo(nxtlev, ptx, lev); /* 处理项 */
    	}
    	return 0;
    }
    //项
    int term(bool*fsys, int *ptx, int lev)
    {
    	enum symbol mulop; /* 用于保存乘除法符号 */
    	bool nxtlev[symnum];
    	memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    	nxtlev[times] = true;
    	nxtlev[slash] = true;
    	factordo(nxtlev, ptx, lev); /* 处理因子 */
    	while (sym == times || sym == slash)
    	{
    		mulop = sym;
    		getsymdo;
    		factordo(nxtlev, ptx, lev);
    	}
    	return 0;
    }
    
    //因子
    int factor(bool*fsys, int *ptx, int lev)
    {
    	int i;
    	bool nxtlev[symnum];
    	if (sym == ident) /* 因子为常量或者变量 */
    	{
    		i = position(id, *ptx); /* 查找名字 */
    		if (i == 0)
    		{
    			error(1); /* 标识符未声明 */
    			//getsymdo;
    			//break;
    		}
    		else if(table[i].kind==procedur)
    			error(6); //因子为过程时,使得操作数不合法或表达式运算不合法,导致错误6:操作数类型不匹配或操作数类型与操作符不匹配
    		getsymdo;
    	}
    	else if (sym == number) /* 因子为数*/
    	{
    		if (num >= 100000000000000)
    		{
    			error(14);
    			num = 0;
    		}
    		getsymdo;
    	}	
    	else if (sym == lparen) /* 因子为表达式 */
    	{
    		getsymdo;
    		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    		nxtlev[rparen] = true;
    		expressiondo(nxtlev, ptx, lev);
    		if (sym == rparen)
    		{
    			getsymdo;
    		}
    		else
    		{
    			error(22); /* 缺少右括号 */
    			getsymdo;
    		}
    	}
    	return 0;
    }
    //条件
    int condition(bool* fsys, int* ptx, int lev)
    {
    	enum symbol relop;
    	bool nxtlev[symnum];
    	if (sym == oddsym) // 准备按照 odd 运算处理
    	{
    		getsymdo;
    		expressiondo(fsys, ptx, lev);
    	}
    	else
    	{
    		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    		nxtlev[eql] = true;
    		nxtlev[neq] = true;
    		nxtlev[lss] = true;
    		nxtlev[leq] = true;
    		nxtlev[gtr] = true;
    		nxtlev[geq] = true;
    		expressiondo(nxtlev, ptx, lev);
    		if (sym != eql && sym != neq && sym != lss && sym != leq && sym != gtr && sym != geq)
    		{
    			error(20);
    		}
    		else
    		{
    			relop = sym;
    			getsymdo;
    			expressiondo(fsys, ptx, lev);
    		}
    	}
    	return 0;
    }
    
    #pragma endregion
    

    4.3 递归下降子程序的声明

    分程序:
    int block(int lev, int tx, bool* fsys)
    常量声明处理:
    int constdeclaration(int * ptx, int lev, int * pdx)
    变量定义:
    int vardeclaration(int * ptx, int lev, int * pdx)
    语句:
    int statement(bool* fsys, int * ptx, int lev)
    表达式:
    int expression(bool*fsys, int*ptx, int lev)
    项:
    int term(bool*fsys, int *ptx, int lev)
    因子:
    int factor(bool*fsys, int *ptx, int lev)
    条件:
    int condition(bool* fsys, int* ptx, int lev)
    
    

    5 常变量说明

    变量:
    在这里插入图片描述

    常量:

    enum symbol {}//包含32种类型符号的枚举
    enum object { 	constant,	variable,	procedur};
    //object 为三种标识符的类型
    struct tablestruct{//名字表结构
    	char name[al]; // 名字 
    	enum object kind; // 类型仅三种
    };
    

    在这里插入图片描述

    6 运行结果

    测试的txt文件需要与debug的exe文件在同一目录
    (1)测试一:对错误1、2、3、4进行测试。
    测试代码t1.txt:

    var m,n,q;
    procedure gcd;
    begin
    	while r#0 do
    	begin
    		q := m / n;
    		r := m - q * n;
    		m := n;
    		n := r;
    	end;
    end;
    procedure gcd;
    begin
    end;
    var m;
    begin
    	call gcd;
    	call fun;
    end.
    

    在这里插入图片描述

    (2)测试二:对错误5、6、7进行测试。
    测试代码t2.txt:

    var m,n,q;
    const con = 5;
    procedure var;
    begin
    end;
    procedure fun;
    const co = fun;
    const con = 6;
    begin
    end;
    begin
    	m:=fun+2;
    	fun:=1;
    	m:=1;
    	con:=2;
    	con:=con;
    	m:=con;	
    end.
    

    在这里插入图片描述
    (3)测试三:对错误8、9、10进行测试。
    测试代码t3.txt:

    var m;
    procedure fun;
    begin
    end;
    begin
    	read m;
    	write(m;
    	write(m);
    	read(m);
    	read(var);
    	call fun;
    	call var;
    	call m;
    end.
    

    在这里插入图片描述
    (4)测试四:对错误12、14、16、18、20进行测试。
    测试代码t4.txt:

    var m;
    var a b;
    const con=1234567891234567;
    procedure fun;
    begin
    end;
    begin
    	if m=5
    	then m:=m+1;
    	if m=4
    	m:=m-1;
    	while m>=7
    	do m:=m-1;
    	while m=6
    	m:=1+2;	
    	if m-m
    	then write(m);
    end.
    
    

    在这里插入图片描述
    (5)测试五:对错误22、24进行测试。
    测试代码t5.txt:

    var m;
    var a b;
    const con=1234567891234567;
    procedure fun;
    begin
    end;
    begin
    	m:=(6+8)/5+(4);
    	m:=(5+7;
    end
    
    

    在这里插入图片描述
    (6)测试六:无错误测试。
    测试代码t6.txt:

    var m, n, r, q;
    procedure gcd;
    begin
    while r#0 do
    begin
    q := m / n;
    r := m - q * n;
    m := n;
    n := r;
    end;
    end;
    begin
    read(m);
    read(n);
    if m < n then
    begin
    r := m;
    m := n;
    n := r;
    end;
    begin
    r:=1;
    call gcd;
    write(m);
    if odd r
    then write(m)
    end;
    end.
    
    

    在这里插入图片描述

    展开全文
  • 十、设计SAMPLE语言的语法、语义分析器,输出四元式的中间结果。 检查要求: a)启动程序后,先输出作者姓名、班级、学号(可用汉语、英语或拼音)。 b)请求输入测试程序名,键入程序名后自动开始编译。 c)输出四元式...
  • 编译原理 语义分析

    千次阅读 2020-01-08 09:20:19
    其中,OP是运算符,argl,arg2分别是第一和第二个运算对象,result是编译程序为存放中间运算结果而引进的变量,常称为临时变量。当OP是一目运算时,常常将运算对象定义为arg1。 四元式出现的顺序和语法成份的计值...

    1. 语义与语法的区别

    在这里插入图片描述
    <1> 语法与语义的关系
    语法是指语言的结构、即语言的“样子”;语义是指附着于语言结构上的实际含意 ,即语言的“意义”。
    对于语法和语义:
    语义不能离开语法独立存在;
    语义远比语法复杂;
    同一语言结构可包含多种含意,不同语言结构可表示相同含意;
    语法与语义之间没有明确的界线。

    重点:语义分析的两个作用

    1.检查是否结构正确的句子所表示的意思也合法;
    2.执行规定的语义动作,如:
    表达式求值
    符号表填写
    中间代码生成等

    <3> 语义分析的方法

    语法制导翻译

    2. 中间代码

    在这里插入图片描述

    重点:要求中间代码具有如下特性,以便于编译器的开发移植和代码的优化:

    便于语法制导翻译;
    既与机器指令的结构相近,又与具体机器无关。

    3.后缀式

    在这里插入图片描述

    定义

    一个表达式E的后缀形式可以如下定义:
    (1)如果E是一个变量或常量,则E的后缀式是E本身。
    (2)如果E是E1 op E2形式的表达式,这里op是任何二元操作符,则E的后缀式为E1’E2’ op,这里E1’和E2’分别为E1和E2的后缀式。
    (3)如果E是(E1)形式的表达式,则E1的后缀式就是E的后缀式。
    如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+
    (a+b)c-(a+b)/e的后缀表达式为:
    (a+b)c-(a+b)/e
    →((a+b)c)((a+b)/e)-
    →((a+b)c
    )((a+b)e/)-
    →(ab+c
    )(ab+e/)-
    →ab+c
    ab+e/-

    算法实现

    将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
    首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
    (1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
    (2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
    (3)若取出的字符是“(”,则直接送入S1栈顶。
    (4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
    (5)重复上面的1~4步,直至处理完所有的输入字符
    (6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
    完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!

    4.后缀式的计算

    在这里插入图片描述
    在这里插入图片描述

    5.三地址码

    在这里插入图片描述
    在这里插入图片描述

    6.四元式主要由四部分组成:

    (OP,arg1,arg2,result)
    其中,OP是运算符,argl,arg2分别是第一和第二个运算对象,result是编译程序为存放中间运算结果而引进的变量,常称为临时变量。当OP是一目运算时,常常将运算对象定义为arg1。
    在这里插入图片描述
    四元式出现的顺序和语法成份的计值顺序相一致。
    四元式之间的联系是通过临时变量实现的,这样易于调整和变动四元式。
    便于优化处理。

    三地址代码

    三地址代码是四元式的另一种表示形式

    在这里插入图片描述

    例题有文法 G 和 G 的语法制导翻译如下:

    E → E1*T { E.place=newtemp;

    emit(*,E1.place,T.place,E.place; }

    | T { E.place=T.place; }

    T → T1+F { T.place=newtemp;

    emit(+,T1.place,F.place,T.place; }

    | F { T.place=F.place; }

    F → (E) { F.place=E.place; }

    | id { F.place=id.name; }

    (a) 求句型 (T+F)*id 的短语、直接短语以及句柄;

    (b) 根据语法制导翻译写出句子 ab+cd 的中间代码;

    © (若 a=3 , b=5 , c=7 , d=8 ,请给出中间代码计算结果;

    (d) 将文法 G 简化为: E → E*T|T , T → T+F|F , F → id 。给出它的识别活前缀的 DFA 。
    在这里插入图片描述
    在这里插入图片描述

    7.符号表

    在这里插入图片描述

    8. 数组元素的引用

    (1)数组元素的地址计算

    注意是行主存储还是列主存储
    在这里插入图片描述
    (2)☆数组元素引用的语法制导翻译(考试热点之一)

    9. 布尔表达式

    在这里插入图片描述
    布尔表达式的计算有两种方法:数值表示的直接计算和逻辑表示的短路计算
    在这里插入图片描述
    在这里插入图片描述

    ☆ 布尔表达式短路计算的翻译:短路计算的控制流,真出口与假出口,真出口链与假出口链,拉链回填技术(P207 例4.41)(考试热点之一)

    10. 控制语句

    控制语句的分类:①无条件转移、②条件转移、③循环语句、④分支语句

    无条件转移(goto)\条件转移(if、while)
    条件转移的语法制导翻译:P213 例4.42

    11.过程的定义与声明

    在这里插入图片描述

    左值和右值

    在这里插入图片描述
    在这里插入图片描述
    左值式地址,右值是值
    根据参数传递的左右值不同
    调用可分为:
    1.值调用 (传递右值)
    2.引用调用 (传递左值)

    拉链回填

    在这里插入图片描述

    展开全文
  • 附录c 编译程序实验 实验目的:用c语言对一个简单语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。 语法分析 C2.1 实验目的 编制一个递归下降分析程序,实现对词法分析...
  • 编译原理-语义分析器(C语言源码)

    热门讨论 2011-06-01 22:39:29
    编译原理语义分析器,实现分析部分C语言的语法成分,将其翻译成三地址代码。
  • 实验任务阅读已有编译器的经典语义分析程序,并测试语义分析器的输出。实验内容(1)选择一个编译器,如:TINY或PL/0,其它编译器也可(需自备源代码)。(2)阅读语义分析程序,加上你自己的理解。尤其要求对相关函数...

    (一)学习经典的语义分析器(2小时)

    实验目的

    学习已有编译器的经典语义分析源程序。

    实验任务

    阅读已有编译器的经典语义分析源程序,并测试语义分析器的输出。

    实验内容

    (1)选择一个编译器,如:TINY或PL/0,其它编译器也可(需自备源代码)。

    (2)阅读语义分析源程序,加上你自己的理解。尤其要求对相关函数与重要变量的作用与功能进行稍微详细的描述。若能加上学习心得则更好。TINY语言请参考《编译原理及实践》第6.5节;PL/0语言请参考相关实现文档。

    (3)理解符号表的定义(栏目设置)与基于抽象语法树的类型检查/推论的实现方法(树遍历)。

    (4)测试语义分析器。对TINY语言要求输出测试程序的符号表与测试结果。对PL/0语言要求给出测试程序的各种符号表的内容。

    TINY语言:

    测试用例一:sample.tny。

    测试用例二:用TINY语言自编一个程序计算任意两个正整数的最大公约数与最大公倍数。

    PL/0语言:

    测试用例一~三:test.pls,test2.pls,a1.pls。

    (二)实现一门语言的语义分析器(6小时)

    实验目的

    通过本次实验,加深对语义分析的理解,学会编制语义分析器。

    实验任务

    用C或JAVA语言编写一门语言的语义分析器。

    实验内容

    (1)语言确定:C-语言,其定义在《编译原理及实践》附录A中。也可选择其它语言,不过要有该语言的详细定义(可仿照C-语言)。一旦选定,不能更改,因为要在以后继续实现编译器的其它部分。鼓励自己定义一门语言。

    (2)完成C-语言的符号表的定义设计。规划类型检查/推论的实现方法。

    (3)仿照前面学习的语义分析器,编写选定语言的语义分析器。

    (4)准备2~3个测试用例,测试并解释程序的运行结果。

    声明

    因在2018-2019秋季学期中,湖南大学编译原理实验6和实验7(语义分析和代码生成)首次变成了必做实验(之前是选做,几乎没有学长学姐选),难度过大,这里的代码借鉴了git上的一些,不完全本人原创。

    源代码下载和使用

    https://pan.baidu.com/s/1FToQNdN0Li-R_-UiZPuLvQ

    密码:txkz

    注意,这一份代码运行在Linux系统下,作者的是Ubuntu 14.04。

    运行方法:首先在文件夹下执行make,然后会生成一个compiler文件,那个就是C-的编译器,执行命令:

    ./compiler -a -f  文件名

    即可完成其编译,当然包括语义分析。

    实验知识点讲解及源代码函数分析

    1、学习经典的语义分析器(TINY)

    TINY语言的语义分析器在loucomp文件夹下的analyze.c和symtab.c中,其对应的主要功能,analyse是用于语义分析本身,而symtab则是用于生成其对应的符号表。

    进入语义分析部分的代码,则在MAIN.c中:

    #if !NO_ANALYZE

    if (! Error)

    { if (TraceAnalyze) fprintf(listing,"\nBuilding Symbol Table...\n");

    buildSymtab(syntaxTree);

    if (TraceAnalyze) fprintf(listing,"\nChecking Types...\n");

    typeCheck(syntaxTree);

    if (TraceAnalyze) fprintf(listing,"\nType Checking Finished\n");

    }

    其中,如果要进入语义分析阶段并有分析输出,则需要设置NO ANALYZE标记位为1,这样主函数才能够调用到build_Symtab和typeCheck函数,从而完成语义分析的工作。

    下面,我们来分析语义分析器的输入输出,语义分析属于编译器前端的最后一部分,在此之前,编译器最开始的输入是一段代码,经过词法分析,输出的是词法单元,从而被语法分析单元所识别;语法分析的输入是一个个词法单元,通过分析这些词法单元之间的逻辑,利用递归下降等方法,形成一棵语法树,并将语法树的根结点存储在一个TreeNode类中,从而通过根结点就可以实现对于整个语法树的遍历(一般是前序遍历);之后,来到了语义分析部分,语义分析的输入是一个语法树,这里我们的输入是根结点;语义分析的输出,则是符号表和语法报错信息。

    符号表的意义在于,分析代码中所有的声明,比如变量函数等内容;而语法报错信息,则会通过语法树结点关系,检测相邻词法单元是否符合文法规则:比如,int 1和int a两种输入,在语法分析阶段均可通过,但是在语义分析阶段,int 1会被识别为一个错误,因为根据语法规则,int是一个声明,声明后面只能跟着一个变量名ID,而词法单元1的属性是NUM,int后面是不允许接着一个NUM的。这样的实现例子还有很多,具体需要看语言本身的定义。

    2、实现C Minus语言的语义分析器

    在本次实验的第二个任务中,要求实现一个C-语言的语义分析器,我们采用的是增量编程的方法,在TINY编译器上,结合C-的词法和语法特点,利用TINY编译器的现有构架,在此基础之上,完成C-的语义分析器。

    2.1  主函数部分(MAIN.C)

    while (getToken() != ENDOFFILE)

    {

    /* do nothing */

    }; //词法分析部分

    #else

    fprintf(listing, "*** Parsing source program...\n");

    syntaxTree = Parse(); //调用PARSE函数进行语法分析

    /* Tracing enabled?  Let's have it... */

    if (TraceParse)

    {

    fprintf(listing, "*** Dumping syntax tree\n");

    printTree(syntaxTree);

    };

    #if !NO_ANALYSE

    if (!Error) //开始进行语义分析

    {

    fprintf(listing, "*** Building symbol table...\n");

    buildSymbolTable(syntaxTree);

    fprintf(listing, "*** Performing type checking...\n");

    typeCheck(syntaxTree);

    }

    如上主函数部分,C-的语义分析和TINY一样,首先需要经过词法分析部分,之后声明一个语法树结点syntaxTree,对于该结点调用语法分析函数Parse,这样,语法树的根结点就被存储在syntaxTree这个根结点中,对它进行遍历就可以访问整个语法树;之后,也是需要将标记位ANALYSE设置为1,这样,会进入到语义分析的部分。

    在语义分析部分,我们调用了两个函数,一个是buildSymbolTable,这个函数用于构建符号表;另外一个是typeCheck,这个函数用于遍历语法树,检测词法单元之间的逻辑关系是否符合语法规则,然后输出语法错误的原因和出错地点(行数)。

    2.2  生成符号表(ANALYSE.C、SYMTABLE.C)

    buildSymboltable这个函数在ANALYSE.C中,其定义如下:

    void buildSymbolTable(TreeNode *syntaxTree)

    {

    /* Format headings */

    if (TraceAnalyse)

    {

    drawRuler(listing, "");

    fprintf(listing,"Scope Identifier Line Is a Symbol type\n");

    fprintf(listing,"depth Decl. parm?\n");

    fprintf(listing,"--------------------------------------------------\n");

    }

    declarePredefines();   /* make input() and output() visible in globals */

    buildSymbolTable2(syntaxTree);

    /* set the isGlobal field in appropriate nodes: needed in code generator */

    markGlobals(syntaxTree);

    /* Dump the global scope, if it's asked for */

    if (TraceAnalyse)

    {

    drawRuler(listing, "GLOBALS");

    dumpCurrentScope();

    drawRuler(listing, "");

    fprintf(listing, "*** Symbol table dump complete\n");

    }

    }

    可以看出,该函数主要有四个步骤:

    (1)该函数调用了drawruler函数,用于确定符号表的格式,为后期打印符号表做好准备。该函数的主要功能,是控制符号表的输出规模,规定一个统一的格式。

    (2)该函数调用了declarePredefines函数做了一个输入输出声明的预处理工作:

    该函数主要是为C-语言的I/O操作,即全局变量中的input函数和output函数创建符号表结点,并将其插入符号表。通过我们阅读C-语言的语法定义,可以了解到,一个标准的C-语言程序需要有输入和输出,而这样的程序IO操作即是通过input和output函数实现的,因此,C-程序中一定包含着input和output,因此,在预处理阶段,我们先将其加入到符号表中。

    以上两个环节,均为预处理阶段,下面则是正式的符号表生成操作:

    (3)该函数调用了buildSymboltable2函数,用于正式建立符号表。

    可以看出,该函数的输入是语法树根结点,即我们需要遍历整个语法树,来寻找何种词法单元结点,需要插入到符号表中。

    接下来涉及到了符号表的概念,符号表是整个代码中,所有声明过的变量和函数的记录者。比如在C-语言中,变量的声明只有一种,那就是INT,而函数的声明有两种,INT和VOID。

    因此,我们得到了一个结论,那就是只有结点类型为DECK(声明结点)这样的结点,才需要插入符号表中。在上一步语法分析中,我们已经得出了各个结点的类型,因此我们一边遍历语法树,一边判断当前遍历到的结点是不是属于一个声明:

    如果这个结点的类型是声明,那么就需要插入到符号表;而声明也分为两种,一种是变量的声明,一种是函数的声明;变量的声明比较好处理,直接插入到符号表中即可;而函数的声明处理,规则稍微多一些,因为函数的内部,也可以有一系列的变量声明,此时,如果结点检测到它自己输入声明结点,且声明类型为函数,那么就会调用之前提到过的drawruler函数,增加一张符号表,用于处理该函数内部的变量声明。

    对于非声明类结点,只需要略过即可,但是需要进行错误检测。

    这里调用了一个lookupSymbol函数,去查找当前结点的名字,这是因为,在其他语法树结点中,往往包含许多子结点,共同来实现一个功能,比如一个运算语句a+=1;这个语句中,a是否有被定义过呢?答案是不确定的,此时我们需要对于该结点的名字,也就是a,去到符号表中查找一下,是否存在之前某个地方定义过a,a是否已经在符号表中,如果a在之前没有被定义,说明这个语句是错误的,因为它应用了一个没有定义的词法单元,故可以输出错误信息。

    (4)该函数最后调用了dumpCurrentScope函数,将符号表打印出来。

    以上是对于建立符号表函数的分析。

    那么符号表是如何工作的呢?这里涉及到第三步中调用的insertSymbol函数:

    待插入符号表的信息,包括声明的变量的名字,该变量对应的树节点和该变量所出现的行数,其中行数的信息,已经在词法分析中完成存储了。

    insert操作,主要是两部分,第一步已经在上图中展示。它调用了一个symbol already declared函数,确认检测一下是否有同名变量的重复声明,如果该待插入符号表的变量名字,和已经在符号表中的一个元素名称相同,那么就会报错,比如在同一个函数中,不能重复声明两个一样的变量。

    第二部分则是通过了合法性判断,确认了该函数中无重复变量声明,因此就可以插入到符号表中,具体是通过数组模拟了一个哈希表,将符号的相关信息,比如名字,是否为函数的参数、以及出现的行数,记录到表中。

    至此,符号表建立函数分析完毕。

    2.3  检查语法错误(ANALYSE.C)

    在建立完符号表之后,语义分析的下一个任务是对于语法错误的检查,该部分的实现原理是,通过检查相邻语法树结点的词法属性,确保是符合常规的。

    具体的实现函数在ANALYSE.C中的type check函数:

    type check直接调用了一个遍历函数traverse,传入的三个参数,第一个是syntaxTree,代表语法树,第二个nullProc如下图,它是一个函数,代表遍历的终止条件,即遍历到叶子结点时,语法树遍历停止,第三个函数则是需要重点分析的checkNode,它制定了语法规则分析的过程。

    上图展示的是遍历结束条件nullProc。遍历到语法树的叶子结点即结束。

    下面是语法分析的规则函数check NODE:

    【提醒一下,语法分析这里的截图不全,因为结点类型太多了,可以对照上面链接的代码阅读】

    check node函数的工作原理是,一边遍历语法树,遍历到当前的一个结点之后,检查该结点的相邻结点是否符合语法规则,如果不符合就报错,如果符合就可以继续。

    上图展示的是声明结点的处理规则:如果该节点是声明结点,那么它有三种情况,第一种是声明了一个数字,那么它的下一个结点必须是数字;如果声明的是函数,那么结点类型必须是函数声明;如果声明的是数组,它的声明类型必须也是数组,这样对应起来。

    类似的例子还有很多。

    下面再举一个if语句的例子:上图中,语法树的结点是if,代表条件判断,其结点中必须要有数字,否则不符合if条件判断的规则,就会报错。

    其他的语句判断法则,根据C-语言的语法详细定义,由于该函数输入的是结点的语法类型,因此需要对于每种语法类型进行一一构造判断条件即可完成这一部分的内容。如果不符合条件,那么就会报错。这里通过的是switch语句进行条件判断,因此很容易确认程序中的语法犯了一个什么样的错误;另外,由于词法分析中已经确定了各个词法单元所出现在的行数,因此也能够很容易的定位到错误发生的位置。

    至此分析完毕,语义分析的过程结束。

    运行结果

    输入数据(Cminus语言)

    //A program to perform Euclid's algorithm to computer

    //the greatest common denominator (GCD) of a pair of integers.

    int gcd(int u, int v)

    {

    if (v == 0) return u ;

    else return gcd(v,u-u/v*v);

    /* u-u/v*v == u mod v */

    }

    void main(void)

    {   int x; int y;

    x = input(); y = input();

    output(gcd(x,y));

    }

    输出

    展开全文
  • 内含(何**班) 三次实验的代码(java)和报告(报告已经删去个人...实验三(语义分析):基于实验二给出的文法,给出SDD或SDT,编制语义分析程序,要求将错误信息输出到错误文件中,并输出输入程序对应的三地址码;
  • 编译原理 语义分析

    千次阅读 2021-05-23 20:40:41
    阅读语义分析程序并理解3.理解符号表的定义4.测试语义分析器(二)实现一门语言的语义分析器四、系统设计(C-语言的语义分析器)1.完成C-语言的符号表的定义设计。规划类型检查/推论的实现方法。1.1.文件结构1.2....
  • 用java语言编写的词法分析器、语法分析器和语义分析器,已经内置了静态的基本语言,通过文件读入代码,上传供各位学习交流使用。
  • 免费)编译原理语义分析实验报告,word
  • 语义分析代码

    2018-01-11 11:16:21
    1. 掌握自顶向下语义分析中语义子程序的添加过程; 2. 掌握“拉链”、“回填”操作的原理及实现; 3. 根据 MiniC 的上下文无关文法,对赋值语句、算术表达式、关系表达式、if-else 语句、while 语句、布尔表达式...
  • 编译程序原理与实现:第6章 语义分析(1).ppt
  • 在实现词法、语法分析程序的基础上,编写相应的语义子程序,进行语义处理,加深对语法制导翻译原理的理解,进一步掌握将语法分析所识别的语法范畴变换为某种中间代码(四元式)的语义分析方法,并完成相关语义分析器...
  • 按实习目的和要求,用PASCAL语言编写一个语法分析程序,同时考虑相应的数据结构。 ⑵ 调试 调试例子应包括符合语法规则的算术表达式,以及分析程序能够判别的若干错例。 ⑶ 输出 对于所输入的算术表达式,不论对错,...
  • 文章目录1 实验目的和内容1.1 实验目的1.2 实验内容1.3 实验要求2 设计思想2.1 语义规则2.2 递归下降翻译器2.3 递归下降子程序伪代码3 算法流程4 源程序5 调试数据5.1 测试...(2)掌握目前普遍采用的语义分析方法─
  • 实验三语义分析程序的设计与实现一、实验目的:加深对语法分析器工作过程的理解;能够采用一种编程语言实现简单的语义分析程序;能够使用自己编写的分析程序对简单的程序段进行语义分析,生成中间代码。二、实验内容...
  • 在语法树中去掉一些对翻译不必要的信息后,获得 的更有效的源程序的中间表示,这种经过变换后的语法 树称为抽象语法树。 内部结点代表操作符,它的孩子代表对应的操作数。 树表示法: 索引法: 2. 举例说明什么是DAG...
  • 编译原理实验六】语义分析

    千次阅读 2018-12-23 16:25:27
    (2)阅读语义分析程序,加上你自己的理解。尤其要求对相关函数与重要变量的作用与功能进行稍微详细的描述。若能加上学习心得则更好。TINY语言请参考《编译原理及实践》第6.5节;PL/0语言请参考相关实现文档。 ...
  • 第七章 语义分析和中间代码产生 本章感觉有点难,看得不太懂,谁有好的资料,欢迎分享; 按我的思路整理,重点如下: 三地址代码、四元式序列 适用于一遍扫描分析的回 填技术。 用回填技术实现布尔表达式和控制流...
  • 编译原理语义分析java实现

    热门讨论 2009-03-28 11:36:33
    编译原理语义分析java实现程序报告,由语法和此分析衍生而来,生成四元式输出

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,244
精华内容 15,697
关键字:

编译程序的语义分析输出