编译原理 实验4 语义分析(基于PL/0,使用C++代码编写)

您所在的位置:网站首页 英语逻辑错误类型12种 编译原理 实验4 语义分析(基于PL/0,使用C++代码编写)

编译原理 实验4 语义分析(基于PL/0,使用C++代码编写)

2023-06-29 03:53| 来源: 网络整理| 查看: 265

文章目录 1 实验任务2 实验内容3 错误类型声明4 文件结构与代码4.1 代码结构4.2 详细代码4.3 递归下降子程序的声明 5 常变量说明6 运行结果

1 实验任务

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

2 实验内容

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

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

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

3 错误类型声明

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

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

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

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

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

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

4.1 代码结构

在这里插入图片描述

4.2 详细代码

pl0.h:

#include # 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 #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 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 int i; for (i = 0; i int i; for (i = 0; i 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 if (k k = (i + j) / 2; if (strcmp(id, word[k]) i = k + 1; } } while (i j) { sym = wsym[k]; } else { sym = ident; } } else { if (ch >= '0'&&ch num = 10 * num + ch - '0'; k++; getchdo; } while (ch >= '0'&&ch nmax) { error(14); } } else { if (ch == ':') // 检测赋值符号 { getchdo; if (ch == '=') { sym = becomes; getchdo; } else { sym = nul; // 不能识别的符号 } } else { if (ch == '


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3