湖南大学编译原理实验一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文件,完成词法分析器,能够输出识别出的tokentype ,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的代码大部分由Cpython构成,使用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对程序运行不起到任何作用。

实验难点

  1. 实验环境的搭建以及配置,比如ubuntu20.04的安装以及flex编译环境的配置等。
  2. 学习并掌握cminus-f语法,提供其词法的正则表达式。
  3. 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,得到如下输出:

结果分析:/*//*/部分可以正确识别(没有输出因为直接过滤了),结果正确。同时数组的识别也正确。

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

焦妮敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值