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

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

intellij plugin


既然没有现成的工具那就自己写一个吧,演示效果如下:



考虑到我们主要是用 PyCharm 开发,正好 jetbrains 也提供了 SDK 用于开发插件,所以 UI 层面可以不用额外考虑了。


使用流程很简单,只需要导入 DDL 语句就可以生成 Python 所需要的 Model 代码。

例如导入以下 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


便会生成对应的 Python 代码:


class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    userName = db.Column(db.String)  # 用户名
    password = db.Column(db.String)  # 密码
    roleId = db.Column(db.Integer)  # 角色ID


词法解析


仔细对比源文件及目标代码会很容易找出规律,无非就是解析出表名、字段、及字段的属性(是否为主键、类型、长度),最后再转换为 Python 所需要的模板即可。


在我动手之前我认为是非常简单的,无非就是解析字符串,但实际上手后发现不是那么回事;主要是有以下几个问题:


  1. 如何识别出表名称?


  1. 同样的如何识别出字段名称,同时还得关联上该字段的类型、长度、注释。


  1. 如何识别出主键?


总结一句话,如何通过一系列规则识别出一段字符串中的关键信息,这同样也是 MySQL Server 所做的事情。


在开始真正解析 DDL 之前,先来看下一段简单的脚本如何解析:


x = 20


按照我们平时开发的经验,这条语句分为以下几部分:


  • x 表示变量


  • = 表示赋值符号


  • 20 表示赋值结果


所以我们对这段脚本的解析结果应当为:


VAR    x
GE      =
VAL    100


这个解析过程在编译原理中称为”词法解析“,可能大家听到编译原理这几个字就头大(我也是);对于刚才那段脚本我们可以编写一个非常简单的词法解析器生成这样的结果。


状态迁移


再开始之前先捋一下思路,可以看到上文的结果中通过 VAR 表示变量、GE 表示赋值符号 ”=“、VAL 表示赋值结果,现在需要重点记住这三个状态。


在依次读取字符解析时,程序就是在这几个状态中来回切换,如下图:



  1. 默认为初始状态。


  1. 当字符为字母时进入 VAR 状态。


  1. 当字符为 ”=“ 符号时进入 GE 状态。



同理,当不满足这几个状态时候又会回到初始从而再次确认新的状态。


光看图有点抽象,直接来看核心代码:


public class Result{
        public TokenType tokenType ;
        public StringBuilder text = new StringBuilder();
    }


首先定义了一个结果类,收集最终的解析结果;其中的 TokenType 就对应了图中的三种状态,简单的用枚举值来表示。


public enum TokenType {
    INIT,
    VAR,
    GE,
    VAL
}


首先对应到第一张图:初始化状态。


需要对当前解析的字符定义一个 TokenType



和图中描述的流程一致,判断当前字符给定一个状态即可。


接着对应到第二张图:状态之间的转换。



会根据不同的状态进入不同的 case,在不同的 case 中判断是否应当跳转到其他状态(进入 INIT 状态后会重新生成状态)。


举个例子: x = 20:


首选会进入 VAR 状态,接着下一个字符为空格,自然在 38 行中重新进入初始状态,导致再次确定下一个字符 = 进入 GE 状态。


当脚本为 ab = 30: 第一个字符为 a 也是进入 VAR 状态,第二个字符为 b,依然为字母,所以进入 36 行,状态不会改变,同时将 b 这个字符追加进来;后续步骤就和上一个例子一致了。


多说无益,建议大家自己跑一下单测就会明白: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