精讀《手寫 SQL 編譯器 - 語法樹》

1 引言

重回 「手寫 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']]性能

你們不難看出問題:當默認語法樹彙集在一塊兒,就沒法脫離文法結構單獨理解語法含義了,爲了脫離文法結構理解語法樹,咱們須要將其抽象爲一個有規可循的結構。

2 精讀

經過上面的分析,咱們須要對 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 結果)。

3 總結

本文介紹瞭如何生成語法樹,並說明了 默認語法樹 的存在,以及咱們之因此要一個定製的語法樹,是爲了更方便的理解含義。

同時介紹瞭如何經過 JS 運行一套完整的語法解析器,以及如何提供自定義 AST 結構的能力。

本文介紹的模型,只是爲了便於理解而定製的簡化版,瞭解所有細節,請訪問 cparser

最後說一下爲什麼要作這個語法解析器。現在有許多開源的 AST 解析工具,但筆者要解決的場景是語法自動提示,須要在語句不完整,甚至錯誤的狀況,給出當前光標位置的全部可能輸入。因此經過完整重寫語法解析器內核,在解析的同時,生成語法樹的同時,也給出光標位置下一個可能輸入提示,在通用錯誤場景自動從錯誤中恢復。

目前在作性能優化,通用 SQL 文法還在陸續完善中,目前僅可當學習參考,不要用於生產環境。

4 更多討論

討論地址是: 精讀《手寫 SQL 編譯器 - 語法樹》 · Issue #99 · dt-fe/weekly

若是你想參與討論,請點擊這裏,每週都有新的主題,週末或週一發佈。

相關文章
相關標籤/搜索