求助,怎么不能运行,一个词法分析器的功能

&>&词法分析器 (注释最全)
程序可直接运行
词法分析器 (注释最全)
程序可直接运行
上传大小:976KB
保留字表 标识符表 常数表
整型 浮点型
特殊字符处理
输出二元组
程序可直接运行
综合评分:1
{%username%}回复{%com_username%}{%time%}\
/*点击出现回复框*/
$(".respond_btn").on("click", function (e) {
$(this).parents(".rightLi").children(".respond_box").show();
e.stopPropagation();
$(".cancel_res").on("click", function (e) {
$(this).parents(".res_b").siblings(".res_area").val("");
$(this).parents(".respond_box").hide();
e.stopPropagation();
/*删除评论*/
$(".del_comment_c").on("click", function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_invalid/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parents(".conLi").remove();
alert(data.msg);
$(".res_btn").click(function (e) {
var parentWrap = $(this).parents(".respond_box"),
q = parentWrap.find(".form1").serializeArray(),
resStr = $.trim(parentWrap.find(".res_area_r").val());
console.log(q);
//var res_area_r = $.trim($(".res_area_r").val());
if (resStr == '') {
$(".res_text").css({color: "red"});
$.post("/index.php/comment/do_comment_reply/", q,
function (data) {
if (data.succ == 1) {
var $target,
evt = e || window.
$target = $(evt.target || evt.srcElement);
var $dd = $target.parents('dd');
var $wrapReply = $dd.find('.respond_box');
console.log($wrapReply);
//var mess = $(".res_area_r").val();
var mess = resS
var str = str.replace(/{%header%}/g, data.header)
.replace(/{%href%}/g, 'http://' + window.location.host + '/user/' + data.username)
.replace(/{%username%}/g, data.username)
.replace(/{%com_username%}/g, data.com_username)
.replace(/{%time%}/g, data.time)
.replace(/{%id%}/g, data.id)
.replace(/{%mess%}/g, mess);
$dd.after(str);
$(".respond_box").hide();
$(".res_area_r").val("");
$(".res_area").val("");
$wrapReply.hide();
alert(data.msg);
}, "json");
/*删除回复*/
$(".rightLi").on("click", '.del_comment_r', function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_comment_del/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parent().parent().parent().parent().parent().remove();
$(e.target).parents('.res_list').remove()
alert(data.msg);
//填充回复
function KeyP(v) {
var parentWrap = $(v).parents(".respond_box");
parentWrap.find(".res_area_r").val($.trim(parentWrap.find(".res_area").val()));
评论共有1条
cao,没有源代码发个屁呀 举报
VIP会员动态
CSDN下载频道资源及相关规则调整公告V11.10
下载频道用户反馈专区
下载频道积分规则调整V1710.18
spring mvc+mybatis+mysql+maven+bootstrap 整合实现增删查改简单实例.zip
资源所需积分/C币
当前拥有积分
当前拥有C币
输入下载码
为了良好体验,不建议使用迅雷下载
词法分析器 (注释最全)
程序可直接运行
会员到期时间:
剩余下载个数:
剩余积分:0
为了良好体验,不建议使用迅雷下载
积分不足!
资源所需积分/C币
当前拥有积分
您可以选择
程序员的必选
绿色安全资源
资源所需积分/C币
当前拥有积分
当前拥有C币
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
您的积分不足,将扣除 10 C币
为了良好体验,不建议使用迅雷下载
无法举报自己的资源
你当前的下载分为234。
你还不是VIP会员
开通VIP会员权限,免积分下载
你下载资源过于频繁,请输入验证码
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
若举报审核通过,可返还被扣除的积分
被举报人:
请选择类型
资源无法下载 ( 404页面、下载失败、资源本身问题)
资源无法使用 (文件损坏、内容缺失、题文不符)
侵犯版权资源 (侵犯公司或个人版权)
虚假资源 (恶意欺诈、刷分资源)
含色情、危害国家安全内容
含广告、木马病毒资源
*投诉人姓名:
*投诉人联系方式:
*版权证明:
*详细原因:
词法分析器 (注释最全)
程序可直接运行词法分析器代码
阅读:732次&&&时间: 00:00:00&&
前几日,学习了编译原理,写了一个词法分析器,以供自己学习之用,分析器写的有点简陋,有待改进。代码如下:#include &stdio.h&#include &string.h&#include &process.h&#define MAX 50void main(){FILE *in,*/*存放输入字符串和输出单词串的文件*/char arr[MAX];/*arr数组存放单词符号*//*currentchar存放当前输入字符*/int i=0;/*arr数组的一个指针*//*以读方式打开输入文件*/if((in=fopen("infile.txt","r"))==NULL){&printf("can not open file\n");&exit(0);}/*以写方式打开输出文件*/if((out=fopen("outfile.txt","w"))==NULL){&printf("cannout open outfile\n");&&& exit(1);}//去掉开头空格currentchar=fgetc(in);while(currentchar==' '){ &currentchar=fgetc(in);}while(){/*常数部分判断*/if((currentchar&='a'&&currentchar&='z')||(currentchar&='A'&&currentchar&='Z')||(currentchar&='0'&&currentchar&='9')||currentchar=='('||currentchar==')'||currentchar=='{'||currentchar=='}'||currentchar=='&'||currentchar=='&'||currentchar==';'||currentchar==':'||currentchar=='+'||currentchar=='-'||currentchar=='*'||currentchar=='/'||currentchar=='='||currentchar=='!'||currentchar=='&'||(ch=currentchar)=='\n'||currentchar==' '||currentchar=='['||currentchar==']'){i=0;//清空arr字符数组if(currentchar&='0'&&currentchar&='9'){&arr[i++]=&&& currentchar=fgetc(in);//再读入下一个字符&&& while(currentchar&='0'&&currentchar&='9')&{&&& arr[i++]=&&& currentchar=fgetc(in);&}&&& if(currentchar!='.')//如果当前读入的符号不为小数点,则常数读完毕,输出此常数&{&&& arr[i++]='\0';&&& fprintf(out,"%s%d,%c%s%c%s\n","(",15,'"',arr,'"',")");//格式化输出到目标文件,15代表种别码, arr为内码值,规定用数字本身代表&}&&& else//当前字符为小数点则继续向下读入&{&&& arr[i++]=&&& currentchar=fgetc(in);&&& while(currentchar&='0'&&currentchar&='9')&{&&& arr[i++]=&&& currentchar=fgetc(in);&}&&& arr[i++]='\0';&&& fprintf(out,"%s%d,%c%s%c%s\n","(",15,'"',arr,'"',")");&}&&& while(currentchar==' ')&{ &currentchar=fgetc(in);&}}/*判断标识符和关键字部分*/if((currentchar&='a'&&currentchar&='z')||(currentchar&='A'&&currentchar&='Z')){i=0;//清空arr字符数组&&& while((currentchar&='a'&&currentchar&='z')||(currentchar&='0'&&currentchar&='9')||(currentchar&='A'&&currentchar&='Z'))&{&&& arr[i++]=&&& currentchar=fgetc(in);&}//把字符数组arr和关键字表比较,判断单词串是关键字还是标识符&&& arr[i++]='\0';&&& if(strcmp(arr,"if")==0)&&& fprintf(out,"%s%d,%c%s%c%s\n","(",0,'"',arr,'"',")");&&& else if(strcmp(arr,"else")==0)&&& fprintf(out,"%s%d,%c%s%c%s\n","(",1,'"',arr,'"',")");&&& else if(strcmp(arr,"while")==0)&&& fprintf(out,"%s%d,%c%s%c%s\n","(",2,'"',arr,'"',")");&&& else if(strcmp(arr,"do")==0)&&& fprintf(out,"%s%d,%c%s%c%s\n","(",3,'"',arr,'"',")");&else&fprintf(out,"%s%d,%c%s%c%s\n","(",14,'"',arr,'"',")");}while(currentchar==' '){ &currentchar=fgetc(in);}//判断标识符包括:+, -, *, /, (, ), {, }, &, &, ;, :, !=, &=, &=, ==, =.if(currentchar=='+'){&&& fprintf(out,"%s%d,%c%c%c%s\n","(",9,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='-'){&fprintf(out,"%s%d,%c%c%c%s\n","(",10,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='*'){&fprintf(out,"%s%d,%c%c%c%s\n","(",11,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='/'){&fprintf(out,"%s%d,%c%c%c%s\n","(",12,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='='){&i=0;&arr[i++]=&currentchar=fgetc(in);&if(currentchar=='=')&{& arr[i++]=& arr[i++]='\0';& fprintf(out,"%s%d,%c%s%c%s\n","(",13,'"',arr,'"',")");&&&& currentchar=fgetc(in);&}&else&{&arr[i++]='\0';&&fprintf(out,"%s%d,%c%s%c%s\n","(",17,'"',arr,'"',")");&&& }}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar==';'){&fprintf(out,"%s%d,%c%c%c%s\n","(",4,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='{'){&fprintf(out,"%s%d,%c%c%c%s\n","(",5,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='}'){&fprintf(out,"%s%d,%c%c%c%s\n","(",6,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='['){&fprintf(out,"%s%d,%c%c%c%s\n","(",20,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar==']'){&fprintf(out,"%s%d,%c%c%c%s\n","(",21,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='('){&fprintf(out,"%s%d,%c%c%c%s\n","(",7,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar==')'){&fprintf(out,"%s%d,%c%c%c%s\n","(",8,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar==':'){&i=0;&arr[i++]=&currentchar=fgetc(in);&if(currentchar==':')&{& arr[i++]=& arr[i++]='\0';& fprintf(out,"%s%d,%c%s%c%s\n","(",18,'"',arr,'"',")");&&&& currentchar=fgetc(in);&}&else&{&arr[i++]='\0';&&fprintf(out,"%s%d,%c%s%c%s\n","(",16,'"',arr,'"',")");&&& }}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='&'){&i=0;&arr[i++]=&currentchar=fgetc(in);&if(currentchar=='=')&{& arr[i++]=& arr[i++]='\0';& fprintf(out,"%s%d,%c%s%c%s\n","(",13,'"',arr,'"',")");&&&& currentchar=fgetc(in);&}&else&{&arr[i++]='\0';&&fprintf(out,"%s%d,%c%s%c%s\n","(",13,'"',arr,'"',")");&&& }}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='&'){&i=0;&arr[i++]=&currentchar=fgetc(in);&if(currentchar=='=')&{& arr[i++]=& arr[i++]='\0';& fprintf(out,"%s%d,%c%s%c%s\n","(",13,'"',arr,'"',")");&&&& currentchar=fgetc(in);&}&else&{&arr[i++]='\0';&&fprintf(out,"%s%d,%c%s%c%s\n","(",13,'"',arr,'"',")");&&& }}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='!'){&i=0;&arr[i++]=&currentchar=fgetc(in);&if(currentchar=='=')&{& arr[i++]=& arr[i++]='\0';& fprintf(out,"%s%d,%c%s%c%s\n","(",13,'"',arr,'"',")");&&&& currentchar=fgetc(in);&}}while(currentchar==' '){ &currentchar=fgetc(in);}if(currentchar=='&'){&fprintf(out,"%s%d,%c%c%c%s\n","(",19,'"',currentchar,'"',")");&&& currentchar=fgetc(in);}while(currentchar==' '){ &currentchar=fgetc(in);}while((ch=currentchar)=='\n'){&currentchar=fgetc(in);}&while(currentchar==' '){&currentchar=fgetc(in);}}else{&fprintf(out,"%s,%c%c%c\n","error",'"',currentchar,'"');&&& currentchar=fgetc(in);}}//结束开头while语句/*关闭文件退出*/fclose(in);fclose(out);}注:infile.txt,@outfile.txt,<FONT face=宋体 color=#4年11月11日1:02:22
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
Copyright &
All Rights Reserved3.3k 次阅读
标签:至少1个,最多5个
要为 tao 语言设计词法分析器,首先得知道 tao 语言是一种什么样的语言。不过呢,我脑海里还没有 tao 语言具体形象。我还是先贴一段 tao 语言的代码,大概展示下这是怎么回事吧。
def say_hello_world(who)
print “hello world ” + who
var name = “tao”
say_hello_world name
#我是注释,注释是这样写的
看起来可能是这个样子。这个系列中的 tao 语言我会采取一边写编译器,一边设计的形式。细节在之后的章节会逐步补充完整。本章只是些词法分析器,因此不必了解太多细节(也不必设计出来)。因此,tao语言的概念也暂时止步于此不作细节上的讨论。
OK,不过首先我们得把词法分析器能生成的单词类型定义好了。嗯,我想想,显然一个语言至少要这么几种类型嘛。
关键字(Keyword)
符号(Sign)
标示符(Identifier)
其中关键字和符号很好理解,标示符,则具体而言,就是用户定义的变量、函数定义中的函数名和参数名、被调用函数的函数名等等这些东西。
严格来说,仅由数字、英语字母、下划线的字符串(可以以'?'、'!'为结尾,但不能以数字为开头),如果没有被关键字占用,则统统称之为标示符。其正则表达式形如:
^[a-zA-Z_][0-9a-zA-Z_]*[!\?]?$
当然,关键字的形式和标示符是一样的(至少要满足标示符的正则表达式定义)。
然后,数据类型也要有:
数字类型(Number)
字符串(String)
正则表达式(RegEx)
其中数字类型的形式和标示符的形式极为类似,不同点在于,数字类型要求必须以数字开头(当然结尾不可以接'?'和'!')这两个符号啦。其正则表达式形如:
^[0-9][0-9a-zA-Z_]*$
如此以来数字类型就和传统意义上的数字不一样了。形如"110police"也会被当做数字。此外,像3.14这种实数却不会被当作数字。嗯,这的确是问题,但是我不想在现在就解决这个问题,暂时不管啦,请先无视~~~。
另一个是正则表达式的定义,tao 语言中将 ` 符号置于两端来表示正则表达式。(什么,找不到这个符号,嗯,这个符号在“1”的左边,直接按下即可打出。)为什么用这个符号而不用 / 分开呢,猜一猜为什么呢?嗯,真正开始编码的时候揭示答案吧。
再然后,这些对编译器意义不大,但还是有点用处的类型:
注释(Annotation)
空格(Space)
回车(NewLine)
其中注释以 # 开始,单行注释。
最后的最后,还有一个类型是必须:
终止符(EndSymbol)
这个类型不对应任何程序员可输入的形式,实际上,词法分析器会在解析完所有源代码之后,自动以一个该类型的单词结尾。这个类型是专门给语法分析器(Parser)使用的。
3 收藏&&|&&7
一边写编译器一边设计,你这也太不靠谱了
一边写编译器一边设计,你这也太不靠谱了
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。09:31 提问
词法分析器的实现步骤
各位大神,求助,词法分析器的实现步骤和思路,尽量详细一点,谢谢各位了
按赞数排序
编译,简单的说,就是把源程序转换为可执行程序。从hello world 说程序运行机制 里面简单的说明了程序运行的过程,以及一个程序是如何一步步变成可执行文件的。在这个过程中,编译器做了很多重要的工作。对底层该兴趣的我,自然的,也就迫切想搞清楚编译的内部实现,也就是编译的原理。
这篇文章主要说的是编译器前端,词法分析器的原理,最后会给出一个词法分析器的简单实现。
编译简单的说,就是把源程序转化为另一种形式的程序,而其中关键的部分就是理解源程序所要表达的意思,才能转化为另一种源程序。
可以用一个比喻来说明问题:人A和人B想要交谈,但是他们都不知道彼此的语言,这就需要一个翻译C,同时懂得A和B的语言。有了C做中间层,A和B才能正常交流。C的作用就有点像编译器,它必须能理解源程序所要表达的意思,才能把信息传递给另一个。
编译器也一样,它的输入是语言的源文件(一般可以是文本文件)对于输入的文件,首先要分离出这个输入文件的每个元素(关键字、变量、符号、、)
然后根据语言的文法,分析这些元素的组合是否合法,以及这些组合所表达的意思。
程序设计语言和自然语言不一样,都是用符号来描述,每个特定的符号表示特定的意思,而且程序设计语言是上下文无关的。上下文无关就是某一个特定语句所要表达的意思和它所处的上下文没有关系,只有它自身决定。
这篇博文主要说的就是词法分析,也就是把输入的符号串整理成特定的词素。
词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等
(1) 关键字 是由程序语言定义的具有固定意义的标识符。例如,Pascal 中的begin,end,if,while都是保留字。这些字通常不用作一般标识符。
(2) 标识符 用来表示各种名字,如变量名,数组名,过程名等等。
常数的类型一般有整型、实型、布尔型、文字型等。
(4) 运算符 如+、-、*、/等等。
如逗号、分号、括号、等等。
词法分析器所输出单词符号常常表示成如下的二元式:
(单词种别,单词符号的属性值)
单词种别通常用整数编码。标识符一般统归为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种。运算符可采用一符一种的方法。界符一般用一符一种的方法。对于每个单词符号,除了给出了种别编码之外,还应给出有关单词符号的属性信息。单词符号的属性是指单词符号的特性或特征。
比如如下的代码段:
while(i&=j) i--
经词法分析器处理后,它将被转为如下的单词符号序列:
词法分析分析器作为一个独立子程序
词法分析是编译过程中的一个阶段,在语法分析前进行。词法分析作为一遍,可以简化设计,改进编译效率,增加编译系统的可移植性。也可以和语法分析结合在一起作为一遍,由语法分析程序调用词法分析程序来获得当前单词供语法分析使用。
词法分析器设计
输入、预处理
词法分析器工作的第一步是输入源程序文本。在许多情况下,为了更好地对单词符号识别,把输入串预处理一下。预处理主要滤掉空格,跳过注释、换行符等。
词法分析过程中,有时为了确定词性,需超前扫描若干个字符。
对于FORTRAN 语言,关键字不作为保留字,可作为标识符使用, 空格符号没有任何意义。为了确定词性,需超前扫描若干个字符。
在FORTRAN中
DO99K=1,10
IF(5.EQ.M) I=10
DO99K=1.10
这四个语句都是正确的语句。语句1和2 分别是DO和IF语句,语句3和4是赋值语句。为了正确区别1和3,2和4语句,需超前扫描若干个字符。
DO99K=1,10
IF(5.EQ.M) I=10
DO99K=1.10
语句1和3的区别在于符号之后的第一个界符:一个为逗号,另一个为句末符。语句2和4的主要区别在于右括号后的第一个字符:一个为字母,另一个为等号。为了识别1、2中的关键字,必须超前扫描多个字符。超前到能够肯定词性的地方为止。为了区别1和3,必须超前扫描到等号后的第一个界符处。对于语句2、4来说,必须超前扫描到与IF后的左括号相对应的那个右括号之后的第一个字符为止。
状态转换图
词法分析器使用状态转换图来识别单词符号。状态转换图是一张有限方向图。在状态转换图中,有一个初态,至少一个终态。
其中0为初态,2为终态。这个转换图识别(接受)标识符的过程是:从初态0开始,若在状态0之下输入字符是一个字母,则读进它,并转入状态1。在状态1之下,若下一个输入字符为字母或数字,则读进它,并重新进入状态1。一直重复这个过程直到状态1发现输入字符不再是字母或数字时(这个字符也已被读进)就进入状态2。状态2是终态,它意味着到此已识别出一个标识符,识别过程宣告终止。终态结上打个星号意味着多读进了一个不属于标识符部分的字符,应把它退还给输入口中 。如果在状态0时输入字符不为“字母”,则意味着识别不出标识符,或者说,这个转换图工作不成功。
正规表达式与正规集
正规表达式是说明单词的一种重要的表示法(记号),是定义正规集的工具。在词法分析中,正规表达式用来描述标示符可能具有的形式。
定义(正规式和它所表示的正规集):
设字母表为S,
1. e和?都是S上的正规式,它们所表示的正规集分别为{e}和{ };
2. 任何a? S,a是S上的一个正规式,它所表示的正规集为{a};
3. 假定U和V都是S上的正规式,它们所表示的正规集分别为L(U)和L(V),那么,(U), U|V, U·V, U*也都是正规式,它们所表示的正规集分别为L(U), L(U)?L(V), L(U)L(V)和(L(U))*;
4. 仅由有限次使用上述三步骤而定义的表达式才是S上的正规式,仅由这些正规式所表示的字集才是S上的正规集。
正规式的运算符的“ 1/2 ”读为“或” ,“· ”读为“连接”;“*”读为“闭包”(即,任意有限次的自重复连接)。
在不致混淆时,括号可省去,但规定算符的优先顺序为“(”、“)”、“*”、“· ”、“ 1/2 ” 。连接符“· ”一般可省略不写。
“*”、“· ”和“ 1/2 ” 都是左结合的。
例 令S={a,b}, S上的正规式和相应的正规集的例子有:
     {a}
(a 1/2 b)(a
{aa,ab,ba,bb}
{e ,a,a, ……任意个a的串}
{b, ba, baa, baaa, …}
(a 1/2 b)*
{e ,a,b,aa,ab ……所有由a和b
(a 1/2 b)*(aa 1/2 bb)(a 1/2 b)*
{S*上所有含有两个相继的a
或两个相继的b组成 的串}
定理:若两个正规式U和V所表示的正规集相同,则说U和V等价,写作U=V。
证明b(ab)*=( ba)*b
证明:因为L(b(ab)*)={b}{e, ab, abab, ababab, …}
={b, bab, babab, bababab, …}
L((ba)*b) ={e, ba, baba, bababa, …}{b}
={b, bab, babab, bababab, …}
= L(b(ab)*)
b(ab)*=( ba)*b
设U,V,W为正规式,正规式服从的代数规律有:
(1) U 1/2 V=V 1/2 U
(2) U 1/2 (V 1/2 W)=(U 1/2 V) 1/2 W
(3) U(VW)=(UV)W
(4) U(V 1/2 W)=UV 1/2 UW
(V 1/2 W)U=VU 1/2 WU
(5) eU=U e=U
分析器的简单实现
上文主要介绍了词法分析的一些相关的知识,而对词法分析器的具体实现还没有具体提到,为了能更好的理解词法分析,我写了一个简单的词法分析器。
虽然说是语法分析器,但实现的功能很简单,只是对输入的程序把注释去掉,其中用到了上面关于状态转换图部分的知识。
一般的程序设计语言, 注释部分的形式为;
/* 注释部分、、、、*/
我们的程序总是顺序的一个一个字符读取输入文件的。我们的目的是把注释部分去掉,那么对于输入的字符流,我们只要识别出“/*”就知道后面的部分是注释部分,直到识别输入流中出现"*/"为止。
对字符流的处理是一个一个进行的,每读入一个字符,就判断,如果字符是“/”,就说明后面 的部分可能是注释,再看下一个输入字符,如果是“*”, 就是上面所说的情况:“ /*”那么后面的部分就是注释部分,然后再用相同的方法找出"*/"就可以了。
这个识别的过程就可以用状态转换图来清晰的表示:
对于读入的每个符号都要进行判断,如果是“/”说明后面的部分有可能是注释,进入状态1。如果后面的输入是“*”那么就可以确定以后的内容为注释内容,如果后面的输入不是"*",说明后面的内容不是注释,前面出现的"/"可能是做除号使用,如“5/3”
其实上面的流程图也就对应了程序实现的逻辑,可以用switch-case 来实现,对于每个输入,判断后跳转到相应的状态,然后继续判断。
下面是程序伪代码:
while((ch=getchar())!=EOF)
  switch(state)
    case 1 :if ch=="/",state=2,
    case 2: if ch=="*",state=3
        else state=1;
    case 3:..........
    case 4:..........
词法分析器
这个程序比较简单,就不给出源代码了。接下来是一个简单的词法分析器的代码,可以实现对关键字(如 while end if 等),对数字的识别,去掉空格符等。
下面是这个分析器的功能:
待分析的简单语言的词法
所有关键字都是小写。
运算符和界符:
其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:
ID=letter(letter| digit)*
NUM=digit digit *
空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。
各种单词符号对应的种别码
词法分析程序的功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。
其中:syn为单词种别码;
token为存放的单词自身字符串;
sum为整型常数。
下面是程序源代码,基于上面的讨论,应该比较好了解了。
char prog[80],token[8];
int syn,p,m=0,n,row,sum=0;
char *rwtab[6]={"begin","if","then","while","do","end"};
void scaner()
共分为三大块,分别是标示符、数字、符号,对应下面的 if
for(n=0;n&8;n++) token[n]=NULL;
ch=prog[p++];
while(ch==' ')
ch=prog[p];
if((ch&='a'&&ch&='z')||(ch&='A'&&ch&='Z'))
//可能是标示符或者变量名
while((ch&='0'&&ch&='9')||(ch&='a'&&ch&='z')||(ch&='A'&&ch&='Z'))
token[m++]=
ch=prog[p++];
token[m++]='\0';
for(n=0;n&6;n++)
//将识别出来的字符和已定义的标示符作比较,
if(strcmp(token,rwtab[n])==0)
else if((ch&='0'&&ch&='9'))
while((ch&='0'&&ch&='9'))
sum=sum*10+ch-'0';
ch=prog[p++];
if(sum&32767)
else switch(ch)
//其他字符
case'&':m=0;token[m++]=
ch=prog[p++];
if(ch=='&')
token[m++]=
else if(ch=='=')
token[m++]=
case'&':m=0;token[m++]=
ch=prog[p++];
if(ch=='=')
token[m++]=
case':':m=0;token[m++]=
ch=prog[p++];
if(ch=='=')
token[m++]=
case'*':syn=13;token[0]=
case'/':syn=14;token[0]=
case'+':syn=15;token[0]=
case'-':syn=16;token[0]=
case'=':syn=25;token[0]=
case';':syn=26;token[0]=
case'(':syn=27;token[0]=
case')':syn=28;token[0]=
case'#':syn=0;token[0]=
case'\n':syn=-2;
default: syn=-1;
int main()
cout&&"Please input string:"&&
cin.get(ch);
prog[p++]=
while(ch!='#');
switch(syn)
case 11: cout&&"("&&syn&&","&&sum&&")"&&
case -1: cout&&"Error in row "&&row&&"!"&&
case -2: row=row++;
default: cout&&"("&&syn&&","&&token&&")"&&
while (syn!=0);
改程序在C-free5上调试通过
下面是程序截图:
这里主要说的是编译器中的词法分析,还介绍了词法分析的一些相关知识,最后给出了一个很简单的词法分析器的实现。
编译,简单的说,就是把源程序转换为可执行程序。从hello world 说程序运行机制 里面简单的说明了程序运行的过程,以及一个程序是如何一步步变成可执行文件的。在这个过程中,编译器做了很多重要的工作。对底层该兴趣的我,自然的,也就迫切想搞清楚编译的内部实现,也就是编译的原理。
这篇文章主要说的是编译器前端,词法分析器的原理,最后会给出一个词法分析器的简单实现。
编译简单的说,就是把源程序转化为另一种形式的程序,而其中关键的部分就是理解源程序所要表达的意思,才能转化为另一种源程序。
可以用一个比喻来说明问题:人A和人B想要交谈,但是他们都不知道彼此的语言,这就需要一个翻译C,同时懂得A和B的语言。有了C做中间层,A和B才能正常交流。C的作用就有点像编译器,它必须能理解源程序所要表达的意思,才能把信息传递给另一个。
编译器也一样,它的输入是语言的源文件(一般可以是文本文件)对于输入的文件,首先要分离出这个输入文件的每个元素(关键字、变量、符号、、)
然后根据语言的文法,分析这些元素的组合是否合法,以及这些组合所表达的意思。
程序设计语言和自然语言不一样,都是用符号来描述,每个特定的符号表示特定的意思,而且程序设计语言是上下文无关的。上下文无关就是某一个特定语句所要表达的意思和它所处的上下文没有关系,只有它自身决定。
这篇博文主要说的就是词法分析,也就是把输入的符号串整理成特定的词素。
词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等
(1) 关键字 是由程序语言定义的具有固定意义的标识符。例如,Pascal 中的begin,end,if,while都是保留字。这些字通常不用作一般标识符。
(2) 标识符 用来表示各种名字,如变量名,数组名,过程名等等。
常数的类型一般有整型、实型、布尔型、文字型等。
(4) 运算符 如+、-、*、/等等。
如逗号、分号、括号、等等。
词法分析器所输出单词符号常常表示成如下的二元式:
(单词种别,单词符号的属性值)
单词种别通常用整数编码。标识符一般统归为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种。运算符可采用一符一种的方法。界符一般用一符一种的方法。对于每个单词符号,除了给出了种别编码之外,还应给出有关单词符号的属性信息。单词符号的属性是指单词符号的特性或特征。
比如如下的代码段:
while(i&=j) i--
经词法分析器处理后,它将被转为如下的单词符号序列:
词法分析分析器作为一个独立子程序
词法分析是编译过程中的一个阶段,在语法分析前进行。词法分析作为一遍,可以简化设计,改进编译效率,增加编译系统的可移植性。也可以和语法分析结合在一起作为一遍,由语法分析程序调用词法分析程序来获得当前单词供语法分析使用。
词法分析器设计
输入、预处理
词法分析器工作的第一步是输入源程序文本。在许多情况下,为了更好地对单词符号识别,把输入串预处理一下。预处理主要滤掉空格,跳过注释、换行符等。
词法分析过程中,有时为了确定词性,需超前扫描若干个字符。
对于FORTRAN 语言,关键字不作为保留字,可作为标识符使用, 空格符号没有任何意义。为了确定词性,需超前扫描若干个字符。
在FORTRAN中
DO99K=1,10
IF(5.EQ.M) I=10
DO99K=1.10
这四个语句都是正确的语句。语句1和2 分别是DO和IF语句,语句3和4是赋值语句。为了正确区别1和3,2和4语句,需超前扫描若干个字符。
DO99K=1,10
IF(5.EQ.M) I=10
DO99K=1.10
语句1和3的区别在于符号之后的第一个界符:一个为逗号,另一个为句末符。语句2和4的主要区别在于右括号后的第一个字符:一个为字母,另一个为等号。为了识别1、2中的关键字,必须超前扫描多个字符。超前到能够肯定词性的地方为止。为了区别1和3,必须超前扫描到等号后的第一个界符处。对于语句2、4来说,必须超前扫描到与IF后的左括号相对应的那个右括号之后的第一个字符为止。
状态转换图
词法分析器使用状态转换图来识别单词符号。状态转换图是一张有限方向图。在状态转换图中,有一个初态,至少一个终态。
其中0为初态,2为终态。这个转换图识别(接受)标识符的过程是:从初态0开始,若在状态0之下输入字符是一个字母,则读进它,并转入状态1。在状态1之下,若下一个输入字符为字母或数字,则读进它,并重新进入状态1。一直重复这个过程直到状态1发现输入字符不再是字母或数字时(这个字符也已被读进)就进入状态2。状态2是终态,它意味着到此已识别出一个标识符,识别过程宣告终止。终态结上打个星号意味着多读进了一个不属于标识符部分的字符,应把它退还给输入口中 。如果在状态0时输入字符不为“字母”,则意味着识别不出标识符,或者说,这个转换图工作不成功。
正规表达式与正规集
正规表达式是说明单词的一种重要的表示法(记号),是定义正规集的工具。在词法分析中,正规表达式用来描述标示符可能具有的形式。
定义(正规式和它所表示的正规集):
设字母表为S,
1. e和?都是S上的正规式,它们所表示的正规集分别为{e}和{ };
2. 任何a? S,a是S上的一个正规式,它所表示的正规集为{a};
3. 假定U和V都是S上的正规式,它们所表示的正规集分别为L(U)和L(V),那么,(U), U|V, U·V, U*也都是正规式,它们所表示的正规集分别为L(U), L(U)?L(V), L(U)L(V)和(L(U))*;
4. 仅由有限次使用上述三步骤而定义的表达式才是S上的正规式,仅由这些正规式所表示的字集才是S上的正规集。
正规式的运算符的“ 1/2 ”读为“或” ,“· ”读为“连接”;“*”读为“闭包”(即,任意有限次的自重复连接)。
在不致混淆时,括号可省去,但规定算符的优先顺序为“(”、“)”、“*”、“· ”、“ 1/2 ” 。连接符“· ”一般可省略不写。
“*”、“· ”和“ 1/2 ” 都是左结合的。
例 令S={a,b}, S上的正规式和相应的正规集的例子有:
     {a}
(a 1/2 b)(a
{aa,ab,ba,bb}
{e ,a,a, ……任意个a的串}
{b, ba, baa, baaa, …}
(a 1/2 b)*
{e ,a,b,aa,ab ……所有由a和b
(a 1/2 b)*(aa 1/2 bb)(a 1/2 b)*
{S*上所有含有两个相继的a
或两个相继的b组成 的串}
定理:若两个正规式U和V所表示的正规集相同,则说U和V等价,写作U=V。
证明b(ab)*=( ba)*b
证明:因为L(b(ab)*)={b}{e, ab, abab, ababab, …}
={b, bab, babab, bababab, …}
L((ba)*b) ={e, ba, baba, bababa, …}{b}
={b, bab, babab, bababab, …}
= L(b(ab)*)
b(ab)*=( ba)*b
设U,V,W为正规式,正规式服从的代数规律有:
(1) U 1/2 V=V 1/2 U
(2) U 1/2 (V 1/2 W)=(U 1/2 V) 1/2 W
(3) U(VW)=(UV)W
(4) U(V 1/2 W)=UV 1/2 UW
(V 1/2 W)U=VU 1/2 WU
(5) eU=U e=U
分析器的简单实现
上文主要介绍了词法分析的一些相关的知识,而对词法分析器的具体实现还没有具体提到,为了能更好的理解词法分析,我写了一个简单的词法分析器。
虽然说是语法分析器,但实现的功能很简单,只是对输入的程序把注释去掉,其中用到了上面关于状态转换图部分的知识。
一般的程序设计语言, 注释部分的形式为;
/* 注释部分、、、、*/
我们的程序总是顺序的一个一个字符读取输入文件的。我们的目的是把注释部分去掉,那么对于输入的字符流,我们只要识别出“/*”就知道后面的部分是注释部分,直到识别输入流中出现"*/"为止。
对字符流的处理是一个一个进行的,每读入一个字符,就判断,如果字符是“/”,就说明后面 的部分可能是注释,再看下一个输入字符,如果是“*”, 就是上面所说的情况:“ /*”那么后面的部分就是注释部分,然后再用相同的方法找出"*/"就可以了。
这个识别的过程就可以用状态转换图来清晰的表示:
对于读入的每个符号都要进行判断,如果是“/”说明后面的部分有可能是注释,进入状态1。如果后面的输入是“*”那么就可以确定以后的内容为注释内容,如果后面的输入不是"*",说明后面的内容不是注释,前面出现的"/"可能是做除号使用,如“5/3”
其实上面的流程图也就对应了程序实现的逻辑,可以用switch-case 来实现,对于每个输入,判断后跳转到相应的状态,然后继续判断。
下面是程序伪代码:
while((ch=getchar())!=EOF)
  switch(state)
    case 1 :if ch=="/",state=2,
    case 2: if ch=="*",state=3
        else state=1;
    case 3:..........
    case 4:..........
词法分析器
这个程序比较简单,就不给出源代码了。接下来是一个简单的词法分析器的代码,可以实现对关键字(如 while end if 等),对数字的识别,去掉空格符等。
下面是这个分析器的功能:
待分析的简单语言的词法
所有关键字都是小写。
运算符和界符:
其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:
ID=letter(letter| digit)*
NUM=digit digit *
空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。
各种单词符号对应的种别码
词法分析程序的功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。
其中:syn为单词种别码;
token为存放的单词自身字符串;
sum为整型常数。
下面是程序源代码,基于上面的讨论,应该比较好了解了。
char prog[80],token[8];
int syn,p,m=0,n,row,sum=0;
char *rwtab[6]={"begin","if","then","while","do","end"};
void scaner()
共分为三大块,分别是标示符、数字、符号,对应下面的 if
for(n=0;n&8;n++) token[n]=NULL;
ch=prog[p++];
while(ch==' ')
ch=prog[p];
if((ch&='a'&&ch&='z')||(ch&='A'&&ch&='Z'))
//可能是标示符或者变量名
while((ch&='0'&&ch&='9')||(ch&='a'&&ch&='z')||(ch&='A'&&ch&='Z'))
token[m++]=
ch=prog[p++];
token[m++]='\0';
for(n=0;n&6;n++)
//将识别出来的字符和已定义的标示符作比较,
if(strcmp(token,rwtab[n])==0)
else if((ch&='0'&&ch&='9'))
while((ch&='0'&&ch&='9'))
sum=sum*10+ch-'0';
ch=prog[p++];
if(sum&32767)
else switch(ch)
//其他字符
case'&':m=0;token[m++]=
ch=prog[p++];
if(ch=='&')
token[m++]=
else if(ch=='=')
token[m++]=
case'&':m=0;token[m++]=
ch=prog[p++];
if(ch=='=')
token[m++]=
case':':m=0;token[m++]=
ch=prog[p++];
if(ch=='=')
token[m++]=
case'*':syn=13;token[0]=
case'/':syn=14;token[0]=
case'+':syn=15;token[0]=
case'-':syn=16;token[0]=
case'=':syn=25;token[0]=
case';':syn=26;token[0]=
case'(':syn=27;token[0]=
case')':syn=28;token[0]=
case'#':syn=0;token[0]=
case'\n':syn=-2;
default: syn=-1;
int main()
cout&&"Please input string:"&&
cin.get(ch);
prog[p++]=
while(ch!='#');
switch(syn)
case 11: cout&&"("&&syn&&","&&sum&&")"&&
case -1: cout&&"Error in row "&&row&&"!"&&
case -2: row=row++;
default: cout&&"("&&syn&&","&&token&&")"&&
while (syn!=0);
改程序在C-free5上调试通过
下面是程序截图:
这里主要说的是编译器中的词法分析,还介绍了词法分析的一些相关知识,最后给出了一个很简单的词法分析器的实现。
编译,简单的说,就是把源程序转换为可执行程序。从hello world 说程序运行机制 里面简单的说明了程序运行的过程,以及一个程序是如何一步步变成可执行文件的。在这个过程中,编译器做了很多重要的工作。对底层该兴趣的我,自然的,也就迫切想搞清楚编译的内部实现,也就是编译的原理。
这篇文章主要说的是编译器前端,词法分析器的原理,最后会给出一个词法分析器的简单实现。
编译简单的说,就是把源程序转化为另一种形式的程序,而其中关键的部分就是理解源程序所要表达的意思,才能转化为另一种源程序。
可以用一个比喻来说明问题:人A和人B想要交谈,但是他们都不知道彼此的语言,这就需要一个翻译C,同时懂得A和B的语言。有了C做中间层,A和B才能正常交流。C的作用就有点像编译器,它必须能理解源程序所要表达的意思,才能把信息传递给另一个。
编译器也一样,它的输入是语言的源文件(一般可以是文本文件)对于输入的文件,首先要分离出这个输入文件的每个元素(关键字、变量、符号、、)
然后根据语言的文法,分析这些元素的组合是否合法,以及这些组合所表达的意思。
程序设计语言和自然语言不一样,都是用符号来描述,每个特定的符号表示特定的意思,而且程序设计语言是上下文无关的。上下文无关就是某一个特定语句所要表达的意思和它所处的上下文没有关系,只有它自身决定。
这篇博文主要说的就是词法分析,也就是把输入的符号串整理成特定的词素。
词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等
(1) 关键字 是由程序语言定义的具有固定意义的标识符。例如,Pascal 中的begin,end,if,while都是保留字。这些字通常不用作一般标识符。
(2) 标识符 用来表示各种名字,如变量名,数组名,过程名等等。
常数的类型一般有整型、实型、布尔型、文字型等。
(4) 运算符 如+、-、*、/等等。
如逗号、分号、括号、等等。
词法分析器所输出单词符号常常表示成如下的二元式:
(单词种别,单词符号的属性值)
单词种别通常用整数编码。标识符一般统归为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种。运算符可采用一符一种的方法。界符一般用一符一种的方法。对于每个单词符号,除了给出了种别编码之外,还应给出有关单词符号的属性信息。单词符号的属性是指单词符号的特性或特征。
比如如下的代码段:
while(i&=j) i--
经词法分析器处理后,它将被转为如下的单词符号序列:
词法分析分析器作为一个独立子程序
词法分析是编译过程中的一个阶段,在语法分析前进行。词法分析作为一遍,可以简化设计,改进编译效率,增加编译系统的可移植性。也可以和语法分析结合在一起作为一遍,由语法分析程序调用词法分析程序来获得当前单词供语法分析使用。
词法分析器设计
输入、预处理
词法分析器工作的第一步是输入源程序文本。在许多情况下,为了更好地对单词符号识别,把输入串预处理一下。预处理主要滤掉空格,跳过注释、换行符等。
词法分析过程中,有时为了确定词性,需超前扫描若干个字符。
对于FORTRAN 语言,关键字不作为保留字,可作为标识符使用, 空格符号没有任何意义。为了确定词性,需超前扫描若干个字符。
在FORTRAN中
DO99K=1,10
IF(5.EQ.M) I=10
DO99K=1.10
这四个语句都是正确的语句。语句1和2 分别是DO和IF语句,语句3和4是赋值语句。为了正确区别1和3,2和4语句,需超前扫描若干个字符。
DO99K=1,10
IF(5.EQ.M) I=10
DO99K=1.10
语句1和3的区别在于符号之后的第一个界符:一个为逗号,另一个为句末符。语句2和4的主要区别在于右括号后的第一个字符:一个为字母,另一个为等号。为了识别1、2中的关键字,必须超前扫描多个字符。超前到能够肯定词性的地方为止。为了区别1和3,必须超前扫描到等号后的第一个界符处。对于语句2、4来说,必须超前扫描到与IF后的左括号相对应的那个右括号之后的第一个字符为止。
状态转换图
词法分析器使用状态转换图来识别单词符号。状态转换图是一张有限方向图。在状态转换图中,有一个初态,至少一个终态。
其中0为初态,2为终态。这个转换图识别(接受)标识符的过程是:从初态0开始,若在状态0之下输入字符是一个字母,则读进它,并转入状态1。在状态1之下,若下一个输入字符为字母或数字,则读进它,并重新进入状态1。一直重复这个过程直到状态1发现输入字符不再是字母或数字时(这个字符也已被读进)就进入状态2。状态2是终态,它意味着到此已识别出一个标识符,识别过程宣告终止。终态结上打个星号意味着多读进了一个不属于标识符部分的字符,应把它退还给输入口中 。如果在状态0时输入字符不为“字母”,则意味着识别不出标识符,或者说,这个转换图工作不成功。
正规表达式与正规集
正规表达式是说明单词的一种重要的表示法(记号),是定义正规集的工具。在词法分析中,正规表达式用来描述标示符可能具有的形式。
定义(正规式和它所表示的正规集):
设字母表为S,
1. e和?都是S上的正规式,它们所表示的正规集分别为{e}和{ };
2. 任何a? S,a是S上的一个正规式,它所表示的正规集为{a};
3. 假定U和V都是S上的正规式,它们所表示的正规集分别为L(U)和L(V),那么,(U), U|V, U·V, U*也都是正规式,它们所表示的正规集分别为L(U), L(U)?L(V), L(U)L(V)和(L(U))*;
4. 仅由有限次使用上述三步骤而定义的表达式才是S上的正规式,仅由这些正规式所表示的字集才是S上的正规集。
正规式的运算符的“ 1/2 ”读为“或” ,“· ”读为“连接”;“*”读为“闭包”(即,任意有限次的自重复连接)。
在不致混淆时,括号可省去,但规定算符的优先顺序为“(”、“)”、“*”、“· ”、“ 1/2 ” 。连接符“· ”一般可省略不写。
“*”、“· ”和“ 1/2 ” 都是左结合的。
例 令S={a,b}, S上的正规式和相应的正规集的例子有:
     {a}
(a 1/2 b)(a
{aa,ab,ba,bb}
{e ,a,a, ……任意个a的串}
{b, ba, baa, baaa, …}
(a 1/2 b)*
{e ,a,b,aa,ab ……所有由a和b
(a 1/2 b)*(aa 1/2 bb)(a 1/2 b)*
{S*上所有含有两个相继的a
或两个相继的b组成 的串}
定理:若两个正规式U和V所表示的正规集相同,则说U和V等价,写作U=V。
证明b(ab)*=( ba)*b
证明:因为L(b(ab)*)={b}{e, ab, abab, ababab, …}
={b, bab, babab, bababab, …}
L((ba)*b) ={e, ba, baba, bababa, …}{b}
={b, bab, babab, bababab, …}
= L(b(ab)*)
b(ab)*=( ba)*b
设U,V,W为正规式,正规式服从的代数规律有:
(1) U 1/2 V=V 1/2 U
(2) U 1/2 (V 1/2 W)=(U 1/2 V) 1/2 W
(3) U(VW)=(UV)W
(4) U(V 1/2 W)=UV 1/2 UW
(V 1/2 W)U=VU 1/2 WU
(5) eU=U e=U
分析器的简单实现
上文主要介绍了词法分析的一些相关的知识,而对词法分析器的具体实现还没有具体提到,为了能更好的理解词法分析,我写了一个简单的词法分析器。
虽然说是语法分析器,但实现的功能很简单,只是对输入的程序把注释去掉,其中用到了上面关于状态转换图部分的知识。
一般的程序设计语言, 注释部分的形式为;
/* 注释部分、、、、*/
我们的程序总是顺序的一个一个字符读取输入文件的。我们的目的是把注释部分去掉,那么对于输入的字符流,我们只要识别出“/*”就知道后面的部分是注释部分,直到识别输入流中出现"*/"为止。
对字符流的处理是一个一个进行的,每读入一个字符,就判断,如果字符是“/”,就说明后面 的部分可能是注释,再看下一个输入字符,如果是“*”, 就是上面所说的情况:“ /*”那么后面的部分就是注释部分,然后再用相同的方法找出"*/"就可以了。
这个识别的过程就可以用状态转换图来清晰的表示:
对于读入的每个符号都要进行判断,如果是“/”说明后面的部分有可能是注释,进入状态1。如果后面的输入是“*”那么就可以确定以后的内容为注释内容,如果后面的输入不是"*",说明后面的内容不是注释,前面出现的"/"可能是做除号使用,如“5/3”
其实上面的流程图也就对应了程序实现的逻辑,可以用switch-case 来实现,对于每个输入,判断后跳转到相应的状态,然后继续判断。
下面是程序伪代码:
while((ch=getchar())!=EOF)
  switch(state)
    case 1 :if ch=="/",state=2,
    case 2: if ch=="*",state=3
        else state=1;
    case 3:..........
    case 4:..........
词法分析器
这个程序比较简单,就不给出源代码了。接下来是一个简单的词法分析器的代码,可以实现对关键字(如 while end if 等),对数字的识别,去掉空格符等。
下面是这个分析器的功能:
待分析的简单语言的词法
所有关键字都是小写。
运算符和界符:
其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:
ID=letter(letter| digit)*
NUM=digit digit *
空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。
各种单词符号对应的种别码
词法分析程序的功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。
其中:syn为单词种别码;
token为存放的单词自身字符串;
sum为整型常数。
下面是程序源代码,基于上面的讨论,应该比较好了解了。
char prog[80],token[8];
int syn,p,m=0,n,row,sum=0;
char *rwtab[6]={"begin","if","then","while","do","end"};
void scaner()
共分为三大块,分别是标示符、数字、符号,对应下面的 if
for(n=0;n&8;n++) token[n]=NULL;
ch=prog[p++];
while(ch==' ')
ch=prog[p];
if((ch&='a'&&ch&='z')||(ch&='A'&&ch&='Z'))
//可能是标示符或者变量名
while((ch&='0'&&ch&='9')||(ch&='a'&&ch&='z')||(ch&='A'&&ch&='Z'))
token[m++]=
ch=prog[p++];
token[m++]='\0';
for(n=0;n&6;n++)
//将识别出来的字符和已定义的标示符作比较,
if(strcmp(token,rwtab[n])==0)
else if((ch&='0'&&ch&='9'))
while((ch&='0'&&ch&='9'))
sum=sum*10+ch-'0';
ch=prog[p++];
if(sum&32767)
else switch(ch)
//其他字符
case'&':m=0;token[m++]=
ch=prog[p++];
if(ch=='&')
token[m++]=
else if(ch=='=')
token[m++]=
case'&':m=0;token[m++]=
ch=prog[p++];
if(ch=='=')
token[m++]=
case':':m=0;token[m++]=
ch=prog[p++];
if(ch=='=')
token[m++]=
case'*':syn=13;token[0]=
case'/':syn=14;token[0]=
case'+':syn=15;token[0]=
case'-':syn=16;token[0]=
case'=':syn=25;token[0]=
case';':syn=26;token[0]=
case'(':syn=27;token[0]=
case')':syn=28;token[0]=
case'#':syn=0;token[0]=
case'\n':syn=-2;
default: syn=-1;
int main()
cout&&"Please input string:"&&
cin.get(ch);
prog[p++]=
while(ch!='#');
switch(syn)
case 11: cout&&"("&&syn&&","&&sum&&")"&&
case -1: cout&&"Error in row "&&row&&"!"&&
case -2: row=row++;
default: cout&&"("&&syn&&","&&token&&")"&&
while (syn!=0);
改程序在C-free5上调试通过
下面是程序截图:
这里主要说的是编译器中的词法分析,还介绍了词法分析的一些相关知识,最后给出了一个很简单的词法分析器的实现。
哈哈,最近刚写完词法语法分析,更新到博客上,github上有源码,手机不好发连接,自取吧
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!
其他相关推荐

我要回帖

更多关于 词法分析器代码 的文章

 

随机推荐