一、实验内容
扩充的语法规则有:实现while、do while、for语句和求余计算式子,具体文法规则自行构造。
可参考:P97及P136的文法规则。
(1)While-stmt --> while exp do stmt-sequence endwhile
(2)Dowhile-stmt-->do stmt-sequence while exp
(3)for-stmt-->for identifier:=simple-exp to simple-exp do stmt-sequence enddo 步长递增1
(4)for-stmt-->for identifier:=simple-exp downto simple-exp do stmt-sequence enddo 步长递减1
要求:
(1)要提供一个源程序编辑界面,以让用户输入源程序(可保存、打开源程序)
(2)可由用户选择是否生成语法树,并可查看所生成的语法树。
(3)应该书写完善的软件文档
二、实验设备
操作系统:Windows7
软件:VC6.0
三、 实验分析
1.由于用MFC的Windows的时候在读入文件的时候出现错误,估最后决定采用dos界面,错误界面如下:
2.扩展文法while,do while,for语句和求余计算式子
三、 实验步骤
1. 用vc6.0将《编译原理及实践》书中附录B的代码文件建立工程,并将以下文件添加进工程目录:main.c,globals.h,parse.c,parse.h,scan.c,scan.h,util.c,util.h。
2. 在globals.h文件中
a. 修改最大保留字数量#define MAXRESERVED 15。
b. 在typedef enum{} TokenType结构中增加保留字WHILE,DO,TO,DOWNTO,FOR,ENDDO,ENDWHILE,MOD。
c. 在typedef enum {} StmtKind结构中增加语句类型WhileK,DoWhileK,ForK。
3. 在scan.c文件中
a. 在static struct {} reservedWords[MAXRESERVED]中增加保留字关联字符
{"while",WHILE}{"endwhile",ENDWHILE},{"do",DO},{"to",TO},{"downto",DOWNTO},{"for",FOR},{"enddo",ENDDO}。
b. 在TokenType getToken(void)函数中switch (c)模块下增加状态
case '%':currentToken=MOD; break;
4. 在util.h文件中
a. 在void printToken( TokenType token, const char* tokenString )函数中switch (token)模块下增加状态:
…
case WRITE:
//增加
case WHILE:
case DO:
case TO:
case DOWNTO:
case FOR:
case ENDDO:
case ENDWHILE:
//结束
fprintf(listing, "reserved word: %s\n",tokenString);break;
…
…
case TIMES: fprintf(listing,"*\n");break;
//增加
case MOD:fprintf(listing,"%%\n"); break;
//结束
case OVER:fprintf(listing,"/\n"); break;
…
b. 在void printTree( TreeNode * tree )函数中switch(tree->kind.stmt)模块下增加状态:
…
caseWriteK:fprintf(listing,"Write\n");break;
//增加
case WhileK:fprintf(listing,"While\n");break;
caseDoWhileK:fprintf(listing,"Do\n");break;
caseForK:fprintf(listing,"For\n");break;
//结束
default:fprintf(listing,"UnknownExpNode kind\n");break;
…
5. 在parse.c文件中
a. 增加静态成员函数定义static TreeNode * while_stmt(void),static TreeNode* dowhile_stmt(void),static TreeNode * for_stmt(void)。
b. 分别实现static TreeNode * while_stmt(void),staticTreeNode * dowhile_stmt(void),static TreeNode *for_stmt(void)函数,代码如下:
TreeNode* while_stmt(void) //扩充的while 语句文法
{
TreeNode * t=newStmtNode(WhileK);
match(WHILE);
if(t!=NULL)
t->child[0]=exp();
match(DO);
if(t!=NULL)
t->child[1]=stmt_sequence();
match(ENDWHILE);
return t;
}
TreeNode* dowhile_stmt(void) //扩充的dowhile语句文法
{
TreeNode *t=newStmtNode(DoWhileK);
match(DO);
if(t!=NULL)
t->child[0]=stmt_sequence();
match(WHILE);
if(t!=NULL)
t->child[1]=exp();
return t;
}
TreeNode* for_stmt(void) //扩充的for语句文法
{
TreeNode *t=newStmtNode(ForK);
match(FOR);
if ((t!=NULL) &&(token==ID))
t->attr.name =copyString(tokenString);
match(ID);
match(ASSIGN);
if(t!=NULL)
t->child[0]=simple_exp();
if(token==TO) //步长递增1
match(TO);
if(token==DOWNTO) //步长递减1
match(DOWNTO);
if(t!=NULL)
t->child[1]=simple_exp();
match(DO);
if(t!=NULL)
t->child[2]=stmt_sequence();
match(ENDDO);
return t;
}
c. TreeNode *stmt_sequence(void)函数中增加stmt_sequence文法的follow集while((token!=ENDFILE)&&(token!=END)&&(token!=ELSE)&&(token!=UNTIL)&&(token!=ENDWHILE)&&(token!=WHILE)&&(token!=ENDDO))
d. 在TreeNode * statement(void)函数中switch (token)模块下增加状态:
case WHILE:t= while_stmt();break;
case DO:t=dowhile_stmt();break;
case FOR:t=for_stmt();break;
e. 在TreeNode * term(void)增加代码while((token==TIMES)||(token==OVER)||(token==MOD))
6. 在main.c文件中,修改main函数,部分代码如下:
TreeNode* syntaxTree;
charpgm[120],ch[120];
intn,flag=1;//状态标志
while(flag){
switch(flag){
case 1:
printf("-------TINY扩充语言的语法分析-------\n");
printf("--打开文件:\n");
printf("1.SAMPLE.TNY\n");
printf("2.while_test.tny\n");
printf("3.dowhile_test.tny\n");
printf("4.for_test.tny\n");
printf("5.结束程序 \n");
printf("\n");printf("\n");
printf("请选择(1-5):");
scanf("%d",&n);
while(n<1||n>5){
printf("-----输入有误,请重新输入:\n");
scanf("%d",&n);
}
switch(n){
case 1://打开文件SAMPLE.TNY
source=fopen("SAMPLE.TNY","r");
strcpy(pgm,"SAMPLE.TNY");break;
case 2://打开文件while_test.tny
source=fopen("while_test.tny","r");
strcpy(pgm,"while_test.tny");break;
case 3://打开文件dowhile_test.tny
source=fopen("dowhile_test.tny","r");
strcpy(pgm,"dowhile_test.tny");break;
case 4://打开文件for_test.tny
source=fopen("for_test.tny","r");
strcpy(pgm,"for_test.tny");break;
case 5://结束程序
default:
fclose(source);//关闭文件
exit(0);
}
if (source==NULL){
fprintf(stderr,"File %snot found\n",pgm);exit(1);
}
case 2:
printf("*******输入源程序成功!*******\n");
printf("*******1.查看源程序\n");
printf("*******2.查看生成语法树\n");
printf("*******3.返回上一级\n");
printf("请选择(1-3):");
scanf("%d",&n);
while(n<1||n>3){
printf("*****输入有误,请重新输入:\n");
scanf("%d",&n);
}
if(n==3) {flag=1; fclose(source);break;}
else{
flag=2;
switch(n){
case 1://打开源程序
while(!feof(source)){
if(fgets(ch,120,source)!=0)printf("%s",ch);
}
fseek(source,0L,0);break;//读取结束,将文件指针指向头位置
case 2://查看生成树
listing = stdout;
fprintf(listing,"\nTINYCOMPILATION: %s\n",pgm);
syntaxTree = parse();
if (TraceParse) {
fprintf(listing,"\nSyntaxtree:\n");
printTree(syntaxTree);
}
fseek(source,0L,0);break;//查看结束,将文件指针指向头位置
}
}
case 3:
printf("\n");
printf("*****1.返回上一级\n");
printf("*****2.结束程序\n");
printf("请选择(1-2):");
scanf("%d",&n);
while(n<1||n>2){
printf("*****输入有误,请重新输入:\n");
scanf("%d",&n);
}
if(n==1)flag=2;
else if(n==2)flag=0;
}
}
fclose(source);
三、 实验结果
主界面包括选择打开四个文件和结束程序,其中四个文件分别是SAMPLE.TNY,while_test.tny,dowhile_test.tny,for_test.tny下面分别打开测试文件和其生成的语法树:
a. 测试文件SAMPLE.TNY:
{Sample program
in TINY language -
computes factorial
}
readx; { input an integer }
if0 < x then { don't compute if x <= 0 }
fact := 1;
repeat
fact := fact * x;
x := x % 1;
until x = 0;
write fact { output factorial of x }
end
生成的语法树:
a. 测试文件while_test.tny:
readx; { input an integer }
while0 < x do
fact := 1;
repeat
fact := fact * x;
x := x % 2
until x = 0;
write fact
endwhile
生成的语法树:
a. 测试文件dowhile_test.tny
readx; { input an integer }
do
fact := 1;
repeat
fact := fact * x;
x := x % 2
until x = 0;
write fact
while x=0
生成的语法树:
a. 测试文件for_test.tny
readx; { input an integer }
forx:=1 to y+5 do
fact := 1;
repeat
fact := fact * x;
x := x % 2
until x = 0;
write fact { output factorial of x }
enddo
生成的语法树