PL0源代码(C语言版)
/*PL/0 编译系统C版本头文件 pl0.h*/ # define norw 13 //a number of reserved word /*关键字个数*/ # define txmax 100 //length of identifier table /*名字表容量*/ # define nmax 14 //max number of digits in numbers /*number的最大位数*/ # define al 10 //length of identifier /*符号的最大长度*/ # define amax 2047 //maximum address /*地址上界*/ # define levmax 3 //max depth of block nesting /*最大允许过程嵌套声明层数[0,lexmax]*/ # define cxmax 200 //size of code array /*最多的虚拟机代码数*/ /*符号*/ 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, }; /*--------------*/ enum fct{ //fct类型分别标识类PCODE的各条指令 lit, opr, lod, sto, cal, inte, jmp, jpc, //书本P23 }; #define fctnum 8 /*--------------*/ struct instruction //指令 { enum fct f; //功能码 int l; //层次差 int a; //P23 };
FILE * fas; //输出名字表 FILE * fa; //输出虚拟机代码 FILE * fa1; //输出源文件及其各行对应的首地址 FILE * fa2; //输出结果
bool tableswitch; //显示名字表与否 bool listswitch; //显示虚拟机代码与否 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]; //表示声明开始的符号集合 ,declaring begin symbol set bool statbegsys[symnum]; //表示语句开始的符号集 , statement bool facbegsys[symnum]; //表示因子开始的符号集合 ,factor /*------------------------------*/
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; //fin文本文件用于指向输入的源程序文件 FILE* fout; //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); //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 输出名字表 */ #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"); //返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中 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,"Iput pl/0 file ?"); fprintf(fa1,"%s\n", fname); init(); /*初始化*/ err=0; //错误计数器置0 cc=cx=ll=0; ch=' ';
PL0编译程序源程序
procedure constdeclaration; (*常量定义的处理*) begin if sym = ident then begin getsym; if sym in [eql,becomes] then begin if sym = becomes then error(1); getsym; if sym = number then begin enter(constant); getsym; end else error(2) end else error(3) end else error(4); end; (*constdeclaration 结束*)
until kk = k; id:= a; i:= 1; (*二分法查保留字表*) j:= norw; repeat k:= (i+j) div 2; if id <= word[k] then j:= k-1; if id >= word[k] then i:= k+1 until i>j; if i-1 > j then sym:= wsym[k] else sym:= ident; end else if ch in ['0'..'9'] then begin (*number*) k:= 0; num:= 0; sym:= number; repeat num:= 10 * num+(ord(ch)-ord('0')); k:= k+1; getch until not (ch in ['0'..'9']); if k > nmax then error(30); end else if ch = ':' then begin getch; if ch = '=' then begin sym:= becomes; getch; end else sym:= nul; end else (*<=*)
PL0语言编译程序C++面向对象实现
PL0语言编译程序C++面向对象实现类关系:源码:头文件Lexa.h(词法分析)#ifndef LEXA_H#define LEXA_H#include <fstream>#include "sysdef.h"#include <iostream>using namespace std;// 词法符号struct Symbol{int kind;int value;char str[20];};// 词法分析const int BufferSize = 160; // 双缓冲,每一个大小为80 class Buffer{public:Buffer();void putback();bool Getch(char&);void init();private:ifstream in;char data[BufferSize];int curPos;bool last;// 是否是最后一次缓冲int lastIndex;friend class LexA;};class LexA{public:LexA(char* filename = NULL);bool Open(char* filename);bool GetSym();inline bool IsSpace(char);inline bool IsNumber(char);inline bool IsChar(char);inline bool IsOperator(char);inline bool IsSeparator(char);/*debuginline int GetType(){return V alue.kind;}inline int GetV alue(){return V alue.value;}*/private:Symbol V alue;Buffer buf;friend class SynA;};bool LexA::IsChar(char ch){return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); }bool LexA::IsNumber(char ch){return (ch >= '0' && ch <= '9');}bool LexA::IsOperator(char ch){return (ch =='+' || ch == '-' || ch == '*' || ch == '/'|| ch == '>' || ch == '<' || ch == '=' || ch == '#' || ch == ':'); }bool LexA::IsSeparator(char ch){return (ch == '.' || ch == ',' || ch == ';' || ch == '(' || ch == ')'); }bool LexA::IsSpace(char ch){return (ch == ' ' || ch == '\t' || ch == '\n');}#endifSyna.h(语法分析)#ifndef SYNA_H#define SYNA_H#include "sysdef.h"#include "lexa.h"// 错误处理类struct ErrorItem{int id;int hang;int lie;};const int MaxErrorNum = 100;class Error{public:Error(){ErrorNum = 0;}int Add(int id, int hang, int lie = 0){if(ErrorNum < MaxErrorNum){error[ErrorNum].id = id;error[ErrorNum].hang = hang;error[ErrorNum++].lie = lie;}else return -1;return 1;}void OutPut(){for(int i = 0; i <= ErrorNum-1; i++)cout << error[i].id << endl;}private:ErrorItem error[MaxErrorNum];int ErrorNum;// 语法分析需要处理错误,表项管理把错误编号返回friend class SynA;};const int MAXNAMELEN = 30;struct tableItem{char name[MAXNAMELEN];int kind;int value_lev;int adr;int size;};const int TableSize = 100;class Table{public:Table():tx(0), lev(0){}int enter(tableItem&);int find(Symbol& sym, int& result);int ChangeLev(int dir);void List(){cout << "变量名\t类型\t值或者层\t地址" << endl;for(int i = 0; i <= tx-1; i++){cout << data[i].name << '\t';if(data[i].kind == V ARIABLE){cout << "variable" << '\t' << data[i].value_lev << '\t' <<data[i].adr;}else if(data[i].kind == CONSTANT){cout << "constant" << '\t' << data[i].value_lev;}else{cout << "procedure" << '\t' << data[i].value_lev << '\t' << data[i].adr;}cout << endl;}}private:tableItem data[TableSize];// tx为即将填入的地方int tx, lev;friend class SynA;};// 语法分析struct Code{int fun;int lev;int a;};struct Buff{Buff(){full = false;};bool full;Symbol sym;};const char FUN[8][4] = {"LIT", "LOD", "STO", "CAL", "INT", "JMP", "JPC", "OPR"};class SynA{public:SynA(char* filename);bool GetSym(Symbol& sym);int block();int program(int& start);int constProcess();int varProcess();int procedureProcess();int Statement();int Condition();int Expression();int Item();int Factor();void ListCode(){for(int i = 0; i <= cx-1; i++){cout << i << '\t' << FUN[code[i].fun-60] << ' ' << code[i].lev << ' ' << code[i].a <<endl;}}void ListError(){err.OutPut();}void ListTable(){table.List();}private:LexA lex;int cx, dx; //当前代码行数Table table;tableItem tbItem;Code code[100];Error err;Buff buf;};#endifCpp文件lexa.cpp#include "lexa.h"// 关键字表const int KeyWordNum = 13;struct KeyWord{char name[10];int ID;};KeyWord KeyWordTable[KeyWordNum] = {{"const", CONST}, {"var", V AR}, {"procedure", PROCEDURE}, {"call", CALL}, {"begin", BEGIN}, {"end", END},{"if", IF}, {"then", THEN}, {"while", WHILE},{"do", DO}, {"read", READ}, {"write", WRITE},{"odd", ODD}};// 运算符表const int OperatorNum = 11;struct Operator{char name[3];int ID;};Operator OperatorTable[OperatorNum]= {{"+", PLUS}, {"-", MINUS}, {"*", TIME}, {"/", DEVIDE},{">", GREA TER}, {"<", SMALLER}, {"=", EQUAL}, {"#", UNEQUAL}, {">=", GE}, {"<=", SE}, {":=", EQUAL1}};//分隔符表const int SeparatorNum = 5;struct Separator{char name;int ID;};Separator SeparatorTable[SeparatorNum] = {{'.', PERIOD}, {',', COMMA}, {';', SEMICOLON},{'(', LPARENTHESIS}, {')', RPARENTHESIS}};Buffer::Buffer(){last = false;}// 遇到文件尾,返回falsebool Buffer::Getch(char& ch){if(last){if(curPos == lastIndex) return false;ch = data[curPos++];curPos %= BufferSize;return true;}if(BufferSize/2+1 == curPos && !last){if(!in.read(data, BufferSize/2)){lastIndex = in.gcount();last = true;}}if(curPos == 1 && !last){if(!in.read(data+BufferSize/2, BufferSize/2)){lastIndex = in.gcount()+BufferSize/2;last = true;}}ch = data[curPos++];curPos %= BufferSize;return true;}void Buffer::putback(){if(curPos == 0) curPos = BufferSize-1;else{curPos -= 1;curPos %= BufferSize;}}void Buffer::init(){if(!in.read(data, BufferSize/2)){last = true;lastIndex = in.gcount();}curPos = 0;}LexA::LexA(char* filename){if(filename){buf.in.open(filename);if(buf.in.fail()){cout << "Can't open file:" << filename << endl;return;}buf.init();}}bool LexA::Open(char* filename){buf.in.open(filename);if(buf.in.fail()){cout << "Can't open file:" << filename << endl;return false;}buf.init();return true;}bool LexA::GetSym(){char ch;do{if(!buf.Getch(ch)) return false;}while(IsSpace(ch));if(IsNumber(ch)){int temp = ch-'0';do{if(buf.Getch(ch)){if(IsNumber(ch)){temp *= 10;temp += ch-'0';}else{buf.putback();break;}}else break;}while(!IsSpace(ch));V alue.kind = NUMBER;V alue.value = temp;return true;}if(IsChar(ch)){int i = 0;V alue.str[i] = ch;do{i++;if(buf.Getch(ch)){if(IsChar(ch) || IsNumber(ch)){V alue.str[i] = ch;}else if(IsSpace(ch)) break;else{buf.putback();break;}}else{break;}}while(1);V alue.str[i] = 0;for(i = 0; i <= KeyWordNum-1; i++){if(strcmp(V alue.str, KeyWordTable[i].name) == 0){ V alue.kind = KEYWORD;V alue.value = KeyWordTable[i].ID;return true;}}V alue.kind = IDENTSYM;return true;}if(IsOperator(ch)){char temp[3];temp[0] = ch;temp[1] = 0;buf.Getch(ch);if(!IsOperator(ch)) buf.putback();else if(ch == '='){if(temp[0] == '<' || temp[0] == '>' || temp[0] == ':'){temp[1] = '=';temp[2] = 0;}}else buf.putback();for(int i = 0; i <= OperatorNum-1; i++){if(strcmp(temp, OperatorTable[i].name) == 0){V alue.kind = OPERA TORSYM;V alue.value = OperatorTable[i].ID;return true;}}}if(IsSeparator(ch)){for(int i = 0; i <= SeparatorNum-1; i++){if(ch == SeparatorTable[i].name){V alue.kind = SEPARA TORSYM;V alue.value = SeparatorTable[i].ID;return true;}}}return false;}Syna.cpp#include "lexa.h"#include "syna.h"// table成员函数的实现// 返回错误号,1表示成功int Table::enter(tableItem& item){if(tx+1 == TableSize){return UNKNOWERROR;}data[tx].adr = item.adr;data[tx].kind = item.kind;strcpy(data[tx].name, );data[tx].size = item.size;data[tx++].value_lev = item.value_lev;return 1;}// 找到sym后返回1,result为data中序号int Table::find(Symbol& sym, int& result){for(int i = tx-1; i >= 0; i--){if(sym.kind == NUMBER){if(sym.value == data[i].value_lev){result = i;return 1;}}else if(sym.kind == IDENTSYM){if(strcmp(sym.str, data[i].name) == 0){result = i;return 1;}}else{return UNKNOWERROR;}}return 1;}int Table::ChangeLev(int dir){if(dir == -1) lev --;else lev ++;if(lev < 0){return UNKNOWERROR;}if(lev > 3){return UNKNOWERROR;}return 1;}// 语法分析类的实现SynA::SynA(char* filename){if(!lex.Open(filename)) exit(1);cx = 1;dx = 3;}bool SynA::GetSym(Symbol& sym){if(buf.full){sym = buf.sym;buf.full = false;return true;}if(!lex.GetSym()){err.Add(UNKNOWERROR, 0, 0);return false;};sym = lex.V alue;return true;}int SynA::block(){Symbol sym;int start;if(program(start) != 1) return -1;if(!GetSym(sym)) return -1;if(sym.value != PERIOD) return -1;if(GetSym(sym)){// 不明文件尾err.Add(UNKNOWERROR, 0, 0);return -1;}return 1;}int SynA::program(int& start){Symbol sym;if(!GetSym(sym)) return -1;if(sym.value == CONST){if(constProcess() != 1) return -1;if(!GetSym(sym)) return -1;}if(lex.V alue.value == V AR){if(varProcess() != 1) return -1;if(!GetSym(sym)) return -1;}if(lex.V alue.value == PROCEDURE){ int temp = dx;dx = 3;table.ChangeLev(1);if(procedureProcess() != 1) return -1;table.ChangeLev(-1);dx = temp;}else{buf.full = true;buf.sym = sym;}if(table.lev == 0){code[0].fun = JMP;code[0].lev = 0;code[0].a = cx;}start = cx;int var_num = cx++;//int指令将要写入的位置int var_num_lev = table.lev;do{int tttemp = Statement();if(tttemp == 0) break;else if(tttemp != 1) return -1;else{}Symbol sym1;if(table.lev != 0){if(!GetSym(sym1)) return -1;if(sym1.value == SEMICOLON){break;}else{buf.full = true;buf.sym = sym1;}}}while(1);code[cx].fun = OPR;code[cx].lev = 0;code[cx++].a = 0;int temp_lev = table.data[table.tx-1].value_lev, num = 0; for(int i = table.tx-1; i >= 0; i--){if(temp_lev == table.data[i].value_lev) num ++;else break;}if(var_num_lev == table.lev){code[var_num].fun = INT;code[var_num].lev = 0;code[var_num].a = num;}return 1;}int SynA::constProcess(){Symbol sym;tbItem.kind = CONSTANT;do{if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){strcpy(, sym.str);if(!GetSym(sym)) return -1;if(sym.value == EQUAL){if(!GetSym(sym)) return -1;if(sym.kind == NUMBER) tbItem.value_lev = sym.value;table.enter(tbItem);}else if(sym.value == EQUAL1){err.Add(UNKNOWERROR, 0, 0);return -1;}else{err.Add(UNKNOWERROR, 0, 0);return -1;}}else{err.Add(UNKNOWERROR, 0, 0);return -1;}if(!GetSym(sym)) return -1;}while(sym.value == COMMA);if(sym.value != SEMICOLON){err.Add(UNKNOWERROR, 0, 0);return -1;}return 1;}int SynA::varProcess(){Symbol sym;tbItem.kind = V ARIABLE;do{if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){tbItem.adr = dx++;tbItem.value_lev = table.lev;strcpy(, sym.str);table.enter(tbItem);// 重名错误}else{err.Add(UNKNOWERROR, 0, 0);return -1;}if(!GetSym(sym)) return -1;}while(sym.value == COMMA);if(sym.value != SEMICOLON){err.Add(UNKNOWERROR, 0, 0);return -1;};return 1;}int SynA::procedureProcess(){Symbol sym;tbItem.kind = PROCEDURE;if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){strcpy(, sym.str);tbItem.adr = cx;table.enter(tbItem);int temp = cx++;if(!GetSym(sym)) return -1;if(sym.value != SEMICOLON){err.Add(UNKNOWERROR, 0, 0);return -1;}int start;if(program(start) != 1) return -1;code[temp].fun = JMP;code[temp].lev = 0;code[temp].a = start;}else{err.Add(UNKNOWERROR, 0, 0);return -1;}return 1;}int SynA::Statement(){int result, temp, temp1;Symbol sym;if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){if(!table.find(sym, result)) return false;if(table.data[result].kind != V ARIABLE) return -1;if(!GetSym(sym)) return -1;if(sym.value == EQUAL1){if(Expression() != 1) return -1;code[cx].fun = STO;code[cx].lev = table.lev-table.data[result].value_lev;code[cx++].a = table.data[result].adr;}else return -1;return 1;}switch(sym.value){case CALL:if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){if(!table.find(sym, result)) return -1;if(table.data[result].kind != PROCEDURE) return -1;code[cx].fun = CAL;code[cx].lev = table.lev-table.data[result].value_lev;code[cx++].a = table.data[result].adr;}break;case BEGIN:do{int ttemp = Statement();if(ttemp != 1 && ttemp != 0){return -1;}if(!GetSym(sym)) return -1;if(sym.value == SEMICOLON){continue;}else if(sym.value == END)break;else return -1;}while(1);break;case IF:if(Condition() != 1) return -1;//判断条件是否成立code[cx].fun = JPC;code[cx].lev = 0;temp = cx;cx ++;if(!GetSym(sym)) return -1;if(sym.value != THEN) return -1;if(Statement() != 1) return -1;code[temp].a = cx;break;case WHILE:temp1 = cx;if(Condition() != 1) return -1;code[cx].fun = JPC;code[cx].lev = 0;temp = cx;cx ++;if(!GetSym(sym)) return -1;if(sym.value != DO) return -1;if(Statement() != 1) return -1;code[cx].fun = JMP;code[cx].lev = 0;code[cx++].a = temp1;code[temp].a = cx;break;case READ:if(!GetSym(sym)) return -1;if(sym.value !=LPARENTHESIS) return -1;do{if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){if(!table.find(sym, result)) return -1;else{if(table.data[result].kind == V ARIABLE){code[cx].fun = OPR;code[cx].lev = 0;code[cx++].a = 16;code[cx].fun = STO;code[cx].lev = 0;code[cx++].a = table.data[result].adr;}else return -1;}}else return -1;if(!GetSym(sym)) return -1;if(sym.value != COMMA) break;}while(1);if(sym.value != RPARENTHESIS) return -1;break;case WRITE:if(!GetSym(sym)) return -1;if(sym.value !=LPARENTHESIS) return -1;do{if(Expression() != 1) return -1;code[cx].fun = OPR;code[cx].lev = 0;code[cx++].a = 15;if(!GetSym(sym)) return -1;if(sym.value != COMMA) break;}while(1);if(sym.value != RPARENTHESIS) return -1;break;default:// 空语句,返回0buf.full = true;buf.sym = sym;return 0;}return 1;}int SynA::Condition(){Symbol sym;Symbol temp_sym;if(!GetSym(sym)) return -1;if(sym.value == ODD){if(Expression() != 1) return -1;code[cx].fun = OPR;code[cx].lev = 0;code[cx++].a = 6;}else{buf.full = true;buf.sym = sym;if(Expression() != 1) return -1;if(!GetSym(temp_sym)) return -1;if(Expression() != 1) return -1;code[cx].fun = OPR;code[cx].lev = 0;switch(temp_sym.value){case EQUAL:code[cx].a = 8;break;case UNEQUAL:code[cx].a = 9;break;case SMALLER:code[cx].a = 10;break;case SE:code[cx].a = 11;break;case GREA TER:code[cx].a = 12;break;case GE:code[cx].a = 13;break;default:return -1;}cx ++;}return 1;}int SynA::Expression(){Symbol sym;bool Minus = false;if(!GetSym(sym)) return -1;if(sym.value == MINUS){//将-1放到栈顶,并乘法Minus = true;}else{buf.full = true;buf.sym = sym;}bool first = true;int value;do{if(Item() != 1) return -1;if(!first){// 生成加减法指令code[cx].fun = OPR;if(value == PLUS) code[cx].a = 2;else code[cx].a = 3;code[cx++].lev = 0;}first = false;if(!GetSym(sym)) return -1;if(sym.value == PLUS || sym.value == MINUS){ value = sym.value;}else{buf.full = true;buf.sym = sym;break;}}while(1);if(Minus){// 生成乘以-1的指令code[cx].fun = LIT;code[cx].a = -1;code[cx++].lev = 0;code[cx].fun = OPR;code[cx].a = 4;code[cx++].lev = 0;}return 1;}int SynA::Item(){Symbol sym;bool first = true;int value;do{if(Factor() != 1) return -1;if(!first){// 生成乘法或除法指令code[cx].fun = OPR;if(value == TIME) code[cx].a = 4;else code[cx].a = 5;code[cx++].lev = 0;}first = false;if(!GetSym(sym)) return -1;if(sym.value == TIME || sym.value == DEVIDE){value = sym.value;}else{buf.full = true;buf.sym = sym;return 1;}}while(1);}int SynA::Factor(){int result;Symbol sym;if(!GetSym(sym)) return -1;if(sym.kind == IDENTSYM){if(table.find(sym, result) == 1){//取变量到栈顶if(table.data[result].kind == V ARIABLE){code[cx].fun = LOD;code[cx].lev = table.lev-table.data[result].value_lev;code[cx++].a = table.data[result].adr;}else if(table.data[result].kind == CONSTANT){//取常量到栈顶code[cx].fun = LIT;code[cx].lev = 0;code[cx++].a = table.data[result].value_lev;}else{return -1;}}else return -1;}else if(sym.kind == NUMBER){//取数到栈顶code[cx].fun = LIT;code[cx].lev = 0;code[cx++].a = sym.value;}else if(sym.kind == LPARENTHESIS){if(Expression() != 1) return -1;if(!GetSym(sym)) return -1;if(sym.kind != RPARENTHESIS) return -1;}else{// buf.full = true;// buf.sym = sym;return -1; //并非程序错误,返回上一级继续判断}return 1;}T est.cpp#include "lexa.h"#include "syna.h"// 词法分析测试/*int main(){LexA lex("in4.txt");while(lex.GetSym()){}return 0;}*/int main(){SynA syn("in4.txt");if(syn.block() != 1){cout << "Have Error List:" << endl;syn.ListError();}cout << "Table:" << endl;syn.ListTable();cout << endl;cout << "Code:" << endl;syn.ListCode();return 0;}测试数据:const a=10;var b,c;procedure p;beginc:=b+aend;beginread(b);while b#0 dobegincall p;write(2*c);read(b) end;end.以上数据保存在in4.txt中结果:a)一个能正确运行的例子结果完全正确b)源代码存在错误的情况:结尾出现多余代码,输出Error提示指出了源代码有错误,但未明确指出错误位置和类型。
编译原理课件-PL0编译程序
OPR 0 0 OPR 0 1 OPR 0 2 OPR 0 3 OPR 0 4 OPR 0 5 OPR 0 6 OPR 0 7 OPR 0 8 OPR 0 9 OPR 0 10 OPR 0 11
过程调用结束后,返回调用点并退栈 栈顶元素取反 次栈顶与栈顶相加,退两个栈元素,结果值进栈 次栈顶减去栈顶,退两个栈元素,结果值进栈 次栈顶乘以栈顶,退两个栈元素,结果值进栈 次栈顶除以栈顶,退两个栈元素,结果值进栈 栈顶元素的奇偶判断,结果值在栈顶
word[1]:=‘begin
‘;word[2]:=‘call
...
word[13]:=‘write ‘;
查到時找到相應的內部表示
Wsym[1]:=beginsym; wsym[2]:=callsym;
…
wsym[13]:=writesym;
‘;
字元對應的單詞表: ssym[‘+’]:=plus; ssym[‘-’]:=minus;
所有運算都在棧頂(零地址機) 指令格式:
fla
f
功能碼
l 層次差 (識別字引用層減去定義層)
a
根據不同的指令有所區別
目標指令有8條:
①LIT:將常量值取到運行棧頂。a域為常數值。 ② LOD:將變數放到棧頂。a域為變數在所說明層中的相對位置, l為調用層 與說明層的層差值。 ③ STO:將棧頂的內容送入某變數單元中。a,l域的含意同LOD指令。 ④ CAL:調用過程的指令。a為被調用過程的目標程式入口地址,l為層差。 ⑤ INT:為被調用的過程(或主程序)在運行棧中開闢數據區。a域為開闢的單 元個數。 ⑥ JMP:無條件轉移指令,a為轉向地址。 ⑦ JPC:條件轉移指令,當棧頂的布爾值為非真時,轉向a域的地址,否則順 序執行。 ⑧ OPR:關係運算和算術運算指令。將棧頂和次棧頂的內容進行運算,結果 存放在次棧頂,此外還可以是讀寫等特殊功能的指令,具體操作由a域值給 出。(詳見解釋執行程式)。
第2章 PL0语言简介
常量说明部分 *) 变量说明部分 *) 过程说明部分 *)
PL/0程序 示例
DO
Q的过程体
P的过程体 主程序体
程序
分程序
.
内的文字表示非终结符 或 内的文字或符号表示终结符
分程序
const
ident ,
=
number
;
var
, ;
ident
;
procedure
ident
;
分〉的递归子程序实现
procedure term; begin factor; while sym in [ times, slash ] do begin getsym; factor; end end;
〈因子〉的递归子程序实现 procedure factor; begin if sym <> ident then begin if sym <> number then begin if sym = ‘(‘ then begin getsym; expr; if sym = ‘)’ then getsym else error end else error end end end;
READ ( <标识符> )
A
递归子程序法
递归子程序法:对应每个非终结符语法单元,,编一 个独立的处理过程(或子程序)。语法分析从读入第 一个单词开始,由非终结符<程序>(即开始符)出发, 沿语法描述图箭头所指出的方向进行分析。当遇到非 终结符时,则调用相应的处理过程,从语法描述图看, 也就进入了一个语法单元,再沿当前所进入的语法单 元所指箭头方向继续进行分析。当遇到描述图中是终 结符时,则判断当前读入的单词是否与图中的终结符 相匹配,若匹配,再读取下一个单词继续分析。遇到 分支点时,将当前的单词与分支点上多个终结符逐个 相比较,若都不匹配时可能是进入下一个非终结符语 法单位或是出错。
PL0语言编译程序分析和详细注释(Pascal版
PL/0语言编译程序分析PL/0语言是Pascal语言的一个子集,我们这里分析的PL/0的编译程序包括了对PL/0语言源程序进行分析处理、编译生成类PCODE代码,并在虚拟机上解释运行生成的类PCODE代码的功能。
PL/0语言编译程序采用以语法分析为核心、一遍扫描的编译方法。
词法分析和代码生成作为独立的子程序供语法分析程序调用。
语法分析的同时,提供了出错报告和出错恢复的功能。
在源程序没有错误编译通过的情况下,调用类PCODE解释程序解释执行生成的类PCODE代码。
词法分析子程序分析:词法分析子程序名为getsym,功能是从源程序中读出一个单词符号(token),把它的信息放入全局变量sym、id和num中,语法分析器需要单词时,直接从这三个变量中获得。
(注意:语法分析器每次用完这三个变量的值就立即调用getsym子程序获取新的单词供下一次使用。
而不是在需要新单词时才调用getsym过程)。
getsym过程通过反复调用getch子过程从源程序过获取字符,并把它们拼成单词。
getch过程中使用了行缓冲区技术以提高程序运行效率。
词法分析器的分析过程:调用getsym时,它通过getch过程从源程序中获得一个字符。
如果这个字符是字母,则继续获取字符或数字,最终可以拼成一个单词,查保留字表,如果查到则为保留字,把sym变量赋成相应的保留字类型值;如果没有查到,则这个单词应是一个用户自定义的标识符(可能是变量名、常量名或是过程的名字),把sym 置为ident,把这个单词存入id变量。
查保留字表时使用了二分法查找以提高效率。
如果getch获得的字符是数字,则继续用getch获取数字,并把它们拼成一个整数,然后把sym置为number,并把拼成的数值放入num变量。
如果识别出其它合法的符号(比如:赋值号、大于号、小于等于号等),则把sym则成相应的类型。
如果遇到不合法的字符,把sym置成nul。
语法分析子程序分析:语法分析子程序采用了自顶向下的递归子程序法,语法分析同时也根据程序的语意生成相应的代码,并提供了出错处理的机制。
实验一 PL O语言的词法分析程序GETSYM
实验一PL O语言的词法分析程序GETSYM一、实验目的加深和巩固对于词法分析的了解和掌握;初步认识PL/0语言的基础和简单的程序编写;通过本实验能够初步的了解和掌握程序词法分析的整个过程;提高自己上机和编程过程中处理具体问题的能力。
二、实验要求实验前先阅读完总体的预备知识以及实验相关的基础知识;要求自己独立完成实验,不得抄袭别人的结果;编写和调试过程中出现的问题做好记录;实验程序调试完成后,用给定的PL0测试程序(test.pl0)进行测试,由老师检查测试结果,并给予相应的成绩;实验完成后,要上交实验报告。
三、实验原理PL/O语言的编译程序,是用高级语言PASCAL语言书写的。
整个编译过程是由一些嵌套及并列的过程或函数完成。
词法分析程序是独立的过程GETSYM完成,供语法分析读单词时使用。
语法分析是由过程BLOCK完成。
采用自顶向下的递归子程序法。
所产生的目标程序为假象栈式计算机的汇编语言。
对目标程序的执行是由PASCAL语言书写的解释程序进行的。
因此PL/O语言可以在配备PASCAL语言的任何机器上实现。
由于PL/O语言编译程序是适合教学用的实例,它的数据类型只有整形数,数据运算只有四则运算。
语句有复制语句、条件语句、While型循环语句、输入、输出语句和不带参数允许递归调用过程语句及复合语句。
词法分析程序GETSYM的功能包括:1、滤空格,空格在词法分析时是一种不可缺少的界符,而在语法分析时是无用的,所以必须滤掉。
2、识别保留子:设有一张保留字表。
对每个字母打头的字母、数字字符串要查此表。
若查着则为保留字,对应的类别放在SYM中。
如IF对应值为THENSYM。
3、识别标识符:对用户定义的标识符将IDENT放在SYM中,标识符本身的值放在ID中。
4、拼数:当所取单词是数字时,将树的类别NUMBER放在SYM中,数值本身的值放在NUM 中。
5、拼复合词:对两个字符组成的算符如:=、:=、=等单词,识别后将类别送SYM中。
pl0语言简介
4
#define #define #define #define #define #define #define #define #define #define #define
becomes beginsym endsym ifsym thensym whilesym dosym callsym constsym varsym procsym
0x80000 0x100000 0x200000 0x400000 0x800000 0x1000000 0x2000000 0x4000000 0x8000000 0x10000000 0x20000000
enum object { constant, variable, proc }; enum fct { lit, opr, lod, sto, cal, Int, jmp, jpc }; typedef struct{ enum fct f; // function code long l; // level long a; // displacement address } instruction; /* lit 0, a : load constant a opr 0, a : execute operation a lod l, a : load variable l, a sto l, a : store variable l, a cal l, a : call procedure a at level l Int 0, a : increment t-register by a jmp 0, a : jump to a jpc 0, a : jump conditional to a */ char ch; unsigned long sym; char id[al+1]; long num; long cc; long ll; long kk, err; long cx; char line[81]; // // // // // // last character read last symbol read last identifier read last number read character count line l包括标准输入和输出)操作有较大区别。 (9)两种语言的注释形式不同。 (10)由于 int 是 C 语言的关键字,因此中间语言指令的操作码 int 被改成 Int。 (11)改写版本中增加了制表符作为单词的分隔符,因为现在大家已经习惯用制表符。 带来的问题是编译器报错的箭头所指位置可能不准确。 Niklaus Wirth 设计的编译器中有一些不影响大局的小问题 (也许是 Niklaus Wirth 有意为 之) ,我们也没有修改。例如: (1)没有检查一个分程序中的名字是否重复声明,导致出现重复声明的名字时,默认 的是最后出现的那一个。 (2)按附录 1.1 节的文法,常量定义和变量声明用逗号分隔,但实际情况是用分号分 隔也被认可。 (3)无符号整数超过一定位数时,以报告编号为 30 而不是 31 的错误更为准确。 下面是编译器两个文件 pl0.h 和 pl0.c 的代码。 (1)文件 pl0.h #include <stdio.h> #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define norw txmax nmax al amax levmax cxmax nul ident number plus minus times slash oddsym eql neq lss leq gtr geq lparen rparen comma semicolon period 11 100 14 10 2047 3 2000 0x1 0x2 0x4 0x8 0x10 0x20 0x40 0x80 0x100 0x200 0x400 0x800 0x1000 0x2000 0x4000 0x8000 0x10000 0x20000 0x40000 // // // // // // // no. of reserved words length of identifier table max. no. of digits in numbers length of identifiers maximum address maximum depth of block nesting size of code array
PL0语言涉及的头文件
涉及的头文件有<stdio.h>,”pl0.h”,”string.h”<Stdio.h> :stdio就是指“standard input & output"(标准输入输出)/** stdio.h* This file has no copyright assigned and is placed in the Public Domain.* This file is a part of the mingw-runtime package.* No warranty is given; refer to the file DISCLAIMER within the package.* Definitions of types and prototypes of functions for standard input and output. * NOTE: The file manipulation functions provided by Microsoft seem to* work with either slash (/) or backslash (\) as the directory separator.*/stdio.h所包含的函数:文件访问Fopen //打开文件Freopen //将已存在的流指针和新文件连接Fflush //刷新缓存区Fclose //关闭文件二进制输入/输出Fread //直接流读操作Fwrite //直接流写操作非格式化输入/输出fgetc/getc //输入一个字符/ 字符输入(控制台)fputc/putc //字符输出/ 字符输出(控制台)ungetc //把一个字符退回到输入流中fgets //字符串输入fputs //字符串输出格式化输入/输出scanf/fscanf/sscanf //格式输入printf/fprintf/sprint //格式输出文件定位Ftell //得到文件位置fseek //文件位置移动fgetpos //得到文件位置fsetpos//文件位置设置rewind //文件位置复零位错误处理Feof //文件结尾判断Ferror //文件错误检测perror //得到错误提示字符串文件操作Remove //删除文件Rename //修改文件名称Tmpfile //生成临时文件名称“string.h” :是关于字符数组的函数定义的头文件字符串拷贝,块拷贝(目的和源存储区不可重叠) memcpy 块拷贝(目的和源存储区可重叠) memmove串拷贝strcpy按长度的串拷贝strncpy字符串连接函数串连接strcat按长度连接字符串strncat串比较函数块比较memcmp字符串比较strcmp字符串比较(用于非英文字符) strcoll按长度对字符串比较strncmp字符串转换strxfrm字符与字符串查找字符查找memchr字符查找strchr字符串查找strcspn字符串查找strpbrk字符串查找strspn字符串查找strstr字符串分解strtok杂类函数字符串设置memset错误字符串映射strerror求字符串长度strlen“pl0.h”:Pl/0编译系统C版本头文件,描述Pl/0语言名字表中的类型、虚拟代码,虚拟代码结构及各种宏定义等。
pl0
第一部分 PL/0语言及其编译器1. PL/0语言介绍PL/0程序设计语言是一个较简单的语言,它以赋值语句为基础,构造概念有顺序、条件和重复(循环)三种。
PL/0有子程序概念,包括过程定义(可以嵌套)与调用且有局部变量说明。
PL/0中唯一的数据类型是整型,可以用来说明该类型的常量和变量。
当然PL/0也具有通常的算术运算和关系运算。
具体的PL/0语法图如下。
1.1PL/0语言的语法图 程序程序体语句序列条件表达式因子2. PL/0语言编译器本书所提供的PL/0语言编译器的基本工作流程如图1-1所示:源程序执行结果图1-1 PL/0编译器基本工作流程2.1 词法分析PL/0的语言的词法分析器将要完成以下工作: (1) 跳过分隔符(如空格,回车,制表符);(2) 识别诸如begin,end,if,while 等保留字; (3) 识别非保留字的一般标识符,此标识符值(字符序列)赋给全局量id,而全局量sym 赋值为SYM_IDENTIFIER。
(4) 识别数字序列,当前值赋给全局量NUM,sym 则置为SYM_NUMBER; (5) 识别:=,<=,>=之类的特殊符号,全局量sym 则分别被赋值为SYM_BECOMES,SYM_LEQ,SYM_GTR 等。
相关过程(函数)有getsym(),getch(),其中getch()为获取单个字符的过程,除此之外,它还完成:(1) 识别且跳过行结束符;(2) 将输入源文件复写到输出文件;(3) 产生一份程序列表,输出相应行号或指令计数器的值。
2.2 语法分析我们采用递归下降的方法来设计PL/0编译器。
以下我们给出该语言的FIRST 和FOLLOW 集合。
非终结符(S) FIRST(S)FOLLOW(S)程序体 const var procedure ident callif begin while. ;语句 ident call begin if while . ; end条件 odd + - ( ident numberthen do表达式+ - ( ident number . ; ) R end then do 项 ident number ( . ; ) R + - end then do 因子 ident number (. ; ) R + - * / end then do注:表中R 代表六个关系运算符。
第2章__PL0编译程序的实现
词法分析程序
语法分析程序
表 格 管 理 程 序 语义分析程序 中间代码生成程序 出 错 处 理 程 序
代码优化程序
目标代码生成程序
注意
上述编译过程的阶段划分只是一种典型的分法, 事实上并不是所有的编译程序都分成这样几个 阶段的。 有些编译程序对优化没有什么要求,优化阶段 就可省去。在某些情况下,为了加快编译速度, 中间代码产生阶段也可以去掉。有些最简单的 编译程序是在语法分析的同时产生目标代码。 但是,多数实用编译程序的工作过程大致都像 上面所说的那六个阶段。
其它
编译程序的另外两个重要的工作是表格管理和 出错处理。 编译过程中源程序的各种信息被保留在种种不 同的表格里,编译各阶段的工作都涉及到构造、 查找或更新有关的表格,因此需要有表格管理 的工作;如果编译过程中发现源程序有错误, 编译程序应报告错误的性质和错误发生的地点, 并且将错误所造成的影响限制在尽可能小的范 围内,使得源程序的其余部分能继续被编译下 去,有些编译程序还能自动校正错误,这些工 作称之为出错处理。
CONST A=10; (* 常量说明部分 *) VAR B,C; (* 变量说明部分 *) PROCEDURE P; (* 过程说明部分 *) VAR D;(* P的局部变量说明部分 *) PROCEDURE Q; (* P的局部过程说明部分 *) VAR X; BEGIN READ(X); D:=X; Q过程体 WHILE X#0 DO CALL P; END; BEGIN WRITE(D); p过程体 CALL Q; END; BEGIN CALL P; 主程序体 END.
〈标识符〉∷=〈字母〉{〈字母〉|〈数字〉}
<过程说明部分>::=<过程首部><分程序>{; <过程说明部分>} <过程首部>::=PROCEDURE<标识符>;
