📄 pl0.c
字号:
#include<stdio.h>
#include <memory.h>
#include"string.h"
#include"pl0.h"
#define stacksize 500
int main()
{
bool nxtlev[symnum];
printf("Input PL0 file's name:");
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");
init();//初始化变量
err = 0;//错误计数
cc = cx = ll = 0;
ch = ' ';
//首先取得一个符号,若成功则开始调用block()开始编译。
if(-1 != getsym())
{
fa = fopen("fa.tmp","w");
fas = fopen("fas.tmp","w");
addset(nxtlev,declbegsys,statbegsys,symnum);//语句和声明所对应的行为true
nxtlev[period] = true;//period代表“.”,即PL/0程序的结束符
if(-1 == block(0,0,nxtlev))//block parameter:int lev,int tx,bool* fsys;
{
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("There are %d errors in pl0 program!\n",err);
}
}
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]), "cos");
strcpy(&(word[4][0]), "do");
strcpy(&(word[5][0]), "end");
strcpy(&(word[6][0]), "exp");
strcpy(&(word[7][0]), "if");
strcpy(&(word[8][0]), "odd");
strcpy(&(word[9][0]), "procedure");
strcpy(&(word[10][0]), "read");
strcpy(&(word[11][0]), "sin");
strcpy(&(word[12][0]), "tan");
strcpy(&(word[13][0]), "then");
strcpy(&(word[14][0]), "var");
strcpy(&(word[15][0]), "while");
strcpy(&(word[16][0]), "write");
/*根据字母表,设置保留字符号*/
wsym[0] = beginsym;
wsym[1] = callsym;
wsym[2] = constsym;
wsym[3] = cossym;
wsym[4] = dosym;
wsym[5] = endsym;
wsym[6] = expsym;
wsym[7] = ifsym;
wsym[8] = oddsym;
wsym[9] = procsym;
wsym[10] = readsym;
wsym[11] = sinsym;
wsym[12] = tansym;
wsym[13] = thensym;
wsym[14] = varsym;
wsym[15] = whilesym;
wsym[16] = writesym;
/*word数组的行数和wsym的行数相等*/
/*设置指令名称*/
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");
strcpy(&(mnemonic[fun][0]),"fun");
/*设置符号集*/
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;
facbegsys[cossym] = true;
facbegsys[sinsym] = true;
facbegsys[tansym] = true;
facbegsys[expsym] = true;
numlen = 0;
}
int inset(int e,bool*s)
{
if(s == NULL)
{
printf("NULL pointer error!(in inset function)\n");
exit(1);
}
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];//语句和声明所对应的行为true
}
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;
}
//else
void error(int n)//n记录出错位置
{
char space[81];
memset(space,32,81);
space[cc-1] = '\0';
printf("%s ^ %d\n",space,n);
fprintf(fa1,"%s ^ %d\n",space,n);
err++;
}
/*
函数功能简述:为getsym()函数返回一个字符,该函数在cc==ll的时候从文件中读取一行数据,
放在line[]中,以后每次从line[]中取一个字符然后返回,当取完时(cc==ll)在读取。
cc==ll有两种情况,一是第一次调用,二是由于不断的调用已将line中的数据取完。
*/
int getch()
{
//printf("I come into the function getch!\n");
if(cc == ll)
{
if(feof(fin))//程序还没有遇到句点,但文件已经结束,编译程序认为程序不完整,并返回-1.
{
printf("program incomplete");
return -1;
}
ll = 0;
cc = 0;
printf("%d ",cx);
fprintf(fa1,"%d ",cx);
ch = ' ';
while(ch != 10)//10是回车换行,32是空格
{
//由于该循环是先把字符存放在line里,在检查。
//因此回车符也被存放在了line里边。
if(EOF == fscanf(fin,"%c",&ch))//EOF表示文件结束
{
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 == 9 || ch == 10)//9=='\t' and 10 == '\n';
{
getchdo;
}
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))//字母
{
k=0;
do
{
if(k<MAX_IDENT_LENGTH)//MAX_IDENT_LENGTH(=10) is the longest length of the identifier.
{
a[k] = ch;//a 临时符号集,记录获得的完整的字符串或数字串
k++;
}
getchdo;
}while((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9'));
a[k] = 0;
strcpy(id,a);//char id[MAX_IDENT_LENGTH+1],得到标识符;
i = 0;
j = NUM_OF_KEYWORD -1;//NUM_OF_KEYWORD's value is 17.保留字
do//折半查找,找到读入字符串的位置
{
k = (i+j)/2;
/*
下面两句话比较关键,在这里简单说明一下:
当 id==word[k] 时,两个if语句都会执行,于是
j = k - 1 while i = k + 1;
假如 k=8的话,则当两个if都执行之后,j = 7 while i = 8;
于是 当 i-1 > j 时,word[k] 肯定是我们要找的
*/
if(strcmp(id,word[k])<=0)//临时串的首字母与symbol中的某个字符的首字母比较
{
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')//数字
{
k = 0;
num = 0;
sym = number;//标记数字
do//读一个完整的数字串
{
num = 10*num +ch-'0';
k++;
getchdo;
}while(ch>='0'&&ch<='9');
k--;
numlen = k;//add by daka
if(numlen > MAX_NUM_LENGTH)//读入的数字串的长度大于最大允许长度,抛出异常
{
error(31);
}
}
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(sym == '=')
{
sym = geq;//标记>=
getchdo;//继续读
}
else
{
sym = gtr;//>
}
}
else
{
sym = ssym[ch];//标记“,”或“;”或“(”或“)”
if(sym != period)//如果不等结束符“.”
{
getchdo;
}
}
}
}
}
}
return 0;
}
/*
f: 功能码
l: 层次差(标识符引用层减去定义层)
a: 根据不同的指令有所区别
*/
int gen(enum fac x,int y,int z)
{
if(cx > MAX_CODE_NUM)//当产生的代码数量超出code数组的长度时,编译程序报错,并返回-1.
{
printf("Program too long");
return -1;
}
code[cx].f = x;
code[cx].l = y;
code[cx].a = z;
cx++;//地址递增
return 0;
}
int test(bool* s1,bool* s2,int n)
{
bool bs1,bs2;
if(s1 == NULL || s2 == NULL)
{
printf("NULL pointer error!\n");
exit(1);
}
if(! inset(sym,s1))
{
error(n);
bs2 = inset(sym,s2);
bs1 = inset(sym,s1);
while((!bs1)&&(!bs2))
{
getsymdo;
bs2 = inset(sym,s2);
bs1 = inset(sym,s1);
}
}
return 0;
}
/*
block会对一个分程序进行分析,分析对象包括,常量声明,变量声明,过程声明
当遇到过程声明时会再次递归调用block继续深入分析。
<分程序> ::= [<常量说明部分>][<变量说明名>][<过程说明部分><语句>
前三个部分都是可选,只有语句是必须的。
int tx(名字表的索引)是作为参数传进去的,而且传的值,于是所有的修改
在函数返回的时候恢复原样,相应的它曾今在名字表中占的空间将被别人覆盖(可能)
lev:当前分程序所在的层
tx:名字表当前尾指针
fsys:当前模块后跟符号集合
*/
int block(int lev,int tx,bool* fsys)//数组fsys中,声明,语句和结束符“.”所对应的位置值为true
{
int i;
int dx;//名字分配到的相对地址
int tx0;//保留初始的tx
int cx0;//保留初始的cx
bool nxtlev[symnum];/*在下级函数的参数中,符号集合均为参数,但由于使用数组实现,传进来的是指针,
为防止下级函数改变上级函数的集合,特开辟新的空间传递给下级函数
*/
/*
调用该函数,有两种情况1、程序刚开始运行,读入第一个符号后开始调用该函数。(见mian函数)
2、在分析程序的过程中遇到了过程说明,会递归调用这个函数。下面我们仅对第二中情况进行讨论,
当遇到过程说明符时,于是读入过程的标识符,并将该标识符登记到名字表中,紧接着开始调用block()
这时tx指向名字表的末尾(正好是过程标识符所在),tx0将这个位置保存下来,供以后显示名字表使用。
*/
dx = 3;
tx0 = tx;
//每个过程的第一条指令一定是一条跳转指令,跳到程序体的开始
table[tx0].adr = cx;//modified by daka tx---->tx0
//cx的值就是下面gen(jmp,0,0)中jmp指令的地址。
cx0 = cx;
gendo(jmp,0,0);//把 jmp 0 0 加入虚拟代码中,成功继续执行
if(lev > MAX_LEVEL)//大于最大允许层,抛出异常
{
error(32);
}
do
{
if(sym == constsym)//常量
{
getsymdo;//成功读取一个单词
do
{
constdeclarationdo(&tx,lev,&dx);//是常量声明,继续执行
while(sym == comma)//","
{
getsymdo;
constdeclarationdo(&tx,lev,&dx);
}
if(sym == semicolon)//";"
{
getsymdo;
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -