编译原理:cminus_compiler-2021-fall Lab1

2023-10-12 15:40

本文主要是介绍编译原理:cminus_compiler-2021-fall Lab1,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

说点什么

某湖的编译原理实验。这个实验其实原本是中科大他们那边的编译原理实验项目,然后我们的编译原理实验就是果果和他们py的(X)

注意:本博客仅供参考!!!

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 ./tests/lab1/token ./tests/lab1/TA_token
# 如果结果完全正确,则没有任何输出结果
# 如果有不一致,则会汇报具体哪个文件哪部分不一致

请注意助教提供的testcase并不能涵盖全部的测试情况,完成此部分仅能拿到基础分,请自行设计自己的testcase进行测试。

1.3 提交要求和评分标准

·提交要求

本实验的提交要求分为两部分:实验部分的文件和报告,git提交的规范性。

·实验部分:
·需要完善./src/lab1/lexical_analyer.l文件;
·需要在./Report/lab1/report.md撰写实验报告。
·实验报告内容包括:
·实验要求、实验难点、实验设计、实验结果验证、实验反馈(具体参考report.md);
·实验报告推荐提交 PDF 格式。

·git提交规范:
·不破坏目录结构(report.md所需的图片请放在Reports/lab1/figs/下);
·不上传临时文件(凡是自动生成的文件和临时文件请不要上传,包括lex.yy.c文件以及各位自己生成的tokens文件);
·git log言之有物(不强制, 请不要git commit -m ‘commit 1’, git commit -m ‘sdfsdf’,每次commit请提交有用的comment信息)

·评分标准

·源代码测试: 60%
·git提交规范(20分);
·实现词法分析器并通过给出的6个测试样例(一个10分,共60分);
·提交后通过助教进阶的多个测试用例(20分)。

·实验报告:40%

代码

%option noyywrap
%{
/*****************声明和选项设置  begin*****************/
#include <stdio.h>
#include <stdlib.h>#include "lexical_analyzer.h"int lines;
int pos_start;
int pos_end;/*****************声明和选项设置  end*****************/%}%%/******************TODO*********************//****请在此补全所有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+1;return LT;}
"<=" {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******/
%%
/****************C代码 start*************//// \brief analysize a *.cminus file
///
/// \param input_file, 需要分析的文件路径
/// \param token stream, Token_Node结构体数组,用于存储分析结果,具体定义参考lexical_analyer.hvoid analyzer(char* input_file, Token_Node* token_stream){lines = 1;pos_start = 1;pos_end = 1;if(!(yyin = fopen(input_file,"r"))){printf("[ERR] No input file\n");exit(1);}printf("[START]: Read from: %s\n", input_file);int token;int index = 0;while(token = yylex()){switch(token){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;}elsepos_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;}case ERROR:printf("[ERR]: unable to analysize %s at %d line, from %d to %d\n", yytext, lines, pos_start, pos_end);default :if (token == ERROR){sprintf(token_stream[index].text, "[ERR]: unable to analysize %s at %d line, from %d to %d", yytext, lines, pos_start, pos_end);} else {strcpy(token_stream[index].text, yytext);}token_stream[index].token = token;token_stream[index].lines = lines;token_stream[index].pos_start = pos_start;token_stream[index].pos_end = pos_end;index++;if (index >= MAX_NUM_TOKEN_NODE){printf("%s has too many tokens (> %d)", input_file, MAX_NUM_TOKEN_NODE);exit(1);}}}printf("[END]: Analysis completed.\n");return;
}
/****************C代码 end*************/

大部分操作就是挺正常的写,然后注释,空格,回车,错误这四个需要在后面单独处理一下,但是好像错误的代码是已经给好的(大概?)
写这篇博客的时候已经是放假了,已经记不清当时做这个实验的心路历程了,就记得大概:好像这个一开始写的时候是根本毫无头绪,然后看了前辈完成的代码的时候就突然醒悟这个应该怎么做,然后就发现其实挺简单的(大概?)
这里补充一个[注释]的解释吧,可能你们会有问题(超级贴心的学姐
在这里插入图片描述

这篇关于编译原理:cminus_compiler-2021-fall Lab1的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/196817

相关文章

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu

hdu4059容斥原理

求1-n中与n互质的数的4次方之和 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWrit

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

TL-Tomcat中长连接的底层源码原理实现

长连接:浏览器告诉tomcat不要将请求关掉。  如果不是长连接,tomcat响应后会告诉浏览器把这个连接关掉。    tomcat中有一个缓冲区  如果发送大批量数据后 又不处理  那么会堆积缓冲区 后面的请求会越来越慢。

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

PHP原理之内存管理中难懂的几个点

PHP的内存管理, 分为俩大部分, 第一部分是PHP自身的内存管理, 这部分主要的内容就是引用计数, 写时复制, 等等面向应用的层面的管理. 而第二部分就是今天我要介绍的, zend_alloc中描写的关于PHP自身的内存管理, 包括它是如何管理可用内存, 如何分配内存等. 另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们