柠檬中令牌析构函数的自定义释放函数
Custom deallocation function for tokens destructor in Lemon
我希望 Lemon 解析一个简单的类 C 表达式,支持对一组具有已知名称的预定义变量进行整数和字符串比较。为简单起见,我们假设它仅支持 字符串比较。因此,以下字符串是我正在谈论的表达式种类的一个很好的例子:
a == "literal_1" || a == "literal_2"
因此,我的词法分析器必须按照以下顺序为解析器提供值:
void *p = parserAlloc(malloc);
parser(p, TOK_VARIABLE_A, NULL);
parser(p, TOK_OPERATOR_EQ, NULL);
parser(p, TOK_LITERAL, strdup("literal_1"));
parser(p, TOK_OPERATOR_OR, NULL);
parser(p, TOK_VARIABLE_A, NULL);
parser(p, TOK_OPERATOR_EQ, NULL);
parser(p, TOK_LITERAL, strdup("literal_2"));
parserFree(p, free);
我必须复制传递给解析器的文字字符串,因为它们可能包含我必须首先解码的转义序列。但是谁负责在解析完成后释放内存呢?幸运的是,柠檬用它的 %destructor
指令来拯救,所以我可以写:
%token_destructor TOK_LITERAL { free($$); }
但实际上,我不想在我的解析器和词法分析器中对 malloc
、strdup
和 free
的用法进行硬编码。我希望能够将分配器和解除分配器函数作为参数传递,但不仅在 parserInit
和 parserFree
中使用它们,而且还用于令牌分配和解除分配。
如何为 parserAlloc
声明附加参数以同时传递 malloc
和 free
? Lemon 中有 %extra_argument
指令,但它让我在每次输入令牌时都传递我的参数。
parserAlloc
的 malloc
参数没有存储在任何地方,因为 lemon 生成的解析器从不分配内存。 [注 1] 当然,free
函数也不会存储在任何地方,因为直到您调用 parserFree
.
才会提供它
通常,您也不需要在解析器操作中使用 alloc 函数,但如果您使用 %destructor
/%token-destructor
,那么您将需要一个 free
函数。唯一记录在案的机制是额外参数功能,正如您所说,它需要在每次调用解析器时提供参数。这有点烦人,特别是因为解析器立即将它存储到解析器状态结构中(即 parse
的第一个参数),但事实就是这样。更改起来很容易,而且 Lemon 不受阻碍,因此您可以进行所需的更改。但是按照规定,%extra-argument
是唯一的办法。
如果你在你的动作中同时需要 alloc 和 free 函数,不管出于什么原因,你可以让 %extra-argument
成为一个指向结构的指针(这实际上是 %extra-argument
的正常情况) ;该结构将包含指向这两个函数的指针。或者,您可以使用具有标准 realloc
接口的函数:realloc(NULL, sz)
等同于 malloc(sz)
而 realloc(p, 0)
等同于 free(p)
(只要 p
不为空)。有关详细信息,请参阅 man realloc
。这不会打扰柠檬解析器,因为它从不使用 malloc 或 free。
备注
- 这不完全正确。据我所知,有一个未记录的功能:如果您设置
%stack-size 0
,那么生成的解析器将在它溢出之前重新分配解析器堆栈,而不是抛出错误。在那种情况下,解析器使用标准库 realloc
来分配或重新分配堆栈,而不是提供给 parserAlloc
的 malloc 函数,并且 parserFree
使用标准库 [=12] 释放堆栈=], 而不是作为参数传递的函数。
我希望 Lemon 解析一个简单的类 C 表达式,支持对一组具有已知名称的预定义变量进行整数和字符串比较。为简单起见,我们假设它仅支持 字符串比较。因此,以下字符串是我正在谈论的表达式种类的一个很好的例子:
a == "literal_1" || a == "literal_2"
因此,我的词法分析器必须按照以下顺序为解析器提供值:
void *p = parserAlloc(malloc);
parser(p, TOK_VARIABLE_A, NULL);
parser(p, TOK_OPERATOR_EQ, NULL);
parser(p, TOK_LITERAL, strdup("literal_1"));
parser(p, TOK_OPERATOR_OR, NULL);
parser(p, TOK_VARIABLE_A, NULL);
parser(p, TOK_OPERATOR_EQ, NULL);
parser(p, TOK_LITERAL, strdup("literal_2"));
parserFree(p, free);
我必须复制传递给解析器的文字字符串,因为它们可能包含我必须首先解码的转义序列。但是谁负责在解析完成后释放内存呢?幸运的是,柠檬用它的 %destructor
指令来拯救,所以我可以写:
%token_destructor TOK_LITERAL { free($$); }
但实际上,我不想在我的解析器和词法分析器中对 malloc
、strdup
和 free
的用法进行硬编码。我希望能够将分配器和解除分配器函数作为参数传递,但不仅在 parserInit
和 parserFree
中使用它们,而且还用于令牌分配和解除分配。
如何为 parserAlloc
声明附加参数以同时传递 malloc
和 free
? Lemon 中有 %extra_argument
指令,但它让我在每次输入令牌时都传递我的参数。
parserAlloc
的 malloc
参数没有存储在任何地方,因为 lemon 生成的解析器从不分配内存。 [注 1] 当然,free
函数也不会存储在任何地方,因为直到您调用 parserFree
.
通常,您也不需要在解析器操作中使用 alloc 函数,但如果您使用 %destructor
/%token-destructor
,那么您将需要一个 free
函数。唯一记录在案的机制是额外参数功能,正如您所说,它需要在每次调用解析器时提供参数。这有点烦人,特别是因为解析器立即将它存储到解析器状态结构中(即 parse
的第一个参数),但事实就是这样。更改起来很容易,而且 Lemon 不受阻碍,因此您可以进行所需的更改。但是按照规定,%extra-argument
是唯一的办法。
如果你在你的动作中同时需要 alloc 和 free 函数,不管出于什么原因,你可以让 %extra-argument
成为一个指向结构的指针(这实际上是 %extra-argument
的正常情况) ;该结构将包含指向这两个函数的指针。或者,您可以使用具有标准 realloc
接口的函数:realloc(NULL, sz)
等同于 malloc(sz)
而 realloc(p, 0)
等同于 free(p)
(只要 p
不为空)。有关详细信息,请参阅 man realloc
。这不会打扰柠檬解析器,因为它从不使用 malloc 或 free。
备注
- 这不完全正确。据我所知,有一个未记录的功能:如果您设置
%stack-size 0
,那么生成的解析器将在它溢出之前重新分配解析器堆栈,而不是抛出错误。在那种情况下,解析器使用标准库realloc
来分配或重新分配堆栈,而不是提供给parserAlloc
的 malloc 函数,并且parserFree
使用标准库 [=12] 释放堆栈=], 而不是作为参数传递的函数。