使用称为“ Lemon”的代码生成器程序生成用于SQLite的SQL语言解析器。Lemon程序读取输入语言的语法,并发出C代码以实现该语言的解析器。
Lemon没有其自己的源存储库。相反,Lemon由SQLite源代码树中的几个文件组成:
lemon.html →Lemon的原始详细用法文档和程序员参考。
lemon.c →实用程序的源代码,该实用程序读取语法文件并生成相应的解析器C代码。
lempar.c →生成的解析器C代码的模板。“柠檬”实用程序读取此模板并插入其他代码以生成解析器。
Lemon生成LALR(1)解析器。它的操作类似于更熟悉的工具Yacc和 Bison,但是Lemon增加了重要的改进,包括:
语法语法不太容易出错-使用符号名称表示语义值,而不是使用Yacc的“ $ 1”样式位置表示法。
在Lemon中,分词器调用解析器。Yacc以相反的方式操作,解析器调用标记器。Lemon方法是可重入且线程安全的,而Yacc使用全局变量,因此两者都不是。对于SQLite,重入性尤其重要,因为某些SQL语句对解析器进行递归调用。例如,当解析CREATE TABLE语句时,SQLite递归调用解析器以生成INSERT语句,以在sqlite_schema表中添加新条目。
Lemon具有非终端析构器的概念,可用于在语法错误或其他中止的解析之后回收内存或其他资源。
在SQLite中的两个地方都使用Lemon。
Lemon的主要用途是创建SQL语言解析器。Lemon将一个语法文件(parse.y)编译为parse.c和parse.h。parse.c文件无需进一步修改即可合并到合并中。
Lemon还用于在FTS5扩展中为查询模式表达式生成解析器。在这种情况下,输入语法文件是fts5parse.y。
将代码生成器工具作为项目的一部分托管的优点之一是,可以优化工具以满足整个项目的特定需求。柠檬受益于这种作用。多年来,Lemon解析器生成器已得到扩展和增强,以为SQLite提供新功能和改进的性能。专为SQLite使用而设计的Lemon的一些特定增强功能包括:
Lemon具有“备用”令牌的概念。SQL语言包含大量关键字,这些关键字可能会与标识符名称冲突。Lemon具有指定某些关键字的能力,并且能够“回退”到标识符。如果关键字在上下文中出现在输入令牌流中,否则该上下文将是语法错误,则在引发语法错误之前,令牌会自动转换为其后备。此功能使解析器可以非常宽容用作标识符的保留字,这是SQL语言中经常出现的问题。
为了支持SQLite的100%MC / DC测试目标,Lemon生成的解析器代码没有不可达的分支,并且包含用于测量测试覆盖率的额外(选择的编译时)工具。
Lemon支持语法文件规则的条件编译,因此可以根据编译时选项生成不同的解析器。
作为性能优化,允许Lemon输入语法中的reduce动作包含“ / * A-overwrites-Z * /”形式的注释,以指示规则右侧的语义值“ A”为允许直接覆盖左侧的语义值“ Z”。这种简单的优化减少了用于解析输入语法的下推式自动机中的堆栈操作数,从而提高了解析器的性能。它还使生成的代码小一些。
SQL语句的解析是任何SQL数据库引擎中CPU周期的重要消耗者。不断优化SQLite的努力已使开发人员花费大量时间来调整Lemon以生成更快的解析器。这些努力使柠檬分析器生成器的所有用户受益,而不仅仅是SQLite。但是,如果Lemon是一个单独维护的工具,则对SQLite和Lemon进行协调更改将更加困难,结果将无法完成太多的优化。因此,事实证明,对于SQLite而言,解析器生成器工具包含在源树中对于工具本身和SQLite都是净收益。
Lemon最初是由D. Richard Hipp(也是SQLite的创建者)在1987年至1992年间进入杜克大学研究生院时写的。Lemon的最初创建日期已经丢失,但可能是在1990年左右的某个时候。Lemon产生了LALR(1)解析器。有一个名为“ Lime”的配套LL(1)解析器生成器工具,但是Lime的源代码已丢失。
Lemon源代码最初是作为单独的源文件编写的,后来仅合并为一个“ lemon.c”源文件。
Lemon and SQLite(Hipp)的作者报告说,通过研究John Ousterhout到Tcl的原始源代码,大大提高了他的C编程技能。希普(Hipp)在1993年发现并研究了Tcl。在那之前写过Lemon,然后是SQLite。这两种产品的编码风格有明显的不同,SQLite看起来更简洁,更具可读性并且更易于维护。