重回 「手寫 SQL 編輯器」 系列。以前幾期介紹了 詞法、文法、語法的解析,以及回溯功能的實現,此次介紹如何生成語法樹。git
基於 《回溯》 一文介紹的思路,咱們利用 JS 實現一個微型 SQL 解析器,並介紹如何生成語法樹,如何在 JS SQL 引擎實現語法樹生成功能!github
解析目標是:sql
select name, version from my_table;
文法:typescript
const root = () => chain(selectStatement, many(";", selectStatement)); const selectStatement = () => chain("select", selectList, fromClause); const selectList = () => chain(matchWord, many(",", matchWord)); const fromClause = () => chain("from", matchWord); const statement = () => chain( "select", selectList, "from", chain(tableName, [whereStatement, limitStatement]) );
這是本文爲了方便說明,實現的一個精簡版本。完整版見咱們的開源倉庫 cparser。
root
是入口函數,many()
包裹的文法能夠執行任意次,因此數組
chain(selectStatement, many(";", selectStatement));
表示容許任意長度的 selectStatement
由 ;
號鏈接,selectList
的寫法也同理。性能優化
matchWord
表示匹配任意單詞。編輯器
語法樹是人爲對語法結構的抽象,本質上,若是咱們到此爲止,是能夠生成一個 基本語法樹 的,這個語法樹是多維數組,好比:函數
const fromClause = () => chain("from", matchWord);
這個文法生成的默認語法樹是:['from', 'my_table']
,只不過 from
my_table
具體是何含義,只有當前文法知道(第一個標誌無含義,第二個標誌表示表名)。工具
fromClause
返回的語法樹做爲結果被傳遞到文法 selectStatement
中,其結果多是:['select', [['name', 'version']], ['from', 'my_table']]
。性能
你們不難看出問題:當默認語法樹彙集在一塊兒,就沒法脫離文法結構單獨理解語法含義了,爲了脫離文法結構理解語法樹,咱們須要將其抽象爲一個有規可循的結構。
經過上面的分析,咱們須要對 chain
函數提供修改局部 AST 結構的能力:
const selectStatement = () => chain("select", selectList, fromClause)(ast => ({ type: "statement", variant: "select", result: ast[1], from: ast[2] }));
咱們能夠經過額外參數對默認語法樹進行改造,將多維數組結構改變爲對象結構,並增長 type
variant
屬性標示當前對象的類型、子類型。好比上面的例子,返回的對象告訴使用者:「我是一個表達式,一個 select 表達式,個人結果是 result,個人來源表是 from」。
那麼,chain
函數如何實現語法樹功能呢?
對於每一個文法(每一個 chain
函數),其語法樹必須等待全部子元素執行完,才能生成。因此這是個深度優先的運行過程。
下圖描述了 chain
函數執行機制:
生成結構中有四個基本結構,分別是 Chain、Tree、Function、Match,足以表達語法解析須要的全部邏輯。(不包含 可選、多選 邏輯)。
每一個元素的子節點所有執行完畢,纔會生成當前節點的語法樹。實際上,每一個節點執行完,都會調用 callParentNode
訪問父節點,執行到了這個函數,說明子元素已成功執行完畢,補全對應節點的 AST 信息便可。
對於修改局部 AST 結構函數,需等待整個 ChainNode
執行完畢才調用,並將返回的新 AST 信息存儲下來,做爲這個節點的最終 AST 信息並傳遞給父級(或者沒有父級,這就是根結點的 AST 結果)。
本文介紹瞭如何生成語法樹,並說明了 默認語法樹 的存在,以及咱們之因此要一個定製的語法樹,是爲了更方便的理解含義。
同時介紹瞭如何經過 JS 運行一套完整的語法解析器,以及如何提供自定義 AST 結構的能力。
本文介紹的模型,只是爲了便於理解而定製的簡化版,瞭解所有細節,請訪問 cparser。
最後說一下爲什麼要作這個語法解析器。現在有許多開源的 AST 解析工具,但筆者要解決的場景是語法自動提示,須要在語句不完整,甚至錯誤的狀況,給出當前光標位置的全部可能輸入。因此經過完整重寫語法解析器內核,在解析的同時,生成語法樹的同時,也給出光標位置下一個可能輸入提示,在通用錯誤場景自動從錯誤中恢復。
目前在作性能優化,通用 SQL 文法還在陸續完善中,目前僅可當學習參考,不要用於生產環境。
討論地址是: 精讀《手寫 SQL 編譯器 - 語法樹》 · Issue #99 · dt-fe/weekly
若是你想參與討論,請點擊這裏,每週都有新的主題,週末或週一發佈。