实验报告二
词法分析
一、总体设计思想
1)实验目的:
设计编制并调试一个词法分析程序,加深对构造词法分析器的原
理和技术的理解与应用。
2)实验具体要求:
针对计算机高级程序语言——C 语言子语言,运用恰当的词法分析技术线路,设计和实现C 语言子语言的词法分析器。
编程语言:C 语言/JAV A 语言 平台选择:Linux/Windows 技术线路( 任选其一)
⏹ 正规式→NFA →DFA →min DFA→程序设计 ⏹ 正规文法→NFA →DFA →min DFA→程序设计 词法分析器程序输出形式,参见◤输出形式设计◢ 具有出错处理功能
C 语言子语言词法描述——涉及的单词类如下:
(1)关键字
main if else while do for int void char
(2)运算符和界符
注:#为结束标志符,详见◤程序框架◢
(3)标识符
正规式:ID=letter(letter|digit)*
(4)整型常数
正规式:NUM= digit(digit)*
3)词法分析器构造原理:
词法分析器就是根据语言的此法规则构造出识别其单词的有限自动机,它是一个数学模型,先给出识别各类单词的状态转换图,再将各类单词的状态转换图的初始状态合并成一个唯一的初状态;然后化简并调整冲突的状态编号;最后再将有限自动机变成一个可行的词法分析器。
二、种别码表设计及其在计算机中存放表示
1)单词种别码设计:
单 词 种 别 码 对 照 表
2)输出形式设计:
词法分析器的输入是源程序字符串,输出是对应的单词串。每个单词按照二元组(种别码,单词符号本身)格式输出。
例如:假设源程序为
则词法分析器对应输出的结果是:
三、词法分析程序详细设计
1)算法构造思想:
依据建立的识别单词的DFA ,设计算法,其框架如下。其中, ① syn 存放单词的种别码;
② token 存放符合C 语言子语言词法规则的单词; ③ sum 存放整型常量的单词。 ④ Prog 存放所有输入的字符。
⑤ isSignal 判断是否带正负号(0不带,1负号,2正号)。 ⑥ isDecimal 判断是否是小数。 ⑦ isExp 判断是否是指数。
2)主要模块算法的框图描述:
四、 主要代码
#include #include #include
char prog[80]; //存放所有输入字符
char token[32]; //② token 存放符合C 语言子语言词法规则的单词; char ch; //单个字符
int syn,p,m,n; // ① syn存放单词的种别码 double sum; //③ sum存放整型常量的单词。 int count;
int isSignal; //是否带正负号(0不带,1负号,2正号) int isDecimal; //是否是小数 double decimal; //小数 int isExp; //是否是指数 int index; //指数幂 int isNegative; //是否带负号 double temp; int temp2; int repeat; void scanner();
char *rwtab[9]={"main", "if", "else", "while", "do", "for" , "int" , "char" , "void"}; void main() { p=0; count=0; isDecimal=0; index=0; repeat=0;
printf("\nplease input a range of data, with the end of #:\n"); do{
ch=getchar(); prog[p++]=ch;
}while(ch!='#'); //输入以#号键结束 p=0; do{
scanner(); //扫描,单词 switch(syn) { case 11:
if(isDecimal==0)
{
//加了1个强制类型转换 printf("(%d,%d) ",syn,(int)sum); break; }
else if(isExp==1) {
printf("(%d,%e) ",syn,sum); isExp=0; isDecimal=0; break; }
else if(isDecimal==1) {
printf("(%d,%f) ",syn,sum); isDecimal=0; break; } case -1:
printf("输入错误:") printf(" \n ");
break; default:
printf("(%d,%s) ",syn,token); } }while(syn!=0); getchar(); }
void scanner() {
sum=0; decimal=0; m=0;
for(n=0;n
ch=prog[p++]; //从prog 中读出一个字符到ch 中 while(ch==' ') //跳过空字符(无效输入) ch=prog[p++];
if(((ch>='a')&&(ch='A')&&(ch
while(((ch>='a')&&(ch='A')&&(ch='0')&&(ch
token[m++]=ch; //ch=>token ch=prog[p++]; //读下一个字符 }
token[m++]='\0'; p--; //回退一格 syn=10;
//如果是"main", "if", "else", "while", "do", "for" , "int" , "char" , "void"标识符中的一个
for(n=0;n
if(strcmp(token,rwtab[n])==0) {
syn=n+1; break; } }
else if((ch>='0')&&(ch
if(isSignal==1) {
//token[m++]='-'; }
while((ch>='0')&&(ch
sum=sum*10+ch-'0'; //ch中数字本身是当做字符存放的 ch=prog[p++]; } if(ch=='.') {
isDecimal=1; ch=prog[p++];
count=0; //之前忘了清零,123.123+123.123#两个浮点数就无法识别 while((ch>='0')&&(ch
//pow(x,y)计算x 的y 次幂 temp=(ch-'0')*pow(0.1,++count); decimal=decimal+temp; //AddToDec(); ch=prog[p++]; }
sum=sum+decimal; }
if(ch=='e'||ch=='E') {
isExp=1; ch=prog[p++]; if(ch=='-') {
isNegative=1; ch=prog[p++]; }
while((ch>='0')&&(ch
//指数
index=index*10+ch-'0'; ch=prog[p++]; } //10的幂
//123e3代表123*10(3)
//sum=sum*pow(10,index);是错误的 if(isNegative)
sum=sum*pow(0.1,index); else
sum=sum*pow(10,index); }
if(isSignal==1) {
sum=-sum; isSignal=0; } p--; syn=11; }
else switch(ch) {
case '
token[m++]=ch;
ch=prog[p++];
if(ch=='=')
{
syn=22;
token[m++]=ch;
}
else
{
syn=20;
p--;
}
break;
case '>':
m=0;
token[m++]=ch;
ch=prog[p++];
if(ch=='=')
{
syn=24;
token[m++]=ch;
}
else
{
syn=23;
p--;
}
break;
case ':':
syn=17; token[m++]=ch;
break;
case '+':
temp2=prog[p];
token[m++]=ch;
if((temp2>='0')&&(temp2
{
isSignal=2; //isSignal正数
ch=prog[p++];
repeat=0;
goto IsNum;
}
if(((temp2=='+')||(temp2=='-'))&&(repeat==0)) //如果重复出现符号,才将后边的+,-视为正负号
{
repeat=1;
//ch=prog[p++];
}
syn=13;
break;
case '-':
temp2=prog[p];
token[m++]=ch;
if((temp2>='0')&&(temp2
{
isSignal=1;
ch=prog[p++]; //读“-”下一个字符
repeat=0;
goto IsNum; //转到数字的识别
}
if(((temp2=='+')||(temp2=='-'))&&(repeat==0)) //如果重复出现符号,才将后边的+,-视为正负号
{
repeat=1; //预言会重复
//ch=prog[p++]; //读下一个字符
}
syn=14;
break;
case '*':
temp2=prog[p];
token[m++]=ch;
syn=15;
break;
case '/':
syn=16;
token[m++]=ch;
break;
case '=':
syn=18;
token[m++]=ch;
break;
case ';':
syn=26;
token[m++]=ch;
break;
case '(':
temp2=prog[p];
token[m++]=ch;
syn=27;
break;
case '{':
temp2=prog[p];
token[m++]=ch;
syn=12;
break;
case '}':
temp2=prog[p];
token[m++]=ch;
syn=19;
break;
case ')':
syn=28;
token[m++]=ch;
break;
case'#':
syn=0;
token[m++]=ch;
break;
default:
syn=-1;
}
}
五、测试结果
1)字符串输入测试:
2)小数输入测试:
六、设计体会与收获
通过此次试验,使我对词法分析器有了更加深入的了解,也使我对编译原理这门课程产生了浓厚的兴趣,虽然在实验过程中遇到了不少的困难,但是在同学以及老师共同的帮助下,都基本一一解决,使我懂得了团队精神的重要性。同时通过本次的实验,使我重新学习了C 语言编程方法,知识也得到了相应的扩展,思考问题的方式也能够变得多样化和灵活化。总的来说,本学期编译原理课程的实验让我受益匪浅。