当前位置: 首页 > news >正文

自己动手写编译器:创建由 C 语言编译而成的语法解析器

在上一章节,我们完成了由 c 语言设计的输入系统,本节我们看看如何在前一节的基础上完成一个由 c 语言设计并编译出来的词法解析器。整个解析器的基本设计思路是:
1,由我们上一节设计的输入系统将字符串从文件中读入。
2,由我们前面 GoLex 程序设计生成的状态机代码负责读入步骤 1 读入的字符串进行识别。
3,由 c 语言设计的模板代码驱动步骤1 和 2 的执行
我们看看具体的操作情况。首先我们需要将上一节设计的输入系统对应的函数放入头文件,在 CLex 项目中增加一个头文件l.h,其代码内容如下:

#ifndef __L_H
#define __L_H
extern  int ii_newfile(char* name);
extern  unsigned char* ii_text();
extern  int ii_flush(int force);
extern  int ii_length();
extern  int ii_lineno();
extern  unsigned char* ii_ptext();
extern  int ii_plength();
extern  int ii_plineno();
extern  unsigned  char* ii_mark_start();
extern  unsigned  char* ii_mark_end();
extern  unsigned  char* ii_move_start();
extern  unsigned  char* ii_to_mark();
extern  unsigned  char* ii_mark_prev();
extern  int ii_advance();
extern  int ii_flush(int force);
extern  int ii_fillbuf(unsigned  char* starting_at);
extern  int ii_look(int n);
extern  int ii_pushback(int n);
extern  void ii_term();
extern  void ii_unterm();
extern  int ii_input();
extern  void ii_unput(int c);
extern  int ii_lookahead(int n);
extern  int ii_flushbuf();
#endif

接着在 GoLex 中生成状态机的 c 语言代码,在 main.go 中代码如下(这些代码我们在前面章节讲解和调试过):

func main() {lexReader, _ := nfa.NewLexReader("input.lex", "output.py")lexReader.Head()parser, _ := nfa.NewRegParser(lexReader)start := parser.Parse()parser.PrintNFA(start)nfaConverter := nfa.NewNfaDfaConverter()nfaConverter.MakeDTran(start)nfaConverter.PrintDfaTransition()nfaConverter.MinimizeDFA()fmt.Println("---------new DFA transition table ----")nfaConverter.PrintMinimizeDFATran()//nfaConverter.DoPairCompression()nfaConverter.DoSquash()nfaConverter.PrintDriver()
}

上面代码运行后,我们会在本地生成 lex.yy.c 的文件,我们将文件中的所有代码拷贝到 CLex 的 main.c 文件中。接下来我们需要一段驱动输入系统读入数据,然后调用生成的状态机代码进行字符串识别的”胶水“代码,其名为 yylex,依然在 main.c 中,输入 yylex 函数对应的代码如下:

int yylex() {static int yystate = -1;int yylastaccept;int yyprev;int yynstate;int yylook;  //预读取字符int yyanchor;if(yystate == -1) {//将数据读入缓冲区ii_advance();//ii_advance 使得 Next 指针移动了一位,因此在我们还没有读入任何字符时,需要将其后退回去ii_pushback(1);}yystate = 0;yyprev = 0;yylastaccept = 0;ii_unterm();ii_mark_start();while(1) {/** 这里我们采取贪婪算法,如果当前识别的字符串已经进入识别状态,* 但是还有字符可以读取,那么我们先缓存当前识别状态,然后继续识别后续字符,* 直到文件抵达末尾或者输入的字符导致识别失败,此时我们再返回到上次识别状态* 进行处理,这种方法让我们尽可能获得能进入完成状态的最长字符串*/while(1) {yylook = ii_look(1);if (yylook != EOF) {yynstate = yy_next(yystate, yylook);break;} else {if (yylastaccept) {/** 如果文件数据读取完毕,并且我们抵达过完成状态,那么设置下一个状态为* 非法状态*/yynstate = YYF;break;}else if(yywrap()) {yytext = "";yyleng = 0;return 0;}else {ii_advance();ii_pushback(1);}}}// inner whileif (yynstate != YYF) {//跳转到下一个有效状态printf("Transation from state %d ", yystate);printf(" to state %d on <%c>\n", yynstate, yylook);if (ii_advance() < 0) {//缓冲区已满printf("Line %d, lexeme too long. Discarding extra characters.\n", ii_lineno());ii_flush(1);}yyanchor = Yyaccept[yynstate];if (yyanchor) {yyprev = yystate;yylastaccept = yynstate;ii_mark_end(); //完成一个字符串的识别}yystate = yynstate;} else {//跳转到无效状态,说明输入字符串不合法if (!yylastaccept) {//忽略掉非法字符printf("Ignoring bad input\n");ii_advance();} else {//回退到上一次接受状态ii_to_mark();if (yyanchor & 2) {//末尾匹配,将末尾回车符号放回缓冲区ii_pushback(1);}if (yyanchor & 1) {//开头匹配,忽略掉字符串开头的回车符号ii_move_start();}ii_term();//获取当前识别的字符串,极其长度和所在行号yytext = (char*) ii_text();yyleng = ii_length();yylineno = ii_lineno();printf("Accepting state%d, ", yylastaccept);printf("line %d: <%s>\n", yylineno, yytext);switch (yylastaccept) {/** 这里根据接受状态执行其对应的代码,实际上这里的代码* 后面会由 GoLex 生成*/case 3:case 4:printf("%s is a float number", yytext);return FCON;default:printf("internal error, yylex: unkonw accept state %d.\n", yylastaccept);break;}}ii_unterm();yylastaccept = 0;yystate = yyprev;}}// outer while
}

