Материал: 12562633_1109924Ispol_zo

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Простое приложение – калькулятор

Рассмотрим простое приложение – арифметический калькулятор, вычисляющий произвольные арифметические выражения с любым уровнем вложенности скобок. В этом приложении действия, выполняемые при синтаксическом разборе, выполняются с компонентами правил, соответствующий компонент во вставке на языке Си (она производится в фигурных скобках) представлен своим значением, находящимся в стеке. Тип элемента стека в этом варианте – удвоенной точности вещественное число. Значение компонента обозначается метасимволом $N, где цифра N обозначает номер компонента в правиле. Метасимволом $$ обозначается верхушка стека.

Листинг 6. Калькулятор со скобками

/* Пример калькулятора */

%{ /* вставка определения на языке Си */

#define YYSTYPE double

/* тип данных в стеке yacc */

#define QUIT 101010

%}

%token NUMBER /* лексема число */

%left '+' '-' /* левоассоциативные операции */

%left '*' '/' /* левые опер.,больше приоритет */

%left UNARYMINUS./* унарный минус */

%% /* СЕКЦИЯ ПРАВИЛ */ list: /* пусто */

{ prompt(); }

| list '\n' /* список выражений */

{prompt(); }

| list expr '\n'

{if ($2 == QUIT) { return(0);

} else {

fprintf(stdout, "RESULT> %.8g\n", $2); prompt();

}

}

| list error '\n'

{ yyerrok; prompt();

}

;

expr: /* правило для нетерминала выражение */

NUMBER { $$ = $1; }

| '-' expr %prec UNARYMINUS { $$ = -$2; }

| expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 – $3; }

31

| expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; }

| '(' expr ')' { $$ = $2; }

;

/* конец секции */

%%

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

char *progname; /* для сообщения об ошибках */ int lineno = 1;

main(argc, argv) char *argv[];

{

if (argc > 1)

fprintf(stderr, "лишние аргументы\n"); progname = argv[0];

fprintf(stdout,

"\n********************************************");

fprintf(stdout,

"\n* Простой калькулятор *"); fprintf(stdout,

"**********************************************"); putchar ('\n');

yyparse();

fprintf(stdout, "* До свиданья! *\n");

}

yylex() /* лексический анализатор */

{

int c;

while ((c=getchar()) == ' ' || c == '\t');

/* пропустить пробельные символы */ if (c == EOF) return 0;

if (c == '.' || isdigit(c)) { /* число */ ungetc(c, stdin);

scanf("%lf", &yylval); return NUMBER;

}

if (c == '\n') lineno++;

if (c == 'Q' || c == 'q') /* плохой код! */ if ((c=getchar()) == 'U' || c == 'u')

if ((c=getchar()) == 'I' || c == 'i') if ((c=getchar()) == 'T' || c == 't')

{

yylval = QUIT; return NUMBER;

} else return '?';

32

return c;

}

yyerror(s) /* вызывается при ошибках yacc */ char *s;

{

warning (s, (char *) 0);

}

warning(s,t) /* предупреждение */

char *s, *t;

{

fprintf(stderr, "%s: %s", progname, s); if (t) fprintf(stderr, " %s", t);

fprintf(stderr, " near line %d\n", lineno);

}

prompt() /* приглашение к вводу выражения */

{

fprintf(stdout, "READY> ");

}

Лабораторная работа 7

Добавим функции в калькулятор

Расширить простой калькулятор, добавив в него тригонометрические функции (или exp, log, abs).

Требования

Два часа на выполнение, языки реализации – уасс и С. Модифицировать грамматику, вставки на языке С и дополнить лек-

сический анализатор. В секции описаний добавить лексемы SIN, COS, TAN, ATAN и т.д.

Алгоритмизация задачи

Можно использовать функции стандартной библиотеки С для вычисления функций. В лексическом распознавателе придется анализировать символьные строки, выявляя последовательности символов «sin», «cos», etc…

Калькулятор с функциями и константами

В этот калькулятор добавлены переменные (именованные регистры), функции и константы. В стеке теперь может находиться либо имя (Sym), либо значение (val), для этого элемент стека описывается объединением (союзом) %union.

33

Листинг 7. Калькулятор с переменными и функциями

%{

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

#include <string.h>

#include <math.h> int yylex();

double Sqrt (double x); // своя функция sqrt typedef struct Symbol // структура для описания

{ // переменных, констант и char name; short type; // функций type задает union { /* тип объекта */

double val; double (*ptr)(); }u;

struct Symbol *next;

} Symbol;

Symbol *symlist =0; static struct { char *name;

double cval; } Const[]= /* список констант */

{

"PI", 3.1415926536,

 

"E", 2.718281828459,

 

"RAD",57.2958,

 

0,0

 

};

 

static struct {

 

char *name;

 

double (*func)(); }

 

Funcs[]=

/* список функций */

{

 

"sin",sin,

 

"cos",cos,

 

"tan",tan,

 

"sqrt",Sqrt,

 

"abs",fabs,

 

0,0

 

};

 

%}

 

%union {

/* тип элемента стека */

double val;

 

Symbol *sym;

 

};

 

%token <val> NUM

/* лексемы */

%token <sym> VAR FUNC UNDF

 

34

%type <val> expr assign

 

%type <val> prog

 

%right '='

/* операции */

%left '+' '-' %left '*' '/'

%right '^'

%left UNMIN

%%

prog:

'\n' { printf("!\n"); }

| expr '\n'{ $$=$1; printf("=>%lf\n",$$); }

| prog expr '\n' { $$=$2; printf("->%lf\n",$$); }

;

assign: VAR '=' expr

{ $$ = $1->u.val=$3; $1->type=VAR; }

;

expr: NUM { $$ = $1; }

| VAR { $$ = $1->u.val; }

| FUNC '(' expr ')' { $$ = (($1->u.ptr)($3)); }

| expr '+' expr { $$ = $1+$3; }

| expr '-' expr { $$ = $1 – $3; }

| expr '*' expr { $$ = $1*$3; }

| expr '/' expr { if ($3!=0) $$ = $1/$3;

else { printf(" Zero divide\n"); $$=$1; }

}

| expr '^' expr { $$ = pow($1,$3); }

| '-' expr %prec UNMIN { $$ = -$2; }

| assign /* присваивание */

| '(' expr ')' { $$ = $2; }

;

%%

Symbol *install(char *s, short t, double d)

{ /* добавление нового элемента в список */

Symbol *sp;

sp = (Symbol *) malloc(sizeof(Symbol)); sp->name = (char *)malloc(strlen(s)+1); strcpy(sp->name,s);

sp->type = t; sp->u.val=d; sp->next=symlist; symlist=sp;

return sp;

}

Symbol *lookup(char *s)

{ /* поиск в списке */

Symbol *sp;

for (sp=symlist; sp!=(Symbol *)0; sp=sp->next) if (strcmp(sp->name,s)==0)

{

return sp;

}

35

Источник: https://studfile.net/preview/16710218/