打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
编译原理中一个实用的词法分析程序

要进行程序的词法分析,就要先看看自动机的定义:

图1

图2

那么,一个自动机到底起什么作用呢?用人话说,就是判断计算机程序里面的每一个单词是否输入正确,以及这个单词属于什么类型。

本文在上述理论基础上,借用一个网络上的实用词法分析程序,对编译中的词法分析原理进行一些解读。

假设输入如下一段待编译的程序:

main()

{

int a=-5,b=4,j;

if(a>=b)

j=a-b;

else j=b-a;

}

图3

对照这个程序,我们来理解图1图2的内容。

假设我们现在碰到了这个程序的第一个字母m,那么这个m就是图1中的初态S0,因为m后面既可以跟字母、数字,又可以跟符号什么的,而后面跟随的字符类型不同,就可能决定了m开头的这个字符串属于不同的类型集合,比如m1、m=可能是一个变量,而ma可能是一个函数名,也可能是一个字符串等等,图1中的K就代表不同类型的集合(变量、数字、字符串、保留字等等),那么,图2的意思就是,由开始的字符出发,当把这一串字符都读入以后,然后判断读入的这串字符属于什么类型(进入终态),当然,在逐个读入字符的过程中,这个字符串有可能进入不同的状态,比如,i->变量;in->变量;int->关键字,读到空格键结束。这个分析过程叫词法分析。

图3的程序经过词法分析以后要求输出如下:

(2,”main”) (5,”(”) (5,”)”)

(5,”{”) (1,”int”) (2,”a”)

(4,”=”) (3,”-5”) (5,”,”)

(2,”b”) (4,”=”) (3,”4”)

(5,”,”) (2,”j”) (5,”;”)

(1,”if”) (5,”(”) (2,”a”)

(4,”>=”) (2,”b”) (5,”)”)

(2,”j”) (4,”=”) (2,”a”)

(4,”-”) (2,”b”) (5,”;”)

(1,”else”) (2,”j”) (4,”=”)

(2,”b”) (4,”-”) (2,”a”)

(5,”;”) (5,”}”)

一、 实验理论依据

(一)识别各种单词符号

程序语言的单词符号一般分为五种类型:

1:关键字(保留字/ 基本字)if 、while 、begin…

2:标识符:常量名、变量名、函数名…

3:常数:34 、56.78 、true 、'a’ 、…

4:运算符:+ 、- 、* 、/ 、〈 、and 、or 、….

5:界限符:, ; ( ) { } /*…

识别单词:掌握单词的构成规则很重要

标识符的识别:字母| 下划线+( 字母/ 数字/ 下划线)

关键字的识别:与标识符相同,最后查表

常数的识别

界符和算符的识别

大多数程序设计语言的单词符号都可以用转换图来识别,如图4

图4

词法分析器输出的单词符号常常表示为二元式:(单词种别,单词符号的属性值)

单词种别通常用整数编码,如1 代表关键字,2 代表标识符等

(二)超前搜索方法

词法分析时,常常会用到超前搜索方法。

如当前待分析字符串为“a>+” ,当前字符为“>” ,此时,分析器倒底是将其分析为大于关系运算符还是大于等于关系运算符呢?

显然,只有知道下一个字符是什么才能下结论。于是分析器读入下一个字符’+’ ,这时可知应将’>’ 解释为大于运算符。但此时,超前读了一个字符’+’ ,所以要回退一个字符,词法分析器才能正常运行。又比如:'+’ 分析为正号还是加法符号

(三)预处理

预处理工作包括对空白符、跳格符、回车符和换行符等编辑性字符的处理,及删除注解等。由一个预处理子程序来完成。

二、词法分析器的设计

1、 设计方法:

2、 写出该语言的词法规则。

3、 把词法规则转换为相应的状态转换图。

4、 把各转换图的初态连在一起,构成识别该语言的自动机

5、 设计扫描器

6、 把扫描器作为语法分析的一个过程,当语法分析需要一个单词时,就调用扫描器。

扫描器从初态出发,当识别一个单词后便进入终态,送出二元式

#include <stdio.h>

#include <string.h>

FILE *fp;

char cbuffer;

char *key[8]={'if','else','for','while','do','return','break','continue'};

int atype,id=4;

int isalpha( char c) 判断读入的字符是否是字母

{

if((c<='z')&&(c>='a')||(c<='Z')&&(c>='A'))

return 1;

else return 0;

}

int isdigit(char c)

{

if(c>='0'&&c<='9')

return 1;

else return 0;

}

int search(char searchchar[ ],int wordtype) /*判断单词是保留字还是标识符*/

{ 保留字有8个

int i=0;

int p;

switch (wordtype)

{

case 1:for (i=0;i<=7;i++)

{

if (strcmp(key[i],searchchar)==0)

{ p=i+1; break; } /*是保留字则p为非0且不重复的整数*/

else p=0; /*不是保留字则用于返回的p=0*/

}

return(p);

}

}

char alphaprocess(char buffer)

