从上午十一点到下午五点
我因为一道小小的实验就一直坐在电脑前
这就是程序员的青春吗
关于PL/0文法分析程序
PL/0的词法分析程序GETSYM是一个独立的过程,其功能是为语法语义分析提供单词,把输入的字符串形式的源程序分割成一个个单词符号传递给语法语义分析。其主要任务为:
1、滤空格;
2、识别基本字;
3、识别标识符;
4、拼数;
5、拼复合词;
6、输出源程序。
PL/0语言的单词的种类分成基本字(亦称保留字)、运算符、标识符、常数、界符5个大类,以下是针对这5类单词的一种EBNF描叙:
<无符号整数>::=<数字>{<数字>}
<标识符> ::=<字母>{<字母>|<数字>}
<字母> ::=a|b|……|X|Y|Z
<数字> ::=0|1|2|……|8|9
<保留字> ::= const | var | procedure | begin | end | odd | if | then | call | while | do | read |write
<运算符> ::= + | - | * | / | = | # | < | <= | > | >= | :=
<界符> ::= ( | ) | , | ; | .
实验内容就是用C语言实现PL/0词法分析器
01.读入文本文件
文件采用ANSI编码否则文件中存在中文字符时读出来会是乱码。文件中存储的是要分析的PL/0程序,是有换行符的,因此读入的时候应该选择将换行符转化为空格来将整个程序构成一个完整的字符串。
void ReadFile(char *name,char *str)
{
char ch;
int k=0;
FILE *f;
f=fopen(name,"r");
while((ch=fgetc(f))!=EOF)
{
if(ch!='n')///不为换行符则一直直接存入
str[k++] = ch;
else///如果是换行符记作空
str[k++]=' ';
}
str[k]=' ';
fclose(f);
}
02.滤空格操作
前一步读文件得到的字符串我们需要进行滤空格处理,从而生成字符串数组str1,滤去空格后可以初步划分字符串,部分str1可以在此时来判断是否为关键字或者标识符。
///判断是否为保留字,因为读入的是字符串,通过循环遍历来看是否可以与保留字数组中的某一个值相等
int IsKeyWord(char *str)
{
int i;
for(i=0;i<16;++i)
{
if(!strcmp(str,keyword[i])) break;///找到则为保留字
}
return i;
}
03.拼数拼复合词
在运算符类型中,有的运算符不只一个字符,而是像"<="这样的多字符型。而且PL/0程序中像"a=10"这样的语句不同符号之间是不存在空格的,因此我们要将滤去空格的字符串数组进行再次划分,首先根据首字符的类型进行初步划分:字母、常数、运算符、标识符,再继续判断后续字符的类型来判断划分出来的str2具体为什么类型。
///PL0完整判断
int PL0(char *InputFileName,char *str)
{
ReadFile(InputFileName,str);
int len = strlen(str);///文件字符数(包括换行)
int i,j;///循环变量
int len1,len2,k,t,re;
char str1[100];///滤空格后的字符数组
char str2[100];///凑的关键字和符号
i=0;
///循环遍历每一个字符串
while(i<len)
{
j=0;
///扫描到空格且不在末尾则继续扫描
while(str[i]==' '&&i<len)
++i;
///不为空,则不断将字符存入到字符串数组里
while(str[i]!=' '&&i<len)
str1[j++]=str[i++];
///str1为去除空格的初步划分,即滤掉空格操作
str1[j]=' ';///存完了记得末尾一定加上' '
len1=strlen(str1);
k=0;
///开始判断扫描到的字符串的类型
while(k<len1)
{
if(str1[k]=='.')
{
...
}
else if(IsLetter(str1[k]))///开头字符为字母型,可能为保留字或者标识符
{
...
///因为保留字和运算符常数字母之间不一定有空格,所以需要每个字符检查
///str2为拼复合词操作所得字符串
///若当前读到的字符既不是运算符也不是界符即可,可为数字汉字字母等
while((!IsCaculationMark(str1[k]))&&(!IsBounderMark(str1[k]))&&k<len1)
...
///判断是不是基本字并输出
re=IsKeyWord(str2);
if(re<16)
{
...
}
}
///开头字母为数字型
else if(IsNumber(str1[k]))
{
///且接下来的字符也都是数字型
while(IsNumber(str1[k])&&k<len1)
...
}
///开头字母是运算符
else if(IsCaculationMark(str1[k]))
{
///将所有运算符存入str2数组中
while(IsCaculationMark(str1[k])&&k<len1)
...
}
else if(IsBounderMark(str1[k]))///开头为界符型
{
while(IsBounderMark(str1[k]) && k<len1)///将属于界符的符号扣到数组里
...
}
else{
while((!IsCaculationMark(str1[k]))&&(!IsBounderMark(str1[k]))&&(!IsLetter(str1[k]))&&(!IsNumber(str1[k]))&&k<len1)
...
}
}
strcpy(str2,"");
}
fclose(stdout);
return 0;
}
04.输出
在前一步分析文法时应该注意,遇到界符.时应该停止分析文法,.为结束符,此时可以直接退出程序。输出时注意 非法字符需要原样输出,由于划分次数较多,要分清楚非法字符的符号具体是什么。
为了个性化老师提出识别新的关键字的要求:需要分析姓名学号等。因为关键字和标识符要求字母或数字组成,因此中文输入想了很久怎么识别。最后选择在中文前面加上字母,使得初次分类的时候被划分到关键字当中,然后后续字符不是界符运算符字母或数字即可。
end
发表评论