Бизон - передать значения для следующего производства
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 ;