Этот нетерминал из вашей грамматики просто неверен.
operator :
'+' | '-' | '*' | '/' { $<string>$ = strdup(yytext); }
;
Во-первых, в yacc/bison каждое производство имеет действие. Это правило состоит из четырех производств, из которых только последнее имеет связанное действие. Было бы понятнее написать это:
operator : '+'
| '-'
| '*'
| '/' { $<string>$ = strdup(yytext); }
;
, что делает его немного более очевидным, что действие относится только к сокращению от маркера '\'
.
Само действие также неверно. yytext
никогда не должен использоваться за пределами lexer действий, поскольку его значение не является надежным; это будет значение в то время, когда было предпринято самое последнее действие lexer, но так как синтаксический анализатор обычно (но не всегда) читает один маркер вперед, он обычно (но не всегда) будет строкой, связанной с токеном следующего. Вот почему обычный совет заключается в том, чтобы сделать копию yytext
, но идея состоит в том, чтобы скопировать его в правило lexer, присвоив копию соответствующему члену yylval
, чтобы синтаксический анализатор мог использовать семантическое значение токена.
Вам следует избегать использования $<type>$ =
. Не-терминал может иметь только один тип, и он должен быть объявлен в прологе к бизона файла:
%type <string> operator
Наконец, вы обнаружите, что это очень редко полезно иметь не-терминал, который распознает разные потому что разные операторы синтаксически различны. В более полной грамматике выражения вам нужно будет различать a + b * c
, который является суммой a и произведением b и c, и a * b + c
, что является суммой c и произведением a и b. Это может быть сделано с использованием разных нетерминалов для синтаксиса суммы и продукта или с использованием разных производств для выражения без терминалов и устранения неоднозначности с помощью правил приоритета, но в обоих случаях вы не сможете использовать operator
non-terminal который производит +
и *
без разбора.
За что его стоит, вот объяснение того, почему a+b
приводит к выходу aba
:
Производственные operator : '+'
не имеет никакого явного действия, так что в конечном итоге, используя действие по умолчанию, которое $$ = $1
,
Однако правило lexer, которое возвращает '+'
(предположительно - я угадываю здесь), никогда не устанавливает yylval
. Таким образом, yylval
по-прежнему имеет значение, которое было присвоено последнему.
Предположительно (другое предположение), правило lexer, которое производит WORD
, правильно устанавливает yylval.string = strdup(yytext);
. Таким образом, семантическое значение токена '+'
является семантическим значением предыдущего токена WORD
, то есть указателя на строку "a"
.
Так, когда правило
operation_id :
WORD operator WORD
{
printf("%s\n%s\n%s\n",$1, $3, $<string>2);
}
;
исполняет, $1
и $2
оба имеют значение "a"
(два указателя на ту же строку), и $3
имеет значение "b"
.
Понятно, что для $2
явно имеет значение "a"
, но есть еще одна ошибка, ожидающая возникновения. Как написано, ваш парсер утечки памяти, потому что вы никогда не free()
любой из строк, созданных strdup
. Это не очень удовлетворительно, и в какой-то момент вам нужно будет исправить действия, чтобы смысловые значения освобождались, когда они больше не требуются. В этот момент вы обнаружите, что наличие двух семантических значений, указывающих на один и тот же блок выделенной памяти, очень вероятно, что free()
будет вызываться дважды в одном блоке памяти, что является неопределенным поведением (и, вероятно, диагностировать ошибки).