PL/0实验与课设(C语言),增加else、repeat until、for step until do等语句和一些双运算符、注释符
实验部分
实验内容:
1、改单词:不等号# 改为 != ,只有!符号为非法单词,同时#成为非法符号。
2、增加单词(只实现词法分析部分):
保留字 ELSE,FOR,REPEAT, UNTIL,TYPEDEF,STEP
运算符 *=,/=,+=, -=
注释符 //,/* ,*/
3、增加条件语句的ELSE子句(实现语法语义目标代码)。要求:写出相关文法或语法图,分析语义规则的实现。
4、增加 repeat-until语句(实现语法语义目标代码)。要求:写出相关文法或语法图,分析语义规则的实现。
一共就两个文件,一个pl0.h,一个PL0.cpp,代码在文章最后,整个工程文件我也会上传,大家自取!
测试结果
内含多个测试文件,文件名均为拼音。如不等号budenghao,注释符zhushifu等。
1、改单词:不等号# 改为 != ,只有!符号为非法单词,同时#成为非法符号。
2、增加单词(只实现词法分析部分):
由于后面增加语句会使用到下面的保留字,因此不特别展示,仅展示运算符和注释符的测试效果。并且课设会要求增加运算符和注释符的真正功能,因此在此处一起展示完成语法语义后的效果,课设部分则不在重复展示。
保留字 ELSE,FOR,REPEAT, UNTIL,TYPEDEF,STEP
运算符 *=,/=,+=, -=
注释符 //,/* ,*/
3、增加条件语句的ELSE子句(实现语法语义目标代码)。要求:写出相关文法或语法图,分析语义规则的实现。
语法图:
4、增加 repeat-until语句(实现语法语义目标代码)。要求:写出相关文法或语法图,分析语义规则的实现。
语法图:
var x;
begin
x:=1;
repeat
begin
x:=x+1;
end
until x>10;
write(x);
end.
课设部分
课设内容:
1、扩充赋值运算:*=,/=,+=, -=。
2、增加注释:2种注释,// 和/* */
3、扩充语句(Pascal的FOR语句):
for := step until do
测试结果
1和2在实验部分已经展示过效果,此处不在重复展示。
3、for语句
感想:
写这篇博客的目的是在于帮助广大计算机同学们完成这个无聊但是又不得不做的实验和课设,本人当时距离实验课设的提交时间还剩3天,离编译原理考试仅剩2天,我却几乎没学过这门课。本想着能不能问其他同学借一份来参考一下没想到一个个都不愿意,真的是无语。一气之下打算自己完成。然后就是在网络上到处寻找相关的博客资料,结果不是没有就是要钱,真的很烦,特别是这个for语句,全网搜索都找不到免费的,因此万般无奈只好自己亲自操刀。但是我几乎都没学过,就只能看着代码自己慢慢想他是什么道理,虽然最后也没想明白,但看规律也基本看出了点门道,就自己试了下写step步骤的代码,随便试了一下发现居然没问题,当时我激动的不行!测试了好多个例子,全都成功了。不过也许有一些特例可能会无法通过,但是不故意找茬我想应该是没有问题的。毕竟只是为了完成他这个任务而已,没必要跟他较真。整个实验和课设花了我整整一天时间,爆肝。最后只剩下一天的时间来复习编译原理。以后我也会陆续更新一些其他课程的实验和课设,如操作系统、数据库、Linux等,希望对大家有所帮助,让大家在期末的时候不再苦恼于这些垃圾实验和课设,不想大家一搜不是要钱就是VIP,我真的是很烦!
实验部分完整代码
pl0.h
/*PL/0 编译系统C版本头文件 pl0.h*/ # define norw 19 /*关键字个数*/ # define txmax 100 /*名字表容量*/ # define nmax 14 /*number的最大位数*/ # define al 10 /*符号的最大长度*/ # define amax 2047 /*地址上界*/ # define levmax 3 /*最大允许过程嵌套声明层数[0,lexmax]*/ # define cxmax 200 /*最多的虚拟机代码数*/ /*符号*/ enum symbol { nul, ident, number, plus, minus, times, slash, oddsym, eql, neq, pluseql, minuseql, timeseql, slasheql, lss, leq, gtr, geq, lparen, rparen, comma, semicolon, exclamatorymark, period, becomes, beginsym, endsym, ifsym, thensym, whilesym, writesym, readsym, dosym, callsym, constsym, varsym, procsym, repeatsym, untilsym, elsesym, forsym, typedefsym, stepsym }; #define symnum 43 /*-------------*/ enum object{ constant, variable, procedur, }; /*--------------*/ enum fct{ lit, opr, lod, sto, cal, inte, jmp, jpc, }; #define fctnum 8 /*--------------*/ struct instruction { enum fct f; int l; int a; }; FILE * fas; FILE * fa; FILE * fa1; FILE * fa2; bool tableswitch; bool listswitch; char ch; enum symbol sym; char id[al+1]; int num; int cc,ll; int cx; char line[81]; char a[al+1]; struct instruction code[cxmax]; char word[norw][al]; enum symbol wsym[norw]; enum symbol ssym[256]; char mnemonic[fctnum][5]; bool declbegsys[symnum]; bool statbegsys[symnum]; bool facbegsys[symnum]; /*------------------------------*/ struct tablestruct { char name[al]; /*名字*/ enum object kind; /*类型:const,var,array or procedure*/ int val; /*数值,仅const使用*/ int level; /*所处层,仅const不使用*/ int adr; /*地址,仅const不使用*/ int size; /*需要分配的数据区空间,仅procedure使用*/ }; struct tablestruct table[txmax]; /*名字表*/ FILE * fin; FILE* fout; char fname[al]; int err; /*错误计数器*/ /*当函数中会发生fatal error时,返回-1告知调用它的函数,最终退出程序*/ #define getsymdo if(-1==getsym())return -1 #define getchdo if(-1==getch())return -1 #define testdo(a,b,c) if(-1==test(a,b,c))return -1 #define gendo(a,b,c) if(-1==gen(a,b,c))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 gen(enum fct x,int y,int z); int test(bool*s1,bool*s2,int n); 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); void interpret(); 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); void listcode(int cx0); 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); int base(int l,int* s,int b);
PL0.cpp
//A.2 C 版 本 /*编译和运行环境: *1Visual C++6.0,VisualC++.NET and Visual C++.NET 2003 *WinNT, Win 200, WinXP and Win2003 *2 gcc version 3.3.2 20031022(Red Hat Linux 3.3.2-1) *Redhat Fedora core 1 *Intel 32 platform *使用方法: *运行后输入PL/0 源程序文件名 *回答是否输出虚拟机代码 *回答是否输出名字表 *fa.tmp 输出虚拟机代码 *fa1.tmp 输出源文件及其各行对应的首地址 *fa2.tmp 输出结果 *fas.tmp 输出名字表 */ #define _CRT_SECURE_NO_WARNINGS #include #include #include #include"pl0.h" #include"string.h" /*解释执行时使用的栈*/ #define stacksize 500 int main() { bool nxtlev[symnum]; printf("Input pl/0 file ?"); scanf("%s", fname); /*输入文件名*/ fin = fopen(fname, "r"); if (fin) { //printf("List object code ?(Y/N)"); /*是否输出虚拟机代码*/ //scanf("%s", fname); //listswitch = (fname[0] == 'y' || fname[0] == 'Y'); //printf("List symbol table ? (Y/N)"); /*是否输出名字表*/ //scanf("%s", fname); //tableswitch = (fname[0] == 'y' || fname[0] == 'Y'); listswitch = (fname[0] == 'y'); fa1 = fopen("fa1.tmp", "w"); fprintf(fa1, "Iput pl/0 file ?"); fprintf(fa1, "%s\n", fname); init(); /*初始化*/ err = 0; cc = cx = ll = 0; ch = ' '; if (-1 != getsym()) { fa = fopen("fa.tmp", "w"); fas = fopen("fas.tmp", "w"); addset(nxtlev, declbegsys, statbegsys, symnum); nxtlev[period] = true; if (-1 == block(0, 0, nxtlev)) /*调用编译程序*/ { fclose(fa); fclose(fa1); fclose(fas); fclose(fin); printf("\n"); return 0; } fclose(fa); fclose(fa1); fclose(fas); if (sym != period) { error(9); } if (err == 0) { fa2 = fopen("fa2.tmp", "w"); interpret(); fclose(fa2); } else { printf("Errors in pl/0 program"); } } fclose(fin); } else { printf("Can't open file! \n"); } printf("\n"); system("pause"); return 0; } /* *初始化 */ void init() { int i; for (i = 0; i = 'a' && ch = 'a' && ch = '0' && ch = '0' && ch = '0' && ch nmax) { error(30); } } else { if (ch == ':') /*检测赋值符号*/ {//状态6 getchdo; if (ch == '=')//输入:之后又输出=,则识别为赋值符号 {//状态7 sym = becomes; getchdo; } else { sym = nul; /*不能识别的符号*/ } } else { if (ch == '') /*检测大于或大于等于符号*/ {//状态11 getchdo; if (ch == '=')//如果 > 之后又有 = ,则识别为>= { sym = geq; getchdo; } 否则识别为 > else {//状态13 sym = gtr; } } else { if (ch == '!') { getchdo; if (ch == '=') { sym = neq; getchdo; } else { printf(" ! 是非法字符\n"); system(0); } } else { if (ch == '+') //出现+ { getchdo; if (ch == '=') { sym = pluseql;//识别为+= getchdo; } else { sym = plus;//识别为+ } } else { if (ch == '-') //出现- { getchdo; if (ch == '=') { sym = minuseql;//识别为-= getchdo; } else { sym = minus;//识别为- } } else { if (ch == '*') //出现* { getchdo; if (ch == '=') { sym = timeseql;//识别为*= getchdo; } else { sym = times;//识别为* } } else { if (ch == '/') //出现/ { getchdo; if (ch == '=') { sym = slasheql;//识别为/= getchdo; } else { if (ch == '*')//出现/* { getchdo; i = ch; while (i != '*' || ch != '/') { i = ch; getchdo; } if (i != '*' && ch != '/') error(19); else { getchdo; getsymdo; } } else { if (ch == '/')//出现// { i = cx; while (cc != ll) { getchdo; } getsymdo; } else { sym = slash;//识别为/除号 } } } } else { //状态14 sym = ssym[ch];/* 当符号不满足上述条件时,全部按照单字符号处理*/ //getchdo; //richard //printf("输入的单字符为:%c\n", ch); if (sym != period) { getchdo; } //end richard } } } } } } } } } } return 0; } /* *生成虚拟机代码 * *x:instruction.f; *y:instruction.l; *z:instruction.a; */ int gen(enum fct x, int y, int z) { if (cx >= cxmax) { printf("Program too long"); /*程序过长*/ return -1; } code[cx].f = x; code[cx].l = y; code[cx].a = z; cx++; return 0; } /* *测试当前符号是否合法 * *在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集合 *(该部分的后跟符号) test 负责这项检测,并且负责当检测不通过时的补救措施 *程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟 *符号),以及不通过时的错误号 * *S1:我们需要的符号 *s2:如果不是我们需要的,则需要一个补救用的集合 *n:错误号 */ int test(bool* s1, bool* s2, int n) { if (!inset(sym, s1)) { error(n); /*当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合*/ while ((!inset(sym, s1)) && (!inset(sym, s2))) { getsymdo; } } return 0; } /* *编译程序主体 * *lev:当前分程序所在层 *tx:名字表当前尾指针 *fsys:当前模块后跟符号集合 */ int block(int lev, int tx, bool* fsys) { int i; int dx; /*名字分配到的相对地址*/ int tx0; /*保留初始tx*/ int cx0; /*保留初始cx*/ bool nxtlev[symnum]; /*在下级函数的参数中,符号集合均为值参,但由于使用数组 实现,传递进来的是指针,为防止下级函数改变上级函数的 集合,开辟新的空间传递给下级函数*/ dx = 3; tx0 = tx; /*记录本层名字的初始位置*/ table[tx].adr = cx; gendo(jmp, 0, 0); if (lev > levmax) { error(32); } 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(5); /*漏掉了逗号或者分号*/ } } 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(5); } } while (sym == ident); } while (sym == procsym)/*收到过程声名符号,开始处理过程声名*/ { getsymdo; if (sym == ident) { enter(procedur, &tx, lev, &dx);/*记录过程名字*/ getsymdo; } else { error(4);/*procedure后应为标识符*/ } if (sym == semicolon) { getsymdo; } else { error(5);/*漏掉了分号*/ } 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; testdo(nxtlev, fsys, 6); } else { error(5); /*漏掉了分号*/ } } memcpy(nxtlev, statbegsys, sizeof(bool) * symnum); nxtlev[ident] = true; nxtlev[period] = true; testdo(nxtlev, declbegsys, 7); } while (inset(sym, declbegsys)); /*直到没有声明符号*/ code[table[tx0].adr].a = cx; /*开始生成当前过程代码*/ table[tx0].adr = cx; /*当前过程代码地址*/ table[tx0].size = dx; /*声明部分中每增加一条声明都会给dx增加1,声明部分已经结束,dx就是当前过程数据的size*/ cx0 = cx; gendo(inte, 0, dx); /*生成分配内存代码*/ if (tableswitch) /*输出名字表*/ { printf("TABLE:\n"); if (tx0 + 1 > tx) { printf("NULL\n"); } for (i = tx0 + 1; i amax) { error(31); 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; } } /* *查找名字的位置 *找到则返回在名字表中的位置,否则返回0 * *idt: 要查找的名字 *tx::当前名字表尾指针 */ 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; } /* *常量声明处理 */ int constdeclaration(int* ptx, int lev, int* pdx) { if (sym == ident) { getsymdo; if (sym == eql || sym == becomes) { if (sym == becomes) { error(1); /*把=写出成了:=*/ } getsymdo; if (sym == number) { enter(constant, ptx, lev, pdx); getsymdo; } else { error(2); /*常量说明=后应是数字*/ } } else { error(3); /*常量说明标识后应是=*/ } } else { error(4); /*const后应是标识*/ } return 0; } /* * */ int vardeclaration(int* ptx, int lev, int* pdx) { if (sym == ident) { enter(variable, ptx, lev, pdx);//填写名字表 getsymdo; } else { error(4); } return 0; } /* *输入目标代码清单 */ void listcode(int cx0) { int i; if (listswitch) { for (i = cx0; i amax) { error(31); num = 0; } gendo(lit, 0, num); 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); /*缺少右括号*/ } } testdo(fsys, facbegsys, 23); /*银子后有非法符号*/ } } } 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); gendo(opr, 0, 6); /*生成odd指令*/ } 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); switch (relop) { case eql: gendo(opr, 0, 8); break; case neq: gendo(opr, 0, 9); break; case lss: gendo(opr, 0, 10); break; case geq: gendo(opr, 0, 11); break; case gtr: gendo(opr, 0, 12); break; case leq: gendo(opr, 0, 13); break; } } } return 0; } /*解释程序*/ void interpret() { int p, b, t; /*指令指针,指令基址,栈顶指针*/ struct instruction i; /*存放当前指令*/ int s[stacksize]; /*栈*/ printf("start PL0\n\n"); t = 0; b = 0; p = 0; s[0] = s[1] = s[2] = 0; do { i = code[p]; /*读当前指令*/ p++; switch (i.f) { case lit: /*将a的值取到栈顶*/ s[t] = i.a; t++; break; case opr: /*数字、逻辑运算*/ switch (i.a) { case 0: t = b; p = s[t + 2]; b = s[t + 1]; break; case 1: s[t - 1] = -s[t - 1]; break; case 2: t--; s[t - 1] = s[t - 1] + s[t]; break; case 3: t--; s[t - 1] = s[t - 1] - s[t]; break; case 4: t--; s[t - 1] = s[t - 1] * s[t]; break; case 5: t--; s[t - 1] = s[t - 1] / s[t]; break; case 6: s[t - 1] = s[t - 1] % 2; break; case 8: t--; s[t - 1] = (s[t - 1] == s[t]); break; case 9: t--; s[t - 1] = (s[t - 1] != s[t]); break; case 10: t--; s[t - 1] = (s[t - 1] = s[t]); break; case 12: t--; s[t - 1] = (s[t - 1] > s[t]); break; case 13: t--; s[t - 1] = (s[t - 1] 0) { b1 = s[b1]; l--; } return b1; }