Advertisement
avp210159

scalc.c

Feb 1st, 2016
963
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.95 KB | None | 0 0
  1. // avp 2016 (for ru.stackoverflow.com)
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <ctype.h>
  6. #include <math.h>
  7.  
  8. #include "scalc.h" // http://pastebin.com/y9H6B0SQ
  9.  
  10.  
  11. struct calc {
  12.   const char *str; // expression source
  13.   int  rc,         // return code (OK == 0 or some error)
  14.     pos,           // position to scan next lexem
  15.     histop;        // lexems type (operation/number) "history"
  16.   double result;   // current (and final) eval result or just read number
  17. #define STACK(T) struct { T *mem; int size, sp; }
  18.   STACK(double) nums; // stack for numbers (operands)
  19.   STACK(char) ops;    // operations stack
  20. #undef DSTACK
  21. };
  22.  
  23. static const char *msgs[] = {
  24.   "success",
  25.   "out of memory",
  26.   "two operands without operation beetwen them",
  27.   "operand expected, but found ')' or operation",
  28.   "unbalanced '(' ('(' found in stack by EOF)",
  29.   "unbalanced ')' ('(' not found in stack)",
  30.   "Invalid operation in expr() (INTERNAL ERROR)",
  31.   "no operands for current operation",
  32.   "at end operands stack depth too big",
  33.   "at end operations left in stack",
  34.   "invalid input char (letter, graph ...)",
  35.   "empty input",
  36.   "invalid error code",
  37.   "no data"
  38. };
  39.  
  40. // Returns error message text
  41. const char *
  42. scalc_strerr (int code)
  43. {
  44.   if (code == EOF)
  45.     code = CALC_EOD;
  46.   if (code < 0 || code >= (int)(sizeof(msgs) / sizeof(msgs[0])))
  47.     code = CALC_ERR_CODE;
  48.  
  49.   return msgs[code];
  50. }
  51.  
  52. // Previous token is an operation?
  53. #define PREVOP(x) ((x) & 2)
  54.  
  55. #define INCR_STACK 100
  56.  
  57. #define CORRECT_STACK(st) ({ int rc = 1;                \
  58.       if (!(st)->mem) {(st)->size = (st)->sp = 0;}          \
  59.       if ((st)->size == (st)->sp) {                 \
  60.     int s = sizeof(*((st)->mem)) * ((st)->size + INCR_STACK);   \
  61.     void *t = realloc((st)->mem, s);                \
  62.     if (!t)                             \
  63.       rc = 0;                           \
  64.     else {                              \
  65.       (st)->mem = (typeof((st)->mem))t; (st)->size += INCR_STACK;   \
  66.     }                               \
  67.       }                                 \
  68.       rc;                               \
  69.     })
  70.  
  71. #define PUSH(stack, v) ({int rc = CORRECT_STACK(stack); \
  72.       if (rc)                       \
  73.     (stack)->mem[(stack)->sp++] = v;        \
  74.       rc;                       \
  75.     })
  76.  
  77. #define TOP(stack) ({typeof(*((stack)->mem)) v =  \
  78.     (stack)->mem && (stack)->sp ?         \
  79.     (stack)->mem[(stack)->sp - 1] : 0; v;})
  80.  
  81. #define POP(stack) ({typeof(*((stack)->mem)) v =  \
  82.     (stack)->mem && (stack)->sp ?         \
  83.     (stack)->mem[--((stack)->sp)] : 0; v;})
  84.  
  85. #define EMPTY(stack) (!((stack)->mem && (stack)->sp))
  86.  
  87. /*
  88.    Take another token starting with ctx->str[ctx->pos]
  89.    Returns operation code
  90.    (if token is number put it's value to ctx->result)
  91. */
  92. static int
  93. getlex (struct calc *ctx)
  94. {
  95.   int i = ctx->pos, op;
  96.   ctx->histop <<= 1; // shift operations history
  97.  
  98.   // skip spaces
  99.   if (!(ctx->str[i += strspn(ctx->str + ctx->pos, " \t\r\n")])) {
  100.     ctx->pos = i;
  101.     return EOF;
  102.   }
  103.   ctx->pos = i + 1; // new starting position
  104.  
  105.   char *p;
  106.   // take current character and  determine the type of the token
  107.   if ((p = (char *)strchr("+-*/()", op = ctx->str[i]))) {
  108.     // this is operation, clarify it
  109.     if (op == '+' && PREVOP(ctx->histop))
  110.       op = 'P'; // unary `+`
  111.     else if (op == '-' && PREVOP(ctx->histop))
  112.       op = 'M'; // unary `-`
  113.     // `)`  behaves as an operand in an error check
  114.     ctx->histop |= op == ')' ? 0 : 1;
  115.   } else if (isdigit(op) || op == '.') {
  116.     // number?
  117.     ctx->result = strtod(ctx->str + i, &p);
  118.     if (p != ctx->str + i) {
  119.       // realy, next calls will detect possible error
  120.       op = 'N';
  121.       ctx->pos = p - ctx->str; // correct new starting position
  122.     }
  123.   } else
  124.     op = '?';
  125.  
  126.   return op;
  127. }
  128.  
  129.  
  130. // Returns operation priority
  131. static int
  132. priority (int op)
  133. {
  134.   static char
  135.     oprs[] = "+-M*/()",
  136.     prio[] = "2243311";
  137.  
  138.   return op == EOF ? 0 : prio[strchr(oprs, op) - oprs] - '0';
  139. }
  140.  
  141.  
  142. /*
  143.   Performs one elementary operation with operands in the stack
  144.   Returns 0 -- error or 1 -- OK
  145.  */
  146. static int
  147. expr (struct calc *ctx, int op)
  148. {
  149.   int n = op == 'M' ? 1 : 2; // number of operands
  150.  
  151.   if (ctx->nums.sp < n)
  152.     return !(ctx->rc = CALC_NO_NUMS);
  153.  
  154.   switch (op) {
  155.   case '+':
  156.     ctx->result = ctx->nums.mem[ctx->nums.sp - 1] +
  157.       ctx->nums.mem[ctx->nums.sp - 2];
  158.     break;
  159.   case '-':
  160.     ctx->result = ctx->nums.mem[ctx->nums.sp - 2] -
  161.       ctx->nums.mem[ctx->nums.sp - 1];
  162.     break;      
  163.   case '*':
  164.     ctx->result = ctx->nums.mem[ctx->nums.sp - 1] *
  165.       ctx->nums.mem[ctx->nums.sp - 2];
  166.     break;
  167.   case '/':
  168.     ctx->result = ctx->nums.mem[ctx->nums.sp - 2] /
  169.       ctx->nums.mem[ctx->nums.sp - 1];
  170.     break;
  171.   case 'M':
  172.     ctx->result = -(ctx->nums.mem[ctx->nums.sp - 1]);
  173.     break;
  174.     // internal error, unknown op
  175.   default:
  176.     return !(ctx->rc = CALC_ERR_OP);
  177.   }
  178.  
  179.   ctx->nums.mem[ctx->nums.sp - n] = ctx->result;
  180.   ctx->nums.sp -= (n - 1); // adjust the top of the stack
  181.  
  182.   return 1;
  183. }
  184.  
  185. /*
  186.   Push the operation onto the stack,
  187.   popping and then perform the same or lower priority operations.
  188.   Returns 0 -- error or 1 -- OK, last calculation result in ctx->result
  189.  */
  190. static int
  191. eval (struct calc *ctx, int op)
  192. {
  193.   int p1 = priority(op);
  194.  
  195.   while (!EMPTY(&ctx->ops) && (p1  <= priority(TOP(&ctx->ops)))) {
  196.     int top = POP(&ctx->ops);
  197.     if (top == '(')
  198.       return op == EOF ? !(ctx->rc = CALC_NO_RP) : 1; // 1 -- `(` and ')'
  199.  
  200.     if (!expr(ctx, top))
  201.       return 0;
  202.   }
  203.   if (op == ')')
  204.     return !(ctx->rc = CALC_NO_LP);
  205.  
  206.   if (op != EOF && !PUSH(&ctx->ops, op))
  207.     return !(ctx->rc = CALC_OUT_OF_MEM);
  208.  
  209.   return 1;
  210. }
  211.  
  212. /*
  213.   Calculate arithmetic expression in str[],
  214.   put result to *pres.
  215.   Returns: success (0) or error code (can be used to call calc_strerr())
  216.     or EOF if str[] is NULL
  217.  */
  218. int
  219. scalc (const char *str, double *pres)
  220. {
  221.   if (!str)
  222.     return EOF;
  223.   *pres = NAN;
  224.   // init context
  225.   struct calc ctx = {str};
  226.   ctx.result = NAN;
  227.   // simulate that previous lexem was operation for simpler errors checking
  228.   ctx.histop = 1;
  229.  
  230.   int op; // lexem type
  231.   while (ctx.rc == 0 && (op = getlex(&ctx)) != EOF) {
  232.     switch (op) {
  233.       // ignore unar `+`
  234.     case 'P':
  235.       break;
  236.       // push `(` and unar `-` to stack
  237.     case '(':
  238.       if (!PREVOP(ctx.histop)) {
  239.     ctx.rc = CALC_NO_OP;
  240.     break;
  241.       }
  242.     case 'M':
  243.       if(!PUSH(&ctx.ops, op))
  244.     ctx.rc = CALC_OUT_OF_MEM;
  245.       break;
  246.       // push (and eval part of stack) `+` `-` `*` or `/` operations
  247.     case ')':
  248.     case '*':
  249.     case '/':
  250.       if (PREVOP(ctx.histop)) {
  251.     ctx.rc = CALC_NO_VAL;
  252.     break;
  253.       }
  254.     case '+':
  255.     case '-':
  256.       eval(&ctx, op);
  257.       break;
  258.       // push new number to operands stack
  259.     case 'N':
  260.       if (!PREVOP(ctx.histop))
  261.     ctx.rc = CALC_NO_OP;
  262.       else if (!PUSH(&ctx.nums, ctx.result))
  263.       ctx.rc = CALC_OUT_OF_MEM;
  264.       break;
  265.       // invalid input char
  266.     default:
  267.       ctx.rc = CALC_ERR_INPUT;
  268.     }
  269.   }
  270.  
  271.   if (!ctx.rc) // OK, eval stack
  272.     eval(&ctx, EOF);
  273.   *pres = ctx.result;
  274.   // final stacks check
  275.   if (!ctx.rc) {
  276.     if (ctx.nums.sp == 0 && ctx.ops.sp == 0)
  277.       ctx.rc = CALC_NO_INPUT;
  278.     else if (ctx.nums.sp > 1)
  279.       ctx.rc = CALC_NUMS_ERR;
  280.     else if (ctx.ops.sp)
  281.       ctx.rc = CALC_OPRS_ERR;
  282.   }
  283.   free(ctx.nums.mem);
  284.   free(ctx.ops.mem);
  285.  
  286.   return ctx.rc;
  287. }
  288.  
  289. #ifdef STACK_TEST
  290. int
  291. main ()
  292. {
  293.   struct calc cx = {0};
  294.  
  295.   puts("test PUSH/TOP/POP/EMPTY");
  296.  
  297.   PUSH(&cx.ops, 'a');
  298.   PUSH(&cx.ops, 'a');
  299.   PUSH(&cx.ops, 'b');
  300.   PUSH(&cx.ops, 'c');
  301.   PUSH(&cx.ops, 0);
  302.  
  303.   printf("empty top: %f\n", TOP(&cx.nums));
  304.   PUSH(&cx.nums, 9.8);
  305.   printf(" top: %f\n", TOP(&cx.nums));
  306.   PUSH(&cx.nums, 3.14);
  307.   printf(" top: %f\n", TOP(&cx.nums));
  308.   PUSH(&cx.nums, 2.87);
  309.   printf(" top: %f\n", TOP(&cx.nums));
  310.  
  311.   int i;
  312.   for (i = cx.nums.sp - 1; i >= 0; i--)
  313.     printf("%f\n", cx.nums.mem[i]);
  314.  
  315.   puts(cx.ops.mem);
  316.   POP(&cx.ops);
  317.   while (!EMPTY(&cx.ops))
  318.     printf("pop: '%c'\n", POP(&cx.ops));
  319.  
  320.   return puts("END test PUSH/TOP/POP/EMPTY") == EOF;
  321. }
  322. #endif
  323.  
  324. #ifdef LEXEM_TEST
  325. int
  326. main ()
  327. {
  328.   struct calc cx = {0};
  329.   char str[1000];
  330.   int op;
  331.  
  332.   puts("test getlex(), enter expressions, stop by ^D");
  333.   while (fputs("> ", stdout), fgets(str, 1000, stdin)) {
  334.     cx = (typeof(cx)){str};
  335.     while (cx.rc == 0 && (op = getlex(&cx)) != EOF)
  336.       printf("op: '%c'  pos: %d res: %f\n",
  337.          op, cx.pos, op == 'N' ? cx.result : NAN);
  338.     printf("Fin: op: %d  pos: %d\n", op, cx.pos);
  339.   }
  340.  
  341.   cx.str = "900"; cx.rc = cx.pos = 0;
  342.   while (cx.rc == 0 && (op = getlex(&cx)) != EOF)
  343.     printf("Op: '%c'  pos: %d res: %f\n",
  344.        op, cx.pos, op == 'N' ? cx.result : NAN);
  345.   printf("Fin2: op: %d  pos: %d\n", op, cx.pos);
  346.  
  347.   return puts("End") == EOF;
  348. }
  349. #endif
  350.  
  351. #ifdef CALC_TEST
  352.  
  353. int main ()
  354. {
  355.   char str[1000];
  356.   int rc;
  357.   double result;
  358.  
  359.   puts("test calc1(), enter expressions, stop by ^D");
  360.   while (fputs("> ", stdout),
  361.      (rc = scalc(fgets(str, 1000, stdin), &result)) != EOF)
  362.     printf("`%s` rc = %d result = %.16g %s\n",
  363.        str, rc, result, scalc_strerr(rc));
  364.  
  365.   return puts(scalc_strerr(rc)) == EOF;
  366. }
  367. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement