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

       

Простой пример


Этот пример - полная yacc-спецификация, реализующая небольшой калькулятор; калькулятор имеет 26 регистров, помеченных буквами от a до z, и обрабатывает арифметические выражения, построенные при помощи операций

+, -, *, /, % (остаток от деления), & (побитное и), | (побитное или)

и присваиваний. Если на вход калькулятору дается присваивание, оно выполняется; в противном случае выводится значение выражения. Как и в языке C, целая константа, если она начинается с 0 (нуль), трактуется как восьмеричная, иначе - как десятичная.

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

%{

#include <stdio.h> #include <ctype.h>

int regs [26]; int base;

%}

%start list

%token DIGIT LETTER

%left '|' %left '&' %left '+' '-' %left '*' '/' '%' %left UMINUS /* устанавливает приоритет унарного минуса */

%% /* начало секции правил */

list : /* пусто */ | list stat '\n' | list error '\n' { yyerrok; } ;

stat : expr { (void) printf ("%d\n", $1); } | LETTER '=' expr { regs [$1] = $3; } ;

expr : '(' expr ')' { $$ = $2; } | expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; } | expr '%' expr { $$ = $1 % $3; } | expr '&' expr { $$ = $1 & $3; } | expr '|' expr { $$ = $1 | $3; } | '-' expr %prec UMINUS { $$ = - $2; } | LETTER { $$ = regs[$1]; } | number ;

number : DIGIT { $$ = $1; base = ($1==0) ? 8 : 10; } | number DIGIT { $$ = base * $1 + $2; } ;

%% /* начало секции подпрограмм */

int yylex () /* процедура лексического анализа */ { /* возвращает значение LETTER для */ /* строчной буквы, yylval = от 0 до 25, */ /* возвращает значение DIGIT для цифры, */ /* yylval = от 0 до 9, для остальных */ /* символов возвращается их значение */

int c;

/* пропуск пробелов */ while ((c = getchar ()) == ' ') ;

/* c - не пробел */ if (islower (c)) { yylval = c - 'a'; return (LETTER); } if (isdigit (c)) { yylval = c - '0'; return (DIGIT); } return (c); }



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