Flex 词法分析实验实现(电子科技大学编译技术Icoding实验)

news2024/11/23 2:35:09

Flex 词法分析

此为电子科技大学编译技术 实验1:词法分析

将具体实现中的三个文件和自己的实验报告一起上传才能通过

根据词法分析实验中给定的文法,利用 flex 设计一词法分析器,该分析器从标准输入读入源代码后,输出单词的类别编号及附加信息。 附加信息规定如下: 当类别为 Y_IDnum_INTnum_FLOAT 时,附加信息为该类别对应的属性,如 main, 100, 29.3等; 当类别为 关键字 时,附件信息为 KEYWORD; 当类别为 运算符 时,附件信息为 OPERATOR; 当类别为 其它符号时,附件信息为 SYMBOL

单词类别的定义:

enum yytokentype {
	num_INT = 258,
	num_FLOAT = 259,

	Y_ID = 260,

	Y_INT = 261,
	Y_VOID = 262,
	Y_CONST = 263,
	Y_IF = 264,
	Y_ELSE = 265,
	Y_WHILE = 266,
	Y_BREAK = 267,
	Y_CONTINUE = 268,
	Y_RETURN = 269,

	Y_ADD = 270,
	Y_SUB = 271,
	Y_MUL = 272,
	Y_DIV = 273,
	Y_MODULO = 274,
	Y_LESS = 275,
	Y_LESSEQ = 276,
	Y_GREAT = 277,
	Y_GREATEQ = 278,
	Y_NOTEQ = 279,
	Y_EQ = 280,
	Y_NOT = 281,
	Y_AND = 282,
	Y_OR = 283,
	Y_ASSIGN = 284,

	Y_LPAR = 285,
	Y_RPAR = 286,
	Y_LBRACKET = 287,
	Y_RBRACKET = 288,
	Y_LSQUARE = 289,
	Y_RSQUARE = 290,
	Y_COMMA = 291,
	Y_SEMICOLON = 292,

	Y_FLOAT = 293
};

例如对于源代码

int main(){
    return 3;
}

词法分析器的输出为:

<261, KEYWORD>
<260, main>
<285, SYMBOL>
<286, SYMBOL>
<287, SYMBOL>
<269, KEYWORD>
<258, 3>
<292, SYMBOL>
<288, SYMBOL>

1 具体实现

以下具体实现分为了三个文件 —— token.hlexer.llexer_main.c

当然你也可以直接写成一个 .l 文件,具体看补充

1.1 头文件 token.h

创建一个头文件 token.h,定义了词法分析器中所需的标记类型附加信息的数据结构;这个头文件中包含了枚举 yytokentype,定义了各种标记类型的类别编号,以及一个联合 _YYLVAL,用于存储附加信息,具体代码如下:

#ifndef TOKEN_H
#define TOKEN_H
enum yytokentype {
	num_INT = 258,
	num_FLOAT = 259,

	Y_ID = 260,

	Y_INT = 261,
	Y_VOID = 262,
	Y_CONST = 263,
	Y_IF = 264,
	Y_ELSE = 265,
	Y_WHILE = 266,
	Y_BREAK = 267,
	Y_CONTINUE = 268,
	Y_RETURN = 269,

	Y_ADD = 270,
	Y_SUB = 271,
	Y_MUL = 272,
	Y_DIV = 273,
	Y_MODULO = 274,
	Y_LESS = 275,
	Y_LESSEQ = 276,
	Y_GREAT = 277,
	Y_GREATEQ = 278,
	Y_NOTEQ = 279,
	Y_EQ = 280,
	Y_NOT = 281,
	Y_AND = 282,
	Y_OR = 283,
	Y_ASSIGN = 284,

	Y_LPAR = 285,
	Y_RPAR = 286,
	Y_LBRACKET = 287,
	Y_RBRACKET = 288,
	Y_LSQUARE = 289,
	Y_RSQUARE = 290,
	Y_COMMA = 291,
	Y_SEMICOLON = 292,

	Y_FLOAT = 293
};

typedef union _YYLVAL{
	int		token;
	int		int_value;
	float   float_value;
	char*	id_name;
}_YYLVAL;

#endif //TOKEN_H

1.2 Flex文件 lexer.l

创建Flex规则文件 lexer.l,其中包含词法分析器的规则定义:

  • 注释处理:规则中包括了处理注释的规则 (\/\/.*\n)|(\/\*.*\*\/),可以将注释从源代码中过滤掉

  • 十六进制整数:规则可以成功地识别十六进制整数,例如 0x10

  • 标识符:规则中有处理标识符的规则,注意,使用了 strdup(yytext) 来为标识符分配内存。这会为每个标识符创建一个新的动态分配的字符串,要确保在适当的时候释放这些字符串以避免内存泄漏

  • 操作符:规则包括处理各种操作符的规则

  • 浮点数:规则可以成功地识别浮点数,但需要注意浮点数的格式。当前规则要求小数点前面至少有一个数字,例如 0.1,而不支持 .1 这种形式

  • 数值的存储:规则中将整数存储为 yylval.int_value,浮点数存储为 yylval.float_value

具体代码:

%{
    #include "token.h"
%}

    _YYLVAL yylval;

%%
[ \t\n] ;
(\/\/.*\n)|(\/\*.*\*\/) ;

int { return Y_INT; }
float { return Y_FLOAT; }

void { return Y_VOID; }
const { return Y_CONST; }
if { return Y_IF; }
else { return Y_ELSE; }
while { return Y_WHILE; }
break { return Y_BREAK; }
continue { return Y_CONTINUE; }
return { return Y_RETURN; }

"+" { return Y_ADD; }
"-" { return Y_SUB; }
"*" { return Y_MUL; }
"/" { return Y_DIV; }
"%" { return Y_MODULO; }
"<" { return Y_LESS; }
"<=" { return Y_LESSEQ; }
">" { return Y_GREAT; }
">=" { return Y_GREATEQ; }
"!=" { return Y_NOTEQ; }
"==" { return Y_EQ; }
"!" { return Y_NOT; }
"&&" { return Y_AND; }
"||" { return Y_OR; }
"=" { return Y_ASSIGN; }

"(" { return Y_LPAR; }
")" { return Y_RPAR; }
"{" { return Y_LBRACKET; }
"}" { return Y_RBRACKET; }
"[" { return Y_LSQUARE; }
"]" { return Y_RSQUARE; }
"," { return Y_COMMA; }
";" { return Y_SEMICOLON; }

[0-9]+ { yylval.int_value = atoi(yytext); return num_INT; }
[0-9]*\.[0-9]+ { yylval.float_value = atof(yytext); return num_FLOAT; }
[a-zA-Z_][a-zA-Z0-9_]* { yylval.id_name = strdup(yytext); return Y_ID; }
0x[0-9a-fA-F]+ { yylval.int_value = strtol(yytext, NULL, 16); return num_INT; }
%%

1.3 main函数文件 lexer_main.c

创建main函数文件 lexer_main.c ,它将从词法分析器接收的标记类型和附加信息输出为类别编号和附加信息

  • #include 部分包含了标准输入输出头文件和自定义的 token.h 头文件

  • extern int yylex();extern _YYLVAL yylval; 声明了从词法分析器生成的函数和变量

  • while((token = yylex()) != 0) 循环调用 yylex() 函数来获取标记,直到返回值为 0 表示词法分析结束

  • 在循环内部,根据标记的类型进行相应的打印输出。对于标识符、整数和浮点数,使用 yylval 结构中相应的成员来获取值

  • 根据标记类型的范围,将其分类为关键字、运算符或其他符号,并打印相应的输出

  • 使用了 free() 函数来释放在标识符规则中动态分配的 yylval.id_name 内存,以避免内存泄漏

具体代码:

#include <stdio.h>
#include "token.h"

extern int yylex();
extern _YYLVAL yylval;

int main(int argc, char **argv) {
    int token;
    while((token = yylex()) != 0) {
		if(token <= 260){
			switch (token) {
				case Y_ID:
					printf("<%d, %s>\n", token, yylval.id_name);
                    free(yylval.id_name);                    
					break;
				case num_INT:
					printf("<%d, %d>\n", token, yylval.int_value);
					break;
				case num_FLOAT:
					printf("<%d, %f>\n", token, yylval.float_value);
					break;
				default:
                    printf("<UNKNOWN>\n");
                    break;					
            }	
        }
        else{
            if(token <= 269 || token == 293) {
                char words[10] = "KEYWORD";
                printf("<%d, %s>\n", token, words);
            }else if(token <= 284) {
                char words[10] = "OPERATOR";
                printf("<%d, %s>\n", token, words);
            }else if(token <= 292) {
                char words[10] = "SYMBOL";
                printf("<%d, %s>\n", token, words);
            }else{
                printf("<UNKNOWN>\n");
            }            
        }       
    }
    return 0;
}

Icoding:将以上三个文件+实验报告上传即可

2 运行测试

在虚拟机上(我的是 VMware + Ubuntu22.04.3)进行的测试

在这三个文件的目录下,执行:

  1. 使用 Flex 编译 test.l 文件,这将生成 lex.yy.c 文件,其中包含词法分析器的C代码

    flex test.l
    
  2. 使用 GCC 编译 lex.yy.ctest_main.c,并生成可执行文件 test,在这一步,使用 -lfl 标志来链接 Flex 库

    gcc lex.yy.c test_main.c -o test -lfl
    
  3. 运行生成的可执行文件 test,并通过标准输入 < 重定向输入测试文件 test1.sy,从而进行词法分析

    ./test < test1.sy
    

测试文件 test1.sy

// test if-else-if
int ifElseIf() {
  int a;
  a = 5;
  int b;
  b = 10;
  if(a == 6 || b == 0xb) {
    return a;
  }
  else {
    if (b == 10 && a == 1)
      a = 25;
    else if (b == 10 && a == -5)
      a = a + 15;
    else
      a = -+a;
  }

  return a;
}

int main(){
  putint(ifElseIf());
  return 0;
}

运行结果部分展示:
在这里插入图片描述

3 补充

可以直接写成一个 .l 文件,如下 lexer.l

%{
enum yytokentype {
	num_INT = 258,
	num_FLOAT = 259,

	Y_ID = 260,

	Y_INT = 261,
	Y_VOID = 262,
	Y_CONST = 263,
	Y_IF = 264,
	Y_ELSE = 265,
	Y_WHILE = 266,
	Y_BREAK = 267,
	Y_CONTINUE = 268,
	Y_RETURN = 269,

	Y_ADD = 270,
	Y_SUB = 271,
	Y_MUL = 272,
	Y_DIV = 273,
	Y_MODULO = 274,
	Y_LESS = 275,
	Y_LESSEQ = 276,
	Y_GREAT = 277,
	Y_GREATEQ = 278,
	Y_NOTEQ = 279,
	Y_EQ = 280,
	Y_NOT = 281,
	Y_AND = 282,
	Y_OR = 283,
	Y_ASSIGN = 284,

	Y_LPAR = 285,
	Y_RPAR = 286,
	Y_LBRACKET = 287,
	Y_RBRACKET = 288,
	Y_LSQUARE = 289,
	Y_RSQUARE = 290,
	Y_COMMA = 291,
	Y_SEMICOLON = 292,

	Y_FLOAT = 293
};

typedef union _YYLVAL{
	int		token;
	int		int_value;
	float   float_value;
	char*	id_name;
}_YYLVAL;

%}

    _YYLVAL yylval;

%%
[ \t\n] ;
(\/\/.*\n)|(\/\*.*\*\/) ;

int { return Y_INT; }
float { return Y_FLOAT; }

void { return Y_VOID; }
const { return Y_CONST; }
if { return Y_IF; }
else { return Y_ELSE; }
while { return Y_WHILE; }
break { return Y_BREAK; }
continue { return Y_CONTINUE; }
return { return Y_RETURN; }

"+" { return Y_ADD; }
"-" { return Y_SUB; }
"*" { return Y_MUL; }
"/" { return Y_DIV; }
"%" { return Y_MODULO; }
"<" { return Y_LESS; }
"<=" { return Y_LESSEQ; }
">" { return Y_GREAT; }
">=" { return Y_GREATEQ; }
"!=" { return Y_NOTEQ; }
"==" { return Y_EQ; }
"!" { return Y_NOT; }
"&&" { return Y_AND; }
"||" { return Y_OR; }
"=" { return Y_ASSIGN; }

"(" { return Y_LPAR; }
")" { return Y_RPAR; }
"{" { return Y_LBRACKET; }
"}" { return Y_RBRACKET; }
"[" { return Y_LSQUARE; }
"]" { return Y_RSQUARE; }
"," { return Y_COMMA; }
";" { return Y_SEMICOLON; }

[0-9]+ { yylval.int_value = atoi(yytext); return num_INT; }
[0-9]*\.[0-9]+ { yylval.float_value = atof(yytext); return num_FLOAT; }
[a-zA-Z_][a-zA-Z0-9_]* { yylval.id_name = strdup(yytext); return Y_ID; }
0x[0-9a-fA-F]+ { yylval.int_value = strtol(yytext, NULL, 16); return num_INT; }
%%
    
int main(int argc, char **argv) {
    int token;
    while((token = yylex()) != 0) {
		if(token <= 260){
			switch (token) {
				case Y_ID:
					printf("<%d, %s>\n", token, yylval.id_name);
                    free(yylval.id_name);                    
					break;
				case num_INT:
					printf("<%d, %d>\n", token, yylval.int_value);
					break;
				case num_FLOAT:
					printf("<%d, %f>\n", token, yylval.float_value);
					break;
				default:
                    printf("<UNKNOWN>\n");
                    break;					
            }	
        }
        else{
            if(token <= 269 || token == 293) {
                char words[10] = "KEYWORD";
                printf("<%d, %s>\n", token, words);
            }else if(token <= 284) {
                char words[10] = "OPERATOR";
                printf("<%d, %s>\n", token, words);
            }else if(token <= 292) {
                char words[10] = "SYMBOL";
                printf("<%d, %s>\n", token, words);
            }else{
                printf("<UNKNOWN>\n");
            }            
        }       
    }
    return 0;
}

此时只需要执行以下即可测试:

  1. flex lexer.l
    
  2. gcc lex.yy.c -o test -lfl
    
  3. ./test < test1.sy
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux线程同步实例

线程同步实例 1. 生产消费者模型基本概念2. 基于BlockingQueue的生产者消费者模型3. 基于环形队列的生产消费模型4. 线程池 1. 生产消费者模型基本概念 生产者消费者模型是一种常用的并发设计模式&#xff0c;它可以解决生产者和消费者之间的速度不匹配、解耦、异步等问题。生…

Vue 绑定style和class

在应用界面中&#xff0c;某些元素的样式是动态的。class 与 style 绑定就是专门用来实现动态样式效果的技术。 如果需要动态绑定 class 或 style 样式&#xff0c;可以使用 v-bind 绑定。 绑定 class 样式【字符串写法】 适用于&#xff1a;类名不确定&#xff0c;需要动态指…

Stm32_标准库_8_ADC_光敏热敏传感器_测数值

在测量光敏传感器数值得基础上手动将通道改成热敏传感器通道即可 由于温度传感器的测量范围是-20 ~ 105摄氏度&#xff0c;所以输出温度得考虑带上符号这就需要在原有输出光照强度代码的基础上新添加几个函数 函数1&#xff1a; uint16_t AD_Getvailue(uint8_t ADC_Channel){…

六、DHCP实验

拓扑图&#xff1a; DHCP协议&#xff0c;给定一个ip范围使其自动给终端分配IP&#xff0c;提高了IP分配的效率 首先对PC设备选择DHCP分配ip 首先先对路由器的下端配置网关的ip 创建地址池&#xff0c;通过globle的方式实现DHCP ip pool 地址池名称 之后设置地址池的网关地址…

最大数【贪心3】

