Productions like
arr: arr '@' arr;
will always produce shift-reduce conflicts because they are ambiguous. Suppose you had two @
operators in source:
...1 @ ...2 @ ...3
Is this supposed to be parsed as:
arr1: ...1 @ ...2
arr2: ...3
arr3: arr1 @ arr2
or
arr1: ...1
arr2: ...2 @ ...3
arr3: arr1 @ arr2
In other words, does @
associate to the left or to the right? The grammar doesn't specify, and so it is ambiguous.
A common way to solve that is with precedence declarations (see the bash manual) but it can be written directly in the grammar (see below).
However, even leaving the @
operator aside, your grammar does not really do what you want. To start with, you probably want basic array literals to be according to this grammar: [Note 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); }
And then you can define concatenation expressions:
arr_concat
: arr
| arr_concat '@' arr { std::copy($3->begin(), $3->end,
std::back_inserter(*$1));
delete $3; // Note 2
}
Note that the above production is explicit about the associativity of @
. No ambiguity is possible.
Notes:
Here I assume that you have declared a semantic union one of whose types is std::vector<int>*
, because all those void*
casts are ugly and unsafe. It would be something like:
%union {
std::vector<int>* array_pointer;
// ...
}
%type <array_pointer> arr expr_list arr_concat
%%
delete
is necessary to avoid a memory leak, but where you do it depends on your approach to memory management. Unfortunately, once you decide to keep pointers to vectors rather than actually vectors (and you have little choice in this case, since copying vectors with each reduction would be ridiculous), you then become responsible for memory management. Deleting semantic values as soon as they have been incorporated into other semantic values is a simple solution, but it means you must be careful about other copies of the pointer to the allocated object. If you are careful to only ever have one pointer to each allocated object, the immediate delete
will do the trick; otherwise, you'll need some kind of garbage collection, possibly based on reference-counting.