野牛阵列转移减少冲突

Bison arrays shift reduce conflicts

我是 Bison 的新手,很长一段时间以来我一直在尝试创建一个数组和连接规则等等,但我不明白为什么我会在这里减少班次,我该如何做解决它:

arr:
  T_OPEN expr       {$$ = (void *)(new vector<int>());((vector<int>*)$$)->push_back();}
| arr ',' expr      {((vector<int>*)())->push_back();}
| arr T_CLOSE       {}
| arr '@' arr       {/*will add it later*/}

T_OPEN 是“[”,T_CLOSE 是“]”。 @ 应该连接两个数组。 arr 的类型为 void*。部分:

arr '@' arr

导致班次减少冲突。任何解决方案将不胜感激

这样的作品
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 ']'    { $$ = ; }
   | '[' ']'              { $$ = new std::vector<int>; }
expr_list
   : expr                 { $$ = new std::vector<int>; $$->push_back(); }
   | expr_list ',' expr   { ->push_back(); }

然后你可以定义连接表达式:

arr_concat
   : arr
   | arr_concat '@' arr   { std::copy(->begin(), ->end,
                                      std::back_inserter(*));
                            delete ; // Note 2
                          }

请注意,上述产生式明确了 @ 的结合律。不能有歧义。


备注:

  1. 这里我假设您已经声明了一个语义联合,其类型之一是 std::vector<int>*,因为所有这些 void* 强制转换都很丑陋且不安全。它会是这样的:

    %union {
       std::vector<int>*   array_pointer;
       // ...
    }
    %type <array_pointer> arr expr_list arr_concat
    %%
    
  2. delete 是避免内存泄漏所必需的,但是您在哪里执行它取决于您的内存管理方法。不幸的是,一旦您决定保留指向向量的指针而不是实际的向量(在这种情况下您别无选择,因为每次缩减都复制向量是荒谬的),然后您将负责内存管理。一旦语义值被合并到其他语义值中就删除它们是一个简单的解决方案,但这意味着您必须小心指向已分配对象的指针的其他副本。如果你小心翼翼地只让一个指针指向每个分配的对象,立即数 delete 就可以了;否则,您将需要某种垃圾收集,可能基于引用计数。