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

2021SC@SDUSC

特殊指令

        Lemon的输入语法由语法规则和特殊指令组成。我们已经描述了所有的语法规则,现在我们将讨论特殊的指令。

        Lemon中的指令可以按任何顺序出现。你可以把它们放在语法规则之前,或者放在语法规则之后,或者放在语法规则的中间。无所谓。用于为终端分配优先级的指令的相对顺序很重要,但除此之外,Lemon中的指令顺序是任意的。
柠檬支持以下特殊指令:

%code
%default_destructor
%default_type
%destructor
%else
%endif
%extra_argument
%fallback
%if
%ifdef
%ifndef
%include
%left
%name
%nonassoc
%parse_accept
%parse_failure
%right
%stack_overflow
%stack_size
%start_symbol
%syntax_error
%token
%token_class
%token_destructor
%token_prefix
%token_type
%type
%wildcard

%code

        %code表示将一段C/C++代码添加到输出文件的尾部。它主要用于包含一些动作例程(action routines)或者词法器的部分代码。

%default_type

        如果没有在%type中为非终结符指定数据类型,则default_type指定非终结符的数据类型。

%destructor

        为非终结符指定一个资源释放器(destructor)(%token_destructor为终结符指定资源释放器)。当非终结符从分析栈中弹出时,释放器就会被调用,以释放其占用的资源。包括如下情况:

(1)一个规则发生规约,而非终结符的右边却没有C代码;
(2)在错误处理中,出栈操作;
(3)ParseFree函数返回。

        释放器可以做任何操作,但是它主要用来释放非终结符占用的内存或其它资源。例:

	%type nt {
    
    void*}
   		%destructor nt {
    
     free($$); }
  		nt(A) ::= ID NUM.   {
    
     A = malloc( 100 ); }

        在这个例子中,“nt”的数据类型为“void *”。当“nt”的规则发生规约时,为非终结符通过malloc分配空间。之后,当非终结符从栈中弹出时,释放器会被调用,以释放malloc申请的内存。这可以避免内存泄漏。
注意,除非非终结符会在C动作代码中使用,否则,当非终结符从栈中弹出时,就会调用释放器释放资源。如果C代码使用非终结符,则由C代码保证资源的释放。

%token_prefix

        Lemon的生成文件会为每个终结符定义一个整数值。如下:

 	#define AND              1
    #define MINUS            2
    #define OR               3
    #define PLUS             4

        如果愿意,可以通过该指示符为#define的预处理符号加一个前缀。例如,可以在规则文件加上如下:

	%token_prefix    TOKEN_

        则生成的文件的输出如下:

   	#define TOKEN_AND        1
    #define TOKEN_MINUS      2
    #define TOKEN_OR         3
    #define TOKEN_PLUS       4

%include

        由该指示符指定的C代码会包含到生成的分析的顶部。你可以包含任意代码,Lemon会完全拷贝过去。

%extra_argument

        指示Parse函数中第四个参数。 Lemon本身不会做任何处理,但是相应的C代码可以使用该参数。

%parse_accept

        分析器进行语法分析成功时,执行的C代码。如:

    %parse_accept {
    
    
      printf("parsing complete!\n");
    }

%stack_overflow

        当分析器执行发生内部栈溢出时,会执行相应的动作。通常,可以输出错误消息,分析器不能继续执行,而必须重置。例如:

   %stack_overflow {
    
    
     fprintf(stderr,"Giving up.  Parser stack overflow\n");
   }

%name

        默认情况下,Lemon生成的函数都以“Parse”开始,可以通过该指示符修改。例如:

	%name Abcde

这会导致Lemon生成的函数的名字如下:

	AbcdeAlloc(), 
	AbcdeFree(), 
	AbcdeTrace(), and 
	Abcde().

%token_type与%type

        这些指示符用于为分析器的栈中的终结符或非终结指定数据类型。所有终结符都必须是相同的类型,而与应该与Lemon生成的输出文件中的Parse()的第3个参数的类型一致。通常,可以将一个结构指针赋给终结符,如下:

%token_type    {
    
    Token*}

如果终结符的数据类型没有指定,默认为“int”。

        通常,每个非终结符都有各自的数据类型。例如,通常非终结符为指向的分析树的根结点的数据类型指针,该根结点包含非终结符的所有信息。例如:

	%type   expr  {
    
    Expr*}

错误处理

        经过几年的广泛试验,人们发现yacc所使用的错误恢复策略已经达到了预期的效果。这就是lemon的用途。

        当Lemon生成的解析器遇到语法错误时,它首先调用 %syntax_error指令(如果有)指定的代码。然后进入错误恢复策略。错误恢复策略是开始弹出解析器堆栈,直到它进入允许移动名为“error”的特殊非终端符号的状态。然后它移动这个非终端并继续解析。在至少三个新令牌成功移位之前,不会再次调用 %syntax_error错误例程。

        如果解析器弹出其堆栈,直到堆栈为空,并且仍然无法移动错误符号,则会调用 %parse_failure例程,解析器将自身重置为其开始状态,准备开始解析新文件。当然,如果语法中没有“error”非终结符的实例,那么在第一个语法错误时就会发生这种情况。

Guess you like

Origin blog.csdn.net/wy_csdn_sdu/article/details/121459358