首頁 >Java >java教程 >詳細解析java詞法分析器DDL遞歸應用

詳細解析java詞法分析器DDL遞歸應用

WBOY
WBOY轉載
2022-07-25 17:35:472422瀏覽

本篇文章為大家帶來了關於java的相關知識,主要介紹了java詞法分析器DDL遞歸應用詳解,有需要的朋友可以藉鑑參考下,下面一起來看一下,希望對大家有幫助。

詳細解析java詞法分析器DDL遞歸應用

推薦學習:《java影片教學

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所需的範本即可。

在我動手之前我認為是非常簡單的,無非就是解析字串,但實際上手後發現不是那麼回事;主要是有以下幾個問題:

  • #如何識別出表名稱?
  • 同樣的如何辨識出欄位名稱,同時也得關聯上該欄位的類型、長度、註解。
  • 如何辨識出主鍵?

總結一句話,如何透過一系列規則識別出一段字串中的關鍵訊息,這同樣也是 MySQL Server 所做的事情。

在開始真正解析DDL 之前,先來看看下一段簡單的腳本如何解析:

x = 20

##按照我們平常開發的經驗,這條語句分為以下幾部分:

  • x表示變數
  • =表示賦值符號
  • 20表示賦值結果
所以我們對這段腳本的解析結果要當為:

VAR x

GE =

VAL 100

這個解析過程在編譯原理中稱為」詞法解析“,可能大家聽到編譯原理這幾個字就頭大(我也是);對於剛才那段腳本我們可以寫一個非常簡單的詞法解析器來產生這樣的結果。

狀態遷移

再開始之前先捋一下思路,可以看到上文的結果中透過

VAR表示變數、GE表示賦值符號”=“、VAL表示賦值結果,現在需要重點記住這三個狀態。

在依序讀取字元解析時,程式就是在這幾個狀態中來回切換,如下圖:

    預設為初始狀態。
  • 當字元為字母時進入
  • VAR狀態。
  • 當字元為 ”=“ 符號時進入
  • 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 這個字元追加進來;後續步驟就和上一個例子一致了。

多說無益,建議大家自己跑單測就會明白:

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来获取表中的数据了。

推荐学习:《java视频教程

以上是詳細解析java詞法分析器DDL遞歸應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:jb51.net。如有侵權,請聯絡admin@php.cn刪除