摘要:返回的語(yǔ)法樹(shù)作為結(jié)果被傳遞到文法中,其結(jié)果可能是。每個(gè)元素的子節(jié)點(diǎn)全部執(zhí)行完畢,才會(huì)生成當(dāng)前節(jié)點(diǎn)的語(yǔ)法樹(shù)。更多討論討論地址是精讀手寫(xiě)編譯器語(yǔ)法樹(shù)如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。
1 引言
重回 “手寫(xiě) SQL 編輯器” 系列。之前幾期介紹了 詞法、文法、語(yǔ)法的解析,以及回溯功能的實(shí)現(xiàn),這次介紹如何生成語(yǔ)法樹(shù)。
基于 《回溯》 一文介紹的思路,我們利用 JS 實(shí)現(xiàn)一個(gè)微型 SQL 解析器,并介紹如何生成語(yǔ)法樹(shù),如何在 JS SQL 引擎實(shí)現(xiàn)語(yǔ)法樹(shù)生成功能!
解析目標(biāo)是:
select name, version from my_table;
文法:
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]) );
這是本文為了方便說(shuō)明,實(shí)現(xiàn)的一個(gè)精簡(jiǎn)版本。完整版見(jiàn)我們的開(kāi)源倉(cāng)庫(kù) cparser。
root 是入口函數(shù),many() 包裹的文法可以執(zhí)行任意次,所以
chain(selectStatement, many(";", selectStatement));
表示允許任意長(zhǎng)度的 selectStatement 由 ; 號(hào)連接,selectList 的寫(xiě)法也同理。
matchWord 表示匹配任意單詞。
語(yǔ)法樹(shù)是人為對(duì)語(yǔ)法結(jié)構(gòu)的抽象,本質(zhì)上,如果我們到此為止,是可以生成一個(gè) 基本語(yǔ)法樹(shù) 的,這個(gè)語(yǔ)法樹(shù)是多維數(shù)組,比如:
const fromClause = () => chain("from", matchWord);
這個(gè)文法生成的默認(rèn)語(yǔ)法樹(shù)是:["from", "my_table"],只不過(guò) from my_table 具體是何含義,只有當(dāng)前文法知道(第一個(gè)標(biāo)志無(wú)含義,第二個(gè)標(biāo)志表示表名)。
fromClause 返回的語(yǔ)法樹(shù)作為結(jié)果被傳遞到文法 selectStatement 中,其結(jié)果可能是:["select", [["name", "version"]], ["from", "my_table"]]。
大家不難看出問(wèn)題:當(dāng)默認(rèn)語(yǔ)法樹(shù)聚集在一起,就無(wú)法脫離文法結(jié)構(gòu)多帶帶理解語(yǔ)法含義了,為了脫離文法結(jié)構(gòu)理解語(yǔ)法樹(shù),我們需要將其抽象為一個(gè)有規(guī)可循的結(jié)構(gòu)。
2 精讀通過(guò)上面的分析,我們需要對(duì) chain 函數(shù)提供修改局部 AST 結(jié)構(gòu)的能力:
const selectStatement = () => chain("select", selectList, fromClause)(ast => ({ type: "statement", variant: "select", result: ast[1], from: ast[2] }));
我們可以通過(guò)額外參數(shù)對(duì)默認(rèn)語(yǔ)法樹(shù)進(jìn)行改造,將多維數(shù)組結(jié)構(gòu)改變?yōu)閷?duì)象結(jié)構(gòu),并增加 type variant 屬性標(biāo)示當(dāng)前對(duì)象的類(lèi)型、子類(lèi)型。比如上面的例子,返回的對(duì)象告訴使用者:“我是一個(gè)表達(dá)式,一個(gè) select 表達(dá)式,我的結(jié)果是 result,我的來(lái)源表是 from”。
那么,chain 函數(shù)如何實(shí)現(xiàn)語(yǔ)法樹(shù)功能呢?
對(duì)于每個(gè)文法(每個(gè) chain 函數(shù)),其語(yǔ)法樹(shù)必須等待所有子元素執(zhí)行完,才能生成。所以這是個(gè)深度優(yōu)先的運(yùn)行過(guò)程。
下圖描述了 chain 函數(shù)執(zhí)行機(jī)制:
生成結(jié)構(gòu)中有四個(gè)基本結(jié)構(gòu),分別是 Chain、Tree、Function、Match,足以表達(dá)語(yǔ)法解析需要的所有邏輯。(不包含 可選、多選 邏輯)。
每個(gè)元素的子節(jié)點(diǎn)全部執(zhí)行完畢,才會(huì)生成當(dāng)前節(jié)點(diǎn)的語(yǔ)法樹(shù)。實(shí)際上,每個(gè)節(jié)點(diǎn)執(zhí)行完,都會(huì)調(diào)用 callParentNode 訪問(wèn)父節(jié)點(diǎn),執(zhí)行到了這個(gè)函數(shù),說(shuō)明子元素已成功執(zhí)行完畢,補(bǔ)全對(duì)應(yīng)節(jié)點(diǎn)的 AST 信息即可。
對(duì)于修改局部 AST 結(jié)構(gòu)函數(shù),需等待整個(gè) ChainNode 執(zhí)行完畢才調(diào)用,并將返回的新 AST 信息存儲(chǔ)下來(lái),作為這個(gè)節(jié)點(diǎn)的最終 AST 信息并傳遞給父級(jí)(或者沒(méi)有父級(jí),這就是根結(jié)點(diǎn)的 AST 結(jié)果)。
3 總結(jié)本文介紹了如何生成語(yǔ)法樹(shù),并說(shuō)明了 默認(rèn)語(yǔ)法樹(shù) 的存在,以及我們之所以要一個(gè)定制的語(yǔ)法樹(shù),是為了更方便的理解含義。
同時(shí)介紹了如何通過(guò) JS 運(yùn)行一套完整的語(yǔ)法解析器,以及如何提供自定義 AST 結(jié)構(gòu)的能力。
本文介紹的模型,只是為了便于理解而定制的簡(jiǎn)化版,了解全部細(xì)節(jié),請(qǐng)?jiān)L問(wèn) cparser。
最后說(shuō)一下為何要做這個(gè)語(yǔ)法解析器。如今有許多開(kāi)源的 AST 解析工具,但筆者要解決的場(chǎng)景是語(yǔ)法自動(dòng)提示,需要在語(yǔ)句不完整,甚至錯(cuò)誤的情況,給出當(dāng)前光標(biāo)位置的所有可能輸入。所以通過(guò)完整重寫(xiě)語(yǔ)法解析器內(nèi)核,在解析的同時(shí),生成語(yǔ)法樹(shù)的同時(shí),也給出光標(biāo)位置下一個(gè)可能輸入提示,在通用錯(cuò)誤場(chǎng)景自動(dòng)從錯(cuò)誤中恢復(fù)。
目前在做性能優(yōu)化,通用 SQL 文法還在陸續(xù)完善中,目前僅可當(dāng)學(xué)習(xí)參考,不要用于生產(chǎn)環(huán)境。
4 更多討論討論地址是:精讀《手寫(xiě) SQL 編譯器 - 語(yǔ)法樹(shù)》 · Issue #99 · dt-fe/weekly
如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97073.html
摘要:經(jīng)過(guò)連續(xù)幾期的介紹,手寫(xiě)編譯器系列進(jìn)入了智能提示模塊,前幾期從詞法到文法語(yǔ)法,再到構(gòu)造語(yǔ)法樹(shù),錯(cuò)誤提示等等,都是為智能提示做準(zhǔn)備。 1 引言 詞法、語(yǔ)法、語(yǔ)義分析概念都屬于編譯原理的前端領(lǐng)域,而這次的目的是做 具備完善語(yǔ)法提示的 SQL 編輯器,只需用到編譯原理的前端部分。 經(jīng)過(guò)連續(xù)幾期的介紹,《手寫(xiě) SQL 編譯器》系列進(jìn)入了 智能提示 模塊,前幾期從 詞法到文法、語(yǔ)法,再到構(gòu)造語(yǔ)法...
摘要:引言是一個(gè)版語(yǔ)法解析器生成器,具有分詞語(yǔ)法樹(shù)解析的能力。實(shí)現(xiàn)函數(shù)用鏈表設(shè)計(jì)函數(shù)是最佳的選擇,我們要模擬調(diào)用棧了。但光標(biāo)所在的位置是期望輸入點(diǎn),這個(gè)輸入點(diǎn)也應(yīng)該參與語(yǔ)法樹(shù)的生成,而錯(cuò)誤提示不包含光標(biāo),所以我們要執(zhí)行兩次。 1. 引言 syntax-parser 是一個(gè) JS 版語(yǔ)法解析器生成器,具有分詞、語(yǔ)法樹(shù)解析的能力。 通過(guò)兩個(gè)例子介紹它的功能。 第一個(gè)例子是創(chuàng)建一個(gè)詞法解析器 my...
摘要:總結(jié)做語(yǔ)法解析器錯(cuò)誤提示功能時(shí),再次刷新了筆者三觀,原來(lái)我們以為的必然,在編譯器里對(duì)應(yīng)著那么多可能。語(yǔ)法解析器為了讓報(bào)錯(cuò)符合人們的第一直覺(jué),對(duì)錯(cuò)誤信息做了過(guò)濾,只保留剩余數(shù)最短的那條錯(cuò)誤信息。 1 引言 showImg(https://segmentfault.com/img/remote/1460000016244315?w=1522&h=272); 編譯器除了生成語(yǔ)法樹(shù)之外,還要在...
閱讀 3167·2021-11-22 09:34
閱讀 2806·2021-09-22 15:28
閱讀 835·2021-09-10 10:51
閱讀 1865·2019-08-30 14:22
閱讀 2332·2019-08-30 14:17
閱讀 2746·2019-08-30 11:01
閱讀 2306·2019-08-29 17:19
閱讀 3674·2019-08-29 13:17