编译原理lab1

您所在的位置:网站首页 rmarkdown换行符 编译原理lab1

编译原理lab1

2023-04-01 02:32| 来源: 网络整理| 查看: 265

Lab1 实验报告-利用FLEX构造C-Minus-f词法分析器 LAB1实验文档 ⼀、实验目的

学习和掌握词法分析程序的逻辑原理与构造方法。

通过 FLEX 进行实践, 构造 C-Minus-f 词法分析器。

二、实验任务

学习 C-Minus-f 的词法规则

学习 FLEX 工具使用方法

使用 FLEX 生成 C-Minus-f 的词法分析器, 并进行验证

三、实验内容

详细的实验项目文档为Documentations/lab1/README.md · ConchL/cminus_compiler-2022-fall - Gitee.com

1、学习C-Ninus-f的词法规则

C-Minus 是 C 语言的⼀个子集,而 C-Minus-f 则是在 C-Minus 上追加了浮点操作。

1.关键字

else if int return void while float

2.专用符号

+ - * / < >= == != = ; , ( ) [ ] { } /* */

3.标识符ID和整数NUM,通过下列正则表达式定义

letter = a|...|z|A|...|Z digit = 0|...|9 ID = letter+ INTEGER = digit+ FLOAT = (digit+. | digit*.digit+)

4.注释用/*...*/表示,可以超过一行。注释不能嵌套

/*...*/

注: [ , ] 和 [] 是三种不同的 token 。

[] 用于声明数组类型, [] 中间不得有空格

a [] 应被识别为两个 token: a , []

a [1] 应被识别为四个 token: a , [ , 1 , ]

2、学习FLEX工具使用方法

FLEX简介

Lex是LEXical compiler的缩写,是Unix环境下非常著名的工具,主要功能是生成一个词法分析器(scanner)的C源码,描述规则采用正则表达式(regular expression)。描述词法分析器的文件*.l,经过lex编译后,生成一个lex.yy.c 的文件,然后由C编译器编译生成一个词法分析器。

词法分析器,简单来说,其任务就是将输入的各种符号,转化成相应的标识符(token),转化后的标识符很容易被后续阶段处理。

流程如下图:

在这里插入图片描述

首先,FLEX从输入文件*.flex或者stdin读取词法扫描器的规范,从而生成C代码源文件lex.yy.c。然后,编译lex.yy.c并与-lfl库链接,以生成可执行的a.out。最后,a.out分析其输入流,将其转换为一系列token。

我们以一个简单的单词数量统计的程序wc.l为例:

%{ //在%{和%}中的代码会被原样照抄到生成的lex.yy.c文件的开头,您可以在这里书写声明与定义 #include int chars = 0; int words = 0; %} %% /*你可以在这里使用你熟悉的正则表达式来编写模式*/ /*你可以用C代码来指定模式匹配时对应的动作*/ /*yytext指针指向本次匹配的输入文本*/ /*左部分([a-zA-Z]+)为要匹配的正则表达式, 右部分({ chars += strlen(yytext);words++;})为匹配到该正则表达式后执行的动作*/ [a-zA-Z]+ { chars += strlen(yytext);words++;} . {} /*对其他所有字符,不做处理,继续执行*/ %% int main(int argc, char **argv){ //yylex()是flex提供的词法分析例程,默认读取stdin yylex(); printf("look, I find %d words of %d chars\n", words, chars); return 0; }

使用Flex生成lex.yy.c

[TA@TA example]$ flex wc.l [TA@TA example]$ gcc lex.yy.c -lfl [TA@TA example]$ ./a.out hello world ^D look, I find 2 words of 10 chars [TA@TA example]$

注: 在以stdin为输入时,需要按下ctrl+D以退出

至此,你已经成功使用Flex完成了一个简单的分析器!

检验:

3.使用FLEX生成C-Minus-f的词法分析器,并进行验证

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

文本输入:

int a;

则识别结果应为:

int 280 1 2 5 a 285 1 6 7 ; 270 1 7 8

具体的需识别token参考lexical_analyzer.h

特别说明对于部分token,我们只需要进行过滤,即只需被识别,但是不应该被输出到分析结果中。因为这些token对程序运行不起到任何作用。

注意,你所需修改的文件应仅有[lexical_analyer.l]…/…/src/lexer/lexical_analyzer.l)。关于FLEX用法上文已经进行简短的介绍,更高阶的用法请参考百度、谷歌和官方说明。

