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”非终结符的实例,那么在第一个语法错误时就会发生这种情况。