{ int atype; /*保留字数组中的位置*/

int i=-1;

char alphatp[20];

while ((isalpha(buffer))||(isdigit(buffer))||buffer=='_')

{

alphatp[++i]=buffer;

buffer=fgetc(fp);

} /*读一个完整的单词放入alphatp数组中*/

alphatp[i+1]='\0';

atype=search(alphatp,1);/*对此单词调用search函数判断类型*/

if(atype!=0)

{ printf('(%d,\'%s\')\n',atype-1,alphatp); id=1; } 是保留字则输出类型1

else

{ printf('(2,\'%s\')\n',alphatp); id=2; } 是函数名、标识符等则输出类型2

return (buffer);

}

char digitprocess(char buffer) 数字只能全部由0-9构成

{

int i=-1;

char digittp[20];

while ((isdigit(buffer))||buffer=='.') /*1 判断小数*/

{

digittp[++i]=buffer;

buffer=fgetc(fp);

}

digittp[i+1]='\0';

printf('(3,\'%s\')\n',digittp); 是数字则输出类型3

id=3;

return(buffer);

}

char otherprocess(char buffer)

{

char ch[20];

ch[0]=buffer;

ch[1]='\0';

if(ch[0]==','||ch[0]==';'||ch[0]=='{'||ch[0]=='}'||ch[0]=='('||ch[0]==')')

{ printf('(5,\'%s\')\n',ch); 是界符则输出类型5

buffer=fgetc(fp);

id=5;

return(buffer);}

if(ch[0]=='/')

{

buffer=fgetc(fp);

if(buffer=='*') /*2 区分注释符号和除号*/

{

ch[1]=buffer;

buffer=fgetc(fp);

while(buffer!='*')

{

buffer=fgetc(fp);

}

ch[2]=buffer;

buffer=fgetc(fp);

if(buffer=='/')

{

ch[3]=buffer;

ch[4]='\0';

}

printf('(5,\'%s\')\n',ch);

}

else{

printf('(4,\'%s\')\n',ch); 是运算符输出类型4

id=4;

return(buffer);

}

buffer=fgetc(fp);

id=5;

return(buffer);

}

if(ch[0]=='*')

{ printf('(4,\'%s\')\n',ch);

buffer=fgetc(fp);

id=4;

return(buffer);

}

if(ch[0]=='='||ch[0]=='!'||ch[0]=='<'||ch[0]=='>')

{ buffer=fgetc(fp);

if(buffer=='=')

{ ch[1]=buffer;

ch[2]='\0';

printf('(4,\'%s\')\n',ch);

}

else

{ printf('(4,\'%s\')\n',ch);

id=4;

return(buffer);

}

buffer=fgetc(fp);

id=4;

return(buffer);

}

if(ch[0]=='+'||ch[0]=='-')

{ if(id==4) /*在当前符号以前是运算符,则此时为正负号*/

{ buffer=fgetc(fp);

ch[1]=buffer;

ch[2]='\0';

printf('(3,\'%s\')\n',ch);

id=3;

buffer=fgetc(fp);

return(buffer);

}

ch[1]='\0';

printf('(4,\'%s\')\n',ch);

buffer=fgetc(fp);

id=4;

return(buffer);

}

if(ch[0]=='#') /*3 识别头文件*/

{

char t[20];

int i=0;

t[0]=ch[0];

buffer=fgetc(fp);

while((isalpha(buffer))||buffer==' '||buffer=='<'||buffer=='>')

{

t[++i]=buffer;

buffer=fgetc(fp);

}

t[i+1]='\0';

printf('(6,\'%s\')\n',t);

id=6;

return (buffer);

}

if(ch[0]=='\\') /*4 识别转义符号*/

{

buffer=fgetc(fp);

ch[1]=buffer;

printf('(6,\'%s\')\n',ch); 转义符号输出类型6

buffer=fgetc(fp);

return(buffer);

}

if(ch[0]=='|'||ch[0]=='&') /*5 判断或与*/

{

buffer=fgetc(fp);

if(ch[0]==buffer)

{

ch[1]=buffer;

}

ch[2]='\0';

printf('(4,\'%s\')\n',ch);

buffer=fgetc(fp);

return(buffer);

}

if(ch[0]=='''||ch[0]=='\'') /*6 判断双引号和单引号*/

{

printf('(5,\'%s\')\n',ch);

id=5;

buffer=fgetc(fp);

return(buffer);

}

}

int main()

{

if ((fp=fopen('example.c','r'))==NULL) /*只读方式打开一个文件*/

printf('error');

else

{

cbuffer = fgetc(fp); /*fgetc( )函数:从磁盘文件读取一个字符*/

while (cbuffer!=EOF)

{

if(cbuffer==' '||cbuffer=='\n') /*掠过空格和回车符*/

cbuffer=fgetc(fp);

else

if(isalpha(cbuffer))

cbuffer=alphaprocess(cbuffer);

else

if (isdigit(cbuffer))

cbuffer=digitprocess(cbuffer);

else cbuffer=otherprocess(cbuffer);

}

}

return 0;

}

程序运行结果:

纵观整个词法分析程序,难度并不大,就是判断保留字、标识符、数字、运算符和界符,程序里面还有判断转义字符和注释符的功能,这么一个简单的程序,却可以让我们真正了解实际的编译程序大概是怎么构成的,在此基础上,就可以开发出新的语言,等等。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
【编译原理】一个词法分析器源码的剖析
C语言详解——文件读取
学习编译原理时做的词法分析器
c 复制二进制流
C 的文件操作
C语言学习教程第十章-文件(4)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服