3.1 目录结构

整个repo的结构如下

. ├── CMakeLists.txt ├── Documentations │ └── lab1 │ └── README.md /* RBRACE */ \} /******** 关键字 ********/ /* ELSE */ else /* IF */ if /* INT */ int /* FLOAT */ float /* RETURN */ return /* VOID */ void /* WHILE */ while /******** ID和NUM ********/ /* IDENTIFIER */ [a-zA-Z]+ /* INTEGER */ [0-9]+ /* FLOATPOINT */ [0-9]+\.|[0-9]*\.[0-9]+ /* ARRAY */ \[\] /* LETTER */ [a-zA-Z] /******** others ********/ /* EOL */ [\n] /* COMMENT */ \/\*([*]*(([^*/])+([/])*)*)*\*\/ /* BLANK */ [ \f\n\r\t\v] /* ERROR */ .

3、写出指定模式匹配时对应的动作

动作分为两步,一步是更新lines、pos_start、pos_end等参数,另一步是将当前识别的结果进行返回,即return。

pos_start表示当前识别的词的起始位置,pos_end表示结束位置,lines表示对应的行数,return则会将token进行返回。

因此,代码编写如下:

/****请在此补全所有flex的模式与动作 start******/ //STUDENT TO DO \+ {pos_start=pos_end;pos_end=pos_start+1;return ADD;} \- {pos_start=pos_end;pos_end=pos_start+1;return SUB;} \* {pos_start=pos_end;pos_end=pos_start+1;return MUL;} \/ {pos_start=pos_end;pos_end=pos_start+1;return DIV;} \pos_start=pos_end;pos_end=pos_start+2;return LTE;} \> {pos_start=pos_end;pos_end=pos_start+1;return GT;} ">=" {pos_start=pos_end;pos_end=pos_start+2;return GTE;} "==" {pos_start=pos_end;pos_end=pos_start+2;return EQ;} "!=" {pos_start=pos_end;pos_end=pos_start+2;return NEQ;} \= {pos_start=pos_end;pos_end=pos_start+1;return ASSIN;} \; {pos_start=pos_end;pos_end=pos_start+1;return SEMICOLON;} \, {pos_start=pos_end;pos_end=pos_start+1;return COMMA;} \( {pos_start=pos_end;pos_end=pos_start+1;return LPARENTHESE;} \) {pos_start=pos_end;pos_end=pos_start+1;return RPARENTHESE;} \[ {pos_start=pos_end;pos_end=pos_start+1;return LBRACKET;} \] {pos_start=pos_end;pos_end=pos_start+1;return RBRACKET;} \{ {pos_start=pos_end;pos_end=pos_start+1;return LBRACE;} \} {pos_start=pos_end;pos_end=pos_start+1;return RBRACE;} else {pos_start=pos_end;pos_end=pos_start+4;return ELSE;} if {pos_start=pos_end;pos_end=pos_start+2;return IF;} int {pos_start=pos_end;pos_end=pos_start+3;return INT;} float {pos_start=pos_end;pos_end=pos_start+5;return FLOAT;} return {pos_start=pos_end;pos_end=pos_start+6;return RETURN;} void {pos_start=pos_end;pos_end=pos_start+4;return VOID;} while {pos_start=pos_end;pos_end=pos_start+5;return WHILE;} [a-zA-Z]+ {pos_start=pos_end;pos_end=pos_start+strlen(yytext);return IDENTIFIER;} [0-9]+ {pos_start=pos_end;pos_end=pos_start+strlen(yytext);return INTEGER;} [0-9]*\.[0-9]+ {pos_start=pos_end;pos_end=pos_start+strlen(yytext);return FLOATPOINT;} "[]" {pos_start=pos_end;pos_end=pos_start+2;return ARRAY;} [a-zA-Z] {pos_start=pos_end;pos_end=pos_start+1;return LETTER;} [0-9]+\. {pos_start=pos_end;pos_end=pos_start+strlen(yytext);return FLOATPOINT;} \n {return EOL;} \/\*([^\*]|(\*)*[^\*\/])*(\*)*\*\/ {return COMMENT;} " " {return BLANK;} \t {return BLANK;} . {pos_start=pos_end;pos_end=pos_start+strlen(yytext);return ERROR;} /****请在此补全所有flex的模式与动作 end******/

