7.60.
Разработайте простую версию препроцессора для обработки операторов #include.
В качестве прототипа такой программы можно рассматривать такую (она понимает директивы вида #include имяфайла - без <> или "").
#include <stdio.h>
#include <string.h>
#include <errno.h>
char KEYWORD[] = "#include "; /* with a trailing space char */
void process(char *name, char *from){
FILE *fp;
char buf[4096];
if((fp = fopen(name, "r")) == NULL){
fprintf(stderr, "%s: cannot read \"%s\", %s\n",
from, name, strerror(errno));
return;
}
while(fgets(buf, sizeof buf, fp) != NULL){
if(!strncmp(buf, KEYWORD, sizeof KEYWORD - 1)){
char *s;
if((s = strchr(buf, '\n')) != NULL) *s = '\0';
fprintf(stderr, "%s: including %s\n",
name, s = buf + sizeof KEYWORD - 1);
process(s, name);
} else fputs(buf, stdout);
}
fclose(fp);
}
int main(int ac, char *av[]){
int i;
for(i=1; i < ac; i++)
process(av[i], "MAIN");
return 0;
}
7.61.
Разработайте простую версию препроцессора для обработки операторов #define.
Сначала реализуйте макросы без аргументов. Напишите обработчик макросов вида
#macro имя(аргу,менты)
тело макроса - можно несколько строк
#endm
7.62.
Напишите программу, обрабатывающую определения #ifdef, #else, #endif. Учтите,
что эти директивы могут быть вложенными:
#ifdef A
# ifdef B
... /* defined(A) && defined(B) */
# endif /*B*/
... /* defined(A) */
#else /*not A*/
... /* !defined(A) */
# ifdef C
... /* !defined(A) && defined(C) */
# endif /*C*/
#endif /*A*/
7.63.
Составьте программу моделирования простейшего калькулятора, который считывает
в каждой строчке по одному числу (возможно со знаком) или по одной операции сложения
или умножения, осуществляет операцию и выдает результат.
7.64.
Составьте программу-калькулятор, которая производит операции сложения, вычитания, умножения, деления; операнды и знак арифметической операции являются строковыми
аргументами функции main.
7.65.
Составьте программу, вычисляющую значение командной строки, представляющей
собой обратную польскую запись арифметического выражения. Например, 20 10 5 + *
вычисляется как 20 * (10 + 5) .
7.66.
Составьте функции работы со стеком:
- добавление в стек
- удаление вершины стека (с возвратом удаленного значения)
Используйте два варианта: стек-массив и стек-список.
7.67.
Составьте программу, которая использует функции работы со стеком для перевода
арифметических выражений языка Си в обратную польскую запись.
/*#!/bin/cc $* -lm
* Калькулятор. Иллюстрация алгоритма превращения выражений
* в польскую запись по методу приоритетов.
*/
#include <stdio.h>
#include <stdlib.h> /* extern double atof(); */
#include <math.h> /* extern double sin(), ... */
#include <ctype.h> /* isdigit(), isalpha(), ... */
#include <setjmp.h> /* jmp_buf */
jmp_buf AGAIN; /* контрольная точка */
err(n){ longjmp(AGAIN,n);} /* прыгнуть в контрольную точку */
/* ВЫЧИСЛИТЕЛЬ --------------------------------------- */
/* Если вместо помещения операндов в стек stk[] просто
* печатать операнды, а вместо выполнения операций над
* стеком просто печатать операции, мы получим "польскую"
* запись выражения:
* a+b -> a b +
* (a+b)*c -> a b + c *
* a + b*c -> a b c * +
*/
/* стек вычислений */
#define MAXDEPTH 20 /* глубина стеков */
int sp; /* указатель стека (stack pointer) */
double stk[MAXDEPTH];
double dpush(d) double d; /* занести число в стек */
{
if( sp == MAXDEPTH ){ printf("Стек операндов полон\n");err(1);}
else return( stk[sp++] = d );
}
double dpop(){ /* взять вершину стека */
if( !sp ){ printf("Стек операндов пуст\n"); err(2); }
else return stk[--sp];
}
static double r,p; /* вспомогательные регистры */
void add() { dpush( dpop() + dpop()); }
void mult() { dpush( dpop() * dpop()); }
void sub() { r = dpop(); dpush( dpop() - r); }
void divide() { r = dpop();
if(r == 0.0){ printf("Деление на 0\n"); err(3); }
dpush( dpop() / r );
}
void pwr() { r = dpop(); dpush( pow( dpop(), r )); }
void dup() { dpush( dpush( dpop())); }
void xchg(){ r = dpop(); p = dpop(); dpush(r); dpush(p); }
void neg() { dpush( - dpop()); }
void dsin(){ dpush( sin( dpop())); }
void dcos(){ dpush( cos( dpop())); }
void dexp(){ dpush( exp( dpop())); }
void dlog(){ dpush( log( dpop())); }
void dsqrt(){ dpush( sqrt( dpop())); }
void dsqr(){ dup(); mult(); }
/* M_PI и M_E определены в <math.h> */
void pi() { dpush( M_PI /* число пи */ ); }
void e() { dpush( M_E /* число e */ ); }
void prn() { printf("%g\n", dpush( dpop())); }
void printstk(){
if( !sp ){ printf("Стек операндов пуст\n"); err(4);}
while(sp) printf("%g ", dpop());
putchar('\n');
}
/* КОМПИЛЯТОР ---------------------------------------- */
/* номера лексем */
#define END (-3) /* = */
#define NUMBER (-2) /* число */
#define BOTTOM 0 /* псевдолексема "дно стека" */
#define OPENBRACKET 1 /* ( */
#define FUNC 2 /* f( */
#define CLOSEBRACKET 3 /* ) */
#define COMMA 4 /* , */
#define PLUS 5 /* + */
#define MINUS 6 /* - */
#define MULT 7 /* * */
#define DIV 8 /* / */
#define POWER 9 /* ** */
/* Приоритеты */
#define NOTDEF 333 /* не определен */
#define INFINITY 3000 /* бесконечность */
/* Стек транслятора */
typedef struct _opstack {
int cop; /* код операции */
void (*f)(); /* "отложенное" действие */
} opstack;
int osp; /* operations stack pointer */
opstack ost[MAXDEPTH];
void push(n, func) void (*func)();
{
if(osp == MAXDEPTH){ printf("Стек операций полон\n");err(5);}
ost[osp].cop = n; ost[osp++].f = func;
}
int pop(){
if( !osp ){ printf("Стек операций пуст\n"); err(6); }
return ost[--osp].cop;
}
int top(){
if( !osp ){ printf("Стек операций пуст\n"); err(7); }
return ost[osp-1].cop;
}
void (*topf())(){
return ost[osp-1].f;
}
#define drop() (void)pop()
void nop(){ printf( "???\n" ); } /* no operation */
void obr_err(){ printf( "Не хватает )\n" ); err(8); }
/* Таблица приоритетов */
struct synt{
int inp_prt; /* входной приоритет */
int stk_prt; /* стековый приоритет */
void (*op)(); /* действие над стеком вычислений */
} ops[] = {
/* BOTTOM */ {NOTDEF, -1, nop },
/* OPENBRACKET */ {INFINITY, 0, obr_err},
/* FUNC */ {INFINITY, 0, obr_err},
/* CLOSEBRACKET */ {1, NOTDEF, nop }, /* NOPUSH */
/* COMMA */ {1, NOTDEF, nop }, /* NOPUSH */
/* PLUS */ {1, 1, add },
/* MINUS */ {1, 1, sub },
/* MULT */ {2, 2, mult },
/* DIV */ {2, 2, divide },
/* POWER */ {3, 3, pwr }
};
#define stkprt(i) ops[i].stk_prt
#define inpprt(i) ops[i].inp_prt
#define perform(i) (*ops[i].op)()
/* значения, заполняемые лексическим анализатором */
double value; void (*fvalue)();
int tprev; /* предыдущая лексема */
/* Транслятор в польскую запись + интерпретатор */
void reset(){ sp = osp = 0; push(BOTTOM, NULL); tprev = END;}
void calc(){
int t;
do{
if( setjmp(AGAIN))
printf( "Стеки после ошибки сброшены\n" );
reset();
while((t = token()) != EOF && t != END){
if(t == NUMBER){
if(tprev == NUMBER){
printf("%g:Два числа подряд\n",value);
err(9);
}
/* любое число просто заносится в стек */
tprev = t; dpush(value); continue;
}
/* иначе - оператор */
tprev = t;
/* Выталкивание и выполнение операций со стека */
while(inpprt(t) <= stkprt( top()) )
perform( pop());
/* Сокращение или подмена скобок */
if(t == CLOSEBRACKET){
if( top() == OPENBRACKET || top() == FUNC ){
void (*ff)() = topf();
drop(); /* схлопнуть скобки */
/* обработка функции */
if(ff) (*ff)();
}else{ printf( "Не хватает (\n"); err(10); }
}
/* Занесение операций в стек (кроме NOPUSH-операций) */
if(t != CLOSEBRACKET && t != COMMA)
push(t, t == FUNC ? fvalue : NULL );
}
if( t != EOF ){
/* Довыполнить оставшиеся операции */
while( top() != BOTTOM )
perform( pop());
printstk(); /* печать стека вычислений (ответ) */
}
} while (t != EOF);
}
/* Лексический анализатор ---------------------------- */
extern void getn(), getid(), getbrack();
int token(){ /* прочесть лексему */
int c;
while((c = getchar())!= EOF && (isspace(c) || c == '\n'));
if(c == EOF) return EOF;
ungetc(c, stdin);
if(isdigit(c)){ getn(); return NUMBER; }
if(isalpha(c)){ getid(); getbrack(); return FUNC; }
return getop();
}
/* Прочесть число (с точкой) */
void getn(){
int c, i; char s[80];
s[0] = getchar();
for(i=1; isdigit(c = getchar()); i++ ) s[i] = c;
if(c == '.'){ /* дробная часть */
s[i] = c;
for(i++; isdigit(c = getchar()); i++) s[i] = c;
}
s[i] = '\0'; ungetc(c, stdin); value = atof(s);
}
/* Прочесть операцию */
int getop(){
int c;
switch( c = getchar()){
case EOF: return EOF;
case '=': return END;
case '+': return PLUS;
case '-': return MINUS;
case '/': return DIV;
case '*': c = getchar();
if(c == '*') return POWER;
else{ ungetc(c, stdin); return MULT; }
case '(': return OPENBRACKET;
case ')': return CLOSEBRACKET;
case ',': return COMMA;
default: printf( "Ошибочная операция %c\n", c);
return token();
}
}
struct funcs{ /* Таблица имен функций */
char *fname; void (*fcall)();
} tbl[] = {
{ "sin", dsin }, { "cos", dcos },
{ "exp", dexp }, { "sqrt", dsqrt },
{ "sqr", dsqr }, { "pi", pi },
{ "sum", add }, { "ln", dlog },
{ "e", e }, { NULL, NULL }
};
char *lastf; /* имя найденной функции */
/* Прочесть имя функции */
void getid(){
struct funcs *ptr = tbl;
char name[80]; int c, i;
*name = getchar();
for(i=1; isalpha(c = getchar()); i++) name[i] = c;
name[i] = '\0'; ungetc(c, stdin);
/* поиск в таблице */
for( ; ptr->fname; ptr++ )
if( !strcmp(ptr->fname, name)){
fvalue = ptr->fcall;
lastf = ptr->fname; return;
}
printf( "Функция \"%s\" неизвестна\n", name ); err(11);
}
/* прочесть открывающую скобку после имени функции */
void getbrack(){
int c;
while((c = getchar()) != EOF && c != '(' )
if( !isspace(c) && c != '\n' ){
printf("Между именем функции %s и ( символ %c\n", lastf, c);
ungetc(c, stdin); err(12);
}
}
void main(){ calc();}
/* Примеры:
( sin( pi() / 4 + 0.1 ) + sum(2, 4 + 1)) * (5 - 4/2) =
ответ: 23.3225
(14 + 2 ** 3 * 7 + 2 * cos(0)) / ( 7 - 4 ) =
ответ: 24
*/
© Copyright А. Богатырев, 1992-95
Си в UNIX
Назад | Содержание | Вперед