Бизон - передать значения для следующего производства

field_dec: type id_list        ;                                               

id_list: ID punct id_list                                             
         | ID SQUARE_OPEN INTEGER SQUARE_CLOSED punct id_list
         | ID punct
         | ID SQUARE_OPEN INTEGER SQUARE_CLOSED punct
 ;

type: INT | BOOLEAN
;

punct: COMMA | SEMICOLON
;

У меня есть грамматика бизонов, как указано выше, и я пытаюсь создать AST для моего языка программирования, используя эту грамматику. Эта грамматика предназначена для анализа объявлений полей, таких как:

int a;

int a, b, c[10], d;

int a[10];

Я хотел спросить, как я могу передать значение "type" нетерминальному парсеру бизонов, когда оно достигнет производства id_list,

Я хочу сказать, что когда я в id_list На этапе анализа я хочу иметь тип каждого идентификатора, так как мне нужно сохранить его в узле идентификатора. Я знаю, что мы можем передать $$ ценность для верхних производств, но как передать некоторую ценность производствам, которые идут после производства на этапе анализа.

После публикации вопроса я узнал, что мы можем использовать $n с n < 0, но я не могу найти хороший ресурс в сети, чтобы прочитать об этом, а также я попытался проверить $n с n < 0, но это просто дает мне следующую ошибку:

bison.y:125.45-46: error: $0 of ‘callout_arg’ has no declared type         
             | STRING {printf("testing %s\n", $0);};       
                                              ^^

а также

bison.y:124.43-45: error: $-1 of ‘callout_arg’ has no declared type
 callout_arg: expr {printf("testing %s\n", $-1);}
                                           ^^^

2 ответа

Вместо того, чтобы использовать правую рекурсию для определения id_list, что практически никогда не то, что вы хотите, вы можете достичь желаемого эффекта с помощью леворекурсивного правила.

В следующей грамматике тип, связанный с декларатором, всегда доступен как семантическое значение самого декларатора. Я также исправил обработку знаков препинания: только запятые разделяют идентификаторы, в то время как только точки с запятой заканчивают объявления.

Чтобы тип всегда был доступен, необходимо "денормализовать" грамматику, что приводит к определенному количеству повторений. Ниже приведена немного измененная версия.

decl_list: type ID                  { assign_type($1, $2); }
         | type ID '[' INTEGER ']'  { assign_array_type($1, $2, $4); }
         | decl_list ',' ID         { assign_type($1, $3); }
         | decl_list ',' ID '[' INTEGER ']'
                                    { assign_array_type($1, $3, $5};

field_dec: decl_list ';'

Для простоты я предположил, что все семантические значения являются просто строками, но на практике вы, вероятно, захотите определенные типы, представляющие "тип" и "идентификатор". Кроме того, было бы намного чище избежать чрезмерного повторения всех возможных синтаксисов объявления, поскольку вполне может быть более двух вариантов. Это также может быть достигнуто с помощью пользовательского семантического типа.

Вот немного более сложная версия:

%{
#include <stdbool.h>

enum Type {BOOLEAN, INTEGER};
struct Declarator {
    const char* id;
    bool        is_array;
    int         dimension;
}
%}

%union {
    enum Type         type;
    struct Declarator decl;
    const char*       id;
    long              number;
}

%{
   /* Pass semantic values by reference to avoid copying,
      since the struct Declarator is a bit big.
    */
   void assign_type_from_decl(const char** typ,
                              struct Declarator* dec) {
     if (dec->is_array)
       assign_array_type(*typ, dec->id, dec->dimension);
     else
       assign_type(*typ, dec->id);
   }
%}

%token <id>     T_ID
%token <number> T_INTEGER
%token          T_INT     "int"
                T_BOOLEAN "boolean"
%type <type> decl_list
%type <decl> declarator

%%

type     : "boolean"                { $$ = BOOLEAN; }
         | "int"                    { $$ = INTEGER; }
field_dec: decl_list ';'
decl_list: type declarator          { assign_type(&$1, &$2); }
         | decl_list ',' declarator { assign_type(&$1, &$3); }
declarator: ID                      { $$.id = $1;
                                      $$.is_array = false;
                                    }
         | ID '[' INTEGER ']'       { $$.id = $1;
                                      $$.is_array = true;
                                      $$.dimension = $3;
                                    }

Можно было бы заменить использование структуры описателя, используя стек "up-reference", как указано в вопросе. Эти ссылки, на мой взгляд, немного хрупкие, так как они зависят от глобальных знаний о контексте, в котором может происходить производство, и бизон не проверяет их использование. Одним из следствий отсутствия проверки является необходимость указания тега семантического типа при использовании такой ссылки, факт, который неявно упоминается в руководстве по бизонам, поэтому вы получаете сообщение об ошибке от бизона.

Убедиться, что стек правильно настроен для семантического действия, довольно сложно, и обычно требуется использование маркеров (или действий среднего правила). Следующий пример адаптирован из руководства bison с добавлением необходимого явного синтаксиса типа:

 %type <number> sum expr retainer
 %%

 sum:
   expr retainer '+' expr  { ... }
 | expr retainer '-' expr  { ... }
 ;

 retainer:
   /* empty */    { previous_expr = $<number>0; }
 ;

Вы можете ссылаться на предыдущие значения в стеке значений с помощью $<type>0 или же $<type>-N, хотя вы должны быть осторожны, чтобы всегда правильно понимать тип. В вашем случае вы, вероятно, хотите что-то вроде:

%type<type> type
    ... other type declarations for productions

%%

field_dec: type id_list ';' ;

id_list: id_decl { $$ = make_list($1); }
       | id_list ',' { $<type>$ = $<type>0; } id_decl { $$ = append_list($1, $3); }
       ;

id_decl: ID { $$ = declare_id($1, $<type>0; }
       | ID '[' INTEGER ']'
         { $$ = declare_id($1, get_array_type($<type>0, $3)); }
       ;

type: INT { $$ = &int_type; } | BOOLEAN { $$ = &bool_type; }

field_dec_list : /* empty */ | field_dec_list field_dec ;

Если вы используете btyacc, у него есть синтаксический сахар, чтобы сделать это проще (и более безопасным):

%type<type>  type
%type<???>   id_decl(<type>)
%type<list>  id_list(<type>)    
    ... other type declarations for other productions

%%

field_dec: type id_list($1) ';' ;

id_list($t): id_decl($t) { $$ = make_list($1); }
           | id_list($t) ',' id_decl($t) { $$ = append_list($1, $3); }
       ;

id_decl($t): ID { $$ = declare_id($1, $t); }
           | ID '[' INTEGER ']' { $$ = declare_id($1, get_array_type($t, $3)); }
           ;

type: INT { $$ = &int_type; } | BOOLEAN { $$ = &bool_type; }

field_dec_list : /* empty */ | field_dec_list field_dec ;
Другие вопросы по тегам