📄 pl0.c
字号:
error(5);
}
}while(sym == ident);//indet表示用户定义的常量名,变量名,过程名
}
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);//ident:标识符
}
while(sym == procsym)//过程
{
getsymdo;
if(sym == ident)
{
enter(procedur,&tx,lev,&dx);//登记过程名
getsymdo;
}
else
{
error(4);
}
if(sym == semicolon)//";"
{
getsymdo;
}
else
{
error(5);
}
memcpy(nxtlev,fsys,sizeof(bool)*symnum);//把方法block()获得参数fsys的值放入数组nxtlev中
nxtlev[semicolon] = true;
if(-1 == block(lev+1,tx,nxtlev))//<--------------递归调用(Warning:层次增一).
{//变量的分层是在过程的基础上的,此操作是为了获取内层的信息。
//如:过程P的信息(P的内层过程Q以前的信息)已经采集完,层加1,采集Q的信息。
//一次递推,直到过程的信息被采集完。与过程同层的变量的信息也一样采集
return -1;
}
if(sym == semicolon)//";"
{
getsymdo;
//statbegsys:语句开始符号集
memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);//把语句开始符号的标记加到临时数组nxtlev中
nxtlev[ident] = true;//标识符行赋true值
nxtlev[procsym] = true;//过程行赋true值
testdo(nxtlev,fsys,6);/*block调用返回后*/
}
else
{
error(5);
}
}
//statbegsys:语句开始符号集
memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);//
nxtlev[ident] = true; //
nxtlev[period] = true; //准备按语句处理
testdo(nxtlev,declbegsys,7);/*<><><><><><><><>*/
//declbegsys:声明开始符号集
}while(inset(sym,declbegsys));
/*
回填跳转指令的地址,跳转指令格式:(JMP 0 a)无条件跳转到地址a .
由于该程序是先生成内部过程代码,然后生成外部过程代码,这时由程序
格式决定的,也是由于递归调用决定的,因此一个一个内部嵌有子过程的过程
在分析之初根本无法知道应该跳到那里去开始执行,那什么时候才知道呢?很明显
只有把它内部嵌有的所有过程都分析完(产生代码),这时程序才会从递归中退回来
开始真正(因为上面的分析只是一些声明的处理,只有将这些声明处理完后,才会开
始分析语句)分析该过程,这时的第一句代码就是跳转指令应跳到的地址。
*/
code[table[tx0].adr].a = cx;//跳转指令的跳转地址,即过程代码的首地址。
table[tx0].adr = cx;//过程代码的首地址
table[ tx0].size = dx;/*
过程数据区的大小,数据区只针对变量而言,
数据区的大小不是说在table表中占有多少空间,
它指的是在运行栈中的大小。常量被放置在栈中。
因为常量是不允许修改的,因此将放在栈中操作即可。
不需要将其放在数据区中。这也是为什么在登记常量时
没有执行(*pdx)++;这条语句。
*/
//cx0 = cx;
gendo(inte,0,dx);//加入虚拟代码
if(tableswitch)
{
printf("\nHere is the lists of table:%d---%d",tx0,tx);
printf("\n~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ \n");
if(tx0+1 > tx)
{
printf("There is no symbol in the table!\n");
}
for(i = tx0+1;i<=tx;i++)
{
switch(table[i].kind)
{
case constant:
printf("%d\tconst\t%s\t",i,table[i].name);
printf("val=%d\n",table[i].val);
fprintf(fas,"%d\tconst\t%s\t",i,table[i].name);//打印到文件上
fprintf(fas,"val=%d\n",table[i].val);
break;
case variable:
printf("%d\tvar\t%s\t",i,table[i].name);
printf("lev=%d\taddr=%d\t\n",table[i].level,table[i].adr);
fprintf(fas,"%d\tvar\t%s\t",i,table[i].name);
fprintf(fas,"lev=%d\taddr=%d\t\n",table[i].level,table[i].adr);
break;
case procedur:
printf("%d\tproce\t%s\t",i,table[i].name);
printf("lev=%d\taddr=%d\tsize=%d\t\n",table[i].level,table[i].adr,table[i].size);
fprintf(fas,"%d\tproce\t%s\t",i,table[i].name);
fprintf(fas,"lev=%d\taddr=%d\tsize=%d\n",table[i].level,table[i].adr,table[i].size);
break;
}
}
printf("\n~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ \n");
}
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);/*<><><><><><><><>block将要退出时*/
listcode(cx0);
return 0;
}
/*
参数:enum object k ---- 登录到名字表的标识符的类型(常量、变量、过程)。
int* ptx ---- 名字表索引的指针。
int lev ---- 将要登录的符号所在层。
int* pdx ---- 若将要登录的是一个变量,则(*pdx)代表该变量在数据区中的相对位置????
*/
void enter(enum object k,int* ptx,int lev,int* pdx)
{
(*ptx)++;
strcpy(table[(*ptx)].name,id);
table[(*ptx)].kind = k;
switch(k)
{
/*
假如在变量声明的之前有常量声明,那么变量的相对地址与没有常量声明一样吗?
例: const a,b;
var c,d,e,f;
*/
case constant://常量
if(numlen > MAX_NUM_LENGTH)
{
error(31);//数字串太长
num = 0;
}
table[(*ptx)].val = num;
break;
case variable://变量
table[(*ptx)].level = lev;
table[(*ptx)].adr = (*ptx);
(*pdx)++;
break;
case procedur://过程
table[(*ptx)].level = lev;
break;
}
}
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;
}
/*
constdeclaration 函数功能:将常量登记到名字表中,不产生任何代码。
*/
int constdeclaration(int* ptx,int lev,int* pdx)
{
//<常量说明> ::= const <常量定义>{,<常量定义>};
//<常量定义> ::= <标识符> = <无符号整数>
if(sym == ident)
{
getsymdo;
if(sym == eql || sym == becomes)
{
if(sym == becomes)//becomes is ':='
{
error(1);
}
getsymdo;
if(sym == number)
{
enter(constant,ptx,lev,pdx);
getsymdo;
}
else
{
error(2);
}
}
else
{
error(3);
}
}
else
{
error(4);
}
return 0;
}
/*
vardeclaration 函数功能:将变量登记到名字表中,不产生任何代码。
*/
int vardeclaration(int* ptx,int lev,int* pdx)
{
//printf("I come into the function vardeclaration!\n");
if(sym == ident)
{
enter(variable,ptx,lev,pdx);
getsymdo;
}
else
{
error(4);
}
return 0;
}
void listcode(int cx0)
{
int i;
//printf("I come into the function listcode!\n");
if(listswitch)
{
printf("\n* * * * * * * * * * * * * * * * * * * * * * * * * *\n");
printf("\tF\tL\tA\n");
fprintf(fa,"\tF\tL\tA\n");
for(i=cx0;i<cx;i++)
{
//输出格式:指令地址 指令名称 指令所在层 a
printf("%d\t%s\t%d\t%d\n",i,mnemonic[code[i].f],code[i].l,code[i].a);
fprintf(fa,"%d\t%s\t%d\t%d\n",i,mnemonic[code[i].f],code[i].l,code[i].a);
}
printf("* * * * * * * * * * * * * * * * * * * * * * * * * *\n");
}
}
/*
<语句> ::= <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空>
根据sym的值来确定按照某种语句来处理,若是ident调用赋值语句;若是ifsym则调用条件语句;若是whilesym
则调用当型循环语句,依次类推....
<分程序> ::= [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>
block()函数负责常量、变量、过程声明的处理,当声明处理完后就只剩下语句了。
由于block是递归调用,所以内层tx>=外层tx,也就是说内部过程可以调用外部过程声明的变量,但反之不可。
*/
int statement(bool* fsys,int* ptx,int lev)
{
int i,cx1,cx2;
bool nxtlev[symnum];
/*
准备按照赋值语句处理
<赋值语句> ::= <标识符> := <表达式>
*/
if(sym == ident)
{
i = position(id,*ptx);//tx指向表尾
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)
{
gendo(sto,lev-table[i].level,table[i].adr);//lev-table[i].level标识符引用层减去标识符定义层
}
}
}
}
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);
}
else
{
gendo(opr,0,16);
gendo(sto,lev-table[i].level,table[i].adr);
}
getsymdo;
}while(sym == comma);//","
}
if(sym != rparen)
{
error(33);
while(!inset(sym,fsys))
{
getsymdo;
}
}
else
{
getsymdo;
}
}
else
{
if(sym == writesym)
{
getsymdo;
if(sym == lparen)
{
do
{
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
nxtlev[rparen] = true;
nxtlev[comma] = true;
expressiondo(nxtlev,ptx,lev);
gendo(opr,0,14);
}while(sym == comma);
if(sym != rparen)
{
error(33);
}
else
{
getsymdo;
}
}
gendo(opr,0,15);
}
else
{
if(sym == callsym)
{
getsymdo;
if(sym != ident)
{
error(14);
}
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);
/*
产生过程调用指令lev是当前过程的层次,
*/
}
else
{
error(15);
}
}
getsymdo;
}
}
else
{
if(sym == ifsym)
{
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
nxtlev[thensym] = true;
nxtlev[dosym] = true;
conditiondo(nxtlev,ptx,lev);
if(sym == thensym)
{
getsymdo;
}
else
{
error(16);
}
cx1 = cx;
gendo(jpc,0,0);
statementdo(fsys,ptx,lev);
code[cx1].a = cx;
}
else
{
if(sym == beginsym)
{
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
nxtlev[semicolon] = true;
nxtlev[endsym] = true;
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);
}
}
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -