home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / bc-1.03-base.tgz / bc-1.03-base.tar / fsf / bc / dc-eval.c < prev    next >
C/C++ Source or Header  |  1994-08-08  |  16KB  |  580 lines

  1. /* 
  2.  * evaluate the dc language, from a FILE* or a string
  3.  *
  4.  * Copyright (C) 1994 Free Software Foundation, Inc.
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2, or (at your option)
  9.  * any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, you can either send email to this
  18.  * program's author (see below) or write to: The Free Software Foundation,
  19.  * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
  20.  */
  21.  
  22. /* This is the only module which knows about the dc input language */
  23.  
  24. #include "config.h"
  25.  
  26. #include <stdio.h>
  27. #ifdef HAVE_STRING_H
  28. # include <string.h>    /* memchr */
  29. #else
  30. # ifdef HAVE_MEMORY_H
  31. #  include <memory.h>    /* memchr, maybe */
  32. # else
  33. #  ifdef HAVE_STRINGS_H
  34. #   include <strings.h>    /* memchr, maybe */
  35. #  endif
  36. #endif
  37. #endif
  38. #include "dc.h"
  39. #include "dc-proto.h"
  40.  
  41. typedef enum {
  42.     DC_OKAY,        /* no further intervention needed for this command */
  43.     DC_EATONE,        /* caller needs to eat the lookahead char */
  44.     DC_QUIT,        /* quit out of unwind_depth levels of evaluation */
  45.  
  46.     /* with the following return values, the caller does not have to 
  47.      * fret about rescan_stdin's value
  48.      */
  49.     DC_INT,            /* caller needs to parse a dc_num from input stream */
  50.     DC_STR,            /* caller needs to parse a dc_str from input stream */
  51.     DC_SYSTEM,        /* caller needs to run a system() on next input line */
  52.     DC_COMMENT,        /* caller needs to skip to the next input line */
  53.  
  54.     DC_EOF_ERROR    /* unexpected end of input; abort current eval */
  55. } dc_status;
  56.  
  57. static int dc_ibase=10;        /* input base, 2 <= dc_ibase <= DC_IBASE_MAX */
  58. static int dc_obase=10;        /* output base, 2 <= dc_obase */
  59. static int dc_scale=0;        /* scale (see user documentaton) */
  60.  
  61. /* forward reference */
  62. static dc_status dc_evalstr DC_PROTO((dc_data));
  63.  
  64. /* for Quitting evaluations */
  65. static int unwind_depth=0;
  66.  
  67. /* if true, active Quit will not exit program */
  68. static dc_boolean unwind_noexit=DC_FALSE;
  69.  
  70. /* if true, stdin has been mucked with, dc_evalfile() needs to resyncronize */
  71. static dc_boolean rescan_stdin=DC_FALSE;
  72.  
  73.  
  74. /* input_fil and input_str are passed as arguments to dc_getnum */
  75.  
  76. /* used by the input_* functions: */
  77. static FILE *input_fil_fp;
  78. static const char *input_str_string;
  79.  
  80. /* Since we have a need for two characters of pushback, and
  81.  * ungetc() only guarantees one, we place the second pushback here
  82.  */
  83. static int input_pushback;
  84.  
  85. /* passed as an argument to dc_getnum */
  86. static int
  87. input_fil DC_DECLVOID()
  88. {
  89.     if (input_pushback != EOF){
  90.         int c = input_pushback;
  91.         input_pushback = EOF;
  92.         return c;
  93.     }
  94.     return getc(input_fil_fp);
  95. }
  96.  
  97. /* passed as an argument to dc_getnum */
  98. static int
  99. input_str DC_DECLVOID()
  100. {
  101.     if (!*input_str_string)
  102.         return EOF;
  103.     return *input_str_string++;
  104. }
  105.  
  106.  
  107.  
  108. /* takes a string and evals it; frees the string when done */
  109. /* Wrapper around dc_evalstr to avoid duplicating the free call
  110.  * at all possible return points.
  111.  */
  112. static dc_status
  113. dc_eval_and_free_str DC_DECLARG((string))
  114.     dc_data string DC_DECLEND
  115. {
  116.     dc_status status;
  117.  
  118.     status = dc_evalstr(string);
  119.     if (string.dc_type == DC_STRING)
  120.         dc_free_str(&string.v.string);
  121.     return status;
  122. }
  123.  
  124.  
  125. /* dc_func does the grunt work of figuring out what each input
  126.  * character means; used by both dc_evalstr and dc_evalfile
  127.  *
  128.  * c -> the "current" input character under consideration
  129.  * peekc -> the lookahead input character
  130.  */
  131. static dc_status
  132. dc_func DC_DECLARG((c, peekc))
  133.     int c DC_DECLSEP
  134.     int peekc DC_DECLEND
  135. {
  136.     /* we occasionally need these for temporary data */
  137.     /* Despite the GNU coding standards, it is much easier
  138.      * to have these decared once here, since this function
  139.      * is just one big switch statement.
  140.      */
  141.     dc_data datum;
  142.     int tmpint;
  143.  
  144.     switch (c){
  145.     case '_': case '.':
  146.     case '0': case '1': case '2': case '3':
  147.     case '4': case '5': case '6': case '7':
  148.     case '8': case '9': case 'A': case 'B':
  149.     case 'C': case 'D': case 'E': case 'F':
  150.         return DC_INT;
  151.     case ' ':
  152.     case '\t':
  153.     case '\n':
  154.         /* standard command separators */
  155.         break;
  156.  
  157.     case '+':    /* add top two stack elements */
  158.         dc_binop(dc_add, dc_scale);
  159.         break;
  160.     case '-':    /* subtract top two stack elements */
  161.         dc_binop(dc_sub, dc_scale);
  162.         break;
  163.     case '*':    /* multiply top two stack elements */
  164.         dc_binop(dc_mul, dc_scale);
  165.         break;
  166.     case '/':    /* divide top two stack elements */
  167.         dc_binop(dc_div, dc_scale);
  168.         break;
  169.     case '%':
  170.         /* take the remainder from  division of the top two stack elements */
  171.         dc_binop(dc_rem, dc_scale);
  172.         break;
  173.     case '^':    /* exponientiation of the top two stack elements */
  174.         dc_binop(dc_exp, dc_scale);
  175.         break;
  176.     case '<':
  177.         /* eval register named by peekc if
  178.          * less-than holds for top two stack elements
  179.          */
  180.         if (peekc == EOF)
  181.             return DC_EOF_ERROR;
  182.         if (dc_cmpop() <  0)
  183.             if (dc_register_get(peekc, &datum) == DC_SUCCESS)
  184.                 if (dc_eval_and_free_str(datum) == DC_QUIT)
  185.                     return DC_QUIT;
  186.         return DC_EATONE;
  187.     case '=':
  188.         /* eval register named by peekc if
  189.          * equal-to holds for top two stack elements
  190.          */
  191.         if (peekc == EOF)
  192.             return DC_EOF_ERROR;
  193.         if (dc_cmpop() == 0)
  194.             if (dc_register_get(peekc, &datum) == DC_SUCCESS)
  195.                 if (dc_eval_and_free_str(datum) == DC_QUIT)
  196.                     return DC_QUIT;
  197.         return DC_EATONE;
  198.     case '>':
  199.         /* eval register named by peekc if
  200.          * greater-than holds for top two stack elements
  201.          */
  202.         if (peekc == EOF)
  203.             return DC_EOF_ERROR;
  204.         if (dc_cmpop()  > 0)
  205.             if (dc_register_get(peekc, &datum) == DC_SUCCESS)
  206.                 if (dc_eval_and_free_str(datum) == DC_QUIT)
  207.                     return DC_QUIT;
  208.         return DC_EATONE;
  209.     case '?':    /* read a lnie from standard-input and eval it */
  210.         for (c=peekc; c=='\n'; c=getc(stdin))
  211.             ;
  212.         ungetc(c, stdin);
  213.         if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
  214.             return DC_QUIT;
  215.         rescan_stdin = DC_TRUE;
  216.         return DC_OKAY;
  217.     case '[':    /* read to balancing ']' into a dc_str */
  218.         return DC_STR;
  219.     case '!':    /* read to newline and call system() on resulting string */
  220.         return DC_SYSTEM;
  221.     case '#':    /* comment; skip remainder of current line */
  222.         return DC_COMMENT;
  223.  
  224.     case 'c':    /* clear whole stack */
  225.         dc_clear_stack();
  226.         break;
  227.     case 'd':    /* duplicate the datum on the top of stack */
  228.         if (dc_top_of_stack(&datum) == DC_SUCCESS)
  229.             dc_push(dc_dup(datum));
  230.         break;
  231.     case 'f':    /* print list of all stack items */
  232.         dc_printall(dc_obase);
  233.         break;
  234.     case 'i':    /* set input base to value on top of stack */
  235.         if (dc_pop(&datum) == DC_SUCCESS){
  236.             tmpint = 0;
  237.             if (datum.dc_type == DC_NUMBER)
  238.                 tmpint = dc_num2int(datum.v.number, DC_TRUE);
  239.             if ( ! (2 <= tmpint  &&  tmpint <= DC_IBASE_MAX) )
  240.                 fprintf(stderr,
  241.                         "%s: input base must be a number \
  242. between 2 and %d (inclusive)\n",
  243.                         progname, DC_IBASE_MAX);
  244.             else
  245.                 dc_ibase = tmpint;
  246.         }
  247.         break;
  248.     case 'k':    /* set scale to value on top of stack */
  249.         if (dc_pop(&datum) == DC_SUCCESS){
  250.             tmpint = -1;
  251.             if (datum.dc_type == DC_NUMBER)
  252.                 tmpint = dc_num2int(datum.v.number, DC_TRUE);
  253.             if ( ! (tmpint >= 0) )
  254.                 fprintf(stderr,
  255.                         "%s: scale must be a nonnegative number\n",
  256.                         progname);
  257.             else
  258.                 dc_scale = tmpint;
  259.         }
  260.         break;
  261.     case 'l':    /* "load" -- push value on top of register stack named
  262.                  * by peekc onto top of evaluation stack; does not
  263.                  * modify the register stack
  264.                  */
  265.         if (peekc == EOF)
  266.             return DC_EOF_ERROR;
  267.         if (dc_register_get(peekc, &datum) == DC_SUCCESS)
  268.             dc_push(datum);
  269.         return DC_EATONE;
  270.     case 'o':    /* set output base to value on top of stack */
  271.         if (dc_pop(&datum) == DC_SUCCESS){
  272.             tmpint = 0;
  273.             if (datum.dc_type == DC_NUMBER)
  274.                 tmpint = dc_num2int(datum.v.number, DC_TRUE);
  275.             if ( ! (tmpint > 1) )
  276.                 fprintf(stderr,
  277.                         "%s: output base must be a number greater than 1\n",
  278.                         progname);
  279.             else
  280.                 dc_obase = tmpint;
  281.         }
  282.         break;
  283.     case 'p':    /* print the datum on the top of stack */
  284.         if (dc_top_of_stack(&datum) == DC_SUCCESS)
  285.             dc_print(datum, dc_obase);
  286.         break;
  287.     case 'q':    /* quit two levels of evaluation, posibly exiting program */
  288.         unwind_depth = 2;
  289.         unwind_noexit = DC_FALSE;
  290.         return DC_QUIT;
  291.     case 's':    /* "store" -- replace top of register stack named
  292.                  * by peekc with the value popped from the top
  293.                  * of the evaluation stack
  294.                  */
  295.         if (peekc == EOF)
  296.             return DC_EOF_ERROR;
  297.         if (dc_pop(&datum) == DC_SUCCESS)
  298.             dc_register_set(peekc, datum);
  299.         return DC_EATONE;
  300.     case 'v':    /* replace top of stack with its square root */
  301.         if (dc_pop(&datum) == DC_SUCCESS){
  302.             dc_num tmpnum;
  303.             if (datum.dc_type != DC_NUMBER){
  304.                 fprintf(stderr,
  305.                         "%s: square root of nonnumeric attempted\n",
  306.                         progname);
  307.             }else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
  308.                 dc_free_num(&datum.v.number);
  309.                 datum.v.number = tmpnum;
  310.                 dc_push(datum);
  311.             }
  312.         }
  313.         break;
  314.     case 'x':    /* eval the datum popped from top of stack */
  315.         if (dc_pop(&datum) == DC_SUCCESS){
  316.             if (datum.dc_type == DC_STRING){
  317.                 if (dc_eval_and_free_str(datum) == DC_QUIT)
  318.                     return DC_QUIT;
  319.             }else if (datum.dc_type == DC_NUMBER){
  320.                 dc_push(datum);
  321.             }else{
  322.                 dc_garbage("at top of stack", -1);
  323.             }
  324.         }
  325.         break;
  326.     case 'z':    /* push the current stack depth onto the top of stack */
  327.         dc_push(dc_int2data(dc_tell_stackdepth()));
  328.         break;
  329.  
  330.     case 'I':    /* push the current input base onto the stack */
  331.         dc_push(dc_int2data(dc_ibase));
  332.         break;
  333.     case 'K':    /* push the current scale onto the stack */
  334.         dc_push(dc_int2data(dc_scale));
  335.         break;
  336.     case 'L':    /* pop a value off of register stack named by peekc
  337.                  * and push it onto the evaluation stack
  338.                  */
  339.         if (peekc == EOF)
  340.             return DC_EOF_ERROR;
  341.         if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
  342.             dc_push(datum);
  343.         return DC_EATONE;
  344.     case 'O':    /* push the current output base onto the stack */
  345.         dc_push(dc_int2data(dc_obase));
  346.         break;
  347.     case 'P':    /* print the value popped off of top-of-stack;
  348.                  * do not add a trailing newline
  349.                  */
  350.         if (dc_pop(&datum) == DC_SUCCESS){
  351.             if (datum.dc_type == DC_STRING)
  352.                 dc_out_str(datum.v.string, DC_FALSE, DC_TRUE);
  353.             else if (datum.dc_type == DC_NUMBER)
  354.                 dc_out_num(datum.v.number, dc_obase, DC_FALSE, DC_TRUE);
  355.             else
  356.                 dc_garbage("at top of stack", -1);
  357.         }
  358.         break;
  359.     case 'Q':    /* quit out of top-of-stack nested evals;
  360.                  * pops value from stack;
  361.                  * does not exit program (stops short if necessary)
  362.                  */
  363.         if (dc_pop(&datum) == DC_SUCCESS){
  364.             unwind_depth = 0;
  365.             unwind_noexit = DC_TRUE;
  366.             if (datum.dc_type == DC_NUMBER)
  367.                 unwind_depth = dc_num2int(datum.v.number, DC_TRUE);
  368.             if (unwind_depth > 0)
  369.                 return DC_QUIT;
  370.             fprintf(stderr,
  371.                     "%s: Q command requires a positive number\n",
  372.                     progname);
  373.         }
  374.         break;
  375.     case 'S':    /* pop a value off of the evaluation stack
  376.                  * and push it onto the register stack named by peekc
  377.                  */
  378.         if (peekc == EOF)
  379.             return DC_EOF_ERROR;
  380.         if (dc_pop(&datum) == DC_SUCCESS)
  381.             dc_register_push(peekc, datum);
  382.         return DC_EATONE;
  383.     case 'X':    /* replace the number on top-of-stack with its scale factor */
  384.         if (dc_pop(&datum) == DC_SUCCESS){
  385.             tmpint = 0;
  386.             if (datum.dc_type == DC_NUMBER)
  387.                 tmpint = dc_tell_scale(datum.v.number, DC_TRUE);
  388.             dc_push(dc_int2data(tmpint));
  389.         }
  390.         break;
  391.     case 'Z':    /* replace the datum on the top-of-stack with its length */
  392.         if (dc_pop(&datum) == DC_SUCCESS)
  393.             dc_push(dc_int2data(dc_tell_length(datum, DC_TRUE)));
  394.         break;
  395.  
  396.     case ':':    /* store into array */
  397.         if (peekc == EOF)
  398.             return DC_EOF_ERROR;
  399.         if (dc_pop(&datum) == DC_SUCCESS){
  400.             tmpint = -1;
  401.             if (datum.dc_type == DC_NUMBER)
  402.                 tmpint = dc_num2int(datum.v.number, DC_TRUE);
  403.             if (dc_pop(&datum) == DC_SUCCESS){
  404.                 if (tmpint < 0)
  405.                     fprintf(stderr,
  406.                             "%s: array index must be a nonnegative integer\n",
  407.                             progname);
  408.                 else
  409.                     dc_array_set(peekc, tmpint, datum);
  410.             }
  411.         }
  412.         return DC_EATONE;
  413.     case ';':    /* retreive from array */
  414.         if (peekc == EOF)
  415.             return DC_EOF_ERROR;
  416.         if (dc_pop(&datum) == DC_SUCCESS){
  417.             tmpint = -1;
  418.             if (datum.dc_type == DC_NUMBER)
  419.                 tmpint = dc_num2int(datum.v.number, DC_TRUE);
  420.             if (tmpint < 0)
  421.                 fprintf(stderr,
  422.                         "%s: array index must be a nonnegative integer\n",
  423.                         progname);
  424.             else
  425.                 dc_push(dc_array_get(peekc, tmpint));
  426.         }
  427.         return DC_EATONE;
  428.  
  429.     default:    /* What did that user mean? */
  430.         fprintf(stderr, "%s: ", progname);
  431.         dc_show_id(stdout, c, " unimplemented\n");
  432.         break;
  433.     }
  434.     return DC_OKAY;
  435. }
  436.  
  437.  
  438. /* takes a string and evals it */
  439. static dc_status
  440. dc_evalstr DC_DECLARG((string))
  441.     dc_data string DC_DECLEND
  442. {
  443.     const char *s;
  444.     const char *end;
  445.     const char *p;
  446.     size_t len;
  447.     int c;
  448.     int peekc;
  449.     int count;
  450.  
  451.     if (string.dc_type != DC_STRING){
  452.         fprintf(stderr,
  453.                 "%s: eval called with non-string argument\n",
  454.                 progname);
  455.         return DC_OKAY;
  456.     }
  457.     s = dc_str2charp(string.v.string);
  458.     end = s + dc_strlen(string.v.string);
  459.     while (s < end){
  460.         c = *(const unsigned char *)s++;
  461.         peekc = EOF;
  462.         if (s < end)
  463.             peekc = *(const unsigned char *)s;
  464.         switch (dc_func(c, peekc)){
  465.         case DC_OKAY:
  466.             break;
  467.         case DC_EATONE:
  468.             if (peekc != EOF)
  469.                 ++s;
  470.             break;
  471.         case DC_QUIT:
  472.             if (unwind_depth > 0){
  473.                 --unwind_depth;
  474.                 return DC_QUIT;
  475.             }
  476.             return DC_OKAY;
  477.  
  478.         case DC_INT:
  479.             input_str_string = s - 1;
  480.             dc_push(dc_getnum(input_str, dc_ibase, &peekc));
  481.             s = input_str_string;
  482.             if (peekc != EOF)
  483.                 --s;
  484.             break;
  485.         case DC_STR:
  486.             count = 1;
  487.             for (p=s; p<end && count>0; ++p)
  488.                 if (*p == ']')
  489.                     --count;
  490.                 else if (*p == '[')
  491.                     ++count;
  492.             len = p - s;
  493.             dc_push(dc_makestring(s, len-1));
  494.             s = p;
  495.             break;
  496.         case DC_SYSTEM:
  497.             s = dc_system(s);
  498.         case DC_COMMENT:
  499.             s = memchr(s, '\n', (size_t)(end-s));
  500.             if (!s)
  501.                 s = end;
  502.             ++s;
  503.             break;
  504.  
  505.         case DC_EOF_ERROR:
  506.             fprintf(stderr, "%s: unexpected EOS\n", progname);
  507.             return DC_OKAY;
  508.         }
  509.     }
  510.     return DC_OKAY;
  511. }
  512.  
  513.  
  514. /* This is the main function of the whole DC program.
  515.  * Reads the file described by fp, calls dc_func to do
  516.  * the dirty work, and takes care of dc_func's shortcomings.
  517.  */
  518. int
  519. dc_evalfile DC_DECLARG((fp))
  520.     FILE *fp DC_DECLEND
  521. {
  522.     int c;
  523.     int peekc;
  524.     dc_data datum;
  525.  
  526.     for (c=getc(fp); c!=EOF; c=peekc){
  527.         peekc = getc(fp);
  528.         rescan_stdin = DC_FALSE;
  529.         switch (dc_func(c, peekc)){
  530.         case DC_OKAY:
  531.             if (rescan_stdin == DC_TRUE  &&  fp == stdin)
  532.                 peekc = getc(fp);
  533.             break;
  534.         case DC_EATONE:
  535.             peekc = getc(fp);
  536.             break;
  537.         case DC_QUIT:
  538.             if (unwind_noexit != DC_TRUE)
  539.                 return DC_SUCCESS;
  540.             fprintf(stderr,
  541.                     "%s: Q command argument exceeded string execution depth\n",
  542.                     progname);
  543.             if (rescan_stdin == DC_TRUE  &&  fp == stdin)
  544.                 peekc = getc(fp);
  545.             break;
  546.  
  547.         case DC_INT:
  548.             input_fil_fp = fp;
  549.             input_pushback = c;
  550.             ungetc(peekc, fp);
  551.             dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
  552.             break;
  553.         case DC_STR:
  554.             ungetc(peekc, fp);
  555.             datum = dc_readstring(fp, '[', ']');
  556.             dc_push(datum);
  557.             peekc = getc(fp);
  558.             break;
  559.         case DC_SYSTEM:
  560.             ungetc(peekc, fp);
  561.             datum = dc_readstring(stdin, '\n', '\n');
  562.             (void)dc_system(dc_str2charp(datum.v.string));
  563.             dc_free_str(&datum.v.string);
  564.             peekc = getc(fp);
  565.             break;
  566.         case DC_COMMENT:
  567.             while (peekc!=EOF && peekc!='\n')
  568.                 peekc = getc(fp);
  569.             if (peekc != EOF)
  570.                 peekc = getc(fp);
  571.             break;
  572.  
  573.         case DC_EOF_ERROR:
  574.             fprintf(stderr, "%s: unexpected EOF\n", progname);
  575.             return DC_FAIL;
  576.         }
  577.     }
  578.     return DC_SUCCESS;
  579. }
  580.