4.补充C语言对应的代码

由于注释可以分为多行,故需要在识别到注释时,进行额外分析,分析其内所含的换行符\n。识别到换行符,则将lines加1,重置pos_end。

因此,代码编写如下:

case COMMENT: //STUDENT TO DO { pos_start=pos_end; pos_end=pos_start+2; int i=2; while(yytext[i]!='*' || yytext[i+1]!='/') { if(yytext[i]=='\n') { lines=lines+1; pos_end=1; } else pos_end=pos_end+1; i=i+1; } pos_end=pos_end+2; break; } case BLANK: //STUDENT TO DO { pos_start=pos_end; pos_end=pos_start+1; break; } case EOL: //STUDENT TO DO { lines+=1; pos_end=1; break; } 实验结果验证

1、编译:

在这里插入图片描述

成功编译。

2、运行:

在这里插入图片描述

使用了./tests/lab1/test_lexer.py python脚本用于调用lexer批量完成分析任务,生成我们想要的答案。

在这里插入图片描述

3、基础测试:在这里插入图片描述

结果和助教给的正确结果对应上了。

4、自定义测试:

在这里插入图片描述

使用指令./build/lexer ./tests/lab1/testcase/mytest.cminus out,得到如下输出:

在这里插入图片描述

正确。

实验反馈

1、首先是gitee的使用。之前因为项目需要知道了github上的git操作,没接触过gitee,但是发现两者其实用法差不多,知道几个基本操作之后就可以使用了。

2、然后就到了环境配置,环境配置意外卡了好久。上学期做操作系统的ucore实验下了虚拟机上的ubuntu20.04,本来想着直接沿用就行,结果发现四个月没碰不知道为什么打不开了。原来的iso文件找不到了,重新配置比较麻烦,于是选择了助教推荐的WSL2方法,跟着博文配好了。

3、在ubuntu20.04环境时,flex和bison下好之后我以为就配好了,开始学各个符号对应的正则表达式还有c-minus-f。结果写完到最后的编译的时候总是报错(如下图)

在这里插入图片描述

真的卡了一个晚上+一个上午,网络上各种方法试了也不行,结果最后发现是我看漏了cmake../,也没装gcc和g++环境就直接make lexer。被自己无语到了,幸好最后看到了。

4、关于Tokens、正则表达式和Lex的语法。学习了同学推荐的《编译原理及实践》p58及往后几页。

5、在学习C-minus-f的时候,发现它和C还是有所差别的。==比如//并不算注释,只有/**/才算。==这个是后面我用自定义案例(不是现在实验报告里写的这版)测试的时候发现的。

在这里插入图片描述在这里插入图片描述

参考链接

https://blog.csdn.net/m0_51975364/article/details/123237531 https://blog.csdn.net/qq_45890970/article/details/123740300 https://blog.csdn.net/qq_45795586/article/details/122309981 https://iceyblacktea.vercel.app/blog/install-wsl2(需要智慧上网)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3