Очевидно, проблема была в том, что вы устанавливали семантическое значение stmt
, чтобы указывать на локальную переменную, выделенную в стеке. Так как время жизни локальной переменной истекает, как только семантическое действие выполнено, это значение является висящим указателем, и его использование - неопределенное поведение. (Но в этом случае вы, вероятно, обнаружите, что все указатели ссылаются на один и тот же блок памяти, содержимое которого будет меняться при каждом повторном использовании конкретного кадра стека.)
Необходимо убедиться, что значение stmt
является указателем на недавно выделенный NODE
:
stmt : /* ... something ... */
{ $$ = malloc(sizeof *$$);
$$ = (NODE){/* ... */, .next = NULL; }
Это важно. Нет хорошего способа сделать это без какой-либо функции, которая выделяет кучу нового NODE
. Вы можете попытаться уменьшить издержки malloc, сохранив пул NODE
s и утилизировав их самостоятельно, но поскольку большинство современных реализаций malloc
хорошо справляются с утилизацией небольших выделенных ресурсов фиксированного размера, это преждевременная оптимизация.
Код связанного списка будет работать для простых операторов, но использование глобальной переменной для поддержки заголовка списка не очень масштабируемо. Это сделает невозможным синтаксический анализ составных операторов (например, условных выражений и циклов), поскольку в конечном итоге вы будете использовать root
для внутренних списков операторов. Лучше просто построить список естественным путем (который будет обратным) и полностью изменить его в конце:
stmts: stmt /* Default action is fine */
| stmts stmt { $$ = $2; $$->next = $1; }
line : stmts { $$ = reverse($$); }
В этом простом коде, каждый раз, когда stmts: stmts stmt
уменьшается, он помещает новый NODE в перед списка, что, очевидно, создает список в обратном порядке. Таким образом, когда line
уменьшается (что будет после того, как все stmt
были проанализированы), список необходимо поменять местами. Вы можете повернуть на место:
NODE* reverse(NODE* last) {
NODE* next = NULL;
while (last) {
NODE* tmp = last->next;
last->next = next;
next = last;
last = tmp;
}
return next;
}
Это полностью устраняет необходимость в глобальном root
, поэтому составная инструкция может быть правильно собрана.