のようなプロダクション
arr: arr '@' arr;
それらはあいまいであるため、常に shift-reduce 競合が発生します。ソースに 2 つの@
演算子があるとします。
...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
}
上記の生成は の結合性について明示的であることに注意してください@
。あいまいさはありえません。
ノート:
ここで、型の 1 つが であるセマンティック ユニオンを宣言したと仮定します。std::vector<int>*
これらのvoid*
キャストはすべて見苦しく、安全ではないからです。次のようになります。
%union {
std::vector<int>* array_pointer;
// ...
}
%type <array_pointer> arr expr_list arr_concat
%%
delete
メモリリークを避けるために必要ですが、どこで行うかは、メモリ管理へのアプローチによって異なります。残念なことに、実際のベクトルではなくベクトルへのポインターを保持することを決定すると (この場合、削減ごとにベクトルをコピーするのはばかげているため、選択の余地はほとんどありません)、メモリ管理を担当することになります。セマンティック値が他のセマンティック値に組み込まれたらすぐにセマンティック値を削除するのは簡単な解決策ですが、割り当てられたオブジェクトへのポインターの他のコピーに注意する必要があることを意味します。割り当てられた各オブジェクトへのポインターが 1 つだけになるように注意している場合は、即時delete
にそのトリックが実行されます。それ以外の場合は、おそらく参照カウントに基づいた、ある種のガベージ コレクションが必要になります。