Операционная система UNIX. Руководство программиста

     

Использование значений произвольных типов


По умолчанию предполагается, что значения, возвращаемые действиями и лексическим анализатором - целые. yacc поддерживает значения и других типов, в том числе структуры. Кроме того, yacc отслеживает типы и вставляет соответствующие имена элементов объединений, в результате чего получается строго типизированная процедура разбора. При описании стека значений yacc'у указывается объединение (union) всех ожидаемых типов значений. Пользователь задает это объединение и связывает имена элементов объединения с каждой лексемой и нетерминальным символом, у которых может быть значение. Когда на значение ссылаются при помощи конструкций $$ или $n, yacc автоматически вставляет имя соответствующего элемента объединения, так что лишних преобразований не происходит, и, кроме того, утилиты проверки типов, такие как lint(1), ведут себя лояльнее.

Чтобы обеспечить такие возможности типизации, используются три механизма. Во-первых, определение объединения. Оно должно быть сделано пользователем, так как другие подпрограммы, особенно лексический анализатор, должны знать имена элементов объединения. Во-вторых, связывание имени элемента объединения с лексемами и нетерминалами. Наконец, имеется механизм для описания типа тех немногих значений, которые yacc не в состоянии типизировать автоматически.

Чтобы описать объединение, пользователь включает конструкцию

%union { тело объединения

}

в секцию определений. Она означает, что элементы стека значений и внешние переменные yylval и yyval имеют тип, совпадающий с этим объединением. Если yacc вызывается с опцией -d, описание объединения копируется в файл y.tab.h в качестве YYSTYPE.

После того, как тип YYSTYPE определен, надо сопоставить имена элементов объединения именам лексем и нетерминальных символов. Для указания имени элемента объединения используется конструкция

<имя>

Если эта конструкция следует за одним из ключевых слов %token, %right, %left или %nonassoc, то имя элемента объединения сопоставляется лексемам из последующего списка. Так, запись


%left <optype> '+' '-'

означает, что любое обращение к значениям, возвращаемым этими двумя лексемами, снабжается тегом - именем элемента объединения optype. Еще одно ключевое слово %type используется для сопоставления имени элемента объединения нетерминальному символу. Чтобы сопоставить элемент объединения nodetype нетерминальным символам expr и stat, можно записать

%type <nodetype> expr stat

Есть несколько случаев, в которых описанных механизмов недостаточно. Если действие находится внутри правила, возвращаемое этим действием значение не имеет типа, заданного a priori. Аналогично, yacc'у не хватает информации для определения типа при ссылках на значения из левого контекста (таких как $0). В подобных ситуациях тип может быть задан явно ссылкой на имя элемента объединения, которое заключается в угловые скобки < и > и вставляется за первым знаком $. Пример

rule : aaa { $<intval>$ = 3; } bbb { fun ($<intval>2, $<other>0); } ;

иллюстрирует данную возможность. Синтаксис не назовешь "сахарным", но и требуется такая конструкция нечасто.

Средства, описанные в данном разделе, проиллюстрированы в более сложном из приводимых ниже примеров. Отметим, что контроль типов включается только при явном использовании типизации (например, если встречается конструкция %type) и контроль этот является весьма жестким. Так, считается ошибкой вхождение $n или $$

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




Содержание раздела