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

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;elsea = -+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.mzph.cn/news/104794.shtml

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

相关文章

Feign 调用为何POST不支持同时传入多个SpringQueryMap对象,但是GET方法就支持?

Feign 调用为何POST不支持同时传入多个SpringQueryMap对象&#xff0c;但是GET方法就支持&#xff1f; 1.1 问题背景1.2 原因分析1.3 修复方案1.3.1 修复方案一 切换使用GET方法&#xff0c;可以试用多个SpringQueryMap注解 &#xff08;测试实际不行&#xff09;1.3.2 修复方案…

C/C++陷阱——变量名和函数名的冲突问题

C语言/C陷阱——变量名和函数名的冲突问题 先来看这两串代码&#xff1a; 代码一&#xff1a; #include <stdio.h> #include <stdlib.h>int rand 1;int main() {printf("%d\n", rand);return 0; }代码二&#xff1a; #include <stdio.h> #inc…

C++ 线程安全注解

实例开篇 线程安全注解是现代C开发的机制&#xff0c;通常在编译期可以帮助发现一些线程安全问题。 下面直接从实例中来理解。 class Account { private:Mutex mu;int money GUARDED_BY(mu);void Sub(int amount) {money - amount; // writing variable money requires h…

基于php+thinkphp+vue的校园二手交易网站

运行环境 开发语言&#xff1a;PHP 数据库:MYSQL数据库 应用服务:apache服务器 使用框架:ThinkPHPvue 开发工具:VScode/Dreamweaver/PhpStorm等均可 项目简介 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发…

43.241.18.123哪些问题会导致服务器里面时间错误

我们在使用服务器的过程中&#xff0c;有时候可能会发现&#xff0c;服务器里面时间跟标准的时间对不上&#xff0c;那服务器里面时间错误可能由哪些问题引起&#xff1a; 硬件问题&#xff1a;服务器硬件中的时钟或电池可能损坏或失效&#xff0c;导致时间不准确或重置为默认…

2024年计算机专业Java选题推荐✅(最新、最全、最容易通过的选择)

文章目录 前言选题和具体实现详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#x…

【LeetCode刷题(数据结构)】:对称二叉树

给你一个二叉树的根节点 root 检查它是否轴对称 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 提示&#xff1a; 树中节点数目在范围 [1, 1000] 内 -100 < Node.val < 100 对称二叉…

Aviator表达式引擎

一、快速介绍 &#xff08;一&#xff09;Aviator Aviator是一个用于动态表达式求值的高性能、轻量级Java引擎。以下是一些关于Aviator引擎的重要特点和设计目标&#xff1a; 轻量级和高性能&#xff1a; Aviator的设计目标之一是轻量级和高性能。它的大小很小&#xff0c;加…

PyQt中线程和线程信号的使用

PyQt中的线程和线程信号是用于在应用程序中实现多线程编程的重要概念。线程允许你在应用程序中同时执行多个任务&#xff0c;而线程信号用于在不同线程之间进行通信和协调。以下是关于PyQt中线程和线程信号的简要解释&#xff1a; 线程&#xff1a; 线程是应用程序中的并发执行…

@Component 和 @Bean的区别

Component 和 Bean 是Spring框架中用于管理和配置依赖注入的关键注解&#xff0c;用于定义和管理Spring应用程序中的组件。 Component: Component 是一种泛用型的Spring注解&#xff0c;用于标识一个类为Spring组件。Spring会自动扫描所有带有Component 注解的类&#xff0c;并…

介绍一下mysql有哪些索引类型

以下是MySQL的8种不同索引类型的比较&#xff0c;以帮助你了解它们的特点和适用场景&#xff1a; 索引类型用途和特点适用场景B-Tree 索引用于范围查询、等值查找和排序操作大多数查询 &#xff0c;不适合全文搜索和空间数据。唯一索引保证索引列的值唯一&#xff0c;不允许重…

PTA程序辅助设计平台—2023年软件设计综合实践_4(数组及字符串)

6-1 找最小元素/选择排序 - C/C 数组及字符串 &#xff08;找最小元素&#xff09;对于给定的包含10个元素的整数数组&#xff0c;设计一个函数&#xff0c;从数组给定的下标范围[i,j]里找到值最小的元素&#xff0c;返回其下标。该函数的原型可以是int findMin(int a[], int …

【Redis】Java客户端使用list命令

lpush/lrange rpush/rpop/lpop blpop/brpop llen

mac使⽤nginx

⽅法1&#xff1a;homebrew 默认本地已经安装homebrew&#xff1b; 安装与启动 brew install nginx 安装nginx&#xff1b; brew services start nginx 启动nginx nginx⽂件⽬录 1. nginx安装⽂件⽬录/usr/local/Cellar/nginx 2. nginx配置⽂件⽬录/usr/local/etc/nginx 3. con…

Windows 多媒体编程库 DirectX 介绍

目录 1、什么是DirectX&#xff1f; 2、使用DirectX的好处 2.1、DirectX为软件开发者提供硬件无关性 2.2、为硬件开发提供策略 3、DirectX的主体构成 3.1、Direct3D 3.2、DirectDraw 3.3、DirectPlay 3.4、DirectSound 3.5、DirectMusic 3.6、DirectInput 4、Dire…

谈谈多线程与多线程同步

一、线程展开说说 二、走近多线程同步

uniapp快速入门系列(4)- 微信小程序开发

第四章 微信小程序开发 4.1 微信小程序开发与uniapp的融合4.2 微信小程序API在uniapp中的使用4.3 微信小程序常见问题的解决方法问题1: 如何获取用户信息&#xff1f;问题2: 如何获取当前位置&#xff1f;问题3: 如何发送网络请求&#xff1f; 在本章中&#xff0c;我们将学习如…

缓存降级代码结构设计

缓存降级设计思想 接前文缺陷点 本地探针应该增加计数器&#xff0c;多次异常再设置&#xff0c;避免网络波动造成误判。耦合度过高&#xff0c;远端缓存和本地缓存应该平行关系被设计为上下游关系了。公用的远端缓存的操作方法应该私有化&#xff0c;避免集成方代码误操作&…

lvgl 界面管理器

lv_scr_mgr lvgl 界面管理器 适配 lvgl 8.3 降低界面之间的耦合使用较小的内存&#xff0c;界面切换后会自动释放内存内存泄漏检测 使用方法 在lv_scr_mgr_port.h 中创建一个枚举&#xff0c;用于界面ID为每个界面创建一个页面管理器句柄将界面句柄添加到 lv_scr_mgr_por…

机器学习基础之《回归与聚类算法(2)—欠拟合与过拟合》

一、背景 1、上一篇说正规方程的时候&#xff0c;实际情况中使用很少&#xff0c;主要原因它不能解决过拟合。 2、训练集上表现的好&#xff0c;测试集上表现不好—过拟合 二、欠拟合和过拟合 1、欠拟合 训练集&#xff1a;有3个训练集&#xff0c;告诉机器都是天鹅 机器学…