武汉理工大学编译原理实验报告.docx
《武汉理工大学编译原理实验报告.docx》由会员分享,可在线阅读,更多相关《武汉理工大学编译原理实验报告.docx(23页珍藏版)》请在冰点文库上搜索。
武汉理工大学编译原理实验报告
学生学号
实验课成绩
武汉理工大学
学生实验报告书
实验课程名称《编译原理》
开课学院计算机科学与技术学院
指导老师姓名饶文碧
学生姓名
学生专业班级软件
2014—2015学年第1学期
实验课程名称:
编译原理
实验项目名称
词法分析器的设计
实验成绩
实验者
专业班级
软件
组别
同组者
实验日期
第一部分:
实验分析与设计(可加页)
一、实验内容描述(问题域描述)
1.问题描述:
对于常用高级语言(如Pascal、C语言)的各类单词进行词法分析。
2.实验内容:
完成对某一种常用高级语言(如Pascal、C语言、PL/0语言)的各类单词进行词法分析,即对源程序从左到右进行扫描,对组成源程序的字符串拼接成为单词;并把其转换成属性字输出。
3.实验要求:
(1)选择常用高级程序设计语言(如Pascal、C语言、PL/0语言)的源程序作为词法分析对象。
(2)根据教学要求和学生具体情况,从上列语言之一中选取它的一个适当大小的子集,可以选取一类典型单词,也可以尽可能使各种类型的单词都能兼顾到。
其基本要求是:
对源程序从左到右进行扫描,对组成源程序的字符串拼接成为单词;并把其转换成属性字输出。
二、实验基本原理与设计(包括实验方案设计,实验手段的确定,试验步骤等,用硬件逻辑或者算法描述)
1.待分析的简单语言的词法:
(1)关键字:
mainvoidreturnifendthenelseint
所有的关键字都是小写。
(2)运算符和界符:
{},;<<=<>===>>=:
:
=++=--=**=//=
#()
(3)其他单词是标志符(ID)和整形常数(NUM),通过以下正规式定义:
ID=letter(letter|digit)*NUM=digitdigit*
(4)空格空白、制表符和换行符组成。
空格一般用来分隔ID、NUM、运算符、界符和关键字,词法分析阶段通常被忽略。
2.各种单词符号对应的种别码(针对于较为熟悉的c语言)
单词符号
种别码
单词符号
种别码
main
1
=
18
void
2
==
19
return
3
>
20
if
4
>=
21
end
5
:
22
then
6
:
=
23
else
7
+
24
int
8
+=
25
Letter(letter|digit)
9
-
26
digitdigit*
10
-=
27
{
11
*
28
}
12
*=
29
13
/
30
;
14
/=
31
<
15
#
32
<=
16
(
33
<>
17
)
34
3.实验源代码如下:
#include
#include
#include
#include
//头文件库
charTOKEN[30];
char*table[9]={"","main","void","return","if","end","then","else","int"};
//定义关键字
externintlookup(char*);
externvoidout(int,char*);
externvoidreport_error(void);
//函数及全局变量声明
/*extern可置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。
另外,extern也可用来进行链接指定*/
intlookup(char*TOKEN)
{
intm,i;
for(i=1;i<8;i++)
{
if((m=strcmp(TOKEN,table[i]))==0)
//table中关键字匹配
returni;
}
return0;
}
//关键字匹配函数,返回关键字在表中的位置,空出空格
voidout(intc,char*TOKEN)
{
printf("(%d,%s)\n",c,TOKEN);
}
//输出在表中位置和读取的字符
voidreport_error()
{
printf("error!
\n");
}
//输出没有考虑的情况
voidscanner(FILE*fp)
{
charTOKEN[30]={'\0'};
charch;
inti,c;
ch=fgetc(fp);
//获取字符,指针fp并自动指向下一个字符
if(isalpha(ch))
//判断该字符是否是字母
{
TOKEN[0]=ch;
ch=fgetc(fp);
i=1;
while(isalnum(ch))
//判断该字符是否是字母或数字
{
TOKEN[i]=ch;
i++;
ch=fgetc(fp);
}
TOKEN[i]='\0';
fseek(fp,-1,1);
//回退一个字符
c=lookup(TOKEN);
if(c==0)
out(9,TOKEN);
//输出标识符号,定编号为9
else
out(c,TOKEN);
//输出表中编号和关键字
}
elseif(isdigit(ch))
//判断是否是数字
{
TOKEN[0]=ch;
ch=fgetc(fp);
i=1;
while(isdigit(ch))
{
TOKEN[i]=ch;
i++;
ch=fgetc(fp);
}
//判断是否是连续数字
TOKEN[i]='\0';
fseek(fp,-1,1);
out(10,TOKEN);
//输出数字,定编号为10
}
else
{
TOKEN[0]=ch;
switch(ch)
{
case'{':
out(11,TOKEN);
break;
case'}':
out(12,TOKEN);
break;
case',':
out(13,TOKEN);
break;
case';':
out(14,TOKEN);
break;
case'<':
ch=fgetc(fp);
//读取下一个字符,判断<,<=,<>(空标签)
if(ch=='=')
{
TOKEN[1]=ch;
out(15,TOKEN);
}
//判断<=
elseif(ch=='>')
{
TOKEN[1]=ch;
out(17,TOKEN);
}
//判断<>空标签
else
{
fseek(fp,-1,1);
out(16,TOKEN);
}
//判断小于号<
break;
case'=':
ch=fgetc(fp);
if(ch=='=')
{
TOKEN[1]=ch;
out(19,TOKEN);
}
else
{
fseek(fp,-1,1);
out(18,TOKEN);
}
break;
//判断=,==
case'>':
ch=fgetc(fp);
//读取下一个字符,判断>,>=
if(ch=='=')
{
TOKEN[1]=ch;
out(21,TOKEN);
}
else
{
fseek(fp,-1,1);
out(20,TOKEN);
}
break;
case':
':
ch=fgetc(fp);
//读取下一个字符,判断:
:
=,若是关键词写入case或者?
:
则必要
if(ch=='=')
{
TOKEN[1]=ch;
out(23,TOKEN);
}
else
{
fseek(fp,-1,1);
out(22,TOKEN);
}
break;
case'+':
ch=fgetc(fp);
//读取下一个字符,判断+,+=
if(ch=='=')
{
TOKEN[1]=ch;
out(25,TOKEN);
}
else
{
fseek(fp,-1,1);
out(24,TOKEN);
}
break;
case'-':
ch=fgetc(fp);
//读取下一个字符,判断-,-=
if(ch=='=')
{
TOKEN[1]=ch;
out(27,TOKEN);
}
else
{
fseek(fp,-1,1);
out(26,TOKEN);
}
break;
case'*':
ch=fgetc(fp);
//读取下一个字符,判断*,*=
if(ch=='=')
{
TOKEN[1]=ch;
out(29,TOKEN);
}
else
{
fseek(fp,-1,1);
out(28,TOKEN);
}
break;
case'/':
ch=fgetc(fp);
//读取下一个字符,判断/,/=
if(ch=='=')
{
TOKEN[1]=ch;
out(31,TOKEN);
}
else
{
fseek(fp,-1,1);
out(30,TOKEN);
}
break;
case'#':
out(32,TOKEN);
break;
case'(':
out(33,TOKEN);
break;
case')':
out(34,TOKEN);
break;
//判断#(头文件)
default:
report_error();
break;
}
}
}
//扫描函数
intmain(void)
{
FILE*fp;
charch;
if((fp=fopen(".\\tk.txt","r"))==NULL)
//取当前目录下的tk.txt文件的第一个字符
{
fprintf(stderr,"erroropening!
\n");
//格式化输出到流
//错误打印到屏幕上Bydefault,standardinputisreadfromthekeyboard,whilestandardoutputand//standarderrorareprintedtothescreen
exit
(1);
//非0,非正常退出
}
do
{
ch=fgetc(fp);
if(ch=='$')
break;
//作为文件结尾
elseif(ch=='')
scanner(fp);
//空格略过
else
{
fseek(fp,-1,1);
scanner(fp);
}
//扫描
}while(ch!
='$');
system("pause");
return0;
}
三、主要仪器设备及耗材
PC,VisualC++6.0
第二部分:
实验调试与结果分析(可加页)
一、调试过程(包括调试方法描述、实验数据记录,实验现象记录,实验过程发现的问题等)
按照要求编写完代码之后,首先检查一下有没有错误语法错误,然后编译连接运行,重复输出多种表达式及c语言代码,寻找出错误并且改正,力求输出到边到脚,考虑多种情况来保证代码的完善性。
由于c语言中头文件等需要用到#符,则本程序以$作为结尾符号。
二、实验结果及分析(包括结果描述、实验现象分析、影响因素讨论、综合分析和结论等)
(1)输入:
输出
(2)输入
输出
三.实验小结、建议及体会
词法分析(英语:
lexicalanalysis)是计算机科学中将字符序列转换为单词(Token)序列的过程。
进行词法分析的程序或者函数叫作词法分析器(Lexicalanalyzer,简称Lexer),也叫扫描器(Scanner)。
词法分析器一般以函数的形式存在,供语法分析器调用。
在本次实验中,通过对代码的编写,完善和调试,我逐渐深刻地明白了编译原理的工作流程,是怎样一步一步地解析一种高级语言到中间代码的。
同样的转化思想可以应用到生活的方方面面,语言或者说事物的本质才会被慢慢挖掘出来。
实验课程名称:
编译原理
实验项目名称
赋值语句的翻译程序设计
实验成绩
实验者
专业班级
组别
同组者
实验日期
第一部分:
实验分析与设计(可加页)
一、实验内容描述(问题域描述)
1.实验目的
(1)通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。
(2)选择最有代表性的语法分析方法,如递归子程序法;选择对各种常见程序语言都具备的语法结构,如赋值语句,特别是表达式,作为分析对象。
2.实验准备
微机CPUP4以上,256M以上内存,安装好C语言,或C++,或VisualC++.
3.实验时间
4学时
4.实验内容
构造表达式部分的语法分析器。
分析对象〈算术表达式〉的BNF定义如下:
<表达式>:
:
=[+|-]<项>{<加法运算符><项>}
<项>:
:
=<因子>{<乘法运算符><因子>}
<因子>:
:
=<标识符>|<无符号整数>|‘(’<表达式>‘)’
<加法运算符>:
:
=+|-
<乘法运算符>:
:
=*|/
<关系运算符>:
:
==|#|<|<=|>|>=
5.实验要求
将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,
对于语法正确的表达式,报告“语法正确”;
对于语法错误的表达式,报告“语法错误”,指出错误原因。
把语法分析器设计成一个独立一遍的过程。
语法分析器的编写方法采用递归子程序法。
6.输入输出
输入:
PL/0表达式,用实验一的输出形式作为输入。
例如:
对于PL/0表达式,(a+15)*b用下列形式作为输入:
(lparen,()
(ident,a)
(plus,+)
(number,15)
(rparen,))
(times,*)
(ident,b)
输出:
对于语法正确的表达式,报告“语法正确”;
对于语法错误的表达式,报告“语法错误”,指出错误原因。
二、实验基本原理与设计(包括实验方案设计,实验手段的确定,试验步骤等,用硬件逻辑或者算法描述)
实验基本原理:
采用递归子程序法分析输入表达式的语法。
实验源代码如下:
#include
#include
//头文件
charread();
voidwrite();
charlookhead;
voidE();
voidT();
voidG();
voidS();
voidF();
voidmatch(charch);
voiderror();
//函数声明
charstring[10];
inttop;
//全局变量
intmain()
{
write();
//写入实验一的结果数据
lookhead=read();
//读取数据
E();
//递归子程序法语法分析
puts(string);
printf("为合法字符串!
\n");
//按要求输出判断结果
system("pause");
return0;
}
voidE()
{
printf("E->TG\n");
T();
G();
}
voidG()
{
if(lookhead=='+')
{
printf("G->+TG\n");
match('+');
T();
G();
}
elseif(lookhead=='-')
{
printf("G->-TG\n");
match('-');
T();
G();
}
else
{
printf("G->ε\n");
}
}
//+,-符号的语法分析判断
voidT()
{
printf("T->FS\n");
F();
S();
}
voidS()
{
if(lookhead=='*')
{
printf("s->*FS\n");
match('*');
F();
S();
}
elseif(lookhead=='/')
{
printf("S->/FS\n");
match('/');
}
else
{
printf("S->ε\n");
}
}
//乘除符号的语法分析判断
voidF()
{
if(lookhead=='(')
{
printf("F->(E)\n");
match('(');
E();
match(')');
}
elseif(lookhead=='i')
{
match('i');
}
else
error();
}
//(,)和字符的语法分析判断
voidmatch(charch)
{
lookhead=read();
}
//读取下一字符串
voidwrite()
{
intx;
chary,str[100];
top=0;
printf("请输入待分析字符串序列:
\n");
do
{
gets(str);
sscanf(str,"(%d,%c)",&x,&y);
string[top]=y;
top++;
}while(y!
='$');
string[--top]='\0';
top=0;
}
//按要求输入字符串
charread()
{
charcha;
cha=string[top];
top++;
returncha;
}
//挨个读字符串
voiderror()
{
puts(string);
printf("为非法字符串!
\n");
system("pause");
exit(0);
}
//出错处理,按要求输出
三、主要仪器设备及耗材
PC,MiscrosoftVisualC++6.0
第二部分:
实验调试与结果分析(可加页)
一、调试过程(包括调试方法描述、实验数据记录,实验现象记录,实验过程发现的问题等)
编写完代码之后,首先检查一下有没有简单的语法错误登,然后编译连接运行,通过dos自带的复制粘贴,输入实验一的结果,得到实验二的语法分析过程和结果。
秉着简洁明了的原则,本程序能识别表达式即可,故输入实验一结果一得到的代码。
最后用(1,$)来结束输入。
二、实验结果及分析(包括结果描述、实验现象分析、影响因素讨论、综合分析和结论等)
输入
分析结果
三、实验小结、建议及体会
语法分析是词法分析的下一个阶段。
语法分析的任务是识别单词符号是否符合给定的语法规则。
语法分析所依据的是语言的语法规则,即描述程序结构的规则。
通过语法分析确定输入串是否构成一个语法上正确的程序。
通过本次的程序编写和调试,我深刻明白了语法分析的原理和过程。
本程序采用的递归子程序法相对简单直观,易于构造,但要求比较苛刻,必须满足LL
(1)文法,且递归调用较多,考虑得不大全面,未能输出错误原因。
需要改进的路还很长。