1 引言
因為工作關係,需要開發支援眾多方言的 SQL 編輯器,所以複習了一下編譯原理相關知識。
相比編譯原理專家,我們只需要瞭解部分編譯原理即可實現 SQL 編輯器,所以這是一篇寫給前端的編譯原理文章。
解析 SQL 可以分為如下四步:
- 詞法分析,將 SQL 字串拆分成包含關鍵詞識別的字元段(Tokens)。
- 語法分析,利用自頂向下或自底向上的演算法,將 Tokens 解析為 AST,可以手動,也可以自動。
- 錯誤檢測、恢復、提示推斷,都需要利用語法分析產生的 AST。
- 語義分析,做完這一步就可以執行 SQL 語句了,不過對前端而言,不需要深入到這一步,可以跳過。
2 精讀
詞法分析就像刀削麵的過程,拿著一段字串(麵條)一端不斷下刀,當面條被切完也就完成了詞法分析,所以詞法分析是 字串 -> 一堆字元段 的過程。
流程很簡單,難點就在下刀的分寸了,每次砍幾釐米呢?
回到詞法分析,為了準備切分,我們需要定義 SQL 的 Token 有哪些型別,即 Token 分類。
Token 分類
SQL 的 Token 可以分為如下幾類:
- 註釋。
- 關鍵字(
SELECT
、CREATE
)。 - 操作符(
+
、-
、>=
)。 - 開閉合標誌(
(
、CASE
)。 - 佔位符(
?
)。 - 空格。
- 引號包裹的文字、數字、欄位。
- 方言語法(
${variable}
)。
可以看到,在詞法分析階段,我們的 Tokens 不需要關心關鍵詞是什麼,只要識別是不是關鍵詞即可,因為關鍵詞的辨認會留到語法分析時處理。涉及到語意處理就要考慮上下文,而這都不是詞法分析階段要考慮的。
同樣,操作符、空格、文字、佔位符等構成了 SQL 語句的其他部分,最後通過開閉合標誌比如左括號和右括號,讓 SQL 支援子語句。
再強調一次,雖然 SQL 支援子語句,但並不是放在任何位置都是合理的,其他型別 Token 同理,但是詞法分析不需要考慮 Token 是否合理,只要切分即可。
用正則逐段分詞
像大多數語言一樣,SQL 為了方便人類閱讀,採用從左到右的書寫方式,因此分詞方向也從左到右。
我們為每個 Token 型別寫一個函式,比如匹配空格的匹配函式:
function getTokenWhitespace(restStr: string) {
const matches = restStr.match(/^(s+)/);
if (matches) {
return { type, value: matches[1] };
}
}
複製程式碼
restStr
表示掐去頭部剩下的 SQL 字串,所有匹配函式都拿 restStr
進行匹配,已經匹配的不需要再處理。
通過正則 /^(s+)/
匹配到第一個以空格開頭的空格(讀起來有點彆扭),匹配時必須保證以你要匹配的內容開頭,而且只匹配一次,這樣才不會在切詞時發生遺漏。
同理匹配 /**/
型別註釋時,也能通過正則輕而易舉的實現:
function getTokenBlockComment(restStr: string) {
const matches = restStr.match(/^(/*[^]*?(?:*/|$))/);
if (matches) {
return { type, value: matches[1] };
}
}
複製程式碼
其中 (?:*/)
表示匹配到以 */
結尾處,而 (?:*/|$)
後面的 |$
表示或者直接匹配到結尾(如果一直沒有遇到 */
那後面全部當作註釋)。
所以只要 Token 分類得當,並且能為每一個分類寫一個頭匹配正則,分詞功能就實現了 90%。
方言擴充
為了支援某些方言,需要從分詞時就開始做考慮。比如 ${variable}
作為一種變數用法時,我們需要在普通欄位的正則匹配中,加入一項 ${[a-zA-Z0-9]+}
匹配。
如果要支援純中文作為欄位,可以再補充 |u4e00-u9fa5
。
分詞主流程
有了一個個分詞函式,再補充一個不斷匹配、切割字串、再匹配的主函式即可,這一步更簡單:
while (sqlStr) {
token =
getTokenWhitespace(sqlStr, token) | getTokenBlockComment(sqlStr, token);
sqlStr = sqlStr.substring(token.value.length);
tokens.push(token);
}
複製程式碼
上面的函式每取一次 Token,都將取到的 Token 長度丟掉,繼續匹配剩下的字串,直到字串被切分完為止。
有些特殊情況需要拿到上次的 Token 才能判斷下一個 Token 該如何切割,所以將 Token 傳給每一個下一步 Match 函式。
最後,執行這個主函式,分詞就完成了!
3 總結
分詞比較簡單,到這裡就全部結束了。後面即將進入深水區語法分析,敬請期待。
4 更多討論
如果你想參與討論,請點選這裡,每週都有新的主題,週末或週一釋出。