编译原理实验1-词法分析器实现

实验要求

本次实验需要各位同学根据cminux-f的词法补全lexical_analyer.l文件,完成词法分析器,能够输出识别出的tokentype ,line(刚出现的行数)pos_start(该行开始位置)pos_end(结束的位置,不包含)

实验设计

lexical_analyer.l文件由三部分组成:定义部分,识别规则部分,辅助函数部分,每个部分由%%分隔开。

定义部分

我在定义部分写了相关头文件的引用全局变量 lines,pos_start,pos_end 的定义,这些是C语言,所以需要用%{ %}括起来:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3T9AVBqF-1645607971927)(figs/image-20211030114207127.png)]

定义部分还有所有token的正则表达式定义,我根据.h文件知道了需要识别的token有五个部分:运算,符号,关键字,ID和NUM,others。其中前三个部分的正则表达式比较简单,后两个部分的正则表达式容易漏掉情况,比如:

标识符IDENTIFIER,它的第一个字符不能为数字,所以不能简单的写成一个-z_A-Z0-9的闭包,需要在闭包的前面加上[a-z_A-Z];

INTEGER 要分两种情况,一种是第一位为0,一种是第一位不为0;

FLOATPOINT的正则表达式中的**. 前要加转义符\** ,要不然不能识别出浮点数的小数点;浮点数要考虑小数点后面没有数字的情况;

注释COMMENT需要在/* 和*/间可以识别 “\\\” 和 “***” 这样的字符串;

BLANK的正则表达式除了识别空格外,还应该可以识别制表符\t;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hU1JE5kx-1645607971928)(figs/image-20211030115008766.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gU5bkeFB-1645607971929)(figs/image-20211030115938383.png)]

识别规则部分

这部分用来编写词法分析器要执行的操作,即识别到词法单元后需要干什么,它每一条的结构如下:

{词法单元} { 进行的操作;}

因为要求将识别到的token的type打印下来,所以当识别到词法单元时需要返回对应的type值(该值在**lexical_analyzer.h**文件中已经定义)即 return (词法单元),例如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4vNq0fXd-1645607971930)(figs/image-20211030141737015.png)]

而识别到token后对全局变量 lines,pos_start,pos_end的操作我放到辅助函数里面。

注意最后一条规则为判断ERROR,它是 . {操作}形式,表示当前面的都不匹配时就判断为ERROR:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5Ek85LH-1645607971931)(figs/image-20211030144052857.png)]

辅助函数部分

在这部分里实现了void analyzer函数,它对识别到的token进行了处理。我需要补充完函数里面的while循环,我的编写如下:

while(token = yylex()){      
        switch(token){
            case COMMENT:              
                //STUDENT TO DO
                for(int i=0;i<strlen(yytext);i++){              
                if(yytext[i]=='\n'){pos_start=1; pos_end=1;lines+=1;}
                else {pos_end+=1;}
                }
                break;

            case BLANK:
                //STUDENT TO DO
                pos_start=pos_end; pos_end+= strlen(yytext);
                break;

            case EOL:
                //STUDENT TO DO
                ++lines; pos_start=1; pos_end=1; 
                break;

            case ERROR:
                pos_start=pos_end; pos_end+= strlen(yytext);
                printf("[ERR]: unable to analysize %s at %d line, from %d to %d\n", yytext, lines, pos_start, pos_end);
                break;

            default :
                pos_start=pos_end; pos_end+= strlen(yytext);
                printf("%s %d %d %d %d\n",yytext,token,lines,pos_start, pos_end);
                ...
                

每次循环从yylex()中获取到token值,该值就是token对应的type。

然后通过switch语句对不同的token做处理:

  • 当为 COMMENT时,通过for循环遍历获取到的字符串(字符串通过yytext获取,它是char*类型,指向识别规则中的一个正规式匹配的单词的首字符),当遇到\n时需要将行数加1,同时记录开始和结束位置的变量重置为1;
  • 当为BLANK时,需要将开始位置变为上一次的结束位置,同时结束位置需要加上yytext(获取到的字符串)的长度;
  • 当为EOL,即换行时,需要将行数加1,同时记录开始和结束位置的变量重置为1;
  • 当为ERROR时,也要更新开始位置和结束位置,同时返回错误信息;
  • 其它情况的token则是需要输出相应信息: yytext, token, lines, pos_start, pos_end;

实验结果验证

执行下面命令进行验证:

python3 ./tests/lab1/test_lexer.py

diff ./tests/lab1/token ./tests/lab1/TA_token

下面为我自己设计的测试样例:

第一行和第四行用于测试注释里面包含//和*时的识别情况,以及/**/y/**/中y是否会被识别成注释里面的内容;

第五行的下划线和第六行的!没有对应的正则表达式,用于测试error情况;

/**/
void main(void){
    int x; 
    x = input(); /******/y/***/ y=input();
    int res_9tado ;   
    if(x!y)output(resultado);
}

其结果如下:

第一行的注释成功识别(注释不打印出来),第四行的识别到两个注释/******/ ,/***/,两个注释中间的y被识别为标识符;

在这里插入图片描述
第五行的_和第六行的!被识别为ERROR,然后输出ERROR信息,可以看到在ERROR情况下,pos_end和pos_begin也会改变。

实验反馈

  • 通过编写正则表达式我学到了语法糖的含义和使用,比如:
.匹配除换行符以外的任意字符
\w匹配字母或数字或下划线或汉字
\s匹配任意的空白符
\d匹配数字
[^x]匹配除了x以外的任意字符
*重复零次或更多次
+重复一次或更多次

以及学会使用一部分LEX函数,比如:

yytextchar*类型,指向识别规则中的一个正规式匹配的单词的首字符
yylex()从该函数开始分析,可由lex自动生成
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值