Разработка компиляторов

       

Функция yylex вычисляет пару значений,


Пример (продолжение)
int yylex (void) { int ch; while ((ch = getchar ()) == ' '); if (isdigit (ch)) { ungetc (c, stdin); scanf (%i, &yylval); return NUMBER_LC;} return ch; }
Начнем с описания функции yylex .
int yylex (void) { int ch; /* пропускаем пробелы в начале строки */ while ((ch = getchar ()) == ' ');
if (isdigit (ch)) { ungetc (ch, stdin); scanf (%i, &yylval); return NUMBER_LC; }
return ch; }
Функция yylex вычисляет пару значений, одно из которых лексический класс, а другое связанный с ним атрибут. Если лексический класс функция yylex возвращает в качестве своего значения, то атрибут передается анализатору через присваивание переменной yylval . Иначе говоря, то значение, которое присваивается переменной yylval , это значение терминального символа NUMBER.


В секции определений определен терминал NUMBER_LC, который имеет тип int, нетерминал expression, также имеющий тип int, и правила ассоциативности операций, которые могут быть использованы в формуле. Нетерминал lines не должен определяться в этой секции, поскольку он не имеет значения.
%union { int VALUE; }
%token <VALUE> NUMBER_LC
%type <VALUE> expression
%left '+' '-' %left '*' '/'
%start expression /* аксиома грамматики */
%%




Секция правил грамматики содержит правила для двух нетерминалов lines и expression. Правила для нетерминала lines порождают последовательность строчек входного потока, каждая из которых содержит одну формулу (нетерминал expression):
lines: lines expression '\n' { printf ("%I \n", &2); } | lines '\n' | /* empty */ ;
expression: NUMBER_LC { && = &1; } | '(' expression ')' {&& = &2; } | expression '+' expression {&& = &1+&2; } | expression '-' expression {&& = &1-&2; } | expression '*' expression {&& = &1*&2; } | expression '/' expression {&& = &1/&2; }
%%


Секция процедур содержит описание функций yylex, yyerror и, конечно, функции main. Хотя, как уже было сказано, эта секция может быть опущена, если все необходимые функции содержатся в некотором другом файле, который будет компилироваться отдельно.
Итак, секция процедур для нашего примера может выглядеть следующим образом. Для полноты картины описание функции yylex приводится вновь, но на этот раз без комментариев.
int yylex (void) { int ch; /* пропускаем пробелы в начале строки */ while ((ch = getchar ()) == ' ');
if (isdigit (ch)) { ungetc (ch, stdin); scanf ("%i", &yylval); return NUMBER_LC; }
return ch; }
yyerror (char *s) { printf ("error: %s", s); }
main () { return yyparse (); }


Для того, чтобы получить управляющую таблицу анализатора достаточно запустить программу YACC с ключом -v.
Рассмотрим фрагмент таблицы для состояния 2.
+------------------------- STATE 2 -------------------------+ + CONFLICTS: + RULES: lines : lines expression^\n expression : expression^+ expression expression : expression^- expression expression : expression^* expression expression : expression^/ expression + ACTIONS AND GOTOS: + : shift & new state 7 - : shift & new state 8 * : shift & new state 9 / : shift & new state 10 \n : shift & new state 6 : error
В первой строке фрагмента приведено название состояния. Секция CONFLICTS перечисляет встреченные конфликты (подробнее о конфликтах - см. в лекции 9). Секция RULES перечисляет все правила, задействованные в конфигурациях данного состояния (вместо символа точки, используемого в курсе, иcпользуется ^). Секция ACTIONS AND GOTOS представляет собой столбец управляющей таблицы анализатора, соответствующий 2-му состоянию. Подробнее о составлении управляющей таблицы можно узнать в лекции 7.


Пример (продолжение)
int yylex (void) { int ch; while ((ch = getchar ()) == ' '); if (isdigit (ch)) { ungetc (c, stdin); scanf (%i, &yylval); return NUMBER_LC;} return ch; }
Начнем с описания функции yylex .
int yylex (void) { int ch; /* пропускаем пробелы в начале строки */ while ((ch = getchar ()) == ' ');
if (isdigit (ch)) { ungetc (ch, stdin); scanf (%i, &yylval); return NUMBER_LC; }
return ch; }
Функция yylex вычисляет пару значений, одно из которых лексический класс, а другое связанный с ним атрибут. Если лексический класс функция yylex возвращает в качестве своего значения, то атрибут передается анализатору через присваивание переменной yylval . Иначе говоря, то значение, которое присваивается переменной yylval , это значение терминального символа NUMBER.


В секции определений определен терминал NUMBER_LC, который имеет тип int, нетерминал expression, также имеющий тип int, и правила ассоциативности операций, которые могут быть использованы в формуле. Нетерминал lines не должен определяться в этой секции, поскольку он не имеет значения.
%union { int VALUE; }
%token <VALUE> NUMBER_LC
%type <VALUE> expression
%left '+' '-' %left '*' '/'
%start expression /* аксиома грамматики */
%%


Секция правил грамматики содержит правила для двух нетерминалов lines и expression. Правила для нетерминала lines порождают последовательность строчек входного потока, каждая из которых содержит одну формулу (нетерминал expression):
lines: lines expression '\n' { printf ("%I \n", &2); } | lines '\n' | /* empty */ ;
expression: NUMBER_LC { && = &1; } | '(' expression ')' {&& = &2; } | expression '+' expression {&& = &1+&2; } | expression '-' expression {&& = &1-&2; } | expression '*' expression {&& = &1*&2; } | expression '/' expression {&& = &1/&2; }
%%


Секция процедур содержит описание функций yylex, yyerror и, конечно, функции main. Хотя, как уже было сказано, эта секция может быть опущена, если все необходимые функции содержатся в некотором другом файле, который будет компилироваться отдельно.
Итак, секция процедур для нашего примера может выглядеть следующим образом. Для полноты картины описание функции yylex приводится вновь, но на этот раз без комментариев.
int yylex (void) { int ch; /* пропускаем пробелы в начале строки */ while ((ch = getchar ()) == ' ');
if (isdigit (ch)) { ungetc (ch, stdin); scanf ("%i", &yylval); return NUMBER_LC; }
return ch; }
yyerror (char *s) { printf ("error: %s", s); }
main () { return yyparse (); }


Для того, чтобы получить управляющую таблицу анализатора достаточно запустить программу YACC с ключом -v.
Рассмотрим фрагмент таблицы для состояния 2.
+------------------------- STATE 2 -------------------------+ + CONFLICTS: + RULES: lines : lines expression^\n expression : expression^+ expression expression : expression^- expression expression : expression^* expression expression : expression^/ expression + ACTIONS AND GOTOS: + : shift & new state 7 - : shift & new state 8 * : shift & new state 9 / : shift & new state 10 \n : shift & new state 6 : error
В первой строке фрагмента приведено название состояния. Секция CONFLICTS перечисляет встреченные конфликты (подробнее о конфликтах - см. в лекции 9). Секция RULES перечисляет все правила, задействованные в конфигурациях данного состояния (вместо символа точки, используемого в курсе, иcпользуется ^). Секция ACTIONS AND GOTOS представляет собой столбец управляющей таблицы анализатора, соответствующий 2-му состоянию. Подробнее о составлении управляющей таблицы можно узнать в лекции 7.

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