[ Содержание ] [ Предыдущая ] [ Следующая ]
В этой главе обсуждается несколько дополнительных возможностей Yacc-а.
Действия парсера ошибка и принятие могут быть симулированы в коде действий, используя макросы YYACCEPT и YYERROR. YYACCEPT заставляет yyparse вернуть значение 0; YYERROR заставляет парсер вести себя так, как будто текущий символ был синтаксической ошибкой; вызывается yyerror и происходит исправление ошибки. Этот механизм может использоваться для симулирования парсеров с многочисленными маркерами конца и контестно-чувствительной проверки синтаксиса.
Действия могут обращаться к значениям, возвращаемым действиями слева от текущего правила. Механизм точно такой же, что и при обычных действиях, знак доллара с последующей цифрой, но в этом случае цифра может быть 0 или отрицательным числом. Предположим
sent : adj noun verb adj noun
/* look at the sentence ... */
;
adj : THE
{ $$ = THE; }
| YOUNG
{ $$ = YOUNG; }
. . .
;
noun : DOG
{ $$ = DOG; }
| CRONE
{ if( $0 == YOUNG )
printf( "what?\n" );
$$ = CRONE;
}
;
. . .
В действии, следующим за словом CRONE происходит проверка, не был ли предыдущий токен равен YOUNG. Очевидно, это возможно только если известно многое о том, что может быть впереди символа noun на входе. Это совершенно явно неструктурно. Однако, этот механизм в некоторых случаях избавит от больших хлопот, особенно когда необходимо исключить несколько нерегулярных комбинаций из в остальном регулярной структуры.
По умолчанию значения, возвращаемые действиями и лексическим анализатором - целые. Yacc может также поддерживать значения других типов, включая структуры. Вдобавок, Yacc следит за типами и подставляет соответствующие имена членов union, так что получившийся парсер строго типизирован. Стек значений Yacc-а объявлен как union различных типов желаемых значений. Пользователь объявляет union и связывает имена членов union с каждым токеном и нетерминальным символом, имеющим значение. Когда происходит ссылка на значение с помощью конструкции $$ или $n, Yacc автоматически вставляет соответствующее имя union-a, так что не произойдет нежелательных преобразований. Вдобавок, команды проверки типов, такие как Lint [5] будут вести себя значительно тише.
Существует три механизма для обеспечения этой типизации. Во-первых, можно определить union; это должно производиться пользователем, потому что остальные программы, особенно лексический анализатор, должны знать об именах членов union. Во-вторых, можно связать имя члена union с токенами и нетерминальными символами. Hаконец, существует механизм описания типа тех нескольких значений, для которых Yacc не может легко определить тип.
Для объявления union пользователь включает в секцию объявлений:
%union { body of union ... }
Это объявляет стек значений Yacc-а и внешние переменные yylval и yyval как имеющие тип, равный этому union. Если Yacc запущен с опцией -d, объявление union-a копируется в файл yytab.h. С другой стороны union может быть объявлен в заголовочном файле и используется typedef для определения переменной YYSTYPE, представляющей этот union. Таким образом, в заголовочном файле может быть также сказано:
typedef union
{
body of union ...
} YYSTYPE;
Заголовочный файл должен быть включен в секцию объявлений с помощью %{ и %}.
Когдя определен YYSTYPE, имена членов union-a должны быть связаны с различными терминальными и нетерминальными именами. Конструкция
используется для обозначения имени члена union-a. Если эта конструкция следует за одним из ключевых слов %token, %left, %right и %nonassoc, имя члена union связывается с перечисленными токенами. Таким образом,
%left '+' '-'
приведет к тому, что любая ссылка на эти вда токена будут помечены именем члена union-a "optype". Другое ключевое слово %type используется подобным образом для ассоциирования имен членов union-a с нетерминальными символами. Таким образом, можно сказать
%type <nodetype> expr stat
Остается несколько случаев, где этих механизмов недостаточно. Если внутри правила есть действие, то значение, возвращенное этим действием не имеет типа по умолчанию. Подобным образом ссылка на лево-контекстные значения (такие как $0 - см. предыдущий параграф) оставляет Yacc-y нелегкий путь для выяснения типа. В этом случае ссылка может быть типизирована с помощью вставления имени члена union-a между < и >, сразу после начального $. Примером такого использования является
rule : aaa
{ $<intval>$ = 3; }
bbb
{ fun( $<intval>2, $<other>0 ); }
;
Синтаксис не самый удачный, но подобная ситуация возникает редко.
Пример спецификации дан в Приложении C. Средства из этой подсекции не включаются, пока не будут использованы: в частности, использование %type включит эти механизмы. Когда они используются, метод проверки вполне строг. Hапример, использование $n или $$ для обращения к чему-то с неопределенным типом диагностируется. Если эти средства не включены, стек значений Yacc-а используется для хранения целых, как это было исторически.
[ Содержание ] [ Предыдущая ] [ Следующая ]
c 1998-2000 SoloTony (Antonio Solo) | solotony@mail.ru |