PL0编译器扩展
来源:互联网 发布:直通车重要数据 编辑:程序博客网 时间:2024/06/16 23:31
实验四 pl0编译器的扩展
一、实验目的和内容
1. 理解语法、语义分析程序为中心的单遍编译程序组织方法;
2. 理解编译程序的基本逻辑过程(词法分析、语法分析、语义分析及目标代码的生成;
3. 理解编译过程中的符号表、内存管理、错误处理的基本方法;
二、扩展要求(每项50分)
修改PL/0编译程序和类P-code解释程序的源代码,以支持对PL/0语言所进行的如下扩充,并调试通过:
1. 给PL/0语言增加带else子句的条件语句
<条件语句>::=IF <条件>THEN <语句>[ELSE <语句>]
问题:按照你的修改方式,如下PL/0代码执行后变量x的值是什么?你认为这样的结果是否合理?为什么?
- 增加参数
为函数或过程增加参数,实现传值方式。
PROCEDURE<过程名>(<形式参数表>);
或
FUNCTION<函数名>(<形式参数表>);
形式参数表的语法格式和变量声明完全一致。
/* * PL/0 complier program for win32 platform (implemented in C) * * The program has been test on Visual C++ 6.0, Visual C++.NET and * Visual C++.NET 2003, on Win98, WinNT, Win2000, WinXP and Win2003 * */typedef enum { false, true} bool;#define norw 14 /* 关键字个数 */#define txmax 100 /* 名字表容量 */#define nmax 14 /* number的最大位数 */#define al 10 /* 符号的最大长度 */#define amax 2047 /* 地址上界*/#define levmax 3 /* 最大允许过程嵌套声明层数 [0, levmax]*/#define cxmax 500 /* 最多的虚拟机代码数 *//* 符号 */enum symbol { nul, ident, number, plus, minus, times, slash, oddsym, eql, neq, lss, leq, gtr, geq, lparen, rparen, comma, semicolon, period, becomes, beginsym, endsym, ifsym, thensym, whilesym, writesym, readsym, dosym, callsym, constsym, varsym, procsym, elsesym};#define symnum 33/* 名字表中的类型 */enum object { constant, variable, procedur, parametre,//参数类};struct prod //临时记录语法分析时函数参数的个数和名字{ char id[al+1];};struct prod pnow[15];int prodn=0;/* 虚拟机代码 */enum fct { lit, opr, lod, sto, cal, inte, jmp, jpc,};#define fctnum 81/* 虚拟机代码结构 */struct instruction{ enum fct f; /* 虚拟机代码指令 */ int l; /* 引用层与声明层的层次差 */ int a; /* 根据f的不同而不同 */};FILE* fas; /* 输出名字表 */FILE* fa; /* 输出虚拟机代码 */FILE* fa1; /* 输出源文件及其各行对应的首地址 */FILE* fa2; /* 输出结果 */bool listswitch; /* 显示虚拟机代码与否 */bool tableswitch; /* 显示名字表与否 */char ch; /* 获取字符的缓冲区,getch 使用 */enum symbol sym; /* 当前的符号 */char id[al+1]; /* 当前ident, 多出的一个字节用于存放0 */int num; /* 当前number */int cc, ll; /* getch使用的计数器,cc表示当前字符(ch)的位置 */int cx; /* 虚拟机代码指针, 取值范围[0, cxmax-1]*/char line[81]; /* 读取行缓冲区 */char a[al+1]; /* 临时符号, 多出的一个字节用于存放0 */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使用 */ int n; /*参数个数*/};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 -1void 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);
/* * PL/0 complier program for win32 platform (implemented in C) * * The program has been test on Visual C++ 6.0, Visual C++.NET and * Visual C++.NET 2003, on Win98, WinNT, Win2000, WinXP and Win2003 * * 使用方法: * 运行后输入PL/0源程序文件? * 回答是否输出虚拟机代码 * 回答是否输出名字表 * fa.tmp输出虚拟机代码 * fa1.tmp输出源文件及其各行对应的首地址 * fa2.tmp输出结? * fas.tmp输出名字表 */#include <stdio.h>#include "pl0.h"#include "string.h"/* 解释执行时使用的栈 */#define stacksize 500int 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'); fa1 = fopen("fa1.tmp", "w"); fprintf(fa1,"Input 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"); return 0;}/** 初始化*/void init(){ int i; /* 设置单字符符号 */ for (i=0; i<=255; i++) { ssym[i] = nul; } 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]), "else");//增加保留字else strcpy(&(word[5][0]), "end"); strcpy(&(word[6][0]), "if"); strcpy(&(word[7][0]), "odd"); strcpy(&(word[8][0]), "procedure"); strcpy(&(word[9][0]), "read"); strcpy(&(word[10][0]), "then"); strcpy(&(word[11][0]), "var"); strcpy(&(word[12][0]), "while"); strcpy(&(word[13][0]), "write"); /* 设置保留字符号 */ wsym[0] = beginsym; wsym[1] = callsym; wsym[2] = constsym; wsym[3] = dosym; wsym[4] = elsesym; wsym[5] = endsym; wsym[6] = ifsym; wsym[7] = oddsym; wsym[8] = procsym; wsym[9] = readsym; wsym[10] = thensym; wsym[11] = varsym; wsym[12] = whilesym; wsym[13] = writesym; /* 设置指令名称 */ strcpy(&(mnemonic[lit][0]), "lit"); strcpy(&(mnemonic[opr][0]), "opr"); strcpy(&(mnemonic[lod][0]), "lod"); strcpy(&(mnemonic[sto][0]), "sto"); strcpy(&(mnemonic[cal][0]), "cal"); strcpy(&(mnemonic[inte][0]), "int"); strcpy(&(mnemonic[jmp][0]), "jmp"); strcpy(&(mnemonic[jpc][0]), "jpc"); /* 设置符号集 */ 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[81]; memset(space,32,81); space[cc-1]=0; //出错时当前符号已经读完,所以cc-1 printf("****%s!%d\n", space, n); fprintf(fa1,"****%s!%d\n", space, n); err++;}/** 漏掉空格,读取一个字符。** 每次读一行,存入line缓冲区,line被getsym取空后再读一行** 被函数getsym调用。*/int getch(){ if (cc == ll) { if (feof(fin)) { printf("program incomplete"); return -1; } ll=0; cc=0; printf("%d ", cx); fprintf(fa1,"%d ", cx); ch = ' '; while (ch != 10) { //fscanf(fin,"%c", &ch) //richard if (EOF == fscanf(fin,"%c", &ch)) { line[ll] = 0; break; } //end richard printf("%c", ch); fprintf(fa1, "%c", ch); line[ll] = ch; ll++; } printf("\n"); fprintf(fa1, "\n"); } ch = line[cc]; cc++; return 0;}/** 词法分析,获取一个符号*/int getsym(){ int i,j,k; /* the original version lacks "\r", thanks to foolevery */ while (ch==' ' || ch==10 || ch==13 || ch==9) /* 忽略空格、换行、回车和TAB */ { getchdo; } if (ch>='a' && ch<='z') { /* 名字或保留字以a..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') { /* 检测是否为数字:以0..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(30); } } 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]; /* 当符号不满足上述条件时,全部按照单字符符号处理 */ //getchdo; //richard 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; cx0=cx; gendo(jmp, 0, 0); table[tx].n=prodn; table[tx].size=dx+prodn; int j; for(j=0; j<prodn; j++) { tx++; strcpy(table[tx].name,pnow[j].id); table[tx].kind=variable; table[tx].level=lev; table[tx].size=1; table[tx].adr=dx; dx++; } if (lev > levmax) { error(32); } do { if (sym == constsym) /* 收到常量声明符号,开始处理常量声明 */ { getsymdo; /* the original do...while(sym == ident) is problematic, thanks to calculous */ /* 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; /* the original do...while(sym == ident) is problematic, thanks to calculous */ /* 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; prodn=0; if (sym == ident) { enter(procedur, &tx, lev, &dx); /* 记录过程名字 */ getsymdo; if(sym==lparen) { getsymdo; if(sym==varsym) { while(sym==varsym) { getsymdo; if(sym==ident) { strcpy(pnow[prodn].id,id); prodn++; getsymdo; if(sym==comma||sym==rparen) { getsymdo; } //getsymdo; else { error(5); } } else { error(5); } } } else if(sym==rparen) { getsymdo; } //getsymdo; else { error(5); } } } else { error(4); /* procedure后应为标识符 */ } getsymdo; if (sym == semicolon) { getsymdo; } else { error(5); /* 漏掉了分号 */ } //getsymdo; 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<=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 addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size); break; } } printf("\n"); } /* 语句后跟符号为分号或end */ memcpy(nxtlev, fsys, sizeof(bool)*symnum); /* 每个后跟符号集和都包含上层后跟符号集和,以便补救 */ nxtlev[semicolon] = true; nxtlev[endsym] = true; statementdo(nxtlev, &tx, lev); gendo(opr, 0, 0); /* 每个过程出口都要使用的释放数据段指令 */ memset(nxtlev, 0, sizeof(bool)*symnum); /*分程序没有补救集合 */ testdo(fsys, nxtlev, 8); /* 检测后跟符号正确性 */ listcode(cx0); /* 输出代码 */ return 0;}/** 在名字表中加入一项** k: 名字种类const,var or procedure* ptx: 名字表尾指针的指针,为了可以改变名字表尾指针的值* lev: 名字所在的层次,,以后所有的lev都是这样* pdx: dx为当前应分配的变量的相对地址,分配后要增加1*/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 > 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); /* var后应是标识 */ } return 0;}/** 输出目标代码清单*/void listcode(int cx0){ int i; if (listswitch) { for (i=cx0; i<cx; i++) { printf("%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a); fprintf(fa,"%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a); } }}/** 语句处理*/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(11); /* 变量未找到 */ } else { if(table[i].kind != variable) { error(12); /* 赋值语句格式错误 */ i = 0; } else { getsymdo; if(sym == becomes) { getsymdo; } else { error(13); /* 没有检测到赋值符号 */ } memcpy(nxtlev, fsys, sizeof(bool)*symnum); expressiondo(nxtlev, ptx, lev); /* 处理赋值符号右侧表达式 */ if(i != 0) { /* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */ gendo(sto, lev-table[i].level, table[i].adr); } } }//if (i == 0) } else { if (sym == readsym) /* 准备按照read语句处理 */ { getsymdo; if (sym != lparen) { error(34); /* 格式错误,应是左括号 */ } else { do { getsymdo; if (sym == ident) { i = position(id, *ptx); /* 查找要读的变量 */ } else { i=0; } if (i == 0) { error(35); /* read()中应是声明过的变量名 */ } else if (table[i].kind != variable) { error(32); /* read()参数表的标识符不是变量, thanks to amd */ } else { gendo(opr, 0, 16); /* 生成输入指令,读取值到栈顶 */ gendo(sto, lev-table[i].level, table[i].adr); /* 储存到变量 */ } getsymdo; } while (sym == comma); /* 一条read语句可读多个变量 */ } if(sym != rparen) { error(33); /* 格式错误,应是右括号 */ while (!inset(sym, fsys)) /* 出错补救,直到收到上层函数的后跟符号 */ { getsymdo; } } else { getsymdo; } } else { if (sym == writesym) /* 准备按照write语句处理,与read类似 */ { getsymdo; if (sym == lparen) { do { getsymdo; memcpy(nxtlev, fsys, sizeof(bool)*symnum); nxtlev[rparen] = true; nxtlev[comma] = true; /* write的后跟符号为) or , */ expressiondo(nxtlev, ptx, lev); /* 调用表达式处理,此处与read不同,read为给变量赋值 */ gendo(opr, 0, 14); /* 生成输出指令,输出栈顶的值 */ } while (sym == comma); if (sym != rparen) { error(33); /* write()中应为完整表达式 */ } else { getsymdo; } } gendo(opr, 0, 15); /* 输出换行 */ } else { if (sym == callsym) /* 准备按照call语句处理 */ { int k=0; getsymdo; if (sym != ident) { error(14); /* call后应为标识符 */ } else { i = position(id, *ptx); if (i == 0) { error(11); /* 过程未找到 */ } else { if (table[i].kind == procedur) { //gendo(cal, lev-table[i].level, table[i].adr); // 生成call指令 getsymdo; if(sym == lparen) { do { getsymdo; expressiondo(nxtlev,ptx,lev); k++; } while(sym==comma); if(sym==rparen) { getsymdo; } else { error(45); } } if(k!=table[i].n) { printf("参数个数不对"); error(46); } gendo(cal,lev-table[i].level,i); gen(opr,0,7); for(k=0; k<table[i].n; k++) { gen(opr,0,7); } } else { error(15); //call后标识符应为过程 } } } } 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 */ } cx1 = cx; /* 保存当前指令地址 */ gendo(jpc, 0, 0); /* 生成条件跳转指令,跳转地址未知,暂时写0 */ statementdo(fsys, ptx, lev); /* 处理then后的语句 */ cx2=cx; gendo(jmp,0,0); //code[cx1].a=cx; if(sym==elsesym) { getsymdo; //cx1=cx; code[cx1].a=cx;//出现else则可回填if的假链 statementdo(fsys,ptx,lev);//处理else后的语句,处理完则cx也会改变 code[cx2].a=cx;//回填else的跳转地址 }//add-end else { code[cx1].a = cx; //经statement处理后,cx为then后语句执行完的位置,它正是前面未定的跳转地址 code[cx2].a = cx; } } 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(10); /* 缺少分号 */ } statementdo(nxtlev, ptx, lev); } if(sym == endsym) { getsymdo; } else { error(17); /* 缺少end或分号 */ } } else { if (sym == whilesym) /* 准备按照while语句处理 */ { cx1 = cx; /* 保存判断条件操作的位置 */ getsymdo; memcpy(nxtlev, fsys, sizeof(bool)*symnum); nxtlev[dosym] = true; /* 后跟符号为do */ conditiondo(nxtlev, ptx, lev); /* 调用条件处理 */ cx2 = cx; /* 保存循环体的结束的下一个位置 */ gendo(jpc, 0, 0); /* 生成条件跳转,但跳出循环的地址未知 */ if (sym == dosym) { getsymdo; } else { error(18); /* 缺少do */ } statementdo(fsys, ptx, lev); /* 循环体 */ gendo(jmp, 0, cx1); /* 回头重新判断条件 */ code[cx2].a = cx; /* 反填跳出循环的地址,与if类似 */ } else { memset(nxtlev, 0, sizeof(bool)*symnum); /* 语句结束无补救集合 */ testdo(fsys, nxtlev, 19); /* 检测语句结束的正确性 */ } } } } } } } 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); /* 处理项 */ if (addop == minus) { gendo(opr,0,1); /* 如果开头为负号生成取负指令 */ } } 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); /* 处理项 */ if (addop == plus) { gendo(opr, 0, 2); /* 生成加法指令 */ } else { gendo(opr, 0, 3); /* 生成减法指令 */ } } 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); if(mulop == times) { gendo(opr, 0, 4); /* 生成乘法指令 */ } else { gendo(opr, 0, 5); /* 生成除法指令 */ } } return 0;}/** 因子处理*/int factor(bool* fsys, int* ptx, int lev){ int i; bool nxtlev[symnum]; testdo(facbegsys, fsys, 24); /* 检测因子的开始符号 */ /* while(inset(sym, facbegsys)) */ /* 循环直到不是因子开始符号 */ if(inset(sym,facbegsys)) /* BUG: 原来的方法var1(var2+var3)会被错误识别为因子 */ { if(sym == ident) /* 因子为常量或变量 */ { i = position(id, *ptx); /* 查找名字 */ if (i == 0) { error(11); /* 标识符未声明 */ } else { switch (table[i].kind) { case constant: /* 名字为常量 */ gendo(lit, 0, table[i].val); /* 直接把常量的值入栈 */ break; case variable: /* 名字为变量 */ gendo(lod, lev-table[i].level, table[i].adr); /* 找到变量地址并将其值入栈 */ break; case procedur: /* 名字为过程 */ error(21); /* 不能为过程 */ break; } } getsymdo; } else { if(sym == number) /* 因子为数 */ { if (num > 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"); 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 7: //返回参数弹出 t--; 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 11: 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] <= s[t]); break; case 14: printf("%d", s[t-1]); fprintf(fa2, "%d", s[t-1]); t--; break; case 15: printf("\n"); fprintf(fa2,"\n"); break; case 16: printf("?"); fprintf(fa2, "?"); scanf("%d", &(s[t])); fprintf(fa2, "%d\n", s[t]); t++; break; } break; case lod: /* 取相对当前过程的数据基地址为a的内存的值到栈顶 */ s[t] = s[base(i.l,s,b)+i.a]; t++; break; case sto: /* 栈顶的值存到相对当前过程的数据基地址为a的内存 */ t--; s[base(i.l, s, b) + i.a] = s[t]; break; case cal: /* 调用子过程 */ s[t] = base(i.l, s, b); // 将父过程基地址入栈 s[t+1] = b; // 将本过程基地址入栈,此两项用于base函数 s[t+2] = p; //将当前指令指针入栈 int w=0; for( w=0; w<table[i.a].n; w++) { s[t+3+w]=s[t-table[i.a].n+w]; } b = t; // 改变基地址指针值为新过程的基地址 p = i.a; //跳转 break; case inte: /* 分配内存 */ t += i.a; break; case jmp: /* 直接跳转 */ p = i.a; break; case jpc: /* 条件跳转 */ t--; if (s[t] == 0) { p = i.a; } break; } } while (p != 0);}/* 通过过程基址求上l层过程的基址 */int base(int l, int* s, int b){ int b1; b1 = b; while (l > 0) { b1 = s[b1]; l--; } return b1;}
阅读全文
0 0
- PL0编译器扩展
- PL0编译器分析与语法扩展
- PL0编译器TurboPascal版再现
- PL0文法编译器C语言源代码
- PL0扩展(数组,if-else,注释)
- 用C#实现的简单PL0 to C 编译器
- PL0程序设计语言
- pl0笔记
- 编译器进行符号扩展
- GCC编译器扩展支持
- unity 扩展编译器
- 编译器的三个扩展
- PL0编译器TurboPascal版再现时间:2009-07-20 17:24:49来源:网络 作者:未知 点击:52次
- 学习使用的PL/0编译器增强版PL/0plusplusCompiler(四)使用gdb调试PL0词法分析
- 浮点数,编译器位扩展
- C51编译器的扩展关键字
- PL/0编译器设计扩展
- ARM编译器的扩展关键字
- 手动获取截图和uix文件到 UI Automator Viewer 解析
- 安卓 发布平台 汇总
- 博弈—SG
- NOIP 提高组 2004
- Hibernate07_映射关系-MangToOne
- PL0编译器扩展
- Unity3D中生命周期函数的应用
- 1.8 字符指针和字符串处理
- AC自动机详解
- 执行 java -jar xxx.jar 是不是创建进程问题
- Farm Irrigation
- windows bat 命令
- POJ 3690 Constellations 哈希表 矩阵匹配
- 如何使用timeline profile tensorflow 程序