精读《手写 SQL 编译器 - 语法树》

1 弁言

重回 “手写 SQL 编辑器” 系列。之前几期引见了 词法、文法、语法的剖析,以及回溯功用的完成,此次引见怎样天生语法树。

基于 《回溯》 一文引见的思绪,我们应用 JS 完成一个微型 SQL 剖析器,并引见怎样天生语法树,怎样在 JS SQL 引擎完成语法树天生功用!

剖析目的是:

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])
  );

这是本文为了轻易申明,完成的一个精简版本。完全版见我们的开源堆栈
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 函数实行机制:

《精读《手写 SQL 编译器 - 语法树》》

天生构造中有四个基础构造,分别是 Chain、Tree、Function、Match,足以表达语法剖析须要的一切逻辑。(不包括 可选、多选 逻辑)。

每一个元素的子节点悉数实行终了,才会天生当前节点的语法树。实际上,每一个节点实行完,都邑挪用 callParentNode 接见父节点,实行到了这个函数,申明子元素已胜利实行终了,补全对应节点的 AST 信息即可。

关于修正部分 AST 构造函数,需守候全部 ChainNode 实行终了才挪用,并将返回的新 AST 信息存储下来,作为这个节点的终究 AST 信息并通报给父级(或许没有父级,这就是根结点的 AST 效果)。

3 总结

本文引见了怎样天生语法树,并申清楚明了 默许语法树 的存在,以及我们之所以要一个定制的语法树,是为了更轻易的明白寄义。

同时引见了怎样经由历程 JS 运转一套完全的语法剖析器,以及怎样供应自定义 AST 构造的才。

本文引见的模子,只是为了便于明白而定制的简化版,相识悉数细节,请接见 cparser

末了说一下为什么要做这个语法剖析器。现在有很多开源的 AST 剖析东西,但笔者要处理的场景是语法自动提醒,须要在语句不完全,以至毛病的状况,给出当前光标位置的一切能够输入。所以经由历程完全重写语法剖析器内核,在剖析的同时,天生语法树的同时,也给出光标位置下一个能够输入提醒,在通用毛病场景自动从毛病中恢复。

现在在做机能优化,通用 SQL 文法还在连续完美中,现在仅可当进修参考,不要用于临盆环境。

4 更多议论

议论地点是:
精读《手写 SQL 编译器 – 语法树》 · Issue #99 · dt-fe/weekly

假如你想介入议论,请点击这里,每周都有新的主题,周末或周一宣布。

    原文作者:黄子毅
    原文地址: https://segmentfault.com/a/1190000016158400
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