📄 source.cpp
字号:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define NO 14//保留字个数
#define TXMAX 100//标识符表的长度(容量)
#define NMAX 14//数字允许的最长位数
#define AL 10//单词最长长度
#define AMAX 2047//寻址空间
#define LEVMAX 3//最大允许的块嵌套层数
#define CXMAX 200//类PCODE目标代码数组长度(可容纳代码行数)
#define STACKSIZE 500
/********************************************/
char *word[NO]={"begin","call","const","do","else","end","if","odd","procedure","read","then","var",
"while","write"};//保留字表
char *operate[8]={"lit","opr","lod","sto","cal","INT","jmp","jpc"};//操作符
char ch;//主要用于词法分析器,存放最近一次从文件中读出的字符
char id[AL];//词法分析器输出结果之用,存放最近一次识别出来的标识符的名字
char sym[10];//词法分析器输出结果之用,存放最近一次识别出来的token的类型
char line[81];//行缓冲区,用于从文件读出一行,供词法分析获取单词时之用
char a[AL],fname[AL];//fname存放PL/0源程序文件的文件名
enum object{constant,variable,procedur};/*object1为三种标识符的类型*/
enum object kind;
enum fct{lit,opr,lod,sto,cal,INT,jmp,jpc};
FILE *fcode;/*文本文件fcode用于列出源程序*/
FILE *fpro,*fresult;/*文本文件fpro用于列出类PCODE代码,fresult用于记录解释执行类PCODE代码的过程*/
FILE *fin;/*fin文本文件用于指向输入的源程序文件*/
int num;//词法分析器输出结果之用,存放最近一次识别出来的数字的
int cc;//行缓冲区指针
int ll;//行缓冲区长度
int cx;//代码分配指针,代码生成模块总在cx所指位置生成新的代码
int err;//出错总次数
int lev=0,tx,dx=3;//这一次语法分析所在的层次,tx:符号表指针,dx:数据段内存分配指针,
//指向下一个被分配空间在数据段中的偏移位置
struct instruction/*类PCODE指令类型,包含三个字段:指令f、层差l和另一个操作数a*/
{enum fct f;
int l;
int a;
};
struct instruction code[CXMAX+1];
struct tablel/*符号表*/
{char name[AL];
enum object kind;/*符号的类型*/
int val,level,adr,size;/*存放层差、偏移地址和大小*/
};
struct tablel table[TXMAX+1];/*ident table*/
/************出错信息,n出错代码***************/
error(int n)
{
int i;
printf("Error%d:",err);
fprintf(fpro,"Error%d:",err);
for(i=0;i<5;i++){
printf(" ");
}
for(i=0;i<5;i++)
{fputs(" ",fpro);
}
printf("error(%d)!\n",n);
fprintf(fpro,"error(%d)!\n",n);
err=err+1;
}
/*读取原程序中下一个字符过程get_ch*/
void get_ch()
{ if (feof(fin)){
printf("program incomplete");
}
else {
if(cc==ll+1){/*如果行缓冲区指针指向行缓冲区最后一个字符就从文件读一行到行缓冲区*/
ll=0;
cc=0;
while((ch=fgetc(fin))!='\n'){putchar(ch);
fputc(ch,fpro);
line[ll++]=ch;/*把读到的字符存入行缓冲区相应的位置*/
if(ch=='.')
{
printf("\n");
printf("*****User's program is in the end!*****");
break;
}
}
printf("\n");
line[ll]=ch;
fprintf(fpro,"\n");
}
ch=line[cc++];/*读出字符,放入全局变量ch*/
}
}
/***************词法分析过程getsym******************/
void getsym()
{
int i,j,k;
while(ch==' '||ch=='\t'||ch=='\n')//跳过字符中多余的空格和回车
get_ch();
if(ch>='a'&&ch<='z'){ //若读入的ch是字母
k=0;
do{
if(k<AL){ /*如果标识符长度没有超过最大标识符长度
(如果超过,就取前面一部分,把多余的抛弃)*/
a[k]=ch;
k++;
}
get_ch(); //读下一个字符
}while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9'));/*直到读出的不是字母或数字,
由此可知PL/0的标识符构成规则是:
以字母开头,后面跟若干个字母或数字*/
if(k>=AL) error(1);
else while(k<AL-1)
a[k++]='\0';
strcpy(id,a);
/*下面开始二分法查找看读出的标识符是不是保留字之一*/
i=0; //i指向第一个保留字
j=NO-1; //j指向最后一个保留字
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) strcpy(sym,word[k]);
else {
if(strcmp(id,"program")) strcpy(sym,"ident");
else strcpy(sym,"program");
}
}
else //如果读出字符不是字母
if(ch>='0'&&ch<='9'){ //是数字
k=0; //数字位数
num=0; //数字置为0
strcpy(sym,"number"); //置sym为number,表示这一次读到的是数字
do{ //这个循环依次从源文件中读出字符,组成数字
num=10*num+(int)(ch-'0');//num * 10加上最近读出的字符ASCII减'0'的ASCII得到相应的数值
k=k+1; //数字位数加一
get_ch();
}while(ch>='0'&&ch<='9');
if(k>NMAX) error(1);/*number overflow*/
}
else if(ch==':'){ //如果读出的不字母也不是数字而是冒号
get_ch(); //再读一个字符
if(ch=='='){ //如果读到的是等号,正好可以与冒号构成赋值号
strcpy(sym,"becomes");
get_ch();
}
else strcpy(sym,"nul"); //如果不是读到等号,那单独的一个冒号就什么也不是
}
else if(ch=='<'){
get_ch();
if(ch=='='){
strcpy(sym,"leq"); //构成一个小于等于号
get_ch();
}
else //不是等号
strcpy(sym,"lss"); //那就是一个单独的小于号
}
else if(ch=='>'){
get_ch();
if(ch=='='){
strcpy(sym,"geq"); //构成一个大于等于号
get_ch();
}
else strcpy(sym,"gtr"); //是一个单独的大于号 }
}
else {
switch(ch){
case'+': strcpy(sym,"plus");break;
case'-': strcpy(sym,"minus");break;
case'*': strcpy(sym,"times");break;
case'/': strcpy(sym,"slash");break;
case'(': strcpy(sym,"lparen");break;
case')': strcpy(sym,"rparen");break;
case'=': strcpy(sym,"eql");break;
case',': strcpy(sym,"comma");break;
case'.': strcpy(sym,"period");break;
case'#': strcpy(sym,"neq");break;
case';': strcpy(sym,"semicolon");break;
}
get_ch();
}
}
/*
词法分析过程getsym总结:从源文件中读出若干有效字符,组成一个token串,识别它的类型
为保留字/标识符/数字或是其它符号。如果是保留字,把sym置成相应的保留字类型,如果是
标识符,把sym置成ident表示是标识符,于此同时,id变量中存放的即为保留字字符串或标识
符名字。如果是数字,把sym置为number,同时num变量中存放该数字的值。如果是其它的操作符,
则直接把sym置成相应类型。经过本过程后ch变量中存放的是下一个即将被识别的字符.
*/
/**********目标代码生成过程gen*************/
/*
参数:x:要生成的一行代码的助记符
y, z:代码的两个操作数
本过程用于把生成的目标代码写入目标代码数组,供后面的解释器解释执行
*/
void gen(enum fct x,int y,int z)
{
if(cx>CXMAX){
printf("program too long");
}
code[cx].f=x;
code[cx].l=y;
code[cx].a=z;
cx++;
}
/***********登记符号表过程enter***************/
/*
参数:k:欲登记到符号表的符号类型
*/
void enter(enum object k)
{
tx=tx+1;//符号表指针指向一个新的空位
strcpy(table[tx].name,id);
table[tx].kind=k;
switch(k){
case constant:if(num>AMAX){
error(1);
num=0;
}
table[tx].val=num;
break;
case variable:table[tx].level=lev;
table[tx].adr=dx;
dx++;
break;
case procedur:table[tx].level=lev; break;
}
}
/****************常量声明处理过程constdeclaration***************/
void constdeclaration()
{
if(strcmp(sym,"ident")==0){ //常量声明过程开始遇到的第一个符号必然应为标识符
getsym();//获取下一个token
if(strcmp(sym,"eql")==0||strcmp(sym,"becomes")==0){//如果是等号或赋值号
if(strcmp(sym,"becomes")==0) error(2);/*如果是赋值号(常量生明中应该是等号)
抛出2号错误*/
getsym();
if(strcmp(sym,"number")==0){
enter(constant);
getsym();
}
else error(3);//如果等号后接的不是数字,抛出3号错误
}
else error(4);//如果常量标识符后接的不是等号或赋值号,抛出4号错误
}
else error(5);//如果常量声明过程遇到的第一个符号不为标识符,抛出5号错误
}
/*******************变量声明过程vardeclaration*****************/
void vardeclaration()
{
if(strcmp(sym,"ident")==0){//变量声明过程开始遇到的第一个符号必然应为标识符
enter(variable); //将标识符登记到符号表中
getsym();
}
else error(6);//如果变量声明过程遇到的第一个符号不是标识符,抛出6号错误
}
/******************列出当前一层类PCODE目标代码过程listcode**********/
void list()
{
int i;
for(i=0;i<cx;i++){ //从当前层代码开始位置到当前代码位置处,即为本分程序块
printf("%2d %5s %3d %5d\n",i,operate[(int)code[i].f],code[i].l,code[i].a);
//显示出第i行代码的助记符和L与A操作数
fprintf(fcode,"%2d %5s %3d %5d\n",i,operate[(int)code[i].f],code[i].l,code[i].a);
}
}
/**************在符号表中查找指定符号所在位置的函数position*************/
/*
参数:id:要找的符号
返回值:要找的符号在符号表中的位置,如果找不到就返回0
*/
int position(char id[10])
{
int i;
strcpy(table[0].name,id); /*先把id放入符号表0号位置*/
i=tx; /*从符号表中当前位置也即最后一个符号开始找*/
while (strcmp(table[i].name,id)!=0) /*如果当前的符号与要找的不一致*/
i--; /*找前面一个*/
return i; /*返回找到的位置号,如果没找到则一定正好为0*/
}
/****************因子处理过程factor********************/
void factor()
{
void expression();
int i;
while((strcmp(sym,"ident")&&strcmp(sym,"number")&&strcmp(sym,"lparen"))==0){
if(strcmp(sym,"ident")==0){
i=position(id);//查符号表,找到当前标识符在符号表中的位置
if(i==0)error(7);//如果查符号表返回为0,表示没有找到标识符,抛出7号错误
else
switch(table[i].kind){//如果在符号表中找到了当前标识符的位置,开始生成相应代码
case constant:gen(lit,0,table[i].val);
break;
case variable:gen(lod,lev-table[i].level,table[i].adr);
break;
case procedur:error(8);/*如果在因子处理中遇到的标识符是
过程名,抛8号错*/
break;
}
getsym(); //获取下一token,继续循环处理
}
else if(strcmp(sym,"number")==0){
if(num>AMAX){
error(1);num=0;}//如果数字的大小超过允许最大值AMAX,抛出1号错误,把数字按0值处理
gen(lit,0,num);
getsym();
}
else if(strcmp(sym,"lparen")==0){//如果遇到的是左括号
getsym();
expression();
if(strcmp(sym,"rparen")==0) getsym();/*子表达式分析完后,应遇到右括号
如果的确遇到右括号,读取下一个token */
else error(9);//否则抛出9号错误:need a ')'
}
}
}
/******************项处理过程term******************/
void term()
{
int i=0,j=0;
char mulop[10];
factor();//每一个项都应该由因子开始,因此调用factor子程序分析因子
while((strcmp(sym,"times")&&strcmp(sym,"slash"))==0){
strcpy(mulop,sym);//保存当前运算符
getsym();
factor();//运算符后应是一个因子,故调factor子程序分析因子
if(strcmp(mulop,"times")==0) gen(opr,0,4);//如果刚才遇到乘号,生成乘法指令
else gen(opr,0,5);//不是乘号一定是除号,生成除法指令
}
}
/***********************************************/
/****************表达式处理过程expression*****************/
void expression()
{
int m=0,n=0;
char addop[10];
if((strcmp(sym,"plus")&&strcmp(sym,"minus"))==0){//一个表达式可能会由加号或减号开始,表示正负号
strcpy(addop,sym);//把当前的正号或负号保存起来,以便下面生成相应代码
getsym();
term();//正负号后面应该是一个项,调term子程序分析
if(strcmp(addop,"minus")==0)//如果保存下来的符号是负号
gen(opr,0,1); //生成一条1号操作指令:取反运算
} //如果不是负号就是正号,不需生成相应的指令
else term();
while ((strcmp(sym,"plus")&&strcmp(sym,"minus"))==0){//项后应是加运算或减运算
strcpy(addop,sym);
getsym();
term();
if (strcmp(addop,"plus")==0) //如果项与项之间的运算符是加号
gen(opr,0,2); //生成2号操作指令:加法
else gen(opr,0,3); //生成3号操作指令:减法
}
}
/***********************************************/
/********************条件处理过程condition****************/
void condition()
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -