5761225 [rkeene@sledge /home/rkeene/devel/old/bc-dos/dc]$ cat -n eval.c
  1 /* 
  2  * evaluate the dc language, from a FILE* or a string
  3  *
  4  * Copyright (C) 1994, 1997, 1998 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 {DC_FALSE, DC_TRUE} dc_boolean;
 42 
 43 typedef enum {
 44     DC_OKAY = DC_SUCCESS, /* no further intervention needed for this command */
 45     DC_EATONE,      /* caller needs to eat the lookahead char */
 46     DC_QUIT,        /* quit out of unwind_depth levels of evaluation */
 47 
 48     /* with the following return values, the caller does not have to 
 49      * fret about stdin_lookahead's value
 50      */
 51     DC_INT,         /* caller needs to parse a dc_num from input stream */
 52     DC_STR,         /* caller needs to parse a dc_str from input stream */
 53     DC_SYSTEM,      /* caller needs to run a system() on next input line */
 54     DC_COMMENT,     /* caller needs to skip to the next input line */
 55     DC_NEGCMP,      /* caller needs to re-call dc_func() with `negcmp' set */
 56 
 57     DC_EOF_ERROR    /* unexpected end of input; abort current eval */
 58 } dc_status;
 59 
 60 static int dc_ibase=10;     /* input base, 2 <= dc_ibase <= DC_IBASE_MAX */
 61 static int dc_obase=10;     /* output base, 2 <= dc_obase */
 62 static int dc_scale=0;      /* scale (see user documentaton) */
 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 /*
 71  * Used to synchronize lookahead on stdin for '?' command.
 72  * If set to EOF then lookahead is used up.
 73  */
 74 static int stdin_lookahead=EOF;
 75 
 76 
 77 /* input_fil and input_str are passed as arguments to dc_getnum */
 78 
 79 /* used by the input_* functions: */
 80 static FILE *input_fil_fp;
 81 static const char *input_str_string;
 82 
 83 /* Since we have a need for two characters of pushback, and
 84  * ungetc() only guarantees one, we place the second pushback here
 85  */
 86 static int input_pushback;
 87 
 88 /* passed as an argument to dc_getnum */
 89 static int
 90 input_fil DC_DECLVOID()
 91 {
 92     if (input_pushback != EOF){
 93         int c = input_pushback;
 94         input_pushback = EOF;
 95         return c;
 96     }
 97     return getc(input_fil_fp);
 98 }
 99 
100 /* passed as an argument to dc_getnum */
101 static int
102 input_str DC_DECLVOID()
103 {
104     if (!*input_str_string)
105         return EOF;
106     return *input_str_string++;
107 }
108 
109 
110 
111 /* takes a string and evals it; frees the string when done */
112 /* Wrapper around dc_evalstr to avoid duplicating the free call
113  * at all possible return points.
114  */
115 static int
116 dc_eval_and_free_str DC_DECLARG((string))
117     dc_data string DC_DECLEND
118 {
119     dc_status status;
120 
121     status = dc_evalstr(string);
122     if (string.dc_type == DC_STRING)
123         dc_free_str(&string.v.string);
124     return status;
125 }
126 
127 
128 /* dc_func does the grunt work of figuring out what each input
129  * character means; used by both dc_evalstr and dc_evalfile
130  *
131  * c -> the "current" input character under consideration
132  * peekc -> the lookahead input character
133  * negcmp -> negate comparison test (for <,=,> commands)
134  */
135 static dc_status
136 dc_func DC_DECLARG((c, peekc, negcmp))
137     int c DC_DECLSEP
138     int peekc DC_DECLSEP
139     int negcmp DC_DECLEND
140 {
141     /* we occasionally need these for temporary data */
142     /* Despite the GNU coding standards, it is much easier
143      * to have these declared once here, since this function
144      * is just one big switch statement.
145      */
146     dc_data datum;
147     int tmpint;
148 
149     switch (c){
150     case '_': case '.':
151     case '0': case '1': case '2': case '3':
152     case '4': case '5': case '6': case '7':
153     case '8': case '9': case 'A': case 'B':
154     case 'C': case 'D': case 'E': case 'F':
155         return DC_INT;
156     case ' ':
157     case '\t':
158     case '\n':
159         /* standard command separators */
160         break;
161 
162     case '+':   /* add top two stack elements */
163         dc_binop(dc_add, dc_scale);
164         break;
165     case '-':   /* subtract top two stack elements */
166         dc_binop(dc_sub, dc_scale);
167         break;
168     case '*':   /* multiply top two stack elements */
169         dc_binop(dc_mul, dc_scale);
170         break;
171     case '/':   /* divide top two stack elements */
172         dc_binop(dc_div, dc_scale);
173         break;
174     case '%':
175         /* take the remainder from division of the top two stack elements */
176         dc_binop(dc_rem, dc_scale);
177         break;
178     case '~':
179         /* Do division on the top two stack elements.  Return the
180          * quotient as next-to-top of stack and the remainder as
181          * top-of-stack.
182          */
183         dc_binop2(dc_divrem, dc_scale);
184         break;
185     case '|':
186         /* Consider the top three elements of the stack as (base, exp, mod),
187          * where mod is top-of-stack, exp is next-to-top, and base is
188          * second-from-top.  Mod must be non-zero and exp must be a
189          * non-negative integer.   Push the result of raising base to the exp
190          * power, reduced modulo mod.  If we had base in register b, exp in
191          * register e, and mod in register m then this is conceptually
192          * equivalent to "lble^lm%", but it is implemented in a more efficient
193          * manner, and can handle arbritrarily large values for exp.
194          */
195         dc_triop(dc_modexp, dc_scale);
196         break;
197     case '^':   /* exponientiation of the top two stack elements */
198         dc_binop(dc_exp, dc_scale);
199         break;
200     case '<':
201         /* eval register named by peekc if
202          * less-than holds for top two stack elements
203          */
204         if (peekc == EOF)
205             return DC_EOF_ERROR;
206         if ( (dc_cmpop() <  0) == !negcmp )
207             if (dc_register_get(peekc, &datum) == DC_SUCCESS)
208                 if (dc_eval_and_free_str(datum) == DC_QUIT)
209                     return DC_QUIT;
210         return DC_EATONE;
211     case '=':
212         /* eval register named by peekc if
213          * equal-to holds for top two stack elements
214          */
215         if (peekc == EOF)
216             return DC_EOF_ERROR;
217         if ( (dc_cmpop() == 0) == !negcmp )
218             if (dc_register_get(peekc, &datum) == DC_SUCCESS)
219                 if (dc_eval_and_free_str(datum) == DC_QUIT)
220                     return DC_QUIT;
221         return DC_EATONE;
222     case '>':
223         /* eval register named by peekc if
224          * greater-than holds for top two stack elements
225          */
226         if (peekc == EOF)
227             return DC_EOF_ERROR;
228         if ( (dc_cmpop() >  0) == !negcmp )
229             if (dc_register_get(peekc, &datum) == DC_SUCCESS)
230                 if (dc_eval_and_free_str(datum) == DC_QUIT)
231                     return DC_QUIT;
232         return DC_EATONE;
233     case '?':   /* read a line from standard-input and eval it */
234         if (stdin_lookahead != EOF){
235             ungetc(stdin_lookahead, stdin);
236             stdin_lookahead = EOF;
237         }
238         if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
239             return DC_QUIT;
240         return DC_OKAY;
241     case '[':   /* read to balancing ']' into a dc_str */
242         return DC_STR;
243     case '!':   /* read to newline and call system() on resulting string */
244         if (peekc == '<' || peekc == '=' || peekc == '>')
245             return DC_NEGCMP;
246         return DC_SYSTEM;
247     case '#':   /* comment; skip remainder of current line */
248         return DC_COMMENT;
249 
250     case 'a':   /* Convert top of stack to an ascii character. */
251         if (dc_pop(&datum) == DC_SUCCESS){
252             char tmps;
253             if (datum.dc_type == DC_NUMBER){
254                 tmps = (char) dc_num2int(datum.v.number, DC_TOSS);
255             }else if (datum.dc_type == DC_STRING){
256                 tmps = *dc_str2charp(datum.v.string);
257                 dc_free_str(&datum.v.string);
258             }else{
259                 dc_garbage("at top of stack", -1);
260             }
261             dc_push(dc_makestring(&tmps, 1));
262         }
263         break;
264     case 'c':   /* clear whole stack */
265         dc_clear_stack();
266         break;
267     case 'd':   /* duplicate the datum on the top of stack */
268         if (dc_top_of_stack(&datum) == DC_SUCCESS)
269             dc_push(dc_dup(datum));
270         break;
271     case 'f':   /* print list of all stack items */
272         dc_printall(dc_obase);
273         break;
274     case 'i':   /* set input base to value on top of stack */
275         if (dc_pop(&datum) == DC_SUCCESS){
276             tmpint = 0;
277             if (datum.dc_type == DC_NUMBER)
278                 tmpint = dc_num2int(datum.v.number, DC_TOSS);
279             if ( ! (2 <= tmpint  &&  tmpint <= DC_IBASE_MAX) )
280                 fprintf(stderr,
281                         "%s: input base must be a number \
282 between 2 and %d (inclusive)\n",
283                         progname, DC_IBASE_MAX);
284             else
285                 dc_ibase = tmpint;
286         }
287         break;
288     case 'k':   /* set scale to value on top of stack */
289         if (dc_pop(&datum) == DC_SUCCESS){
290             tmpint = -1;
291             if (datum.dc_type == DC_NUMBER)
292                 tmpint = dc_num2int(datum.v.number, DC_TOSS);
293             if ( ! (tmpint >= 0) )
294                 fprintf(stderr,
295                         "%s: scale must be a nonnegative number\n",
296                         progname);
297             else
298                 dc_scale = tmpint;
299         }
300         break;
301     case 'l':   /* "load" -- push value on top of register stack named
302                  * by peekc onto top of evaluation stack; does not
303                  * modify the register stack
304                  */
305         if (peekc == EOF)
306             return DC_EOF_ERROR;
307         if (dc_register_get(peekc, &datum) == DC_SUCCESS)
308             dc_push(datum);
309         return DC_EATONE;
310     case 'n':   /* print the value popped off of top-of-stack;
311                  * do not add a trailing newline
312                  */
313         if (dc_pop(&datum) == DC_SUCCESS)
314             dc_print(datum, dc_obase, DC_NONL, DC_TOSS);
315         break;
316     case 'o':   /* set output base to value on top of stack */
317         if (dc_pop(&datum) == DC_SUCCESS){
318             tmpint = 0;
319             if (datum.dc_type == DC_NUMBER)
320                 tmpint = dc_num2int(datum.v.number, DC_TOSS);
321             if ( ! (tmpint > 1) )
322                 fprintf(stderr,
323                         "%s: output base must be a number greater than 1\n",
324                         progname);
325             else
326                 dc_obase = tmpint;
327         }
328         break;
329     case 'p':   /* print the datum on the top of stack,
330                  * with a trailing newline
331                  */
332         if (dc_top_of_stack(&datum) == DC_SUCCESS)
333             dc_print(datum, dc_obase, DC_WITHNL, DC_KEEP);
334         break;
335     case 'q':   /* quit two levels of evaluation, posibly exiting program */
336         unwind_depth = 1; /* the return below is the first level of returns */
337         unwind_noexit = DC_FALSE;
338         return DC_QUIT;
339     case 'r':   /* rotate (swap) the top two elements on the stack
340                  */
341         if (dc_pop(&datum) == DC_SUCCESS) {
342             dc_data datum2;
343             int two_status;
344             two_status = dc_pop(&datum2);
345             dc_push(datum);
346             if (two_status == DC_SUCCESS)
347                 dc_push(datum2);
348         }
349         break;
350     case 's':   /* "store" -- replace top of register stack named
351                  * by peekc with the value popped from the top
352                  * of the evaluation stack
353                  */
354         if (peekc == EOF)
355             return DC_EOF_ERROR;
356         if (dc_pop(&datum) == DC_SUCCESS)
357             dc_register_set(peekc, datum);
358         return DC_EATONE;
359     case 'v':   /* replace top of stack with its square root */
360         if (dc_pop(&datum) == DC_SUCCESS){
361             dc_num tmpnum;
362             if (datum.dc_type != DC_NUMBER){
363                 fprintf(stderr,
364                         "%s: square root of nonnumeric attempted\n",
365                         progname);
366             }else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
367                 dc_free_num(&datum.v.number);
368                 datum.v.number = tmpnum;
369                 dc_push(datum);
370             }
371         }
372         break;
373     case 'x':   /* eval the datum popped from top of stack */
374         if (dc_pop(&datum) == DC_SUCCESS){
375             if (datum.dc_type == DC_STRING){
376                 if (dc_eval_and_free_str(datum) == DC_QUIT)
377                     return DC_QUIT;
378             }else if (datum.dc_type == DC_NUMBER){
379                 dc_push(datum);
380             }else{
381                 dc_garbage("at top of stack", -1);
382             }
383         }
384         break;
385     case 'z':   /* push the current stack depth onto the top of stack */
386         dc_push(dc_int2data(dc_tell_stackdepth()));
387         break;
388 
389     case 'I':   /* push the current input base onto the stack */
390         dc_push(dc_int2data(dc_ibase));
391         break;
392     case 'K':   /* push the current scale onto the stack */
393         dc_push(dc_int2data(dc_scale));
394         break;
395     case 'L':   /* pop a value off of register stack named by peekc
396                  * and push it onto the evaluation stack
397                  */
398         if (peekc == EOF)
399             return DC_EOF_ERROR;
400         if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
401             dc_push(datum);
402         return DC_EATONE;
403     case 'O':   /* push the current output base onto the stack */
404         dc_push(dc_int2data(dc_obase));
405         break;
406     case 'P':
407         /* Pop the value off the top of a stack.  If it is
408          * a number, dump out the integer portion of its
409          * absolute value as a "base UCHAR_MAX+1" byte stream;
410          * if it is a string, just print it.
411          * In either case, do not append a trailing newline.
412          */
413         if (dc_pop(&datum) == DC_SUCCESS){
414             if (datum.dc_type == DC_NUMBER)
415                 dc_dump_num(datum.v.number, DC_TOSS);
416             else if (datum.dc_type == DC_STRING)
417                 dc_out_str(datum.v.string, DC_NONL, DC_TOSS);
418             else
419                 dc_garbage("at top of stack", -1);
420         }
421         break;
422     case 'Q':   /* quit out of top-of-stack nested evals;
423                  * pops value from stack;
424                  * does not exit program (stops short if necessary)
425                  */
426         if (dc_pop(&datum) == DC_SUCCESS){
427             unwind_depth = 0;
428             unwind_noexit = DC_TRUE;
429             if (datum.dc_type == DC_NUMBER)
430                 unwind_depth = dc_num2int(datum.v.number, DC_TOSS);
431             if (unwind_depth-- > 0)
432                 return DC_QUIT;
433             unwind_depth = 0;   /* paranoia */
434             fprintf(stderr,
435                     "%s: Q command requires a number >= 1\n",
436                     progname);
437         }
438         break;
439 #if 0
440     case 'R':   /* pop a value off of the evaluation stack,;
441                  * rotate the top
442                  remaining stack elements that many
443                  * places forward (negative numbers mean rotate
444                  * backward).
445                  */
446         if (dc_pop(&datum) == DC_SUCCESS){
447             tmpint = 0;
448             if (datum.dc_type == DC_NUMBER)
449                 tmpint = dc_num2int(datum.v.number, DC_TOSS);
450             dc_stack_rotate(tmpint);
451         }
452         break;
453 #endif
454     case 'S':   /* pop a value off of the evaluation stack
455                  * and push it onto the register stack named by peekc
456                  */
457         if (peekc == EOF)
458             return DC_EOF_ERROR;
459         if (dc_pop(&datum) == DC_SUCCESS)
460             dc_register_push(peekc, datum);
461         return DC_EATONE;
462     case 'X':   /* replace the number on top-of-stack with its scale factor */
463         if (dc_pop(&datum) == DC_SUCCESS){
464             tmpint = 0;
465             if (datum.dc_type == DC_NUMBER)
466                 tmpint = dc_tell_scale(datum.v.number, DC_TOSS);
467             dc_push(dc_int2data(tmpint));
468         }
469         break;
470     case 'Z':   /* replace the datum on the top-of-stack with its length */
471         if (dc_pop(&datum) == DC_SUCCESS)
472             dc_push(dc_int2data(dc_tell_length(datum, DC_TOSS)));
473         break;
474 
475     case ':':   /* store into array */
476         if (peekc == EOF)
477             return DC_EOF_ERROR;
478         if (dc_pop(&datum) == DC_SUCCESS){
479             tmpint = -1;
480             if (datum.dc_type == DC_NUMBER)
481                 tmpint = dc_num2int(datum.v.number, DC_TOSS);
482             if (dc_pop(&datum) == DC_SUCCESS){
483                 if (tmpint < 0)
484                     fprintf(stderr,
485                             "%s: array index must be a nonnegative integer\n",
486                             progname);
487                 else
488                     dc_array_set(peekc, tmpint, datum);
489             }
490         }
491         return DC_EATONE;
492     case ';':   /* retreive from array */
493         if (peekc == EOF)
494             return DC_EOF_ERROR;
495         if (dc_pop(&datum) == DC_SUCCESS){
496             tmpint = -1;
497             if (datum.dc_type == DC_NUMBER)
498                 tmpint = dc_num2int(datum.v.number, DC_TOSS);
499             if (tmpint < 0)
500                 fprintf(stderr,
501                         "%s: array index must be a nonnegative integer\n",
502                         progname);
503             else
504                 dc_push(dc_array_get(peekc, tmpint));
505         }
506         return DC_EATONE;
507 
508     default:    /* What did that user mean? */
509         fprintf(stderr, "%s: ", progname);
510         dc_show_id(stdout, c, " unimplemented\n");
511         break;
512     }
513     return DC_OKAY;
514 }
515 
516 
517 /* takes a string and evals it */
518 int
519 dc_evalstr DC_DECLARG((string))
520     dc_data string DC_DECLEND
521 {
522     const char *s;
523     const char *end;
524     const char *p;
525     size_t len;
526     int c;
527     int peekc;
528     int count;
529     int negcmp;
530     int next_negcmp = 0;
531 
532     if (string.dc_type != DC_STRING){
533         fprintf(stderr,
534                 "%s: eval called with non-string argument\n",
535                 progname);
536         return DC_OKAY;
537     }
538     s = dc_str2charp(string.v.string);
539     end = s + dc_strlen(string.v.string);
540     while (s < end){
541         c = *(const unsigned char *)s++;
542         peekc = EOF;
543         if (s < end)
544             peekc = *(const unsigned char *)s;
545         negcmp = next_negcmp;
546         next_negcmp = 0;
547         switch (dc_func(c, peekc, negcmp)){
548         case DC_OKAY:
549             break;
550         case DC_EATONE:
551             if (peekc != EOF)
552                 ++s;
553             break;
554         case DC_QUIT:
555             if (unwind_depth > 0){
556                 --unwind_depth;
557                 return DC_QUIT;
558             }
559             return DC_OKAY;
560 
561         case DC_INT:
562             input_str_string = s - 1;
563             dc_push(dc_getnum(input_str, dc_ibase, &peekc));
564             s = input_str_string;
565             if (peekc != EOF)
566                 --s;
567             break;
568         case DC_STR:
569             count = 1;
570             for (p=s; p<end && count>0; ++p)
571                 if (*p == ']')
572                     --count;
573                 else if (*p == '[')
574                     ++count;
575             len = p - s;
576             dc_push(dc_makestring(s, len-1));
577             s = p;
578             break;
579         case DC_SYSTEM:
580             s = dc_system(s);
581         case DC_COMMENT:
582             s = memchr(s, '\n', (size_t)(end-s));
583             if (!s)
584                 s = end;
585             else
586                 ++s;
587             break;
588         case DC_NEGCMP:
589             next_negcmp = 1;
590             break;
591 
592         case DC_EOF_ERROR:
593             fprintf(stderr, "%s: unexpected EOS\n", progname);
594             return DC_OKAY;
595         }
596     }
597     return DC_OKAY;
598 }
599 
600 
601 /* This is the main function of the whole DC program.
602  * Reads the file described by fp, calls dc_func to do
603  * the dirty work, and takes care of dc_func's shortcomings.
604  */
605 int
606 dc_evalfile DC_DECLARG((fp))
607     FILE *fp DC_DECLEND
608 {
609     int c;
610     int peekc;
611     int negcmp;
612     int next_negcmp = 0;
613     dc_data datum;
614 
615     stdin_lookahead = EOF;
616     for (c=getc(fp); c!=EOF; c=peekc){
617         peekc = getc(fp);
618         /*
619          * The following if() is the only place where ``stdin_lookahead''
620          * might be set to other than EOF:
621          */
622         if (fp == stdin)
623             stdin_lookahead = peekc;
624         negcmp = next_negcmp;
625         next_negcmp = 0;
626         switch (dc_func(c, peekc, negcmp)){
627         case DC_OKAY:
628             if (stdin_lookahead != peekc  &&  fp == stdin)
629                 peekc = getc(fp);
630             break;
631         case DC_EATONE:
632             peekc = getc(fp);
633             break;
634         case DC_QUIT:
635             if (unwind_noexit != DC_TRUE)
636                 return DC_FAIL;
637             fprintf(stderr,
638                     "%s: Q command argument exceeded string execution depth\n",
639                     progname);
640             if (stdin_lookahead != peekc  &&  fp == stdin)
641                 peekc = getc(fp);
642             break;
643 
644         case DC_INT:
645             input_fil_fp = fp;
646             input_pushback = c;
647             ungetc(peekc, fp);
648             dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
649             break;
650         case DC_STR:
651             ungetc(peekc, fp);
652             datum = dc_readstring(fp, '[', ']');
653             dc_push(datum);
654             peekc = getc(fp);
655             break;
656         case DC_SYSTEM:
657             ungetc(peekc, fp);
658             datum = dc_readstring(stdin, '\n', '\n');
659             (void)dc_system(dc_str2charp(datum.v.string));
660             dc_free_str(&datum.v.string);
661             peekc = getc(fp);
662             break;
663         case DC_COMMENT:
664             while (peekc!=EOF && peekc!='\n')
665                 peekc = getc(fp);
666             if (peekc != EOF)
667                 peekc = getc(fp);
668             break;
669         case DC_NEGCMP:
670             next_negcmp = 1;
671             break;
672 
673         case DC_EOF_ERROR:
674             fprintf(stderr, "%s: unexpected EOF\n", progname);
675             return DC_FAIL;
676         }
677     }
678     return DC_SUCCESS;
679 }
5761226 [rkeene@sledge /home/rkeene/devel/old/bc-dos/dc]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 1998-03-09 05:20:45