Опубликован: 23.07.2006 | Доступ: свободный | Студентов: 2213 / 889 | Оценка: 4.28 / 4.17 | Длительность: 21:37:00
Специальности: Системный архитектор
Лекция 8:

Грамматики и YACC

Секция правил грамматики

Секция правил грамматики

A: production_body
     {
             program_fragment;
      }
   ;

Секция грамматических правил состоит из правил, которые записываются следующим образом:

A: production_body;

где A - имя нетерминала, production_body -последовательность нуля или большего количества имен и литералов.

Имена могут быть произвольной длины и содержать буквы, цифры (как обычно, цифра не может быть первой литерой имени), подчеркивания и точки. Литерал состоит из литер, заключенных в апострофы. Как и в языке C, литера обратная косая черта (backslash) используется для задания управляющей последовательности (escape sequence).

Если имеется несколько грамматических правил с одинаковой левой частью, то может использовать литера вертикальная черта для объединения всех правил в одно:

A:  production_body_1
   |  production_body_2
  ;

Заметим, что каждое имя, не объявленное как терминал, считается нетерминалом. Каждый нетерминал должен появиться в левой части, хотя бы одного правила. Нетерминал, являющийся левой частью первого правила, по умолчанию считается аксиомой. Вообще говоря, аксиому можно определить в секции объявлений как:

%start axiom.

Семантики

Семантики

Nonterminal:  production_body_1
                       { semantic_action_1 }
                     |
                    . . .                        
                       production_body_n
                       { semantic_action_n }
                     ;

Грамматические правила могут содержать так называемые семантики (semantic actions), представляющие собой фрагменты программ на языке С, заключенные в фигурные скобки. Например,

lines: lines expr '\n'
           {
printf ("%s\n", %2); 
           }

Нетерминал может иметь значение некоторого типа, который указан в объединении, определенном в секции объявлений. Для этого

  1. нетерминал должен быть объявлен следующим образом:
    %type <имя-вида> имя-нетерминала

    Причем, в объединении должен быть элемент вид имя-вида. Например,

    %union
    {
    ...
    unsigned short int myCounter;
    ...
    } 
    %type <myCounter> counter
    %%
  2. Семантика правил, левой частью которых является этот нетерминал, должна содержать оператор: $$ = значение;

Заметим, что значение может иметь не только нетерминалы, но и терминальные символы. В этом случае терминал должен быть объявлен следующим образом: %type <имя-вида> имя-нетерминала

Естественно, в объединении должен быть элемент вид имя-вида. Например,

%union {... signed int myValue;...}
%token <myValue> NUMBER_LC

Семантики (продолжение)

Поскольку нетерминал, может иметь значение, то если такой нетерминал находится в правой части правила, мы можем воспользоваться его значением. Все имена и литералы, содержащиеся в правой части правила нумеруются слева направо, начиная с единицы. Для того, чтобы воспользоваться значением нетерминала следует написать $номер-нетерминала, например:

T: F { &&=&1; }
   | T*F { && = &1*&2; }
   ;

Заметим, что по умолчанию значение правила и тем самым значение нетерминала, находящегося в его левой части, - это значение первого элемента в нем. Таким образом, предыдущий пример может быть переписан:

T:  F 
   |  T*F { && = &1*&2; }
   ;

На самом деле, семантики могут быть использованы не только в конце правила, но и в середине. Например,

A: B { && = 1; }  
     C { x = &2; y = &3; }
    ;

Правда, при этом надо иметь в виду, что семантики, которые не завершают правило, обрабатываются путем введения нового нетерминала и нового правила, сопоставляющего этот нетерминал пустой строке. Т.е. на самом деле приведенное выше правило эквивалентно следующему:

&&1:   /* пустая правая часть */  { && = 1; }
      ;
A:    B &&1 C { x = &2; y = &3; }
      ;