编译原理lab1 |
您所在的位置:网站首页 › rmarkdown换行符 › 编译原理lab1 |
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 |