2 * evaluate the dc language, from a FILE* or a string
4 * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
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)
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.
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:
19 * The Free Software Foundation, Inc.
20 * 59 Temple Place, Suite 330
21 * Boston, MA 02111 USA
24 /* This is the only module which knows about the dc input language */
30 # include <string.h> /* memchr */
33 # include <memory.h> /* memchr, maybe */
35 # ifdef HAVE_STRINGS_H
36 # include <strings.h> /* memchr, maybe */
43 typedef enum {DC_FALSE, DC_TRUE} dc_boolean;
46 DC_OKAY = DC_SUCCESS, /* no further intervention needed for this command */
47 DC_EATONE, /* caller needs to eat the lookahead char */
48 DC_QUIT, /* quit out of unwind_depth levels of evaluation */
50 /* with the following return values, the caller does not have to
51 * fret about stdin_lookahead's value
53 DC_INT, /* caller needs to parse a dc_num from input stream */
54 DC_STR, /* caller needs to parse a dc_str from input stream */
55 DC_SYSTEM, /* caller needs to run a system() on next input line */
56 DC_COMMENT, /* caller needs to skip to the next input line */
57 DC_NEGCMP, /* caller needs to re-call dc_func() with `negcmp' set */
59 DC_EOF_ERROR /* unexpected end of input; abort current eval */
62 static int dc_ibase=10; /* input base, 2 <= dc_ibase <= DC_IBASE_MAX */
63 static int dc_obase=10; /* output base, 2 <= dc_obase */
64 static int dc_scale=0; /* scale (see user documentaton) */
66 /* for Quitting evaluations */
67 static int unwind_depth=0;
69 /* if true, active Quit will not exit program */
70 static dc_boolean unwind_noexit=DC_FALSE;
73 * Used to synchronize lookahead on stdin for '?' command.
74 * If set to EOF then lookahead is used up.
76 static int stdin_lookahead=EOF;
79 /* input_fil and input_str are passed as arguments to dc_getnum */
81 /* used by the input_* functions: */
82 static FILE *input_fil_fp;
83 static const char *input_str_string;
85 /* Since we have a need for two characters of pushback, and
86 * ungetc() only guarantees one, we place the second pushback here
88 static int input_pushback;
90 /* passed as an argument to dc_getnum */
92 input_fil DC_DECLVOID()
94 if (input_pushback != EOF){
95 int c = input_pushback;
99 return getc(input_fil_fp);
102 /* passed as an argument to dc_getnum */
104 input_str DC_DECLVOID()
106 if (!*input_str_string)
108 return *input_str_string++;
113 /* takes a string and evals it; frees the string when done */
114 /* Wrapper around dc_evalstr to avoid duplicating the free call
115 * at all possible return points.
118 dc_eval_and_free_str DC_DECLARG((string))
119 dc_data string DC_DECLEND
123 status = dc_evalstr(string);
124 if (string.dc_type == DC_STRING)
125 dc_free_str(&string.v.string);
130 /* dc_func does the grunt work of figuring out what each input
131 * character means; used by both dc_evalstr and dc_evalfile
133 * c -> the "current" input character under consideration
134 * peekc -> the lookahead input character
135 * negcmp -> negate comparison test (for <,=,> commands)
138 dc_func DC_DECLARG((c, peekc, negcmp))
141 int negcmp DC_DECLEND
143 /* we occasionally need these for temporary data */
144 /* Despite the GNU coding standards, it is much easier
145 * to have these declared once here, since this function
146 * is just one big switch statement.
153 case '0': case '1': case '2': case '3':
154 case '4': case '5': case '6': case '7':
155 case '8': case '9': case 'A': case 'B':
156 case 'C': case 'D': case 'E': case 'F':
161 /* standard command separators */
164 case '+': /* add top two stack elements */
165 dc_binop(dc_add, dc_scale);
167 case '-': /* subtract top two stack elements */
168 dc_binop(dc_sub, dc_scale);
170 case '*': /* multiply top two stack elements */
171 dc_binop(dc_mul, dc_scale);
173 case '/': /* divide top two stack elements */
174 dc_binop(dc_div, dc_scale);
177 /* take the remainder from division of the top two stack elements */
178 dc_binop(dc_rem, dc_scale);
181 /* Do division on the top two stack elements. Return the
182 * quotient as next-to-top of stack and the remainder as
185 dc_binop2(dc_divrem, dc_scale);
188 /* Consider the top three elements of the stack as (base, exp, mod),
189 * where mod is top-of-stack, exp is next-to-top, and base is
190 * second-from-top. Mod must be non-zero, exp must be non-negative,
191 * and all three must be integers. Push the result of raising
192 * base to the exp power, reduced modulo mod. If we had base in
193 * register b, exp in register e, and mod in register m then this
194 * is conceptually equivalent to "lble^lm%", but it is implemented
195 * in a more efficient manner, and can handle arbritrarily large
198 dc_triop(dc_modexp, dc_scale);
200 case '^': /* exponientiation of the top two stack elements */
201 dc_binop(dc_exp, dc_scale);
204 /* eval register named by peekc if
205 * less-than holds for top two stack elements
209 if ( (dc_cmpop() < 0) == !negcmp )
210 if (dc_register_get(peekc, &datum) == DC_SUCCESS)
211 if (dc_eval_and_free_str(datum) == DC_QUIT)
215 /* eval register named by peekc if
216 * equal-to holds for top two stack elements
220 if ( (dc_cmpop() == 0) == !negcmp )
221 if (dc_register_get(peekc, &datum) == DC_SUCCESS)
222 if (dc_eval_and_free_str(datum) == DC_QUIT)
226 /* eval register named by peekc if
227 * greater-than holds for top two stack elements
231 if ( (dc_cmpop() > 0) == !negcmp )
232 if (dc_register_get(peekc, &datum) == DC_SUCCESS)
233 if (dc_eval_and_free_str(datum) == DC_QUIT)
236 case '?': /* read a line from standard-input and eval it */
237 if (stdin_lookahead != EOF){
238 ungetc(stdin_lookahead, stdin);
239 stdin_lookahead = EOF;
241 if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
244 case '[': /* read to balancing ']' into a dc_str */
246 case '!': /* read to newline and call system() on resulting string */
247 if (peekc == '<' || peekc == '=' || peekc == '>')
250 case '#': /* comment; skip remainder of current line */
253 case 'a': /* Convert top of stack to an ascii character. */
254 if (dc_pop(&datum) == DC_SUCCESS){
256 if (datum.dc_type == DC_NUMBER){
257 tmps = (char) dc_num2int(datum.v.number, DC_TOSS);
258 }else if (datum.dc_type == DC_STRING){
259 tmps = *dc_str2charp(datum.v.string);
260 dc_free_str(&datum.v.string);
262 dc_garbage("at top of stack", -1);
264 dc_push(dc_makestring(&tmps, 1));
267 case 'c': /* clear whole stack */
270 case 'd': /* duplicate the datum on the top of stack */
271 if (dc_top_of_stack(&datum) == DC_SUCCESS)
272 dc_push(dc_dup(datum));
274 case 'f': /* print list of all stack items */
275 dc_printall(dc_obase);
277 case 'i': /* set input base to value on top of stack */
278 if (dc_pop(&datum) == DC_SUCCESS){
280 if (datum.dc_type == DC_NUMBER)
281 tmpint = dc_num2int(datum.v.number, DC_TOSS);
282 if ( ! (2 <= tmpint && tmpint <= DC_IBASE_MAX) )
284 "%s: input base must be a number \
285 between 2 and %d (inclusive)\n",
286 progname, DC_IBASE_MAX);
291 case 'k': /* set scale to value on top of stack */
292 if (dc_pop(&datum) == DC_SUCCESS){
294 if (datum.dc_type == DC_NUMBER)
295 tmpint = dc_num2int(datum.v.number, DC_TOSS);
296 if ( ! (tmpint >= 0) )
298 "%s: scale must be a nonnegative number\n",
304 case 'l': /* "load" -- push value on top of register stack named
305 * by peekc onto top of evaluation stack; does not
306 * modify the register stack
310 if (dc_register_get(peekc, &datum) == DC_SUCCESS)
313 case 'n': /* print the value popped off of top-of-stack;
314 * do not add a trailing newline
316 if (dc_pop(&datum) == DC_SUCCESS)
317 dc_print(datum, dc_obase, DC_NONL, DC_TOSS);
319 case 'o': /* set output base to value on top of stack */
320 if (dc_pop(&datum) == DC_SUCCESS){
322 if (datum.dc_type == DC_NUMBER)
323 tmpint = dc_num2int(datum.v.number, DC_TOSS);
324 if ( ! (tmpint > 1) )
326 "%s: output base must be a number greater than 1\n",
332 case 'p': /* print the datum on the top of stack,
333 * with a trailing newline
335 if (dc_top_of_stack(&datum) == DC_SUCCESS)
336 dc_print(datum, dc_obase, DC_WITHNL, DC_KEEP);
338 case 'q': /* quit two levels of evaluation, posibly exiting program */
339 unwind_depth = 1; /* the return below is the first level of returns */
340 unwind_noexit = DC_FALSE;
342 case 'r': /* rotate (swap) the top two elements on the stack
344 if (dc_pop(&datum) == DC_SUCCESS) {
347 two_status = dc_pop(&datum2);
349 if (two_status == DC_SUCCESS)
353 case 's': /* "store" -- replace top of register stack named
354 * by peekc with the value popped from the top
355 * of the evaluation stack
359 if (dc_pop(&datum) == DC_SUCCESS)
360 dc_register_set(peekc, datum);
362 case 'v': /* replace top of stack with its square root */
363 if (dc_pop(&datum) == DC_SUCCESS){
365 if (datum.dc_type != DC_NUMBER){
367 "%s: square root of nonnumeric attempted\n",
369 }else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
370 dc_free_num(&datum.v.number);
371 datum.v.number = tmpnum;
376 case 'x': /* eval the datum popped from top of stack */
377 if (dc_pop(&datum) == DC_SUCCESS){
378 if (datum.dc_type == DC_STRING){
379 if (dc_eval_and_free_str(datum) == DC_QUIT)
381 }else if (datum.dc_type == DC_NUMBER){
384 dc_garbage("at top of stack", -1);
388 case 'z': /* push the current stack depth onto the top of stack */
389 dc_push(dc_int2data(dc_tell_stackdepth()));
392 case 'I': /* push the current input base onto the stack */
393 dc_push(dc_int2data(dc_ibase));
395 case 'K': /* push the current scale onto the stack */
396 dc_push(dc_int2data(dc_scale));
398 case 'L': /* pop a value off of register stack named by peekc
399 * and push it onto the evaluation stack
403 if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
406 case 'O': /* push the current output base onto the stack */
407 dc_push(dc_int2data(dc_obase));
410 /* Pop the value off the top of a stack. If it is
411 * a number, dump out the integer portion of its
412 * absolute value as a "base UCHAR_MAX+1" byte stream;
413 * if it is a string, just print it.
414 * In either case, do not append a trailing newline.
416 if (dc_pop(&datum) == DC_SUCCESS){
417 if (datum.dc_type == DC_NUMBER)
418 dc_dump_num(datum.v.number, DC_TOSS);
419 else if (datum.dc_type == DC_STRING)
420 dc_out_str(datum.v.string, DC_NONL, DC_TOSS);
422 dc_garbage("at top of stack", -1);
425 case 'Q': /* quit out of top-of-stack nested evals;
426 * pops value from stack;
427 * does not exit program (stops short if necessary)
429 if (dc_pop(&datum) == DC_SUCCESS){
431 unwind_noexit = DC_TRUE;
432 if (datum.dc_type == DC_NUMBER)
433 unwind_depth = dc_num2int(datum.v.number, DC_TOSS);
434 if (unwind_depth-- > 0)
436 unwind_depth = 0; /* paranoia */
438 "%s: Q command requires a number >= 1\n",
443 case 'R': /* pop a value off of the evaluation stack,;
445 remaining stack elements that many
446 * places forward (negative numbers mean rotate
449 if (dc_pop(&datum) == DC_SUCCESS){
451 if (datum.dc_type == DC_NUMBER)
452 tmpint = dc_num2int(datum.v.number, DC_TOSS);
453 dc_stack_rotate(tmpint);
457 case 'S': /* pop a value off of the evaluation stack
458 * and push it onto the register stack named by peekc
462 if (dc_pop(&datum) == DC_SUCCESS)
463 dc_register_push(peekc, datum);
465 case 'X': /* replace the number on top-of-stack with its scale factor */
466 if (dc_pop(&datum) == DC_SUCCESS){
468 if (datum.dc_type == DC_NUMBER)
469 tmpint = dc_tell_scale(datum.v.number, DC_TOSS);
470 dc_push(dc_int2data(tmpint));
473 case 'Z': /* replace the datum on the top-of-stack with its length */
474 if (dc_pop(&datum) == DC_SUCCESS)
475 dc_push(dc_int2data(dc_tell_length(datum, DC_TOSS)));
478 case ':': /* store into array */
481 if (dc_pop(&datum) == DC_SUCCESS){
483 if (datum.dc_type == DC_NUMBER)
484 tmpint = dc_num2int(datum.v.number, DC_TOSS);
485 if (dc_pop(&datum) == DC_SUCCESS){
488 "%s: array index must be a nonnegative integer\n",
491 dc_array_set(peekc, tmpint, datum);
495 case ';': /* retreive from array */
498 if (dc_pop(&datum) == DC_SUCCESS){
500 if (datum.dc_type == DC_NUMBER)
501 tmpint = dc_num2int(datum.v.number, DC_TOSS);
504 "%s: array index must be a nonnegative integer\n",
507 dc_push(dc_array_get(peekc, tmpint));
511 default: /* What did that user mean? */
512 fprintf(stderr, "%s: ", progname);
513 dc_show_id(stdout, c, " unimplemented\n");
520 /* takes a string and evals it */
522 dc_evalstr DC_DECLARG((string))
523 dc_data string DC_DECLEND
535 if (string.dc_type != DC_STRING){
537 "%s: eval called with non-string argument\n",
541 s = dc_str2charp(string.v.string);
542 end = s + dc_strlen(string.v.string);
544 c = *(const unsigned char *)s++;
547 peekc = *(const unsigned char *)s;
548 negcmp = next_negcmp;
550 switch (dc_func(c, peekc, negcmp)){
558 if (unwind_depth > 0){
565 input_str_string = s - 1;
566 dc_push(dc_getnum(input_str, dc_ibase, &peekc));
567 s = input_str_string;
573 for (p=s; p<end && count>0; ++p)
579 dc_push(dc_makestring(s, len-1));
585 s = memchr(s, '\n', (size_t)(end-s));
596 fprintf(stderr, "%s: unexpected EOS\n", progname);
604 /* This is the main function of the whole DC program.
605 * Reads the file described by fp, calls dc_func to do
606 * the dirty work, and takes care of dc_func's shortcomings.
609 dc_evalfile DC_DECLARG((fp))
618 stdin_lookahead = EOF;
619 for (c=getc(fp); c!=EOF; c=peekc){
622 * The following if() is the only place where ``stdin_lookahead''
623 * might be set to other than EOF:
626 stdin_lookahead = peekc;
627 negcmp = next_negcmp;
629 switch (dc_func(c, peekc, negcmp)){
631 if (stdin_lookahead != peekc && fp == stdin)
638 if (unwind_noexit != DC_TRUE)
641 "%s: Q command argument exceeded string execution depth\n",
643 if (stdin_lookahead != peekc && fp == stdin)
651 dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
655 datum = dc_readstring(fp, '[', ']');
661 datum = dc_readstring(stdin, '\n', '\n');
662 (void)dc_system(dc_str2charp(datum.v.string));
663 dc_free_str(&datum.v.string);
667 while (peekc!=EOF && peekc!='\n')
677 fprintf(stderr, "%s: unexpected EOF\n", progname);