题目 分析 代码 class Solution { public:string largestNumber(vector<int>& nums) {vector<string> str;for(auto & x : nums){str.push_back(to_string(x));}sort(str.begin(),str.end(),[](const string& s1,const string& s2){return s1 s…

【JavaEE】_servlet程序的编写方法

目录 1. 创建项目 2. 引入依赖 3. 创建目录结构 3.1 在main目录下创建一个webapp目录 3.2 在webapp目录下创建一个WEB-INF目录 3.3 在WEB-INF目录下创建一个web.xml文件 3.4 在web.xml中进行代码编写 4. 编写代码 4.1 在java目录下创建类 4.2 打印"hello world&…

亚马逊测评安全吗?

测评可以说是卖家非常宝贵的财富&#xff0c;通过测评和广告相结合&#xff0c;可以快速有效的提升店铺的产品销量&#xff0c;提高转化&#xff0c;提升listing权重&#xff0c;但现在很多卖家找真人测评补单后店铺出现问题导致大家对测评的安全性感到担忧&#xff0c;因为真人…

基于php+thinphp+vue的商品购物商城网站

运行环境 开发语言&#xff1a;PHP 数据库:MYSQL数据库 使用框架:ThinkPHPvue 开发工具:VScode/Dreamweaver/PhpStorm等均可 项目简介 基于tpvue的商品定制交易网站实现前台与后台&#xff0c;用户前台&#xff1b;首页、商品信息、我的收藏、留言板、个人中心、后台管理、管…

【c语言】迷宫游戏

之前想写的迷宫游戏今天终于大功告成&#xff0c;解决了随机生成迷宫地图的问题&#xff0c;使用的是深度优先算法递归版本&#xff0c;之前的迷宫找通路问题用的是深度优先算法的非递归实现.之前写过推箱子&#xff0c;推箱子用到了人物的移动&#xff0c;以及碰到墙就不会走&…

如何解决网站被攻击的问题

在当今数字化时代&#xff0c;网站攻击已经成为互联网上的一个常见问题。这些攻击可能会导致数据泄漏、服务中断和用户信息安全问题。然而&#xff0c;我们可以采取一些简单的措施来解决这些问题&#xff0c;以确保网站的安全性和可用性。 使用强密码和多因素认证 密码是保护网…

今年这情况,还能不能选计算机了?

在知乎上看到一个有意思的问题&#xff0c;是劝退计算机的。 主要观点&#xff1a; 计算机从业人员众多加班&#xff0c;甚至需要99635岁危机秃头 综上所属&#xff0c;计算机不仅卷&#xff0c;而且还是一个高危职业呀&#xff0c;可别来干了。 关于卷 近两年确实能明显感觉…

【论文解读】单目3D目标检测 MonoCon(AAAI2022)

本文分享单目3D目标检测&#xff0c;MonoCon模型的论文解读&#xff0c;了解它的设计思路&#xff0c;论文核心观点&#xff0c;模型结构&#xff0c;以及效果和性能。 目录 一、MonoCon简介 二、论文核心观点 三、模型框架 四、模型预测信息与3D框联系 五、损失函数 六、…

CScrollBar 滚动条

1、水平滚动条、垂直滚动条&#xff1b;滚动条中有一个滚动快&#xff0c;用于表示“当前滚动的位置” 2、 3、处理滚动条消息&#xff1a;水平滚动条响应OnHScroll函数&#xff0c;竖直滚动条响应OnVScroll函数。一般在函数中必须经过一下步骤&#xff1a; 1。得到滚动…

【数据结构】二叉树--OJ练习题

目录 1 单值二叉树 2 相同的树 3 另一颗树的子树 4 二叉树的前序遍历 5 二叉树的最大深度 6 对称二叉树 7 二叉树遍历 1 单值二叉树 965. 单值二叉树 - 力扣&#xff08;LeetCode&#xff09; bool isUnivalTree(struct TreeNode* root) {if (root NULL){return true;}…

屏幕亮度调节保护您的眼睛

官方下载地址&#xff1a; 安果移动 视频演示&#xff1a;屏幕亮度调节-保护您的眼睛_哔哩哔哩_bilibili 嗨&#xff0c;亲爱的用户&#xff0c;你是否有过这样的体验&#xff1a;夜晚安静的时刻&#xff0c;想要在抖音上看看热门的舞蹈、在快手上发现生活的 趣味、或是在哔…

[MoeCTF 2023] web题解

文章目录 webhttpcookie彼岸的flagmoe图床大海捞针夺命十三枪 web http 连接到本地后&#xff0c;题目给了我们任务 第一个是要求我们GET传参UwUu第二个是要求我们POST传参Luvu第三个是要求我们cookie值为admin第四个是要求我们来自127.0.0.1第五个是要求我们用MoeBrowser浏…

Spring framework Day14:配置类的Lite模式和Full模式

前言 Lite模式和Full模式是指在软件或系统中的不同配置选项。一般来说&#xff0c;Lite模式是指较为简洁、轻量级的配置&#xff0c;而Full模式则是指更加完整、功能更丰富的配置。 Lite模式通常会去除一些不常用或占用资源较多的功能&#xff0c;以提高系统的运行效率和响应…

【C++】 对象模型与内存模型的区别

目录 0 引言1 C 内存模型2 C 对象模型3 二者区别 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;C专栏&#x1f4a5; 标题&#xff1a;【C】 对象模型与内存模型的区别❣️ 寄语&#xff1a;最重要的只有一件事&#xff01;&#x1f388; 最后&am…

Spring(17) AopContext.currentProxy() 类内方法调用切入

目录 一、简介二、代码示例2.1 接口类2.2 接口实现类2.3 AOP切面类2.4 启动类&#xff08;测试&#xff09;2.5 执行结果 一、简介 背景&#xff1a; 在之前 Spring 的 AOP 用法中&#xff0c;只有代理的类才会被切入。例如&#xff1a;我们在 Controller 层调用 Service 的方式…

nginx的优化和防盗链(重点)

一、nginx的优化&#xff08;重点&#xff09; &#xff08;一&#xff09;隐藏版本号 由于nginxbug多&#xff0c;更新版本速度比较快&#xff0c;一旦版本号暴露出去&#xff0c;有可能给对方提供攻击的漏洞 1、在http大模块中修改 2、修改nginx.h源码包 &#xff08;二&a…