- •1. Принципы работы yacc
- •2. Входные и выходные файлы, структура грамматического ана-
- •3. Процедура построения грамматического анализатора
- •4. Задание входной информации yacc
- •4.1. Структура спецификационного файла
- •4.2. Секция правил
- •4.3. Секция деклараций
- •5. Декларация имен лексем
- •6. Декларация приоритетов и ассоциативности лексем
- •7. Декларация имени начального символа
- •7.1. Секция программ
- •Val. Если функция yylex находится в отдельном файле, то эта
- •7.2. Действия с использованием псевдопеременных
- •8. Конфликты грамматического разбора
- •If '(' условие ')' оператор else
- •9. Структура информационного входного файла y.Output
- •10. Обработка ошибок при грамматическом разборе
- •11. Диагностика ошибок
- •0, Считаются восьмеричными, все остальные - десятичными.
- •Int base, regs[26]; /* в действиях */
- •1. Принципы работы yacc .............................. 3
Val. Если функция yylex находится в отдельном файле, то эта
переменная должна быть объявлена:
extern int yylval;
- 16 -
Уточним, что в дальнейшем значением лексемы мы будем
называть значение, присвоенное при ее распознавании перемен-
ной yylval; значение, возвращаемое функцией yylex, является
номером типа лексемы. Примером значения лексемы могут слуить
числовое значение цифры, вычисленное значение константы,
адрес идентификатора в таблице имен (построение таблицы имен
удобно осуществлять лексическим анализатором). Заметим,
что, хотя значение yylval устанавливается с целью использо-
вания его в действиях, непосредственное обращение к переМен-
ной yylval в действии не имеет смысла (поскольку в yylval
всегда находится значение последней выделенной лексемы).
Доступ в действиях к значениям лексем осуществляется с
помощью специального механизма, описанного в разделе 4.5.
7.2. Действия с использованием псевдопеременных
Для обеспечения связи между действиями, а также между
действиями и лексическим анализатором создаваемые yacc грам-
матические анализаторы поддерживают специальный стек, в
котором сохраняются значения лексем и нетерминальных симво-
лов. Значение лексемы автоматически попадает в стек после
ее распознавания лексическим анализатором (напомним, что им
считается текущее значение переменной yylval). После каждой
свертки вычисляется значение нетерминала, заместившего свер-
нутую строку, и помещается в вершину стека; значения элемен-
тов правой части примененного правила перед этим выталкива-
ются из стека. Заметим, что таким образом к моменту свертки
любого правила все значения нетерминалов в правой части ока-
зываются вычисленными в результате сверток. Способ вычисле-
ния значения нетерминала будет рассмотрен ниже.
Описанный механизм не требует вмешательства пользова-
теля и предоставляет ему следующие возможности:
- Использовать в действиях, осуществляемых после свертки
по правилу, значение любого элемента его правой части.
Доступ к этим значениям обеспечивается набором так
называемых псевдопеременных с именами $1,$2,..., где $i
соответствует значению i-го элемента; элементы правой
части правила нумеруются слева направо без различия
лексем и нетерминальных символов, например, в правиле
P_Head: P_name '(' P_list ')';
псевдопеременные $1,$2,$3,$4 относились бы соответст-
венно к P_name, '(', P_list, ')'.
- Формировать в действиях значение, образованного в
результате свертки нетерминала путем присвоения этого
значения псевдопеременной с именем $$. Так, в правиле
- 17 -
expr: expr '+' expr { $$=$1+$3; }
значением нового нетерминала expr станет сумма ранее
вычисленных значений двух других нетерминалов expr.
Eсли в действии не определяется значение переменной $$
(а также если действие отсутствует), значением нетерми-
нала после свертки по умолчанию становится значение
первого элемента правой части, т.е. неявно выполняется
присваивание
$$=$1;
Пример (вычисление значения целого числа)
%token DIGIT
%%
...
CONST: DIGIT
| CONST DIGIT {$$=$1*10+$2;}
...
%%
yylex()
{
char c;
if((c=getchar())>='0'&& c<='9') {
yylval = c-'0';
return (DIGIT);
}
...
}
Здесь при свертке по первому правилу нетерминал CONST
получает значение первой цифры, присвоенное в функции
yylex переменной yylval; при каждой свертке по второму
правилу явно вычисляется значение нового нетерминала
CONST.
Несколько иная ситуация в отношении использования псев-
допеременных имеет место для правил, содержащих действия
внутри правой части. На самом деле yacc интерпретирует пра-
вило вида
A: B C {действие 1} D {действие 2}
K {действие 3}
как
A: B C EMPTY1 D EMPTY2 K;
EMPTY1: {действие 1}
EMPTY2: {действие 2}
и в месте, где вставлено действие, при разборе
- 18 -
осуществляется свертка по пустому правилу, сопровождающаяся
выполнением указанного действия. При этом независимо от
характера действия очередной элемент в стеке значений отво-
дится для хранения значения неявно присутствующего "пустого"
нетерминала. В действии, находящемся в конце правила, согла-
шение о значениях псевдопеременных остается прежним, если
иметь в виду наличие дополнительных символов. В приведенном
выше правиле в действии 3 для доступа к значениям элементов
B, C, D, K следовало бы использовать соответственно псевдо-
переменные $1, $2, $4, $6; псевдоперемнные $3, $5 хранят
результаты действий 1 и 2. В действиях, находящихся внутри
правила, с помощью псевдоперемнных $i доступны значения рас-
положенных левее элементов, а также результаты предшествую-
щих вставленных в тело действий. Результатом внутреннего
действия (т.е. значением неявного нетерминала) является зна-
чение, присвоенное в этом действии псевдопеременной $$, при
отсутствии такого присваивания результат действия не опреде-
лен. Заметим, что присваивание значения псевдопеременной $$
во внутренних действиях не вызывает предварительной уста-
новки значения нетерминала, стоящего в левой части правила:
это значение в любом случае устанавливется только действием
в конце правила или считается равным значению $1.
В нашем примере в действии 1 доступными являются только
значения элементов B и C (им соответствуют псевдопеременные
$1 и $2), а в действии 2 - значения элементов B, C, D и
результат действия 1 с помощью псевдопеременных $1, $2, $4,
$3.
Следующий пример иллюстрирует варианты использования
псевдопеременных:
%token ИМЯ КЛЮЧ1 КЛЮЧ2 КОНЕЦ
. . .
%%
входной_поток: данные КОНЕЦ
{printf("Данные занесены в файл %s\n",$1);};
данные:
ИМЯ {abc = creat($1,0666);}
КЛЮЧ1 КЛЮЧ2 {option($3,$4);}
упр_строка '\n' {converse(0,$5,$6);
write($1,$6,80);}
текст {converse(1,$5,$8);
write($1,$6,$8);
close($1);};
упр_строка: ...
текст : ...
. . .
- 19 -
Управляющая строка и текст преобразуются в соответствии с
заданными ключами и записываются в файл с указанным именем.
Значением нетерминала данные в результате неявного действия
становится значение лексемы ИМЯ (адрес строки с именем файла
присваивается в лексическом анализаторе переменной yylval).