最后我们在 main 函数中调用 yylex 函数,驱动识别进程,main 函数内容如下:

int main() {int fd = ii_newfile("/Users/my/Documents/CLex/num.txt");if (fd == -1) {printf("value of errno: %d\n", errno);}yylex();return 0;
}

完成上面代码后,我们就对 c 语言代码进行编译,生成可执行文件,注意在上面代码中,我们使用输入系统的 ii_newfile 函数读入了一个名为 num.txt 的文件,这个文件的内容包含要识别的字符串,实际上这个文件地址可以作为程序参数输入,这里为了简单,我们直接写入代码中,在本地创建文件 num.txt,在里面输入一个数字字符串 3.14 然后保存,最后我们执行 c 语言代码编译的程序,输出结果如下:

Transation from state 0  to state 4 on <3>
Transation from state 4  to state 3 on <.>
Transation from state 3  to state 3 on <1>
Transation from state 3  to state 3 on <4>
Accepting state3, line 1: <
3.14>

这里我们可以看到,创建的 c 语言代码能正确的识别给定文件里的字符串为浮点数,同时他打印出了状态机在识别每个字符时的状态跳转,由此基本断定,我们 c 语言代码的设计基本正确,下一节我们的目的是将当前”手动“的阶段全部用程序来替代,例如将 GoLex 生成的代码进行粘贴等操作我们都用代码来完成,当这些代码生成和代码粘贴的动作都由 GoLex 完成后,那么它就变成了在编译原理工具链里有名的 Flex 应用,更多详细内容,请大家在 b 站搜索 Coding 迪斯尼,代码下载:
链接: https://pan.baidu.com/s/1MHkg0qNV8QIEqtC_y0XjnA 提取码: 1r4x

相关文章:

自己动手写编译器:创建由 C 语言编译而成的语法解析器

在上一章节&#xff0c;我们完成了由 c 语言设计的输入系统&#xff0c;本节我们看看如何在前一节的基础上完成一个由 c 语言设计并编译出来的词法解析器。整个解析器的基本设计思路是&#xff1a; 1&#xff0c;由我们上一节设计的输入系统将字符串从文件中读入。 2&#xff0…...

接口设计-增删改查

关于增删改查的 接口设计&#xff0c;比较简单&#xff0c;有一些固定的做法可以使用。 查询列表 查询列表的接口&#xff0c;带上分页的入参&#xff1a; pageNo&#xff0c;pageSize&#xff0c;非必选&#xff0c;并设置默认值。 入参为 dto&#xff0c;根据 dto 从数据库…...

持续持续集成部署-k8s-配置与存储-配置管理:Secret 的应用

持续持续集成部署-k8s-配置与存储-配置管理:Secret 的应用 1. 简介2. 创建 Secret3. docker-registry 的使用1. 简介 与 ConfigMap 类似,用于存储配置信息,但是主要用于存储敏感信息、需要加密的信息,Secret 可以提供数据加密、解密功能。 在创建 Secret 时,要注意如果要…...

ZYNQ7020开发(一):开发环境搭建

文章目录 一、配置Ubuntu 编译环境二、安装Petalinux三、安装JTAG驱动四、安装Vitis一、配置Ubuntu 编译环境 虚拟机环境:VMware Workstation 16 Pro 16.1.0 build-17198959Ubuntu 版本:No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 L…...

Spring Boot插件化开发概念原理及实现

Spring Boot 是一个开源的Java框架&#xff0c;它简化了基于Spring框架的应用程序的开发和部署过程。它提供了一种快速、简单的方式来构建独立的、可执行的Spring应用程序。在Spring Boot中&#xff0c;插件化开发是一种强大的开发模式&#xff0c;它允许开发人员将应用程序的不…...

Ps:PSDT 模板文件

自 Photoshop CC 2015.5 版以后&#xff0c;Ps 中新增了一种文件格式&#xff1a;.PSDT。 说明&#xff1a; PSD、PDD、PSDT 都是 Ps 的专用文件格式&#xff0c;需要继续在 Ps 中进行编辑的文件可存为此类格式。 PSD Photoshop document Photoshop 默认文档格式&#xff0c;支…...

Linux-----nginx的简介,nginx搭载负载均衡以及nginx部署前后端分离项目

目录 nginx的简介 是什么 nginx的特点以及功能 Nginx负载均衡 下载 安装 负载均衡 nginx的简介 是什么 Nginx是一个高性能的开源Web服务器和反向代理服务器。它的设计目标是为了解决C10k问题&#xff0c;即在同一时间内支持上万个并发连接。 Nginx采用事件驱动的异…...

presto插件机制揭秘:探索无限可能的数据处理舞台

文章目录 1. 前言2. Presto插件架构3. Plugin接口3.1 插件协议3.2 插件实现类 4. 插件加载过程4.1 PluginManager 5. 插件应用6. 总结 关键词&#xff1a;Presto Plugin 1. 前言 本文源码环境&#xff1a; presto: prestoDb 0.275版本 在Presto框架中插件机制设计是一种非常常见…...

acwing算法基础之数据结构--并查集算法

目录 1 基础知识2 模板3 工程化 1 基础知识 并查集支持O(1)时间复杂度实现&#xff1a; 将两个集合合并。询问两个元素是否在一个集合中。 基本原理&#xff1a;每个集合用一颗树来表示。树根的编号就是整个集合的编号。每个结点存储它的父结点&#xff0c;p[x]表示x的父结点…...

k8s:二进制搭建 Kubernetes v1.20

目录 1 操作系统初始化配置 2 部署 etcd 集群 2.1 准备签发证书环境 2.2 生成Etcd证书 3 部署 docker引擎 4 部署 Master 组件 5 部署 Worker Node 组件 k8s集群master01&#xff1a;192.168.30.105 kube-apiserver kube-controller-manager kube-scheduler etcd k8s集…...

SpringBoot系列-1启动流程

背景 本文作为SpringBoot系列的开篇&#xff0c;介绍SpringBoot的启动流程&#xff0c;包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架&#xff0c;其是基于约定和自动装配机制对Spring的封装和增强。 由于前面的Spring系列对Spring容器已经进行了较为细致的…...

【记】一次common模块导入无效的bug

首先Maven clean install无用 然后idea清除缓存重启无用 pom.xml文件重载无效 正确解决路径&#xff1a; 1.检查common模块的父工程导入和自身模块的声明是否正确 默认是继承父工程的groupid&#xff0c;可以不用再声明 2.检查子工程是否引入正确的common&#xff0c;org不要…...

1.Netty概述

原生NIO存在的问题(Netty要解决的问题) 虽然JAVA NIO 和 JAVA AIO框架提供了多路复用IO/异步IO的支持&#xff0c;但是并没有提供给上层“信息格式”的良好封装。JAVA NIO 的 API 使用麻烦,需要熟练掌握 ByteBuffer、Channel、Selector等 , 所以用这些API实现一款真正的网络应…...

YOLO目标检测——真实道路车辆检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;自动驾驶技术研发、交通安全监控数据集说明&#xff1a;真实道路车辆检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0c;含voc(xml)、coco(j…...

【Solidity】Solidity中的基本数据类型和复合数据类型

1. 基本数据类型 1.1 整数类型 Solidity支持有符号整数和无符号整数&#xff0c;可以指定位数和范围。以下是一些整数类型的示例&#xff1a; int&#xff1a;有符号整数&#xff0c;可以是正数或负数。2&#xff0c;-45&#xff0c;2023 uint&#xff1a;无符号整数&#x…...

Flutter Set存储自定义对象时 如何保证唯一

在Flutter中&#xff0c;Set和List是两种不同的集合类型&#xff0c;List中存储的元素可以重复&#xff0c;Set中存储的元素不可重复。 如果你想在Set中存储自定义对象&#xff0c;你需要确保对象的唯一性。 这可以通过在自定义类中实现hashCode方法和equals方法来实现。 has…...

Docker容器中执行throttle.sh显示权限报错:RTNETLINK answers: Operation not permitted

在模拟通信环境时&#xff0c;我执行了一下命令&#xff1a; bash ./throttle.sh wan但是&#xff0c;出现了权限的报错&#xff1a;RTNETLINK answers: Operation not permitted 解决方案说简单也挺简单&#xff0c;只需要两步完成。但是其实又蛮繁琐&#xff0c;因为需要将…...

【Linux】jdk、tomcat、MySQL环境搭建的配置安装,Linux更改后端端口

一、作用 工具的组合为开发者和系统管理员提供了构建和运行Java应用程序以及存储和管理数据的完整环境。 JDK&#xff08;Java Development Kit&#xff09;&#xff1a;JDK是Java开发工具包&#xff0c;它提供了开发和运行Java应用程序所需的工具和库。通过安装JDK&#xff0c…...

【WinForm详细教程七】WinForm中的DataGridView控件

文章目录 1.主要属性DataSource行&#xff08;Row 相关属性&#xff09;列&#xff08;Column 相关属性&#xff09;单元格&#xff08;Cell 相关属性&#xff09;逻辑删除AllowUserToAddRowsAllowUserToDeleteRowsAllowUserToOrderColumns其他布局和行为属性 2.控件中的行、列…...

SpringCloudTencent(上)

SpringCloudTencent 1.PolarisMesh介绍2.北极星具备的功能3.北极星包含的组件4.功能特性1.服务管理1.服务注册2.服务发现3.健康检查 2.配置管理 5.代码实战1.环境准备2.服务注册与发现3.远程调用 1.PolarisMesh介绍 1.北极星是腾讯开源的服务治理平台&#xff0c;致力于解决分…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...