湖南大学编译原理实验一cminus_compiler-2021-fall
cminus_compiler-2021-fall前言Lab1 实验文档0. 基础知识0.1 cminus-f词法0.2 FLEX简单使用1. 实验要求1.1 目录结构1.2 编译、运行和验证实验报告实验要求实验难点实验设计实验结果验证实验反馈前言实验文档来源湖南大学编译原理课程,实验报告是笔者写的。Lab1 实验文档0. 基础知识在本次实验中我们讲重点用到FLEX和以C-为基础改编的cminus
cminus_compiler-2021-fall
前言
实验文档来源湖南大学编译原理课程,实验报告是笔者写的。
Lab1 实验文档
0. 基础知识
在本次实验中我们讲重点用到FLEX
和以C-
为基础改编的cminus-f
语言。这里对其进行简单介绍。
0.1 cminus-f词法
C MINUS
是C语言的一个子集,该语言的语法在《编译原理与实践》第九章附录中有详细的介绍。而cminus-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
,]
0.2 FLEX简单使用
FLEX
是一个生成词法分析器的工具。利用FLEX
,我们只需提供词法的正则表达式,就可自动生成对应的C代码。整个流程如下图:
首先,FLEX
从输入文件*.lex
或者stdio
读取词法扫描器的规范,从而生成C代码源文件lex.yy.c
。然后,编译lex.yy.c
并与-lfl
库链接,以生成可执行的a.out
。最后,a.out
分析其输入流,将其转换为一系列token。
我们以一个简单的单词数量统计的程序wc.l为例:
%{
//在%{和%}中的代码会被原样照抄到生成的lex.yy.c文件的开头,您可以在这里书写声明与定义
#include <string.h>
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完成了一个简单的分析器!
1. 实验要求
本次实验需要各位同学根据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
用法上文已经进行简短的介绍,更高阶的用法请参考百度、谷歌和官方说明。
1.1 目录结构
整个repo
的结构如下
.
├── CMakeLists.txt
├── Documentations
│ └── lab1
│ └── README.md <- lab1实验文档说明
├── README.md
├── Reports
│ └── lab1
│ └── report.md <- lab1所需提交的实验报告(你需要在此提交实验报告)
├── include <- 实验所需的头文件
│ └── lexical_analyzer.h
├── src <- 源代码
│ └── lexer
│ ├── CMakeLists.txt
│ └── lexical_analyzer.l <- flex文件,lab1所需完善的文件
└── tests <- 测试文件
└── lab1
├── CMakeLists.txt
├── main.c <- lab1的main文件
├── test_lexer.py
├── testcase <- 助教提供的测试样例
└── TA_token <- 助教提供的关于测试样例的词法分析结果
1.2 编译、运行和验证
lab1
的代码大部分由C
和python
构成,使用cmake
进行编译。
-
编译
# 进入workspace $ cd cminus_compiler-2021-fall # 创建build文件夹,配置编译环境 $ mkdir build $ cd build $ cmake ../ # 开始编译 # 如果你只需要编译lab 1,请使用 make lexer $ make
编译成功将在
${WORKSPACE}/build/
下生成lexer
命令 -
运行
$ cd cminus_compiler-2021-fall # 运行lexer命令 $ ./build/lexer usage: lexer input_file output_file # 我们可以简单运行下 lexer命令,但是由于此时未完成实验,当然输出错误结果 $ ./build/lexer ./tests/lab1/testcase/1.cminus out [START]: Read from: ./tests/lab1/testcase/1.cminus [ERR]: unable to analysize i at 1 line, from 1 to 1 ...... ...... $ head -n 5 out [ERR]: unable to analysize i at 1 line, from 1 to 1 258 1 1 1 [ERR]: unable to analysize n at 1 line, from 1 to 1 258 1 1 1 [ERR]: unable to analysize t at 1 line, from 1 to 1 258 1 1 1 [ERR]: unable to analysize at 1 line, from 1 to 1 258 1 1 1 [ERR]: unable to analysize g at 1 line, from 1 to 1 258 1 1 1
我们提供了
./tests/lab1/test_lexer.py
python脚本用于调用lexer
批量完成分析任务。# test_lexer.py脚本将自动分析./tests/lab1/testcase下所有文件后缀为.cminus的文件,并将输出结果保存在./tests/lab1/token文件下下 $ python3 ./tests/lab1/test_lexer.py ··· ··· ··· #上诉指令将在./tests/lab1/token文件夹下产生对应的分析结果 $ ls ./tests/lab1/token 1.tokens 2.tokens 3.tokens 4.tokens 5.tokens 6.tokens
-
验证
我们使用
diff
指令进行验证。将自己的生成结果和助教提供的TA_token
进行比较。$ diff ./tests/lab1/token ./tests/lab1/TA_token # 如果结果完全正确,则没有任何输出结果 # 如果有不一致,则会汇报具体哪个文件哪部分不一致
请注意助教提供的
testcase
并不能涵盖全部的测试情况,完成此部分仅能拿到基础分,请自行设计自己的testcase
进行测试。
实验报告
实验要求
根据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,我们只需要进行过滤,即只需被识别,但是不应该被输出到分析结果中。因为这些token对程序运行不起到任何作用。
实验难点
- 实验环境的搭建以及配置,比如ubuntu20.04的安装以及flex编译环境的配置等。
- 学习并掌握cminus-f语法,提供其词法的正则表达式。
- FLEX是一个生成词法分析器的工具,我们需要学会其简单应用。
实验设计
1.首先需要编写正则表达式,类似于C语言中的宏定义,如下图所示是对cminus-f语法中的关键字,专用符号,标识符ID等正则表达式的定义,例如我们可以定义0|[1-9][0-9]*
为INTEGER,这样只要读出大于等于0的数字,就识别为INTEGER,关键字else就识别为ELSE。EOL [\n]+
是因为换行符可能不止一个,[\n]+则是语法糖里表示一个或一个以上换行。
2.编写指定模式匹配时对应的动作,yytext指针指向本次匹配的输入文本,我们在左部分写要匹配的正则表达式,右部分为匹配到该正则表达式后执行的动作。除了一些需要特殊的正则表达式如COMMENT要在analyzer
函数中实现外,大部分的都是一样的,先设置pos_start为pos_end即上一次识别结束的位置,然后设置pos_end+=strlen(yytext),strlen(yytext)为这次识别到的长度,最后进行return即可。最后的.{return ERROR;}
表示如果识别到其他未定义的字符则返回错误
3.在analyzer
函数中补充关于匹配到注释COMMENT,空格BLANK以及换行EOL后执行的动作。因为在步骤2时已经return ,所以这里只需要写关于pos_start,pos_end以及lines的相关变化
(1)对于注释COMMENT,先获yytext的长度为len,然后通过while循环判断注释中是否存在换行符,即判断yytext[i]是否为换行符,如果是换行\n的话,将pos_start和pos_end设置为1,lines加1;否则pos_end++;当循环结束则break;
(2)对于空格BLANK,我们只需要设置其pos_start以及pos_end的值变化,pos_start=pos_end,pos_end+=strlen(yytext)如下代码所示:
(3)对于换行EOL,只需要设置其pos_start和pos_end都为1,并让lines加上strlen(yytext),表示行数变化了多少
实验结果验证
1.创建build文件夹,配置编译环境, 执行指令make lexer运行代码,然后执行指令python3 ./tests/lab1/test_lexer.py
生成对应的token。
2.如图所示,6个token与助教提供的TO_token比较时,没有任何输出结果,表示结果完全相同,正确。
3.自己编写一个特殊的代码,对COMMENT注释的正则定义进行测试,判断其是否可以识别 /*//*/
这样的注释,同时检查数组a[],a[n]。
使用指令./build/lexer ./tests/lab1/testcase/7.mytest out
,得到如下输出:
结果分析:/*//*/
部分可以正确识别(没有输出因为直接过滤了),结果正确。同时数组的识别也正确。
更多推荐
所有评论(0)