Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // avp 2016 (for ru.stackoverflow.com)
- // калькулятор арифметики с функциями и переменными
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <math.h>
- #include <limits.h>
- #include <stdint.h>
- #include "fcalc.h" // http://pastebin.com/5qX5isEh
- // список предопределенных функций
- struct dfunc {
- function f;
- const char *name;
- };
- static struct dfunc dfun[] = {
- {(function)sin, "sin"},
- {(function)asin, "asin"},
- {(function)cos, "cos"},
- {(function)acos, "acos"},
- {(function)tan, "tan"},
- {(function)atan, "atan"},
- {(function)fabs, "fabs"},
- {(function)round, "round"},
- {(function)ceil, "ceil"},
- {(function)floor, "floor"},
- {(function)trunc, "trunc"},
- {(function)sqrt, "sqrt"},
- {(function)cbrt, "cbrt"},
- {(function)pow, "pow"},
- {(function)exp, "exp"},
- {(function)log, "log"},
- {(function)log2, "log2"},
- {(function)log10, "log10"}
- };
- static Symtab *calc_fun = 0; /* таблица предопределенных функций,
- строится при первом вызове calc() */
- struct fact { // функция ("параметры" операции F) в стеке оперций
- function f;
- int fsp; // индекс в стеке операндов (начало аргументов вызова функции)
- };
- // контекст вычисляемого выражения
- struct ucalc {
- const char *str; // "исходник" выражения
- int rc, // return code (OK == 0 иначе код ошибки)
- pos, // позиция в str для поиска следующей лексемы
- prevop, // тип предыдущей лексемы (.curlex.type)
- histop; /* "история" видов лексем (operation == 1 / number == 0)
- при каждом вызове calc_getlex() сдвигается влево,
- младший бит устанавливается в вид текущей лексемы */
- struct calc_lexem curlex;
- double result; /* конечный или любой промежуточный результат
- в т.ч. прочитанное calc_getlex() число */
- #define STACK(T) struct { T *mem; int size, sp; }
- STACK(double) nums; // стек опрерандов (чисел)
- STACK(char) ops; // стек операций
- STACK(struct fact) funcs; // "расширение" стека операций для функций
- #undef STACK
- };
- /************************
- HashTable utilities
- *************************/
- // элемент в hashtable (ключ .name[.len])
- struct u_item {
- struct u_item *next;
- union {
- function f;
- double v;
- };
- int len; // реальное количество байт в name[]
- char name[1]; // ключ элемента hashtable
- };
- // начальный размер hashtable
- #define INIT_HTAB_SIZE 5
- /*
- Когда ожидаемая длина списка синонимов достигает HTAB_REHASH
- (т.е. items_counter / capacity >= HTAB_REHASH)
- проводим рехэширование в новую таблицу с размером в количество элементов
- */
- #define HTAB_REHASH 2
- // делаем пустую таблицу p[] с указателями на списки синонимов (struct u_item)
- // p[-2] -- capacity (cast to `ulong`), p[-1] -- items_counter
- // returns &p[0] !!!
- static struct u_item **
- make_htab (uint32_t sz)
- {
- struct u_item **p = (typeof(p))calloc(sizeof(*p), sz + 2);
- if (p) {
- p[0] = (typeof(*p))(ulong)sz;
- p += 2;
- }
- return p;
- }
- static inline uint32_t
- hash32 (const char *s, int len)
- {
- uint32_t h = 0;
- /* Bernstein */
- while (len--)
- h = h * 33 + (uint8_t)(*s++);
- /* Murmur OAAT
- while (len--) {
- h ^= (uint8_t)(*s++);
- h *= 0x5bd1e995;
- h ^= h >> 15;
- }
- */
- /* FNV
- h ^= 2166136261U;
- while (len--) {
- h ^= (uint8_t)(*s++);
- h *= 16777619;
- }
- */
- return h;
- }
- static void
- rehash (struct u_item **htab[])
- {
- struct u_item **t = *htab, **newtab, *p;
- ulong i, j, hsize = (ulong)t[-2], n = (ulong)t[-1];
- if (n)
- if ((newtab = (typeof(newtab))calloc(sizeof(*newtab), n + 2))) {
- newtab[0] = newtab[1] = (typeof(*newtab))n;
- *htab = newtab + 2;
- for (i = 0; i < hsize; i++)
- while ((p = t[i])) {
- t[i] = p->next;
- p->next = (*htab)[j = hash32(p->name, p->len) % n];
- (*htab)[j] = p;
- }
- free(t - 2);
- }
- }
- // проверяет и при необходимости реорганизует (rehash) или создает hashtable
- // (пока реализовано только расширение hashtable)
- static int
- check_htab (struct u_item **htab[])
- {
- if (htab) {
- if (!*htab)
- if (!(*htab = make_htab(INIT_HTAB_SIZE)))
- return 0;
- if ((ulong)(*htab)[-1] / (ulong)(*htab)[-2] >= HTAB_REHASH)
- rehash(htab);
- return 1;
- }
- return 0;
- }
- // возвращает указатель на элемент hashtable
- static struct u_item *
- get_htab (struct u_item **htab[], const char *name, int len)
- {
- struct u_item *p = 0;
- if (len > 0 && check_htab(htab))
- for (p = (*htab)[hash32(name, len) % (ulong)(*htab)[-2]]; p; p = p->next)
- if (len == p->len && memcmp(p->name, name, len) == 0)
- break;
- return p;
- }
- // возвращает указатель на элемент hashtable,
- // если такого элемента нет, то создает его (возвращает NULL, если нет памяти)
- static struct u_item *
- put_htab (struct u_item **htab[], const char *name, int len)
- {
- struct u_item *p = 0;
- if (len > 0 && check_htab(htab)) {
- ulong hsize = (ulong)(*htab)[-2], j = hash32(name, len) % hsize;
- for (p = (*htab)[j]; p; p = p->next)
- if (p->len == len && memcmp(p->name, name, len) == 0)
- break;
- if (!p && (p = (typeof(p))calloc(1, sizeof(*p) + len))) {
- (*(ulong *)(*htab - 1))++; // incr items counter
- p->next = (*htab)[j];
- (*htab)[j] = p;
- memcpy(p->name, name, p->len = len);
- }
- }
- return p;
- }
- // удаляет элемент hashtable (если hashtable не было, создает пустую)
- static int
- del_htab (struct u_item **htab[], const char *name, int len)
- {
- struct u_item *p = 0, **pp = 0;
- if (len > 0 && check_htab(htab)) {
- ulong hsize = (ulong)(*htab)[-2], j = hash32(name, len) % hsize;
- for (p = (*htab)[j], pp = *htab + j; p; pp = &p->next, p = p->next)
- if (len == p->len && memcmp(p->name, name, len) == 0) {
- *pp = p->next;
- (*(ulong *)(*htab - 1))--; // decr items counter
- free(p);
- return 1;
- }
- }
- return 0;
- }
- /*********************************
- calc interface to hashtables
- **********************************/
- /*
- печать hashtable в stdout для отладки или пустого любопытства
- (act < 1 только статистика, иначе еще имена, а act > 1 и их double значения)
- (вызывается для всех известных таблиц из calc() при str = "?";)
- */
- int
- calc_pristat (Symtab t[], int act)
- {
- int rc = 0;
- if (t) {
- long i, sz = (long)t[-2], n = (long)t[-1], nnz = 0, mx = 0;
- printf("table: size=%ld count=%ld %c", sz, n, act > 0 ? '\n' : ' ');
- for (i = 0; i < sz; i++)
- if (t[i]) {
- nnz++;
- int s = 0;
- Symtab p;
- for (p = t[i]; p; p = p->next) {
- s++;
- if (act) {
- char buf[p->len+1];
- memcpy(buf, p->name, p->len);
- buf[p->len] = 0;
- printf("%s ", buf);
- if (act > 1)
- printf("%g", p->v);
- printf("%c", p->next ? ' ' : '\n');
- }
- }
- if (s > mx)
- mx = s;
- }
- printf(" nnz=%ld mx=%ld\n", nnz, mx);
- rc = 1;
- }
- return rc;
- }
- // удаляет таблицу со всеми элементами
- void
- calc_destroy_symtab (Symtab **htab)
- {
- if (htab && *htab) {
- struct u_item **t = *htab, *p;
- ulong hsize = (ulong)t[-2], i;
- for (i = 0; i < hsize; i++)
- while ((p = t[i])) {
- t[i] = p->next;
- free(p);
- }
- free(t - 2);
- *htab = 0;
- }
- }
- // удаляет таблицу с предопределенными функциями (чистит память за calc())
- void
- calc_free ()
- {
- calc_destroy_symtab(&calc_fun);
- }
- // создает переменную или меняет значение существующей
- double *
- calc_set_var (Symtab **p, const char *name, int len, double v)
- {
- struct u_item *pv = put_htab(p, name, len);
- if (pv)
- pv->v = v;
- return pv ? &pv->v : 0;
- }
- // доступ к значению существующей переменной
- double *
- calc_get_var (Symtab **p, const char *name, int len)
- {
- struct u_item *pv = get_htab(p, name, len);
- return pv ? &pv->v : 0;
- }
- // добавляет функцию или меняет указатель на нее
- function *
- calc_set_func (Symtab **p, const char *name, function f)
- {
- // struct u_item *pv = put_htab((struct u_item ***)p, name, len);
- struct u_item *pv = put_htab(p, name, strlen(name));
- if (pv)
- pv->f = f;
- return pv ? &pv->f : 0;
- }
- // доступ к указателю на функцию
- function *
- calc_get_func (Symtab **p, const char *name, int len)
- {
- struct u_item *pv = get_htab(p, name, len);
- return pv ? &pv->f : 0;
- }
- // удаляет из таблицы переменную или функцию
- int
- calc_delsymb (Symtab **p, const char *name)
- {
- return del_htab(p, name, strlen(name));
- }
- // взять функцию из таблицы пользователя,
- // если не нашли, то из предопределенных функций calc()
- static function *
- calc_get_function (Symtab *pcf, const char *name, int len)
- {
- function *pf = pcf ? calc_get_func(&pcf, name, len) : 0;
- if (!pf)
- pf = calc_get_func(&calc_fun, name, len);
- return pf ? pf : 0;
- }
- // инициализация таблицы предопределеных функций
- static void
- ini_func ()
- {
- if (!calc_fun) {
- int i;
- for (i = 0; i < (int)(sizeof(dfun) / sizeof(dfun[0])); i++)
- calc_set_func(&calc_fun, dfun[i].name, dfun[i].f);
- }
- }
- // для g++ [SYMB_INDEX] должны следовать строго по порядку
- static const char *msgs[] = {
- [CALC_OK] = "success",
- [CALC_OUT_OF_MEM] = "out of memory (Unbelivable, internal error?)",
- [CALC_NO_OP] = "two operands without operation beetwen them",
- [CALC_NO_VAL] = "operand expected, but found operation or ')'",
- [CALC_NO_RP] = "unbalanced '('",
- [CALC_NO_LP] = "unbalanced ')'",
- [CALC_ERR_OP] = "Invalid operation in expr() (INTERNAL ERROR)",
- [CALC_NO_NUMS] = "no operands for current operation (INTERNAL ERROR)",
- [CALC_NUMS_ERR] = "at end operands stack depth too big (internal error?)",
- [CALC_OPRS_ERR] = "at end operations left in stack (internal error?)",
- [CALC_ERR_INPUT] = "invalid input char (letter, graph ...)",
- [CALC_NO_FRP] = "unbalanced function '('",
- [CALC_NO_INPUT] = "empty input",
- [CALC_ERR_CODE] = "invalid error code (CALLER ERROR)",
- [CALC_EOD] = "End Of Data",
- [CALC_ARGS_ERR] = "too many function arguments",
- [CALC_ASSG_ERR] = "assignment in middle of expression not implemented",
- [CALC_COMMA_ERR] = "comma (`,`) not in function arguments list",
- [CALC_NO_VAR] = "undefined variable",
- [CALC_NO_FUNC] = "undefined function",
- [CALC_ERR_PRTY] = "eval priority (INTERNAL ERROR)"
- };
- // Получить текст сообщения по коду ошибки
- const char *
- calc_strerr (int code)
- {
- if (code == EOF)
- code = CALC_EOD;
- if (code < 0 || code >= (int)(sizeof(msgs) / sizeof(msgs[0])))
- code = CALC_ERR_CODE;
- return msgs[code];
- }
- /****************************
- Calc code
- ***************************/
- // это пробельные символы
- #define SPACES " \t\v\f\r\n"
- // предыдущая лексема была операцией?
- #define PREVOP(x) ((x) & 2)
- // следующий непробельный символ
- #define PEEKCHR(s, p) ({typeof(s) t = (s); t[(*p) = strspn(t, SPACES)];})
- // макросы для работы со стеком
- #define INCR_STACK 100
- // добавить память если требуется (Unbelievable)
- #define CORRECT_STACK(st) ({ int rc = 1; \
- if (!(st)->mem) {(st)->size = (st)->sp = 0;} \
- if ((st)->size == (st)->sp) { \
- int s = sizeof(*((st)->mem)) * ((st)->size + INCR_STACK); \
- void *t = realloc((st)->mem, s); \
- if (!t) \
- rc = 0; \
- else { \
- (st)->mem = (typeof((st)->mem))t; (st)->size += INCR_STACK; \
- } \
- } \
- rc; \
- })
- #define PUSH(stack, v) ({int rc = CORRECT_STACK(stack); \
- if (rc) \
- (stack)->mem[(stack)->sp++] = v; \
- rc; \
- })
- #define TOP(stack) ({typeof(((stack)->mem)) v = \
- (stack)->mem && (stack)->sp ? \
- &(stack)->mem[(stack)->sp - 1] : 0; v;})
- #define POP(stack) ({typeof(((stack)->mem)) v = \
- (stack)->mem && (stack)->sp ? \
- &(stack)->mem[--((stack)->sp)] : 0; v;})
- #define EMPTY(stack) (!((stack)->mem && (stack)->sp))
- #define CMPTOP(stack, v) (!EMPTY(stack) && *TOP(stack) == (v))
- /*
- Берет следующую лексему, начиная с ctx->str[ctx->pos]
- Returns ее код
- (помещает double значение числа в ctx->result)
- */
- int
- calc_getlex (struct ucalc *ctx)
- {
- int i = ctx->pos, // с этой точки начинаем поиск лексемы
- op; // а это будет ее код
- // организуем историю видов (операнд/операция) лексем
- ctx->histop <<= 1;
- ctx->prevop = ctx->curlex.type;
- // пропустим пробелы
- if (!(ctx->str[i += strspn(ctx->str + ctx->pos, SPACES)])) {
- ctx->pos = i;
- return ctx->curlex.type = EOF;
- }
- ctx->pos = i + 1; // начальная позиция для следующего вызова
- ctx->curlex.pos = i; // начало лексемы
- ctx->curlex.len = 1; // ее длина
- char *p;
- // по текущему символу (`op`) определим тип лексемы
- if ((p = (char *)strchr("+-*/(),", op = ctx->str[i]))) {
- // это операция, уточним какая именно
- if (op == '+' && PREVOP(ctx->histop))
- op = 'P'; // unary `+`
- else if (op == '-' && PREVOP(ctx->histop))
- op = 'M'; // unary `-`
- // а вот `)` должна выглядеть как операнд для проверки на ошибки
- ctx->histop |= (!(op == ')')); // занесем в историю вид лексемы
- } else if (isdigit(op) || op == '.') { // число?
- ctx->result = ctx->curlex.v = strtod(ctx->str + i, &p);
- if (p != ctx->str + i) {
- op = 'N';
- ctx->pos = p - ctx->str; // обновим начальную позицию
- ctx->curlex.len = ctx->pos - ctx->curlex.pos; // и длину лексемы
- }
- } else if (isalpha(op) || op == '_') { // идентификатор?
- int l, c, j;
- // пропустим допустимые для имени символы
- for (l = ctx->pos; (c = ctx->str[l]) && (isalnum(c) || c == '_'); l++);
- ctx->curlex.len = l - ctx->curlex.pos; // и обновим длину лексемы
- op = 'V';
- if ((c = PEEKCHR(ctx->str + l, &j)) == '=')
- op = 'S'; // это оказывается присваивание переменной
- else if (c == '(')
- op = 'F'; // а если так, то обнаружили вызов функции
- ctx->histop |= (op != 'V'); // переменная это операнд, а остальное операция
- ctx->pos = l + ((op == 'V') ? 0 : j + 1); // новая начальная позиция
- } else
- op = '?';
- ctx->curlex.type = op;
- return op;
- }
- /*
- Выполним операцию с операндами из стека и поместим результат в стек
- Returns 0 -- error or 1 -- OK
- */
- static int
- expr (struct ucalc *ctx, int op)
- {
- int n = op == 'M' ? 1 : 2; // число операндов для данной операции
- if (op != 'F' && ctx->nums.sp < n) // это не справедливо для вызова функции
- return !(ctx->rc = CALC_NO_NUMS);
- switch (op) {
- case '+':
- ctx->result = ctx->nums.mem[ctx->nums.sp - 1] +
- ctx->nums.mem[ctx->nums.sp - 2];
- break;
- case '-':
- ctx->result = ctx->nums.mem[ctx->nums.sp - 2] -
- ctx->nums.mem[ctx->nums.sp - 1];
- break;
- case '*':
- ctx->result = ctx->nums.mem[ctx->nums.sp - 1] *
- ctx->nums.mem[ctx->nums.sp - 2];
- break;
- case '/':
- ctx->result = ctx->nums.mem[ctx->nums.sp - 2] /
- ctx->nums.mem[ctx->nums.sp - 1];
- break;
- case 'M':
- ctx->result = -(ctx->nums.mem[ctx->nums.sp - 1]);
- break;
- case 'F':
- { // вызов функции
- // возможные аргументы, обнулим для -Wall
- struct { double d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; } a = {0};
- int isp = ctx->funcs.sp - 1, // индекс функции в стеке функций
- nsp = ctx->nums.sp;
- // перевычислим `n` -- число аргументов функции
- if ((n = nsp - ctx->funcs.mem[isp].fsp) == 0 && !ctx->nums.mem) {
- // и инициализируем стек операндов для вызова функции без аргументов
- PUSH(&ctx->nums, 0);
- POP(&ctx->nums);
- }
- if (n > 10)
- return !(ctx->rc = CALC_ARGS_ERR);
- switch (n - 1) { // скопируем аргументы из стека операндов
- case 9: a.d9 = ctx->nums.mem[--nsp];
- case 8: a.d8 = ctx->nums.mem[--nsp];
- case 7: a.d7 = ctx->nums.mem[--nsp];
- case 6: a.d6 = ctx->nums.mem[--nsp];
- case 5: a.d5 = ctx->nums.mem[--nsp];
- case 4: a.d4 = ctx->nums.mem[--nsp];
- case 3: a.d3 = ctx->nums.mem[--nsp];
- case 2: a.d2 = ctx->nums.mem[--nsp];
- case 1: a.d1 = ctx->nums.mem[--nsp];
- case 0: a.d0 = ctx->nums.mem[--nsp];
- }
- // выполним функцию
- ctx->result = ctx->funcs.mem[isp].f(a.d0, a.d1, a.d2, a.d3,
- a.d4, a.d5, a.d6, a.d7, a.d8, a.d9);
- // уберем функцию из стека функций
- POP(&ctx->funcs);
- }
- break;
- // internal error, unknown op
- default:
- return !(ctx->rc = CALC_ERR_OP);
- }
- // поместим результат в новую вершину стека операндов
- ctx->nums.mem[ctx->nums.sp - n] = ctx->result;
- ctx->nums.sp -= (n - 1); // и скорректируем индекс вершины
- return 1;
- }
- // Returns приоритет операции
- static int
- prty (int op)
- {
- static char
- oprs[] = "+-M*/(),F",
- prio[] = "335441121";
- return op == EOF ? 0 : prio[strchr(oprs, op) - oprs] - '0';
- }
- /*
- Выбираем и выполняем все операции из стека с приоритетом меньшим или
- равным приоритету новой операции (ctx->curlex.type), затем помещаем ее в стек.
- Закрывающая скобка, операция "запятая" (`,`) и EOF в стек не помещаются,
- `)` и `,` вычисляют и удаляют из стека все операции до `(` или `F` на
- вершине, `)` "аннигилирует" вместе с '(` или `F`,
- EOF же, выполняя операции и обнаружив '(` или `F`, возвращает ошибку.
- Returns 0 -- error or 1 -- OK, last calculation result in ctx->result
- */
- static int
- eval (struct ucalc *ctx)
- {
- int op = ctx->curlex.type, p1 = prty(op);
- while (!EMPTY(&ctx->ops) && (p1 <= prty(*TOP(&ctx->ops)))) {
- int top = *POP(&ctx->ops);
- if (top == 'F' || top == '(') {
- switch (op) {
- case EOF:
- return !(ctx->rc = (top == '(' ? CALC_NO_RP : CALC_NO_FRP)); // 0 - err
- case ')':
- return top == '(' ? 1 : expr(ctx, top);
- default:
- return !(ctx->rc = CALC_ERR_PRTY);
- }
- }
- if (!expr(ctx, top))
- return 0;
- }
- if (op == ')')
- return !(ctx->rc = CALC_NO_LP);
- if (op == ',')
- return CMPTOP(&ctx->ops, 'F') ? 1 : !(ctx->rc = CALC_COMMA_ERR);
- // поместим новую операцию в стек
- if (op != EOF && !PUSH(&ctx->ops, op))
- return !(ctx->rc = CALC_OUT_OF_MEM);
- return 1;
- }
- /**
- * calc -- Калькулятор арифметических выражений с переменными и функциями.
- * @str: IN -- строка с выражением
- * @pres: OUT -- указатель для результата вычисления
- * @user: INOUT -- указатель структуры (контекста выражения) с полями
- * .varlist IN -- таблица переменных
- * .funlist IN -- таблица функций
- * .reuslt OUT -- результат вычисления
- * .rc OUT -- код ошибки
- * .curlex OUT -- последняя прочитанная лексема выражения
- * (можно использовать при печати сообщения об ошибке)
- * Returns: 0 -- успешное вычисление, иначе код ошибки @.rc или EOF (@str == 0)
- */
- int
- calc (const char *str, double *pres, struct calc *user)
- {
- if (!str)
- return EOF;
- ini_func(); // инициализация таблицы предопределенных функций
- if (*str == '?') { // печать в stdout таблиц переменных и функций
- fputs("variable ", stdout);
- if (!calc_pristat(user ? user->varlist : 0, 2))
- puts("");
- fputs("user functions ", stdout);
- if (!calc_pristat(user ? user->funlist : 0, 1))
- puts("");
- return fputs("calc functions ", stdout), calc_pristat(calc_fun, 1), 0;
- }
- struct ucalc ctx = {0}; // контекст вычисления выражения
- ctx.str = str;
- ctx.result = NAN;
- if (pres)
- *pres = NAN;
- if (user)
- user->result = NAN;
- ctx.histop = 1; // моделируем, что предыдущая лексема была операцией
- int loop = 0, // номер цикла, присваивание допустимо только для loop == 1
- op; // тип лексемы
- struct calc_lexem setlex = {0}; // лексема переменной оператора присваивания
- /*
- Основной цикл вычисления.
- Вычисляем до первой ошибки (ctx.rc != 0) или до конца выражения (op == EOF).
- Алгоритм:
- Берем очередную лексему.
- Операции `(`, M (унарный минус) и F (вызов функции) сразу помещаем
- в стек операций, для остальных операций вызываем eval(),
- которая выполняет операции с меньшим или равным приоритету текущей
- операции из стека над операндами из стека операндов, а затем помещает
- текущую операцию в стек.
- Операнды (числа и значения переменных) заносим в стек операндов.
- */
- while (ctx.rc == 0 && (op = calc_getlex(&ctx)) != EOF) {
- loop++;
- switch (op) {
- // игнорируем унарный плюс
- case 'P':
- break;
- // обрабатываем присваивание
- case 'S':
- if (loop == 1) // допустимо только в начале выражения
- setlex = ctx.curlex;
- else
- ctx.rc = CALC_ASSG_ERR;
- break;
- // эти операции сразу в стек
- case '(':
- case 'F':
- if (!PREVOP(ctx.histop)) {
- ctx.rc = CALC_NO_OP;
- break;
- }
- if (op == 'F') {
- function *pf = calc_get_function(user ? user->funlist : 0,
- str + ctx.curlex.pos, ctx.curlex.len);
- if (!pf) {
- ctx.rc = CALC_NO_FUNC;
- break;
- }
- // запомним атрибуты вызова функции (ее адрес и SP стека операндов)
- struct fact fs = {*pf, ctx.nums.sp};
- if(!PUSH(&ctx.funcs, fs))
- ctx.rc = CALC_OUT_OF_MEM;
- }
- case 'M':
- if(!PUSH(&ctx.ops, op))
- ctx.rc = CALC_OUT_OF_MEM;
- break;
- // выполним операцию, eval() положит ее в стек
- case ')':
- case ',':
- case '*':
- case '/':
- if (PREVOP(ctx.histop) && !(op == ')' && ctx.prevop == 'F')) {
- ctx.rc = CALC_NO_VAL;
- break;
- }
- case '+':
- case '-':
- eval(&ctx);
- break;
- // запомним операнд в стеке операндов
- case 'V':
- { // для переменной найдем ее значение
- double *p = calc_get_var(user ? &user->varlist : 0,
- str + ctx.curlex.pos, ctx.curlex.len);
- if (!p) {
- ctx.rc = CALC_NO_VAR;
- break;
- }
- ctx.result = *p;
- }
- case 'N':
- if (!PREVOP(ctx.histop))
- ctx.rc = CALC_NO_OP;
- else if (!PUSH(&ctx.nums, ctx.result))
- ctx.rc = CALC_OUT_OF_MEM;
- break;
- // прочли какой-то неизвестный символ
- default:
- ctx.rc = CALC_ERR_INPUT;
- }
- }
- if (!ctx.rc)
- eval(&ctx); // OK, вычислим все из стека
- // финальные проверки вроде бы безошибочного вычисления
- if (!ctx.rc) {
- if (ctx.nums.sp == 0 && ctx.ops.sp == 0)
- ctx.rc = CALC_NO_INPUT; // на самом деле просто ничего не было
- else if (ctx.nums.sp > 1)
- ctx.rc = CALC_NUMS_ERR;
- else if (ctx.ops.sp)
- ctx.rc = CALC_OPRS_ERR;
- else if (setlex.len && user && // и наконец, присваивание переменной
- !calc_set_var(&user->varlist, str + setlex.pos, setlex.len,
- ctx.result))
- ctx.rc = CALC_OUT_OF_MEM;
- }
- free(ctx.nums.mem);
- free(ctx.ops.mem);
- free(ctx.funcs.mem);
- if (pres)
- *pres = ctx.result;
- if (user) {
- user->curlex = ctx.curlex;
- user->result = ctx.result;
- }
- return ctx.rc;
- }
- #ifdef STACK_TEST
- int
- main ()
- {
- struct ucalc cx = {0};
- puts("test PUSH/TOP/POP/EMPTY");
- PUSH(&cx.ops, 'a');
- PUSH(&cx.ops, 'a');
- PUSH(&cx.ops, 'b');
- PUSH(&cx.ops, 'c');
- PUSH(&cx.ops, 0);
- double *dp = TOP(&cx.nums);
- printf("empty top: %f\n", dp ? *dp : NAN);
- PUSH(&cx.nums, 9.8);
- printf(" top: %f\n", *TOP(&cx.nums));
- PUSH(&cx.nums, 3.14);
- printf(" top: %f\n", *TOP(&cx.nums));
- PUSH(&cx.nums, 2.87);
- printf(" top: %f\n", *TOP(&cx.nums));
- int i;
- for (i = cx.nums.sp - 1; i >= 0; i--)
- printf("%f\n", cx.nums.mem[i]);
- puts(cx.ops.mem);
- POP(&cx.ops);
- while (!EMPTY(&cx.ops))
- printf("pop: '%c'\n", *POP(&cx.ops));
- return puts("END test PUSH/TOP/POP/EMPTY") == EOF;
- }
- #endif
- #ifdef LEXEM_TEST
- int
- main ()
- {
- struct ucalc cx = {0};
- char str[1000];
- int op;
- puts("test getlex(), enter expressions, stop by ^D");
- while (fputs("> ", stdout), fgets(str, 1000, stdin)) {
- cx = (struct ucalc){0}; cx.str = str;
- while (cx.rc == 0 && (op = calc_getlex(&cx)) != EOF)
- printf("op: '%c' pos: %d res: %f [%d %d]\n",
- op, cx.pos, op == 'N' ? cx.result : NAN,
- cx.curlex.pos, cx.curlex.len);
- printf("Fin: op: %d pos: %d\n", op, cx.pos);
- }
- cx.str = "900"; cx.rc = cx.pos = 0;
- while (cx.rc == 0 && (op = calc_getlex(&cx)) != EOF)
- printf("Op: '%c' pos: %d res: %f\n",
- op, cx.pos, op == 'N' ? cx.result : NAN);
- printf("Fin2: op: %d pos: %d\n", op, cx.pos);
- return puts("End") == EOF;
- }
- #endif
- #ifdef CALC_TEST
- // grad to radian
- static double rad (double g)
- {
- return g / (360.0 / (2 * M_PI));
- }
- // radian to grad
- static double grad (double r)
- {
- return r * (360.0 / (2 * M_PI));
- }
- static double drand()
- {
- return rand() / (double)RAND_MAX;
- }
- int main ()
- {
- struct calc cx = {0};
- calc_set_func(&cx.funlist, "rad", (function)rad);
- calc_set_func(&cx.funlist, "grad", (function)grad);
- calc_set_func(&cx.funlist, "rand", (function)drand);
- // calc_pristat(cx.funlist, 1);
- char str[1000];
- int rc = 0;
- #if 1
- puts("test calc1(), enter expressions, stop by ^D");
- while (fputs("> ", stdout),
- (rc = calc(fgets(str, 1000, stdin), 0, &cx)) != EOF) {
- if (!rc)
- printf("%.16g\n", cx.result);
- else {
- int i, l = strlen(str) - 1;
- str[l] = 0;
- puts(str);
- for (i = 0; i < l; i++)
- if (i == cx.curlex.pos) {
- putchar('^'); break;
- } else
- putchar(isspace(str[i]) ? str[i] : ' ');
- printf(" %s\n", calc_strerr(rc));
- str[l] = '\n';
- }
- #if 0
- printf("`%s` rc = %d result = %.16g [t: %d '%c' p: %d l: %d] %s\n",
- cx.str, rc, cx.result,
- cx.curlex.type, cx.curlex.type, cx.curlex.pos, cx.curlex.len,
- calc_strerr(rc));
- #endif
- }
- #endif
- calc_destroy_symtab(&cx.varlist);
- calc_destroy_symtab(&cx.funlist);
- calc("?", 0, 0);
- calc_free();
- puts("Final");
- return puts(calc_strerr(rc)) == EOF;
- }
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement