Parsing 野牛不使用语义值,而是使用行为的副作用,这是一种好的做法吗?
我有一种语言,其中所有事物的语义都是一组字符或数组。所以我有以下几点:Parsing 野牛不使用语义值,而是使用行为的副作用,这是一种好的做法吗?,parsing,bison,Parsing,Bison,我有一种语言,其中所有事物的语义都是一组字符或数组。所以我有以下几点: typedef struct _array { union { char *chars; // start of string void *base; // start of array }; unsigned n; // number of valid elements in above unsigned allocated; // number
typedef struct _array {
union {
char *chars; // start of string
void *base; // start of array
};
unsigned n; // number of valid elements in above
unsigned allocated; // number of allocated elements for above
} array;
#define YYSTYPE array
我可以用
void append(YYSTYPE *parray, YYSTYPE *string);
假设语法(SSCCE)为:
所以我接受一系列的单词。对于每个单词,语义值都变成了字符数组,然后我想将每个字符都附加到整个序列的数组中
有几种可能的方法来设计动作:
array
符号具有类型array
的语义值。如果我这样做,那么数组单词
的操作将不得不将数组$1
复制到$$
,这很慢,所以我不喜欢这样array
符号具有类型array*
的语义值。现在,对于数组单词
,我只需将*$1
添加到数组中,然后将$$
设置为等于$1
。但我不喜欢这个有两个原因。首先,语义不是指向数组
,而是数组
。第二,对于规则array:WORD
的操作,我必须malloc
这个结构,它很慢。是的,“append”有时会执行malloc
,但是如果我分配的足够多,就不会频繁。出于性能原因,我希望避免任何不必要的malloc
数组
设置语义值,使用globals:
静态YYSTYPE g_阵列代码>
YYSTYPE*g_parray=&g_数组代码>
append(g_parray, word_array)
按照整个语法的工作方式,我只需要一个g_数组
。以上是我能想到的最快的。但这真的是一个糟糕的设计——有很多全局变量,没有语义值,相反,一切都是全局变量的副作用
所以,就我个人而言,我一个都不喜欢。对于野牛来说,哪一个是公认的最佳实践 在大多数情况下,使用全局变量没有意义。bison的现代版本或多或少都有
%parse param
指令,它允许您拥有某种“解析上下文”。上下文可能负责所有内存分配等
它可能反映了当前的解析状态-i。E有“当前<代码>数组代码>等”的概念。在这种情况下,您的语义操作可以依赖于上下文知道您在哪里
%{
typedef struct tagContext Context;
typedef struct tagCharString CharString;
void start_words(Context* ctx);
void add_word(Context* ctx, CharString* word);
%}
%union {
CharString* word;
}
%parse-param {Context* ctx}
%token<word> WORD
%start words
%%
words
: { start_words(ctx); } word
| words word
;
word
: WORD { add_word(ctx, $1); }
;
这两种方法之间的性能差异似乎可以忽略不计
内存清理
如果您的输入文本在解析时没有错误,则您始终负责清理所有动态内存。但是,如果输入文本导致分析错误,解析器将不得不丢弃一些标记。在这种情况下,可能有两种清理方法
首先,您可以跟踪上下文中的所有内存分配,并在销毁上下文时释放它们
其次,您可以依赖bison析构函数:
%{
void free_word_list(WordList* word_list);
%}
%destructor { free_word_list($$); } <word_list>
%{
无效自由词列表(词列表*词列表);
%}
%析构函数{free_word_list($$);}
您的第一种方法与我的第三种方法类似,只是没有全局方法,我将以此作为我的答案,谢谢。你的第二种方法与我的第二种方法完全相同。
%{
typedef struct tagContext Context;
typedef struct tagCharString CharString;
typedef struct tagWordList WordList;
// word_list = NULL to start a new list
WordList* add_word(Context* ctx, WordList* prefix, CharString* word);
%}
%union {
CharString* word;
WordList* word_list;
}
%parse-param {Context* ctx}
%token<word> WORD
%type<word_list> words words_opt
%start words
%%
words
: words_opt WORD { $words = add_word(ctx, $words_opt, $WORD); }
;
words_opt
: %empty { $words_opt = NULL; }
| words
;
%{
void free_word_list(WordList* word_list);
%}
%destructor { free_word_list($$); } <word_list>