2015-06-09 2 views
-1

я написал простой грамматики:Bison/Flex значение печати терминала с альтернативной

operations : 
    /* empty */ 
    | operations operation ';' 
    | operations operation_id ';' 
    ; 

operation : 
    NUM operator NUM 
    { 
     printf("%d\n%d\n",$1, $3); 
    } 
    ; 

operation_id : 
    WORD operator WORD 
    { 
     printf("%s\n%s\n%s\n",$1, $3, $<string>2); 
    } 
    ; 

operator : 
    '+' | '-' | '*' | '/' 
    { 
     $<string>$ = strdup(yytext); 
    } 
    ; 

Как вы можете видеть, я определил operator, который распознает один из 4-х символов. Теперь я хочу напечатать этот символ в operation_id. Проблема в том, что логика в operator работает только для последнего символа в альтернативе. Так что если я напишу a/b; печатает ab/ и это круто. Но для других операций, например. a + b; это принт aba. Что я делаю не так?

* I ommited новые символы линий в примере вывода.

ответ

3

Этот нетерминал из вашей грамматики просто неверен.

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:

  1. Производственные operator : '+' не имеет никакого явного действия, так что в конечном итоге, используя действие по умолчанию, которое $$ = $1 ,

  2. Однако правило lexer, которое возвращает '+' (предположительно - я угадываю здесь), никогда не устанавливает yylval. Таким образом, yylval по-прежнему имеет значение, которое было присвоено последнему.

  3. Предположительно (другое предположение), правило lexer, которое производит WORD, правильно устанавливает yylval.string = strdup(yytext);. Таким образом, семантическое значение токена '+' является семантическим значением предыдущего токена WORD, то есть указателя на строку "a".

  4. Так, когда правило

    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() будет вызываться дважды в одном блоке памяти, что является неопределенным поведением (и, вероятно, диагностировать ошибки).

Смежные вопросы