课程设计报告
题 目 PL/0
扩充
课 程 名 称 计算机编译原理课设 院 部 名 称 信息技术学院 专 业 计算机科学与技术
班 级 08计算机科学与技术(2)(本) 学 生 姓 名 陆燕芳 学 号 0805110216 课程设计地点 B513 课程设计学时 20 指 导 教 师 洪蕾
金陵科技学院教务处制
成绩
一.课程设计目的
通过设计调试词法分析程序,实现从源程序中分出各种单词的方法;加深对课堂教学的理解;提高词法分析方法的实践能力。
通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。
二.课程设计小组成员及分工
成员任务分工
徐向阳(组长)、俞倩PL/0编译程序的C 语言源代码输入及功能扩
充
黄丽、符银银PL/0编译程序的C 语言源代码输入及功能分
析
陆燕芳、钱嘉炜PL/0编译程序的C 语言源代码输入及功能测
试
三.课程设计内容
编译原理的实践教学,采用简化编译过程的办法,选择最关键的2个环节──词法分析、语法分析(包括语义处理、产生无优化的目标指令),进行编程和调试训练。每个环节作为一个实践课题。先分别编程调试,再连接在一起总调。在此基础上可以增加扩展功能。
1、词法分析程序
⑴掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。
⑵掌握词法分析的实现方法。
⑶上机调试编出的词法分析程序。
NFA确定化输入:NFA; 输出DFA
2、语法分析程序
选择最有代表性的语法分析方法,如算符优先法、递归子程序法和LR分析法;选择对各种常见程序语言都用的语法结构,如赋值语句,特别是表达式,作为分析对象,并且与所选语法分析方法要比较贴切。
四.课程设计要求
1.实验环境要求
实验的运行环境是Windows操作系统下的TC,编程语言是C,使用lex(是一种用来生成代码扫描器的工具)和Yacc(使用它可以生成解释器,编译器,协议实现等多种程序)。实验提供的文件:
●PL0语言的EBNF描述。
●一些PL0程序例子。
●Lex和YACC的源程序框架
五.课程设计实验步骤
我们对常量、变量、临时变量、保留关键字(if、while、begin、end、else、then、do等)、关系运算符、逻辑运算符、分号、括号等,规定其内部定义如下:
符号种别编码说明
sy_if 0 保留字if
sy_then 1 保留字then
sy_else 2 保留字else
sy_while 3 保留字while
sy_begin 4 保留字begin
sy_do 5 保留字do
sy_end 6 保留字end
a 7 赋值语句
semicolon 8 “ ; ”
e 9 布尔表达式
Jinghao 10 “ # ”
S 11 语句
L 12 复合语句
Tempsy 15 临时变量
EA 18 B and(即布尔表达式中的B∧)
EO 19 B or(即布尔表达式中的B∨)
Plus 34 “ + ”
Times 36 “ * ”
Becomes 38 “ := ”赋值
Op_and 39 “ and ”
Op_or 40 “ or ”
Op_not 41 “ not ”
Rop 42 关系运算符
Lparent 48 “ ( ”
Rparent 49 “ ) ”
Ident 56 变量
Intconst 57 整常量
#include "stdio.h" /*如果使用TC的话,需要配置头文件路径*/
#include "string.h"
#define ACC -2
/************************/
#define sy_if 0
#define sy_then 1
#define sy_else 2
#define sy_while 3
#define sy_begin 4
#define sy_do 5
#define sy_end 6
#define a 7
#define semicolon 8
#define e 9
#define jinghao 10
#define S 11
#define L 12
#define tempsy 15
#define EA 18 /*E and*/
#define E0 19 /E or*/
#define plus 34
#define times 36
#define becomes 38
#define op_and 39
#define op_or 40
#define op_not 41
#define rop 42
#define lparent 48
#define rparent 49
#define ident 56
#define intconst 57
1.2 变量及数据结构说明
编译程序中涉及到的变量及数据结构说明如下:
char ch='\0'; /*从字符缓冲区读取当前字符*/ int count=0; /*词法分析结果缓冲区计数器*/ static char spelling[10]={""}; /*存放识别的字*/
static char line[81]={""}; /*一行字符缓冲区,最多80个字符*/ char *pline; /*字符缓冲区指针*/
static char ntab1[100][10]; /*变量名表,共100项,每项长度10*/ struct ntab
{
int tc; /*真值*/
int fc; /*假值*/
}ntab2[200]; /*在布尔表达式E中保存有关布尔变量的真、假值*/ int label=0; /*指向ntab2的指针*/
struct rwords{
char sp[10];
int sy;
}; /*保留字表的结构,用来与输入缓冲区中的单词进行匹配*/ struct rwords reswords[10]={{"if",sy_if},
{"do",sy_do},
{"else",sy_else},
{"while",sy_while},
{"then",sy_then},
{"begin",sy_begin},
{"end",sy_end},
{"and",op_and},
{"or",op_or},
{"not",op_not}}; /*保留字表初始化,大小为10*/ struct aa{
int sy1; /*存放单词的种别编码*/
int pos; /*存放单词自身的值*/
}buf[1000], /*词法分析结果缓冲区*/
n, /*读取二元式的当前字符*/
n1, /*当前表达式中的字符*/
E, /*非终结符*/
sstack[100], /*算术或布尔表达式加工处理使用的符号栈*/ ibuf[100], /*算术或布尔表达式使用的缓冲区*/
stack[1000]; /*语法分析加工处理使用的符号栈*/
struct aa oth; /*四元式中的空白位置*/
struct fourexp{
char op[10];
struct aa arg1;
struct aa arg2;
int result;
}fexp[200]; /*四元式的结构定义*/
int ssp=0; /*指向sstack栈指针*/
struct aa *pbuf=buf; /*指向词法分析缓冲区的指针*/
int nlength=0; /*词法分析中记录单词的长度*/
int tt1=0; /*变量名表指针*/
char *cfile; /*源程序文件,~为结束符*/
int lnum=0; /*源程序行数记数*/
int sign=0; /*sign=1为赋值语句;=2为while语句;=3为if语句*/ /*******************************************************/
int newt=0; /*临时变量计数器*/
int nxq=100; /*nxq总是指向下一个将要形成的四元式地址,*/
/*每次执行gen()时,地址自动增1*/
int lr; /*扫描LR分析表1过程中保存的当前状态值*/ int lr1; /*扫描LR分析表2或表3所保存的当前状态值*/ int sp=0; /*查找LR分析表时状态栈的栈顶指针*/
int stack1[100]; /*状态栈1定义*/
int sp1=0; /*状态栈1的栈顶指针*/
int num=0; /*算术或布尔表达式缓冲区指针*/
struct ll{
int nxq1; /*记录下一条四元式的地址*/
int tc1; /*真值链*/
int fc1; /*假值链*/
}labelmark[10]; /*记录语句嵌套层次的数组,*/
/*即记录嵌套中每层的布尔表达式E的首地址*/ int labeltemp[10]; /*记录语句嵌套层次的数组,*/
/*即记录每层else之前的四元式地址*/ int pointmark=-1, /*labelmark数组指针*/
pointtemp=-1; /*labeltemp数组指针*/
1.3 主函数main()
void main()
{
cfile=fopen("pas.dat","r"); /*打开C语言源文件*/
readch(); /*从源文件读一个字符*/
scan(); /*词法分析*/
disp1();
disp3();
stack[sp].pos=0;
stack[sp].sy1=-1; /*初始化状态栈*/
stack1[sp1]=0; /*初始化状态栈1*/
oth.sy1=-1;
printf("\n**************** 状态栈变化过程以及归约顺序 ****************\n"); readnu(); /*从二元式读入一个符号*/
lrparse(); /*语法语义分析产生四元式*/
getch();
disp2();
printf("\n程序运行结束!\n");
getch();
}
1.4 词法分析函数说明
(1)读取函数 readline( )、readch( )
词法分析包含从源文件读取字符的操作,但频繁的读文件会影响程序执行效率,故实际上是从源程序文件“pas.dat”中读取一行到输入缓冲区,而词法分析过程中每次读取一个字符时则是通过执行readch()从输入缓冲区获得的;若缓冲区已被读空,则再执行readline ()从pas.dat中读取下一行至输入缓冲区。
/*************从文件读一行到缓冲区*************/
readline()
{
char ch1;
pline=line;
ch1=getc(cfile);
while(ch1!='\n' && !feof(cfile))
{
*pline=ch1;
pline++;
ch1=getc(cfile);
}
*pline='\0';
pline=line;
}
/*************从缓冲区读取一个字符*********************/
readch()
{
if(ch=='\0')
{
readline();
lnum++;
}
ch=*pline;
pline++;
}
(2)扫描函数 scan( )
扫描函数scan()的功能是滤除多余空格并对主要单词进行分析处理,将分析得到的二元式存入二元式结果缓冲区。
/******************扫描主函数**************************/
scan()
{
int i;
while(ch!='~') /*’~’是源程序结束符号*/
{
switch(ch)
{
case ' ':
break;
case 'a' :
case 'b' :
case 'c' :
case 'd' :
case 'e' :
case 'f' :
case 'g' :
case 'h' :
case 'i' :
case 'j' :
case 'k' :
case 'l' :
case 'm' :
case 'n' :
case 'o' :
case 'p' :
case 'q' :
case 'r' :
case 's' :
case 't' :
case 'u' :
case 'v' :
case 'w' :
case 'x' :
case 'y' :
case 'z' : /*保留字和标识符中的字母只能是小写字母*/ identifier(); /*识别保留字和标识符*/
break;
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
number(); /*识别整常数*/
break;
case '<' :
readch();
if(ch=='=')
{
buf[count].pos=0; /*识别’<=’*/
}
else
{
if(ch=='>') buf[count].pos=4; /*识别’<>’*/
else
{
buf[count].pos=1; /*识别’<’*/
pline--;
}
}
buf[count].sy1=rop; /*识别关系运算符*/
count++;
break;
case '>' :
readch();
if(ch=='=')
{
buf[count].pos=2; /*识别’>=’*/
}
else
{
buf[count].pos=3; /*识别’>’*/
pline--;
}
buf[count].sy1=rop; /*识别关系运算符*/
count++;
break;
case '(' :
buf[count].sy1=lparent; /*识别’(’*/
count++;
break;
case ')' :
buf[count].sy1=rparent; /*识别’)’*/
count++;
break;
case '#' :
buf[count].sy1=jinghao; /*识别’#’*/
count++;
break;
case '+' :
buf[count].sy1=plus; /*识别’+’*/
count++;
break;
case '*' :
buf[count].sy1=times; /*识别’*’*/
count++;
break;
case ':' :
readch();
if(ch=='=')
buf[count].sy1=becomes; /*识别’:=’*/
count++;
break;
case '=' :
buf[count].sy1=rop;
buf[count].pos=5; /*识别’=’,关系算符*/ count++;
break;
case ';' :
buf[count].sy1=semicolon; /*识别’;’*/
count++;
break;
}
readch();
}
buf[count].sy1=-1; /*不可识别的符号*/
}
(3)变量处理及变量名表 find( )
变量处理中首先把以字母开头的字母数字串存到spelling[10]数组中,然后进行识别。识别过程是先让它与保留关键字表中的所有关键字进行匹配,若获得成功则说明它为保留关键字,即将其内码值写入二元式结果缓冲区;否则说明其为变量,这时让它与变量名表中的变量进行匹配(变量匹配函数find()),如果成功,则说明该变量已存在并在二元式结果缓冲区中标记为此变量(单词自身值填为该变量在变量名表中的位置),否则将该变量登记到变量名表中,再将这个新变量存入二元式缓存数组中。
/********************** 变量匹配,查找变量名表 *******************/
find(char spel[])
{
int ss1=0;
int ii=0;
while((ss1==0)&&(ii { if(!strcmp(spel,ntab1[ii])) ss1=1; /*查找,匹配*/ ii++; } if(ss1==1) return ii-1; /*查找到*/ else return -1; /*没查找到*/ } /***************标识符和保留字的识别******************/ identifier() { int iii=0,j,k; int ss=0; k=0; do { spelling[k]=ch; k++; readch(); }while(((ch>='a')&&(ch<='z'))||((ch>='0')&&(ch<='9'))); pline--; spelling[k]='\0'; while((ss==0)&&(iii<10)) { if(!strcmp(spelling,reswords[iii].sp)) ss=1; /*保留字匹配*/ iii++; if(ss==1) { buf[count].sy1=reswords[iii-1].sy; /*是保留字*/ } else { buf[count].sy1=ident; /*是标识符,变量名*/ j=find(spelling); if(j==-1) /*没有在变量名表中则添加*/ { buf[count].pos=tt1; /*tt1是变量名表指针*/ strcpy(ntab1[tt1],spelling); tt1++; nlength++; } else buf[count].pos=j; /*获得变量名自身的值*/ } count++; for(k=0;k<10;k++) spelling[k]=''; /*清空单词符号缓冲区*/ } (5)数字识别 number( ) 数字识别将识别出的数字转换为等值的十进制数值并填入二元式结果缓存数组。 /***********************数字识别**********************/ number() { int ivalue=0; int digit; do { digit=ch-'0'; ivalue=ivalue*10+digit; /*数字字符转换为十进制整常数*/ readch(); }while((ch>='0')&&(ch<='9')); buf[count].sy1=intconst; /*整常数单词符号二元式*/ buf[count].pos=ivalue; count++; pline--; } (6)显示函数 显示函数的功能是在屏幕上输出词法分析的结果(即二元式序列和变量名表),同时给出二元式个数及源程序行数统计。 /************** 显示词法分析结果:单词符号二元式 ****************/ disp1() int temp1=0; printf("\n********* 词法分析结果 ************************\n"); for(temp1=0;temp1 { printf("%d\t%d\n",buf[temp1].sy1,buf[temp1].pos); if(temp1==20) { printf("Press any key to continue.......\n"); getch(); } } getch(); } /***************** 打印变量名表 *************************/ void disp3() { int tttt; printf("\n\n程序总共%d行,产生了%d个二元式!\n",lnum,count); getch(); printf("\n************** 变量名表 *********************\n"); for(tttt=0;tttt printf("%d\t%s\n",tttt,ntab1[tttt]); getch(); } 六.上机调试情况 写明源代码运行环境和步骤,用了几个测试用例,并抓图。把源代码打包上交,包括电子版的实验报告。 七.小结 1.课程设计中遇到的问题及解决办法。 2.课程设计还存在哪些问题。 3.对本课程设计有哪些建议。 4.其它需要补充的问题。 5.每个同学写一份对本课程设计有哪些收获和心得体会,依次附在后面,不用另起一页。 八.参考文献 参考文献格式参照学校的论文撰写规范。