手写一个词法分析器(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 最近大部分时间都在撸 Python,其中也会涉及到将数据库表转换为 Python 中 ORM 框架的 Model,但我们并没有找到一个合适的工具来做这个意义不大的”体力活“,所以每次新建表后大家都是根据自己的表结构手写一遍 Model。 一两张表还好,一旦 10 几张表都要写一遍时那痛苦只有自己知道;这时程序员的 slogan 再次印证:一切毫无意义的体力劳动终将被计算机取代。

DDL 解析


简单的解析完成后来看看 DDL 这样的脚本应当如何解析:


CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userName` varchar(20) DEFAULT NULL COMMENT '用户名',
  `password` varchar(100) DEFAULT NULL COMMENT '密码',
  `roleId` int(11) DEFAULT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`),  
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8


原理类似,首先还是要看出规律(也就是语法):


  • 表名是第一行语句,同时以 CREATE TABLE 开头。


  • 每一个字段的信息(名称、类型、长度、备注)都是以 "`" 符号开头 "," 结尾。


  • 主键是以 PRIMART 字符串开头的字段,以 ) 结尾。


根据我们需要解析的数据种类,我这里定义了这个枚举:



然后在初始化类型时进行判断赋值:



由于需要解析的数据不少,所以这里的判断条件自然也就多了。


递归解析


针对于 DDL 的语法规则,我们这里还有需要有特殊处理的地方;比如解析具体字段信息时如何关联起来?


举个例子:


`userName` varchar(20) DEFAULT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',


这里我们解析出来的数据得有一个映射关系:



所以我们只能一个字段的全部信息解析完成并且关联好之后才能解析下一个字段。


于是这里我采用了递归的方式进行解析(不一定是最好的,欢迎大家提出更优的方案)。


} else if (value == '`' && pStatus == Status.BASE_INIT) {
    result.tokenType = DDLTokenType.FI;
    result.text.append(value);
} 


当当前字符为 ”`“ 符号时,将状态置为 "FI"(FieldInfo),同时当解析到为 "," 符号时便进入递归处理。



可以理解为将这一段字符串单独提取出来处理:


`userName` varchar(20) DEFAULT NULL COMMENT '用户名',


接着再将这段字符递归调用当前方法再次进行解析,这时便按照字段名称、类型、长度、注释的规则解析即可。


同时既然存在递归,还需要将子递归的数据关联起来,所以我在返回结果中新增了一个 pid 的字段,这个也容易理解。


默认值为 0,一旦递归后便自增 +1,保证每次递归的数据都是唯一的。


用同样的方法在解析主键时也是先将整个字符串提取出来:


PRIMARY KEY (`id`)


只不过是 "P" 打头 ")" 结尾。


} else if (value == 'P' && pStatus == Status.BASE_INIT) {
    result.tokenType = DDLTokenType.P_K;
    result.text.append(value);
} 



也是将整段字符串递归解析,再递归的过程中进行状态切换 P_K ---> P_K_V 最终获取到主键。



所以通过对刚才那段 DDL 解析得到的结果如下:



这样每个字段也通过了 pid 进行了区分关联。


所以现在只需要对这个词法解析器进行封装,便可以提供一个简单的 API 来获取表中的数据了。



总结


到此整个词法解析器的全部内容都已经完成了。


但这还只是整个编译语言知识点的冰山一角,后续还有语法、语义、中间、目标代码等一系列内容。


本文所有源码及插件地址:


github.com/crossoverJi…


相关文章
|
自然语言处理 前端开发 算法
编译原理 (二)词法分析、语法分析、语义分析以及中间代码生成器的基本概念
编译原理 (二)词法分析、语法分析、语义分析以及中间代码生成器的基本概念
817 0
|
7月前
|
JavaScript 前端开发
不会还有人的批改网还是手写的把
不会还有人的批改网还是手写的把
|
6月前
|
自然语言处理 容器
S语言词法分析器设计
还有很多需要优化的地方,作为小白发出了也和大家一起交流下,这次我是分文件写的,因为考虑到以后的实验都用这一套代码,分文件写方便一点,用的是C++14标准
37 0
|
7月前
|
索引
09 # 手写 some 方法
09 # 手写 some 方法
53 0
|
7月前
|
索引
10 # 手写 every 方法
10 # 手写 every 方法
54 0
|
缓存 前端开发 API
手写中实现并学习ahooks——useRequest
最近业务没有之前紧张了,也是消失了一段时间,也总结了一些之前业务上的问题。 和同事沟通也是发现普通的async + await + 封装api在复杂业务场景下针对于请求的业务逻辑比较多,也是推荐我去学习一波ahooks,由于问题起源于请求,因此作者也是直接从 useRequest 开始看起。
202 1
|
自然语言处理 C语言 C++
编译原理 实验二:词法分析器的手动实现(基于状态机的词法分析器)
编译原理 实验二:词法分析器的手动实现(基于状态机的词法分析器)
1099 0
编译原理 实验二:词法分析器的手动实现(基于状态机的词法分析器)
|
数据采集 自然语言处理 前端开发
再见正则表达式!这次彻底告别手写!
这篇文章的目的是让你能得到完美的正则表达式,而且还不用自己拼。 说到正则表达式,一直是令我头疼的问题,这家伙一般时候用不到,等用到的时候发现它的规则是一点儿也记不住,\d表示一个数字,\s表示包括下划线在内的任意单词字符,也就是 [A-Za-z0-9_],还有[\s\S]*可以匹配包括换行在内的任意字符串。
|
存储 自然语言处理 Unix
编译原理 实验一:词法分析器的自动实现(Lex词法分析)
编译原理 实验一:词法分析器的自动实现(Lex词法分析)
1084 0
编译原理 实验一:词法分析器的自动实现(Lex词法分析)
|
存储 自然语言处理
词法分析器的设计与实现
加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。
208 0