Думайте об этом как о наборе элементов, которые не обязательно все одного типа. У меня есть следующий код:
// The struct I'll use inside Bison to dynamically create collections:
typedef struct ListElementType {
union value {
int intVal;
float floatVal;
char* charptrVal;
} value;
struct ListElementType* next;
} ListElementType;
Тогда в Bison у меня есть:
%union
{
int int_type;
char char_type;
float float_type;
char* charptr_type;
ListElementType* listElementType;
}
//----------------------------------------------------
%token <charptr_type> STRING
%token <int_type> INTEGER
%token <float_type> REAL
%type<listElementType> ElementList
//----------------------------------------------------
//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
{
$$ = malloc(sizeof(listElementType));
$$->next = $1;
$$->value = $3;
}
| LiteralType
{
$$ = malloc(sizeof(listElementType));
$$->next = 0;
$$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
| INTEGER
| REAL
;
Здесь есть несколько вещей/проблем. Но сначала, пытаясь создать синтаксический анализатор, подобный этому, Бизон говорит, что $3 в рекурсивном производстве и $1 в базовом/терминальном случае не имеют объявленных типов. Насколько я понимаю, у них действительно есть объявленные типы. Они имеют тип LiteralType и, как таковые, могут быть либо строками, либо целыми числами, либо числами с плавающей запятой, которые должны быть установлены автоматически, оставив пустыми последние выходные данные терминала (учитывая, что первое, что я сделал, это сделал их тип явным, выбрав соответствующий из глобального объединения) .
Во-вторых, я не ожидаю, что Bison будет жаловаться на отсутствие объявленного типа, а скорее на конфликт или двусмысленность, поскольку я присваиваю $$->value, но $2,$1 может иметь любое из трех возможных значений (в зависимости от того, какое объединение участник был назначен в их соответствующих постановках). В этой ситуации я сделал член значения в структуре ListElementType объединением. Я думал вместо того, чтобы пытаться воспользоваться тем фактом, что первый член структуры будет находиться в месте "метки" самого адреса структуры, а также что все члены объединения начинаются также с адреса памяти объединения, чтобы попытаться и напрямую назначить независимо от тип. Что-то вроде (void)$$ = $2, чем бы ни было $2.
Итак, я изменил код на:
//----------------------------------------------------
ElementList
: ElementList ',' LiteralType
{
$$ = malloc(sizeof(listElementType));
$$->next = $1;
*$$ = (void*)$3;
}
| LiteralType
{
$$ = malloc(sizeof(listElementType));
$$->next = 0;
$$->value = $1;
}
;
//----------------------------------------------------
LiteralType
: STRING
{
$<charptr_type>$ = $1;
}
| INTEGER
{
$<int_type>$ = $1;
}
| REAL
{
$<float_type>$ = $1;
}
;
Теперь я явно установил объединение для случаев INT, REAL, STRING. Что я считал не нужным, но кто-то поправит меня, если я ошибаюсь. И я также попробовал присваивание объединения без типов, но все те же ошибки: у $3 и $1 нет объявленных типов.
Итак, мои мысли и вопросы:
Должен ли я создавать отдельные продукты StringList, IntList и RealList, где единственное, что меняется, это то, что нетерминал с правой стороны прямо соответствует определенному типу элемента в списке, например:
//----------------------------------------------------
ElementList
: IntElementList
| RealElementList
;
IntElementList
: IntElementList ',' INTEGER
{
$$ = malloc(sizeof(listElementType));
$$->next = $1;
$$->intVal = $3;
}
| INTEGER
{
$$ = malloc(sizeof(listElementType));
$$->next = 0;
$$->intVal = $1;
}
RealElementList
: RealElementList ',' REAL
{
$$ = malloc(sizeof(listElementType));
$$->next = $1;
$$->floatVal = $3;
}
| REAL
{
$$ = malloc(sizeof(listElementType));
$$->next = 0;
$$->floatVal = $1;
}
;
Или есть способ указать, что LiteralType может иметь любое из трех значений, а затем попытаться получить бестиповое объединение?
Или весь подход неверен, и есть лучший способ?