66. 精读《手写 SQL 编译器 - 语法分析》

本文介绍了如何手写一个简单的 SQL 语法分析器,使用递归下降方法,涉及 LL(k) 和 LR 解析策略。文章通过迷宫比喻解释了语法分析过程,讲解了词法分析、Match 函数、递归下降解析的基本操作,包括空字符串产生式、单词匹配、连接运算和并运算,并探讨了如何实现回溯功能以达到 LL(∞) 的解析能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 引言

接着上周的文法介绍,本周介绍的是语法分析。

以解析顺序为角度,语法分析分为两种,自顶而下与自底而上。

自顶而下一般采用递归下降方式处理,称为 LL(k),第一个 L 是指从左到右分析,第二个 L 指从左开始推导,k 是指超前查看的数量,如果实现了回溯功能,k 就是无限大的,所以带有回溯功能的 LL(k) 几乎是最强大的。LL 系列一般分为 LL(0)、LL(1)、LL(k)、LL(∞)。

自底而上一般采用移进(shift)规约(reduce)方式处理,称为 LR,第一个 L 也是从左到右分析,第二个 R 指从右开始推导,而规约时可能产生冲突,所以通过超前查看一个符号解决冲突,就有了 SLR,后面还有功能更强的 LALR(1) LR(1)LR(k)。

通过这张图可以看到 LL 家族与 LR 家族的能力范围:

640?wx_fmt=png

如图所示,无论 LL 还是 LR 都解决不了二义性文法,还好所有计算机语言都属于无二义性文法。

值得一提的是,如果实现了回溯功能的 LL(k) -> LL(∞),那么能力就可以与 LR(k) 所比肩,而 LL 系列手写起来更易读,所以笔者采用了 LL 方式书写,今天介绍如何手写无回溯功能的 LL。

另外也有一些根据文法自动生成 parser 的库,比如兼容多语言的 antlr4 或者对 js 支持比较友好的 pegjs。

2 精读

递归下降可以理解为走多出口的迷宫:

640?wx_fmt=png

我们先根据 SQL 语法构造一个迷宫,进迷宫的不是探险家,而是 SQL 语句,这个 SQL 语句会拿上一堆令牌(切分好的 Tokens,详情见 精读:词法分析),迷宫每前进一步都会要求按顺序给出令牌(交上去就没收),如果走到出口令牌刚好交完,就成功走出了迷宫;如果出迷宫时手上还有令牌,会被迷宫工作人员带走。这个迷宫会有一些分叉,在分岔路上会要求你亮出几个令牌中任意一个即可通过(LL1),有的迷宫允许你失败了存档,只要没有走出迷宫,都可以读档重来(LLk),理论上可以构造一个最宽容的迷宫,只要还没走出迷宫,可以在分叉处任意读档(LL∞),这个留到下一篇文章介绍。

词法分析

首先对 SQL 进行词法分析,拿到 Tokens 列表,这些就是探险家 SQL 带上的令牌。

根据上次讲的内容,我们对 select a from b 进行词法分析,可以拿到四个 Token(忽略空格与注释)。

Match 函数

递归下降最重要的就是 Match 函数,它就是迷宫中索取令牌的关卡。每个 Match 函数只要匹配上当前 Token 便将 Token index 下移一位,如果没有匹配上,则不消耗 Token:

 
 

Match 函数就是精简版的 if else,试想下面一段代码:

 
 

通过不断对比与移动 Token 进行判断,等价于下面的 Match 实现:

 
 

这样写出来的语法分析代码可读性会更强,我们能专注精神在对文法的解读上,而忽略其他环境因素。


顺便一提,下篇文章笔者会带来更精简的描述方法:

 
 

让函数式语法更接近文法形式。

最后这种语法不但描述更为精简,而且拥有 LL(∞) 的查找能力,拥有几乎最强大的语法分析能力。

语法分析主体函数

既然关卡(Match)已经有了,下面开始构造主函数了,可以开始画迷宫了。

举个最简单的例子,我们想匹配 select a from b,只需要这么构造主函数:

 
 

为了简化流程,我们把 tokens、tokenIndex 作为全局变量。首先通过 lexer 拿到 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值