实验一 源程序的预处理及词法分析程序设计
一、 实验目的
设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。
二、 实验要求
1、实现预处理功能
源程序中可能包含有对程序执行无意义的符号,要求将其剔除。
首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。
2、实现词法分析功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。其中,
syn为单词种别码。
Token为存放的单词自身字符串。
Sum为整型常量。
具体实现时,可以将单词的二元组用结构进行处理。
3、待分析的C语言子集的词法
1)关键字
main if then while do static int double struct break else long switch case typedef char return const float short continue for void default sizeof do
所有的关键字都是小写。
2)运算符和界符
+ - * / : := < <> <= > >= = ; ( ) #
3)其他标记ID和NUM
通过以下正规式定义其他标记:
ID→letter(letter|digit)*
NUM→digit digit*
letter→a|…|z|A|…|Z
digit→0|…|9…
4)空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
4、各种单词符号对应的种别码
表1 各种单词符号的种别码
单词符号 种别码 单词符号 种别码
main 1 ; 41
if 2 ( 42
then 3 ) 43
while 4 int 7
do 5 double 8
static 6 struct 9
ID 25 break 10
NUM 26 else 11
+ 27 long 12
- 28 switch 13
* 29 case 14
/ 30 typedef 15
** 31 char 16
== 32 return 17
< 33 const 18
<> 34 float 19
<= 35 short 20
> 36 continue 21
>= 37 for 22
= 38 void 23
[ 39 sizeof 24
] 40 # 0
5、 词法分析程序的主要算法思想
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到的单词符号的第一个字符的种类,拼出相应的单词符号。
1. 主程序示意图
主程序示意图如图1所示。
|
图1 词法分析主程序示意图
其中初值包括如下两方面:
(1)关键字表初值
关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下:
char *rwtab[27]={“main”,”if”,”then”,”while”,”do”,” static”,”int”,” double”,”struct”,”break”,”else”,”long”,”switch”,”case”,”typedef”,”char”,”return”,”const”,”float”,”short”,”continue”,”for”,”void”,”default”,”sizeof”,”do”};
(2)程序中需要用到的主要变量:syn,token和sum。
2. 扫描子程序的算法思想
首先设置三个变量:token用来存放构成单词符号的字符串;sum用来存放整型单词;syn用来存放单词符号的种别编码。扫描子程序主要部分流程如图2所示。
|
图2 词法分析程序流程
三、实验报告要求
1.写出编程思路、源代码(或流程图);
2.写出上机调试时发现的问题,以及解决的过程;
3.写出你所使用的测试数据及结果;
4.谈谈你的体会。
5.上机6小时,完成实验报告2小时。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;
//存放处理后的字符串
char tempstr[255]={};
//空格标志
bool temp=false;
//临时数组
char word[255]={};
//keyword关键字
string Keyword[26]={
"main","if","then","while","do","static","defualt","do","int","double","struct","break","else","long","swtich","case","typedf","char","return","const","float","short","continue","for","void","sizeof"};
int Keyword_num[26]={1,2,3,4,5,6,39,40,7,8,9,10,11,
12,13,14,15,16,17,18,19,20,21,22,23,24};
//部分运算符,定界符等
char Symbol[9]={'+','-','*','/','=',';','(',')','#'};
//对应的种码值
int Symbol_num[9]={27,28,29,30,38,41,42,43,0};
//判断是否为字母
bool IsChar(char ch)
{
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
return true;
return false;
}
//判断是否为数字
bool IsDigit(char ch)
{
if(ch>='0'&&ch<='9')
return true;
return false;
}
//判断是否为定界符等
int IsSymbol(char ch)
{
for(int i=0; i<9; i++)
{
if(ch==Symbol[i])
return i;
}
return -1;
}
//判断是否为关键字
int IsKeyword(string str)
{
for(int i=0; i<26; i++)
{
if(str==Keyword[i])
{
return i;
}
}
//不是关键字即为ID
return 25;
}
//空格处理
void Space(char a[])
{
int j=0;
memset(word,0,255);//需要清空,不然可能残留上次的字符串
temp=false;
for(int i=0; i<strlen(a); i++)
{
if(a[i]!=' ' && a[i]!='\t')
{
word[j++]=a[i];
temp=false;
}
else
{
if(!temp&&a[i]!='\t')
{
word[j++]=a[i];
temp=true;
}
}
}
}
//处理"//"注释
void prePro()
{
int j=0;
memset(tempstr,0,255);
for(int i=0; i<strlen(word); i++)
{
if(word[i]=='/'&&word[i+1]=='/')
{
while(i<strlen(word))
{
i++;
}
}
else {
tempstr[j++]=word[i];
}
}
}
int main()
{
char instr[255]={}; //接收输入字符串
bool flag=false; //多行注释标志,false为未处于注释区域
string Token;//存放字符串
char *str=NULL;//存放每行的字符串
char delims[]=" ";//分割标志
//freopen("test.cpp","r",stdin);
//freopen("result.txt","w",stdout); //此行注释后,控制台输出,
//否则文本输出
while((gets(instr))!=NULL)
{
Space(instr);
prePro();
str=strtok(tempstr,delims);//分割字符串
while(str!=NULL)
{
//头文件,宏定义
if(*(str)=='#')
{
printf("#\n");
break;
}
for(int i=0; i<strlen(str);i++)
{
if(*(str+i)=='/')
{
if(*(str+i+1)=='*')
{
flag=true;
break;
}
}
//注释处理: */,注释区域结束
if(*(str+i)=='*'&&flag)
{
if(*(str+i+1)=='/')
{
flag=false;
i++;
break;
}
}
//标识符,关键词
if(IsChar(*(str+i))&&(!flag))
{
// printf("进入标识符判断\n");
while(IsChar(*(str+i))||IsDigit(*(str+i))
||*(str+i)=='_')
{
Token+=*(str+i);
i++;
}
if(IsKeyword(Token)!=25)
{
printf("%s---->%d\n",Token.c_str(),
Keyword_num[IsKeyword(Token)]);
}
else printf("%s---->25\n",Token.c_str());
Token="";
// printf("退出标识符判断\n");
}
if(IsDigit(*(str+i))&&(!flag))
{
// printf("进入数字判断\n");
while(IsDigit(*(str+i)))
{
Token+=*(str+i);
i++;
}
printf("%s------>26\n",Token.c_str());
Token="";
}
//<,<=,<>
if(*(str+i)=='<'&&(!flag))
{
if(*(str+i)=='=') {printf("<=------>35\n");i++;}
if(*(str+i)=='>') {printf("<>------>34\n");i++;}
else printf("<------>33\n");
}
//>,>=
else if(*(str+i)=='>'&&(!flag))
{
if(*(str+i+1)=='=') {printf(">------>37\n");}
else printf(">-------36\n");
}
//:,:=
else if(*(str+i)==':'&&(!flag))
{
if(*(str+i+1)=='=') {printf(":=------->32\n");}
else printf(":-------->31\n");
}
//余下定界符等
else if(IsSymbol(*(str+i))!=-1&&(!flag))
{
printf("%c------->%d\n",*(str+i),
Symbol_num[IsSymbol(*(str+i))]);
}
}
str=strtok(NULL,delims);
}
}
return 0;
system("pause");
}