SQLite源代码分析----------分析器③

2021SC@SDUSC

Lemon输入文件语法

        Lemon语法规范文件的主要目的是为解析器定义语法。但是,输入文件还指定Lemon完成其工作所需的附加信息。使用Lemon的大部分工作是编写一个适当的语法文件。

        Lemon的语法文件在很大程度上是一种自由格式。它没有像yacc或bison那样的分区。任何声明都可以出现在文件中的任何位置。Limes忽略空白(除了需要分离令牌),它遵守与C/C++相同的注释约定。

终点站和非终点站

        终端符号(令牌)是以大写字母开头的任何字母、数字和/或下划线字符的字符串。终端可以在第一个字符之后包含小写字母,但通常的惯例是使终端全部大写。另一方面,非终端是任何字母数字字符串和下划线字符,而不是以小写字母开头的字符串。同样,通常的惯例是让非终端使用所有小写字母。

        在Lemon中,不需要在语法文件的单独部分中声明或标识终端符号和非终端符号。柠檬能够通过检查语法规则生成所有终端和非终端的列表,并且它总是可以通过检查名称的第一个字符的大小写来区分终端和非终端。

        Yacc和Bison允许终端符号具有字母数字名称或单引号中包含的单个字符,例如:‘’)或‘$’。柠檬不允许这种终端符号的替代形式。对于Lemon,所有符号、终端和非终端都必须有字母数字名称。

	int main(int argc, char **argv){
    
    
	  static int version = 0;
	  static int rpflag = 0;
	  static int basisflag = 0;
	  static int compress = 0;
	  static int quiet = 0;
	  static int statistics = 0;
	  static int mhflag = 0;
	  static int nolinenosflag = 0;
	  static int noResort = 0;
	  static int sqlFlag = 0;
	  static int printPP = 0;
	  
	  static struct s_options options[] = {
    
    
	    {
    
    OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."},
	    {
    
    OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."},
	    {
    
    OPT_FSTR, "d", (char*)&handle_d_option, "Output directory.  Default '.'"},
	    {
    
    OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."},
	    {
    
    OPT_FLAG, "E", (char*)&printPP, "Print input file after preprocessing."},
	    {
    
    OPT_FSTR, "f", 0, "Ignored.  (Placeholder for -f compiler options.)"},
	    {
    
    OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."},
	    {
    
    OPT_FSTR, "I", 0, "Ignored.  (Placeholder for '-I' compiler options.)"},
	    {
    
    OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."},
	    {
    
    OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."},
	    {
    
    OPT_FSTR, "O", 0, "Ignored.  (Placeholder for '-O' compiler options.)"},
	    {
    
    OPT_FLAG, "p", (char*)&showPrecedenceConflict,
	                    "Show conflicts resolved by precedence rules"},
	    {
    
    OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."},
	    {
    
    OPT_FLAG, "r", (char*)&noResort, "Do not sort or renumber states"},
	    {
    
    OPT_FLAG, "s", (char*)&statistics,
	                                   "Print parser stats to standard output."},
	    {
    
    OPT_FLAG, "S", (char*)&sqlFlag,
	                    "Generate the *.sql file describing the parser tables."},
	    {
    
    OPT_FLAG, "x", (char*)&version, "Print the version number."},
	    {
    
    OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."},
	    {
    
    OPT_FSTR, "W", 0, "Ignored.  (Placeholder for '-W' compiler options.)"},
	    {
    
    OPT_FLAG,0,0,0}
	  };

语法规则

        Lemon语法文件的主要组件是一系列语法规则。每个语法规则都包含一个非终结符符号,后跟特殊符号“:=”,然后是一个终结符和/或非终结符列表。该规则以句点终止。规则右侧的端子和非端子列表可以为空。规则可以按任何顺序出现,除非第一条规则的左侧被假定为语法的开始符号(除非使用%start_symbol)典型的语法规则序列可能如下所示:

	expr ::= expr PLUS expr.
 	expr ::= expr TIMES expr.
 	expr ::= LPAREN expr RPAREN.
 	expr ::= VALUE.

        与yacc和bison一样,Lemon允许语法指定一个C代码块,每当语法规则被解析器缩减时,该代码块就会被执行。在Lemon中,此操作是通过将C代码(包含在{…}内)放在关闭规则的句点之后来指定的。例如:

	expr ::= expr PLUS expr.   {
    
     printf("Doing an addition...\n"); }

        为了有用,语法动作通常必须与其关联的语法规则相链接。在yacc和bison中,这是通过在动作中嵌入一个“$$”来实现的,表示规则左侧的值,并在规则右侧的位置1、2等处嵌入符号“$1”、“$2”等来表示终端或非终端的值。这个想法非常强大,但也很容易出错。yacc或bison语法中最常见的一个错误来源是错误计算语法规则右侧的符号数,当您真正的意思是“$8”时说“$7”。
        Lemon通过为语法规则中的每个符号指定符号名,然后在操作中使用这些符号名,从而避免了计算语法符号的需要。在yacc或bison中,人们会这样写:

	expr->expr+expr{
    
    $$=$1+$3}

但在Lemon中,同样的规则如下:

	expr(A):=expr(B)加上expr(C)。{
    
    A=B+C;}

        在Lemon规则中,语法规则符号后括号中的任何符号都将成为该符号在语法规则中的占位符。这个占位符可以在相关的C动作中使用,代表该符号的值。将语法规则与其reduce操作联系起来的Lemon表示法在几个方面优于yacc/bison。首先,如上所述,Lemon方法避免了计算语法符号的需要。其次,如果Lemon语法规则中的一个终端或非终端在括号中包含一个链接符号,但该链接符号实际上未在reduce操作中使用,则会生成一条错误消息。例如:

	expr(A):=expr(B)加上expr(C)。{
    
    A=B;}

将生成错误,因为链接符号“C”在语法规则中使用,但在reduce操作中未使用。
        用于链接语法规则以减少操作的Lemon符号也有助于使用析构函数来回收由规则右侧的终端和非终端值分配的内存。

优先规则

       lemon解析歧义的方式与yacc和bison完全相同。Shift-Reduction冲突的解决有利于Shift,减少约简冲突是通过减少语法文件中的第一条规则来解决的。

       就像在yacc和bison中一样,Lemon允许使用优先规则控制解析冲突的解决。可以使用%left、%right或%nonassoc指令为任何端子符号指定优先级值。早期指令中提到的端子符号的优先级低于后期指令中提到的端子符号。例如:

 	%left AND.
  	%left OR.
 	%nonassoc EQ NE GT GE LT LE.
  	%left PLUS MINUS.
  	%left TIMES DIVIDE MOD.
  	%right EXP NOT.

在前面的指令序列中,AND运算符被定义为具有最低优先级。OR运算符的优先级更高一级等等。因此,语法将尝试对歧义表达式进行分组:

     a AND b OR c

像这样

     a AND (b OR c).

当优先级相同时,使用结合性(左、右或非assoc)来确定分组。在我们的例子中是左关联的,所以

     a AND b AND c

被解析成这样

     (a AND b) AND c.

但是exp运算符是右关联的,所以

     a EXP b EXP c

被解析成这样

     a EXP (b EXP c).

非assoc优先级用于非结合算子.所以

     a EQ b EQ c

是个错误。
       非终结符的优先级转换为规则,如下所示:语法规则的优先级等于为其定义优先级的规则中最左侧终结符符号的优先级。这通常是您想要的,但在那些希望语法规则的优先级有所不同的情况下,您可以通过将符号放在规则末尾的句点之后和任何C代码之前的方括号中来指定替代优先级符号。例如:

  expr = MINUS expr.  [NOT]

此规则的优先级等于NOT符号,而不是默认情况下的减号。

       通过了解如何将优先级分配给终端符号和单个语法规则,我们现在可以精确地解释Lemon中解析冲突是如何解决的。排班冲突的解决方法如下:

①如果要移位的令牌或要缩减的规则缺少优先级信息,则进行有利于移位的解析,但报告解析冲突。
②如果要移位的标记的优先级大于要减少的规则的优先级,则进行有利于移位的解析。未报告任何解析冲突。
③如果要移动的标记的优先级小于要减少的规则的优先级,则进行有利于减少操作的解析。未报告任何解析冲突。
④如果先决条件相同且移位标记是右关联的,则以有利于移位的方式进行解析。未报告任何解析冲突。
⑤如果先例相同且移位标记为左关联,则使用reduce进行解析。未报告任何解析冲突。
⑥否则,通过执行移位来解决冲突,并报告解析冲突。

减少冲突通过以下方式:

①如果任一reduce规则缺少优先级信息,则按照语法中首先出现的规则进行解析,并报告解析冲突。
②如果两个规则的优先级不一致,则以最高优先级解决冲突。
③否则,通过按语法中首先出现的规则进行缩减来解决冲突,并报告解析冲突。

猜你喜欢

转载自blog.csdn.net/wy_csdn_sdu/article/details/121322088