编译原理词法分析报告
年 月 日
1、 实验目的
1、为初等函数运算语言构造词法分析器。
2、掌握生成词法分析器的方法,加深对词法分析原理的理解。 3、掌握设计、编制并调试词法分析程序的思想和方法
2、实验内容
一、根据下面的要求设计初等函数运算语言的词法模式,并用正则式表达出来
1、 初等函数运算语言的常量为实数类型,其定义方式为实数的最一般书写方式,如:
123.321。具体要求:不支持整数部分大于0时首数字为0;不支持小数点后结尾为0;不支持科学记数法;不支持仅为整数时有小数点;支持负数符号,不支持正数符号。
2、 初等函数运算语言的变量采用与C语言的标识符定义一样的方式:首字符为字母或
下划线;其他的为字母、数字及下划线的混合串;区分大小写;变量长度不超过32个字符。
3、 初等函数运算语言需要处理的函数仅为表一中所列举的内容。函数的格式及参数内
容也如表一所示。
4、 初等函数运算语言支持四则运算,其计算的符号与C语言相同,为:+-*/。 5、 初等函数运算语言的合法的分隔符包括:空格、制表符、、分行符圆括号(左、右)、
分号。其中空格、制表符、分行符可以出现在任何两个不同的单词中间;圆括号(左、右)用于表达式中,用于改变运算的优先级,以及标识函数的参数;分号用于标识一个语句的结束。
6、 初等函数运算语言支持的常量还包括:PI,E。其中,PI为圆周率,E为自然常数。
二、将正则式转化为最小DFA,给出该DFA的形式化表示和图形表示。 三、根据DFA给出状态转换表。
四、给出初等函数运算语言的记号表,即词法分析中,语言中的记号将分为多少类,每一类型的编码、类型、属性等内容是什么。
五、编写词法分析器,将输入的字符串转化成为记号流,便于后续的语法分析工作。要求词法分析器中能够识别词法错误。
2.1词法模式设计/正则式
分隔符:compart=\t|\n|(|)|;|空格 运算符:operation=+|-|*|/|=|^
变量:variable=[a~zA~Z]([ a~zA~Z_0~9])*
常量:constant=(ε|-)((0|(1~9)(0~9)*)(.(0~9)*(1~9)|ε))|PI|E
2.2DFA
注:id表示字母,num表示数字
2.3状态转换表
注:0是初态,2,6是中间状态,1,3,4,7是终态,其中1表示标示符,3,4,7是实数,8表示不合法的状态,9表示'-'为减号
2.4记号表
3、 实验程序清单
#include #include using namespace std;
#define max 10 char ch =' ';
string key[7]={"sin","cos","tg","ctg","log","lg","ln"};//关键字 char compart[6]={'\t','\n','(',')',';',' '};//分隔符 char operation[5]={'+','-','*','/','='};//运算符
//int s[8]={0,1,2,3,4,6,7,8};//状态集合,0是初态,2,6是中间状态,1,3,4,7是终态, 其中1表示标示符,3,4,7是实数,8表示不合法的状态
int token[23]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22};
//0表示sin,1表示cos,2表示tg,3表示ctg,4表示log,5表示lg,6表示ln,7表示(,8表示),9表示;,10表示?,11表示+,12表示-,13表示*,14表示/,15表示=,
//16表示常量,17表示变量,18表示不可识别标示符,19表示^,20表示逗号,21表示{,22
表示}
char arr[32]; int state=0; int s=0;
bool tag=0;//tag=0表示'-'为负数的负号,tag=1表示'-'为减号 FILE *fp;
int IsKey(string c)
{ //判断是否为关键字 for(int i=0;i
if(key[i].compare(c)==0) return i;//返回下标,下标和其token记号一致 }
return -1; }
bool IsLetter(char c)
{ //判断是否为字母
if(((c='a'))||((c='A'))) return 1; else return 0; }
bool IsNum(char c)
{ //判断是否为1-9的数字 if(c>='1'&&c
bool IsUnderline(char c)
{ //判断是否为下划线 if(c=='_') return 1; else return 0; }
void move(char ch,int s)
{//在状态s接收字符ch后,移动的新状态 //arr=arr+ch; switch(s) { case 0:
{ if(ch=='_'||IsLetter(ch)) state= 1; else { if(ch=='0') state=3; else if(IsNum(ch)) state=4; else if(ch=='-') state=2; else state=8; } break; } case 1: { if(IsNum(ch)||ch=='_'||IsLetter(ch)||ch=='0') state=1; else if(ch=='-') state=9; else state=8; break; } case 2: { if(ch=='0') state=3; else if(IsNum(ch)) state=4; else state=8; break; } case 3: { if(ch=='.') state=6;
else if(ch=='-') state=9; else state=8; break; } case 4: {
if(IsNum(ch)||ch=='0') state=4; else if(ch=='-') state=9; else if(ch=='.') state=6; else state=8; break; } case 6: {
if(ch=='0') state=6; else if(IsNum(ch)) state=7; else state=8; break; } case 7: {
if(ch=='0') state=6; else if(IsNum(ch)) state=7; else if(ch=='-') state=9;
else state=8; break; } case 8: { if(ch=='-') state=9; else state=8; break; } }//switch }
void judge(char arr[]) { if(s==3||s==4||s==7) coutcase'*' : cout
cout
case'(' : cout
void analyse(FILE*fp) { int i=0; while(ch!='EOF') { char arr[32]={'\0'};
while(ch!='('&&ch!=')'&&ch!='
'&&ch!='\t'&&ch!='\n'&&ch!='{'&&ch!='}'&&ch!=','&&ch!='?'&&ch!='-' &&ch!='^'&&ch!=';'&&ch!='+'&&ch!='*'&&ch!='/'&&ch!='='&&ch!='EOF') { if (i
s=state; } i++; ch=fgetc(fp); }//while judge(arr); if(ch=='-') { char arr[32]={'\0'}; i=0; while(ch!='('&&ch!=')'&&ch!='
'&&ch!='\t'&&ch!='\n'&&ch!='{'&&ch!='}'&&ch!=','&&ch!='?'
&&ch!='^'&&ch!=';'&&ch!='+'&&ch!='*'&&ch!='/'&&ch!='='&&ch!='EOF') { move(ch,s); s=state; arr[i]=ch; if(state!=9) { i++; ch=fgetc(fp); } else { //s=9为减号时 char arr[32]={'\0'}; break; } }//while judge(arr); s=0; i=0; ch=fgetc(fp); } else { char arr[32]={'\0'}; ch=fgetc(fp); i=0; s=0; }
}//while
}
void main()
{
char in_fn[30];
FILE * fp;
cout
for(;;)
{
cin>>in_fn;
if((fp=fopen(in_fn,"r"))!=NULL)//意思是文件指针fpin在调用fopen打开文件如果失败,则会成为一个空指针!
break; //文件顺利打开后,指向该流的文件指针就会被返回。
else
cout
}
cout
ch=fgetc(fp);
char arr[32]={'\0'};
arr[0]=ch;
// fseek(fp,-1,1);
analyse(fp);
fclose(fp);
cout
int a;
cin>>a;
}
4、 调试过程和运行结果
5、 程序的主要部分及其功能说明
由DFN得到的状态转换程序:
void move(char ch,int s)
{//在状态s接收字符ch后,移动的新状态
switch(s)
{
//状态0时,当接收到字符ch为字母或下划线时状态S转移到状态1,ch为0时转移到状态3,ch为1-9时转移到状态4,ch为‘-’时,转移到状态2,否则转移到状态8
case 0:
{
if(ch=='_'||IsLetter(ch))
state= 1;
else
{
if(ch=='0')
state=3;
else
if(IsNum(ch))
state=4;
else
if(ch=='-')
state=2;
else
state=8;
}
break;
}
case 1:
{
if(IsNum(ch)||ch=='_'||IsLetter(ch)||ch=='0')
state=1;
else
if(ch=='-')
state=9;
else
state=8;
break;
}
case 2:
{
if(ch=='0')
state=3;
else
if(IsNum(ch))
state=4;
else
state=8;
break;
}
case 3:
{
if(ch=='.')
state=6;
else
if(ch=='-')
state=9;
else
state=8;
break;
}
case 4:
{
if(IsNum(ch)||ch=='0')
state=4;
else
if(ch=='-')
state=9;
else
if(ch=='.')
state=6;
else
state=8;
break;
}
case 6:
{
if(ch=='0')
state=6;
else
if(IsNum(ch))
state=7;
else
state=8;
break;
}
case 7:
{
if(ch=='0')
state=6;
else
if(IsNum(ch))
state=7;
else
if(ch=='-')
state=9;
else
state=8;
break;
}
case 8:
state=8;
}//switch
}
从文件读字符,并进行词法分析,当读入的字符不为运算符和界符时就往下读,并将读到的字符存入数组arr,当遇到运算符和界符时调用judge(arr)进行分析,输出arr所存字符串及其属性,当遇到‘-’时,需要判断它是表示减号还是表示负数的负号,主要是根据其状态来判断,如果输入‘-’后,若其状态没有调到状态9,则其为负数的负号,否则为减号。主要程序如下:
void analyse(FILE*fp)
{
int i=0;
while(ch!='EOF')
{
char arr[32]={'\0'};
while(ch!='('&&ch!=')'&&ch!='
'&&ch!='\t'&&ch!='\n'&&ch!='{'&&ch!='}'&&ch!=','&&ch!='?'&&ch!='-'
&&ch!='^'&&ch!=';'&&ch!='+'&&ch!='*'&&ch!='/'&&ch!='='&&ch!='EOF')
{
if (i
{
arr[i]=ch;
move(ch,s);
s=state;
}
i++;
ch=fgetc(fp);
}//while
judge(arr);
if(ch=='-')//如果遇到‘-’
{
char arr[32]={'\0'};
i=0;
while(ch!='('&&ch!=')'&&ch!='
'&&ch!='\t'&&ch!='\n'&&ch!='{'&&ch!='}'&&ch!=','&&ch!='?'
&&ch!='^'&&ch!=';'&&ch!='+'&&ch!='*'&&ch!='/'&&ch!='='&&ch!='EOF')
{
move(ch,s);
s=state;
arr[i]=ch;
if(state!=9)
{
i++;
ch=fgetc(fp);
}
else
{ //s=9为减号时
char arr[32]={'\0'};
break;
}
}//while
judge(arr);
s=0;
i=0;
ch=fgetc(fp);
}
else
{//是除‘-’外的运算符和界符时
char arr[32]={'\0'};
ch=fgetc(fp);
i=0;
s=0;
}
}//while
}
6、 实验收获体会
通过实验,我理解了如何通过DFA的状态转换来构造词法分析器,掌握了生成词法分析器的方法,加深了对词法分析原理的理解。同时在编程的过程中提高了编程能力。
7、 改进意见
该词法分析器所能识别的字符串是有限的,不够完全,像main,void ,double,int 等许多关键字都没有定义到关键字的数组里面去,仅能被识别为一个变量。