像这样的作品
arr: arr '@' arr;
总是会产生 shift-reduce 冲突,因为它们是模棱两可的。假设您@
在源代码中有两个运算符:
...1 @ ...2 @ ...3
这应该被解析为:
arr1: ...1 @ ...2
arr2: ...3
arr3: arr1 @ arr2
或者
arr1: ...1
arr2: ...2 @ ...3
arr3: arr1 @ arr2
换句话说,@
关联到左边还是右边?语法没有指定,所以它是模棱两可的。
解决这个问题的常用方法是使用优先级声明(参见 bash 手册),但它可以直接用语法编写(参见下文)。
但是,即使将@
运算符放在一边,您的语法也不能真正做到您想要的。首先,您可能希望基本数组文字符合以下语法:[注 1]
arr: '[' expr_list ']' { $$ = $2; }
| '[' ']' { $$ = new std::vector<int>; }
expr_list
: expr { $$ = new std::vector<int>; $$->push_back($1); }
| expr_list ',' expr { $1->push_back($3); }
然后你可以定义连接表达式:
arr_concat
: arr
| arr_concat '@' arr { std::copy($3->begin(), $3->end,
std::back_inserter(*$1));
delete $3; // Note 2
}
请注意,上述产生式明确说明了 的关联性@
。没有歧义是不可能的。
笔记:
在这里,我假设您已经声明了一个语义联合,其类型之一是std::vector<int>*
,因为所有这些void*
强制转换都是丑陋且不安全的。它会是这样的:
%union {
std::vector<int>* array_pointer;
// ...
}
%type <array_pointer> arr expr_list arr_concat
%%
delete
避免内存泄漏是必要的,但你在哪里做取决于你的内存管理方法。不幸的是,一旦你决定保留指向向量的指针而不是真正的向量(在这种情况下你别无选择,因为每次减少都复制向量是荒谬的),你就会负责内存管理。一旦语义值被合并到其他语义值中就删除它们是一个简单的解决方案,但这意味着您必须小心指向已分配对象的指针的其他副本。如果您小心地只拥有一个指向每个分配对象的指针,那么立即数delete
就可以了;否则,您将需要某种垃圾收集,可能基于引用计数。