home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / calc / calc.c next >
Encoding:
C/C++ Source or Header  |  1988-05-17  |  11.0 KB  |  508 lines

  1. /**************************************************************************
  2.  *      calc: a command-line calculator program                           *
  3.  *      Copyright (c) 1988 Wayne Mesard                                   *
  4.  *                                                                        *
  5.  *      This is free software.  It may be reproduced, retransmitted,      *
  6.  *      redistributed and otherwise propogated at will, provided that     *
  7.  *      this notice remains intact and in place.                          *
  8.  *                                                                        *
  9.  *      Please direct bug reports, code enhancements and comments         *
  10.  *      to mesard@BBN.COM.                                                *
  11.  *                                                                        *
  12.  **************************************************************************/
  13.  
  14.  
  15. #include <stdio.h>
  16. #include <ctype.h>
  17. #include <strings.h>
  18. #include <math.h>
  19. #include <setjmp.h>
  20.  
  21. #define PIdiv180 0.017453292519943295
  22. #define PI       3.14159265358979323846
  23. #include "machine.h"
  24. #ifdef Mch_Lsz
  25. #  define TwoToTheNLessOne   (unsigned long)(1<<(Mch_Lsz-1))
  26. #else    /* Assume 16 bits */
  27. #  define TwoToTheNLessOne    32768
  28. #endif
  29.  
  30. #define MAXLINE 512
  31.  
  32. #define OP "{(["
  33. #define CP "})]"
  34. #define SYMBOLS "{([+-/xX*%:&^|<>~gnpst" /* Used to distinguish unary from
  35.                     binary minus.  Letters at end of 
  36.                     string are ends of function names. */
  37.  
  38. #define SYMBOL_LOWERS "xabcdef"     /* Used to detect implied multiplication.
  39.                      * Identifies these as symbols and not
  40.                      * part of a function name.
  41.                      */
  42.  
  43. #define DECFMT "\t% .16g\n"
  44. #define BINFMT "\tb%*s\n"
  45. #define FLTFMT "\t% .*f\n"
  46. #define HEXFMT "\th%lX\n"
  47. #define OCTFMT "\to%lo\n"
  48. #define ASCFMT "\t@%c\n"
  49.  
  50. jmp_buf err_recover;
  51. int abort_on_err = 0;
  52. double answer = 0.0;
  53.  
  54. main(argc, argv)       /* calc: a desk-top calculator */
  55. int argc;
  56. char *argv[];
  57. {
  58.     char    s[MAXLINE];
  59.     int     precision = -1;
  60.     char    mode = '\0';
  61.     int     getwhitlessline(), isatty();
  62.     int        keepchecking = 1;
  63.  
  64.     for (argv++, argc--; argc && **argv == '-' && keepchecking; argv++, argc--)
  65.     switch ((*argv)[1]) {
  66.         case 'p':
  67.         if (isdigit((*argv)[2]))
  68.             precision = atoi( &(*argv)[2]);
  69.         else {
  70.             abort_on_err = 1;
  71.             error("bad argument to -p option");
  72.         }
  73.         break;
  74.         case 'e':
  75.         abort_on_err = 1;
  76.         break;
  77.         case 'd':
  78.         case 'h': 
  79.         case 'o': 
  80.         case 'b':
  81.         case '@':
  82.         mode = (*argv)[1];
  83.         break;
  84.         case '\0':
  85.         keepchecking = 0;
  86.         break;
  87.         default:    /* Oops, this must be a unary minus, not an option. */
  88.         keepchecking = 0;
  89.         argv--;
  90.         argc++;
  91.         break;
  92.     }
  93.  
  94.     if (argc) {
  95.     s[0] = '\0';
  96.     while (argc-- > 0)
  97.         strcat (s, *argv++);
  98.     abort_on_err = 1;
  99.     Compute (mode, s, precision);
  100.     }
  101.     else {
  102.     if (isatty(0))
  103.         printf ("Enter equations.  Blank line to terminate.\n");
  104.     setjmp(err_recover);
  105.     while (getwhitlessline (s))
  106.         Compute (mode, s, precision);
  107.     }
  108. }
  109.  
  110.  
  111. Compute(mode, s, precision)
  112. char mode, *s;
  113. int precision;
  114. {
  115.     double calc();
  116.     char    *ftob(), binnum[MAXLINE];
  117.  
  118.     answer = calc(s);
  119.     switch(mode) {
  120.     case '\0':
  121.     case 'd':
  122.         if (precision >= 0)
  123.         printf(FLTFMT, precision, answer);
  124.         else
  125.         printf(DECFMT, answer);
  126.         break;
  127.     case 'h':
  128.         printf(HEXFMT, (unsigned long) answer);
  129.         break;
  130.     case 'o':
  131.         printf(OCTFMT, (unsigned long) answer);
  132.         break;
  133.     case 'b':
  134.         printf(BINFMT, (precision > 0 ? precision : 0),
  135.                    ftob(binnum, answer));
  136.         break;
  137.     case '@':
  138.         printf(ASCFMT, (char) answer);
  139.         break;
  140.     }
  141. }
  142.  
  143.  
  144.  
  145. /*******
  146.  *  When debugging uncomment this proc and change the following proc's name
  147.  *  to calc2
  148.  *
  149. double calc(e)
  150. char *e;
  151. {
  152.     double calc2();
  153.     double val;
  154.  
  155.     printf("calling with **%s**\n", e);
  156.     val = calc2(e);
  157.     printf("returning **%f**\n", val);
  158.     return(val);
  159. }
  160. *******/
  161.  
  162.  
  163. /* Recursively, parse an equation string.
  164.  * This is hideously inefficient, since findop() is called on each invokation.
  165.  * O(n) would be possible if findop() where modified to walk through the string
  166.  * once and build a priority queue for evaluation of operators.
  167.  *    But hey, the kids love it, and I know for a fact that my Data Structures
  168.  * prof never wrote a linear algorithm in his life:-)
  169.  */
  170.  
  171. double calc(eqn)
  172. char *eqn;
  173. {
  174.     double  vleft, temp;
  175.     long    tempi;
  176.     char    left[MAXLINE], eqncp[MAXLINE];
  177.     char   *findop (), *opptr;
  178.     double  btof();
  179.  
  180.     while (*eqn == ' ' || *eqn == '\t')
  181.     eqn++;
  182.  
  183.     if (!*eqn)
  184.     error("missing expression");
  185.  
  186.     else if (opptr = findop(eqn)) {
  187.     strncpy (left, eqn, opptr - eqn);
  188.     left[opptr - eqn] = '\0';
  189.     vleft = calc(left);
  190.     switch (*opptr) {
  191.         case '+': 
  192.         return(vleft + calc(++opptr));
  193.         case '-': 
  194.         return(vleft - calc(++opptr));
  195.         case '/': 
  196.         if ((temp = calc(++opptr)) == 0.0)
  197.             error ("division by zero");
  198.         else
  199.             return(vleft / temp);
  200.         case 'x': 
  201.         case 'X': 
  202.         case '*': 
  203.         return(vleft * calc(++opptr));
  204.         case '%': 
  205.         if ((temp = fabs(floor (calc(++opptr)+0.5))) == 0.0 ||
  206.                 temp > (TwoToTheNLessOne-1))
  207.             error("bad argument to modulo");
  208.         return((double)((long) (floor (vleft) + 0.5) %
  209.                         (long) temp));
  210.         case ':': 
  211.         return(pow(vleft, calc(++opptr)));
  212.         case '&': 
  213.         return((double)((unsigned long)vleft &
  214.                 (unsigned long)calc(++opptr)));
  215.         case '^': 
  216.         return((double)((unsigned long)vleft ^
  217.                 (unsigned long)calc(++opptr)));
  218.         case '|': 
  219.         return((double)((unsigned long)vleft |
  220.                 (unsigned long)calc(++opptr)));
  221.         case '<': 
  222.         return((double)((unsigned long)vleft <<
  223.                 (unsigned long)calc(++opptr)));
  224.         case '>': 
  225.         return((double)((unsigned long)vleft >>
  226.                 (unsigned long)calc(++opptr)));
  227.  
  228.         default: /* implied multiplication */
  229.         return(vleft * calc(opptr));
  230.         }
  231.     }
  232.  
  233.     else if (index (OP, *eqn)) {
  234.         strcpy(eqncp, ++eqn);
  235.         eqncp[strlen (eqncp) - 1] = '\0';
  236.         return(calc (eqncp));
  237.     }
  238.  
  239.     else if (*eqn == '+')
  240.         return(calc(eqn+1));
  241.     else if (*eqn == '-')
  242.         return(-1.0 * calc(eqn+1));
  243.         else if (*eqn == '~')
  244.         return((double)(~ (unsigned long)calc(eqn+1)));
  245.  
  246.     else if (strncmp(eqn, "sin", 3) == 0)
  247.         return(sin (calc(eqn+3) * PIdiv180));
  248.     else if (strncmp(eqn, "cos", 3) == 0)
  249.         return(cos (calc(eqn+3) * PIdiv180));
  250.     else if (strncmp(eqn, "tan", 3) == 0)
  251.         return(tan (calc(eqn+3) * PIdiv180));
  252.     else if (strncmp(eqn, "atan", 4) == 0)
  253.         return(atan (calc(eqn+4)) / PIdiv180);
  254.  
  255.     else if (strncmp(eqn, "sqrt", 4) == 0)
  256.         return(sqrt (calc(eqn+4)));
  257.     else if (strncmp(eqn, "log", 3) == 0)
  258.         return(log10 (calc(eqn+3)));
  259.     else if (strncmp(eqn, "ln", 2) == 0)
  260.         return(log (calc(eqn+2)));
  261.     else if (strncmp(eqn, "exp", 3) == 0)
  262.         return(exp (calc(eqn+3)));
  263.     else if (strncmp(eqn, "pi", 2) == 0 || strncmp(eqn, "PI", 2) == 0)
  264.         return(PI);
  265.     else if (strncmp(eqn, "prev", 4) == 0)
  266.         return(answer);
  267.  
  268.     else if (*eqn == 'h') {
  269.         sscanf(eqn+1, "%lx", &tempi);
  270.         return((double) tempi);
  271.     }
  272.     else if (*eqn == 'o') {
  273.         sscanf(eqn+1, "%lo", &tempi);
  274.         return((double) tempi);
  275.     }
  276.     else if (*eqn == 'b')
  277.         return(btof(eqn+1));
  278.         else if (*eqn == '@')
  279.         return((double) *(eqn+1));
  280.     else if (!(isdigit(*eqn) || *eqn == '.') )
  281.         error("illegal expression");
  282.     else
  283.         return(atof (eqn));
  284. }
  285.  
  286.  
  287.  
  288. /*
  289.  * Takes a parenthesized expression and returns a pointer to the closing paren.
  290.  */
  291.  
  292. char *findclose(s)
  293. char *s;
  294. {
  295.     register int lev = 0;
  296.  
  297.     for (; *s && !(lev==1 && index(CP, *s)); s++)
  298.     if (index(OP, *s))
  299.         lev++;
  300.     else if (index(CP, *s))
  301.         lev--;
  302.  
  303.     if (!*s)
  304.     error("unmatched open paren");
  305.     else
  306.     return(s);
  307. }
  308.  
  309.  
  310. /** Precedence levels for binary operators **/
  311.  
  312. #define OPTYPE int
  313. #define NONOP  0
  314. #define NULLOP 1
  315. #define EXP    3
  316. #define MULT   5
  317. #define DIV    5
  318. #define MOD    6
  319. #define ADD    7
  320. #define SUBTR  7
  321. #define LSHIFT 8
  322. #define RSHIFT 8
  323. #define BAND   9
  324. #define BXOR   10
  325. #define BOR    11
  326.  
  327. char *findop(s)
  328. char *s;
  329. {
  330.     register OPTYPE op;
  331.     OPTYPE bestop = NULLOP;
  332.     char *bestptr = 0;
  333.     register char last = '\0';
  334.  
  335.     while (*s) {
  336.     op = NONOP;
  337.     if (*s == ' ' || *s == '\t') { /* Don't let lasts get assigned to space */
  338.         s++;
  339.         continue;
  340.     }
  341.     else {
  342.         switch (*s) {
  343.         case ':': 
  344.             op = EXP;
  345.             break;
  346.         case '%':
  347.             op = MOD;
  348.             break;
  349.         case 'x': 
  350.         case 'X': 
  351.         case '*': 
  352.             if (!(*s=='x' && last=='e' && *(s+1)=='p')) /* exp() function */
  353.             op = MULT;
  354.             break;
  355.         case '/': 
  356.             op = DIV;
  357.             break;
  358.         case '+': 
  359.             /* "+" means unary plus (not add) if it follows a
  360.              * symbol or a function name.
  361.              */
  362.             if (!index(SYMBOLS, last))
  363.             op = ADD;
  364.             break;
  365.         case '-': 
  366.             /* "-" means unary minus (not subtract) if it follows a
  367.              * symbol or a function name.
  368.              */
  369.             if (!index(SYMBOLS, last))
  370.             op = SUBTR;
  371.             break;
  372.         case '<':
  373.             op = LSHIFT;
  374.             break;
  375.         case '>':
  376.             op = RSHIFT;
  377.             break;
  378.         case '&':
  379.             op = BAND;
  380.             break;
  381.         case '^':
  382.             op = BXOR;
  383.             break;
  384.         case '|':
  385.             op = BOR;
  386.             break;
  387.         default:
  388.             /* Implied multiplication occurs when a digit or a
  389.              * close paren is followed by a func-call, or an open
  390.              * paren.  The check for "co" and "at" is to distinguish
  391.              * 'c' and 'a' as hex digits and their appearance in
  392.              * "cos" and "atan".
  393.              */
  394.             if ((last && (isdigit(last) || index(CP, last))) && 
  395.                 ((islower(*s)  || index(OP, *s)) ||
  396.              (!isdigit(last) && isdigit(*s))) &&
  397.             (!index(SYMBOL_LOWERS, *s) ||
  398.              !strncmp("co", s, 2) || !strncmp("at", s, 2)))
  399.                 op = MULT;
  400.         }
  401.  
  402.         if (op >= bestop) {
  403.         bestop = op;
  404.         bestptr = s;
  405.         }
  406.     }
  407.  
  408.     if (index(OP, *s))
  409.         s = findclose(s);
  410.  
  411.     last = *s++;
  412.     }
  413.     return(bestptr);
  414. }
  415.  
  416.  
  417.  
  418. /*
  419.  * Places a binary representation of "val" in the string "s" and returns
  420.  * a pointer to the start of that string.  "val" should be (or will be coerced
  421.  * to )an integer between +/- 2^n, where n is the number of bits in a long int.
  422.  */
  423.  
  424. char *ftob(s, val)
  425. char *s;
  426. double val;
  427. {
  428.     unsigned long lval = (val<0.0 ? -val : val);
  429.     unsigned long i;
  430.     char *s0 = s;
  431.  
  432.     if (lval == 0)
  433.     *s++ = '0';
  434.     else 
  435.     for (i = TwoToTheNLessOne; i; i>>=1)
  436.         if (lval & i)
  437.         *s++ = '1';
  438.         else {
  439.         *s = '0';
  440.         if (s != s0)
  441.             s++;
  442.         }
  443.  
  444.     *s = '\0';
  445.     return(s0);
  446. }
  447.  
  448.  
  449.  
  450. /*
  451.  * Takes a string containing a binary number and returns its 
  452.  * decimal equivelant.
  453.  */
  454.  
  455. double btof(s)
  456. char *s;
  457. {
  458.     unsigned long i, val = 0;
  459.  
  460.     for (i = (unsigned long)1<<(strlen(s)-1); i; i>>=1, s++)
  461.     if (*s == '1')
  462.         val |= i;
  463.     else if (*s != '0')
  464.         error("bad binary digit");
  465.  
  466.  
  467.     return((double) val);
  468. }
  469.  
  470.  
  471.  
  472.  
  473. /*
  474.  * Reads a line from the stdin, and puts it in s after striping
  475.  * off all spaces and tabs.
  476.  * Returns the length of s.
  477.  */
  478.  
  479. int getwhitlessline(s)
  480. char *s;
  481. {
  482.     register int i, c;
  483.  
  484.     for(i = 0; i <= MAXLINE && ((c=getchar()) != '\n') && c!=EOF; i+=(c!=' ' && c!='\t'))
  485.     s[i] = c;
  486.     s[i] = '\0';
  487.     return(i);
  488. }
  489.  
  490.  
  491.  
  492. /*
  493.  * Displays an error message and exits unless a jmp_buf has been
  494.  * set to return to in just such emergencies.  (Capt. Kirk always
  495.  * defined a jmp_buf.)
  496.  */
  497.  
  498. error(msg)
  499. char *msg;
  500. {
  501.     printf("calc: error--%s\n", msg);
  502.     if (abort_on_err)
  503.     exit(1);
  504.     else
  505.     longjmp(err_recover, 0);
  506. }
  507.  
  508.