home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 5
/
FreshFish_July-August1994.bin
/
bbs
/
gnu
/
dc-0.2-src.lha
/
src
/
amiga
/
dc-0.2
/
dc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-19
|
18KB
|
910 lines
/*
* `dc' desk calculator utility.
*
* Copyright (C) 1984, 1993 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can either send email to this
* program's author (see below) or write to: The Free Software Foundation,
* Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include "decimal.h" /* definitions for our decimal arithmetic package */
FILE *open_file; /* input file now open */
int file_count; /* Number of input files not yet opened */
char **next_file; /* Pointer to vector of names of input files left */
struct regstack
{
decimal value; /* Saved value of register */
struct regstack *rest; /* Tail of list */
};
typedef struct regstack *regstack;
regstack freeregstacks; /* Chain of free regstack structures for fast realloc */
decimal regs[128]; /* "registers", with single-character names */
regstack regstacks[128]; /* For each register, a stack of previous values */
int stacktop; /* index of last used element in stack */
int stacksize; /* Current allocates size of stack */
decimal *stack; /* Pointer to computation stack */
/* A decimal number can be regarded as a string by
treating its contents as characters and ignoring the
position of its decimal point.
Decimal numbers are marked as strings by having an `after' field of -1
One use of strings is to execute them as macros.
*/
#define STRING -1
int macrolevel; /* Current macro nesting; 0 if taking keyboard input */
int macrostacksize; /* Current allocated size of macrostack and macroindex */
decimal *macrostack; /* Pointer to macro stack array */
int *macroindex; /* Pointer to index-within-macro stack array */
/* Note that an empty macro is popped from the stack
only when an trying to read a character from it
or trying to push another macro. */
int ibase; /* Radix for numeric input. */
int obase; /* Radix for numeric output. */
int precision; /* Number of digits to keep in multiply and divide. */
char *buffer; /* Address of buffer used for reading numbers */
int bufsize; /* Current size of buffer (made bigger when nec) */
decimal dec_read ();
regstack get_regstack ();
int fetch ();
int fgetchar ();
char *concat ();
void pushsqrt ();
void condop ();
void setibase ();
void setobase ();
void setprecision ();
void pushmacro ();
decimal read_string ();
void pushlength ();
void pushscale ();
void unfetch ();
void popmacros ();
void popmacro ();
void popstack ();
void print_obj ();
void print_string ();
void free_regstack ();
void pushreg ();
void execute ();
void fputchar ();
void push ();
void incref ();
void decref ();
void binop ();
main (argc, argv, env)
int argc;
char **argv, **env;
{
ibase = 10;
obase = 10;
precision = 0;
freeregstacks = 0;
bzero (regs, sizeof regs);
bzero (regstacks, sizeof regstacks);
bufsize = 40;
buffer = (char *) xmalloc (40);
stacksize = 40;
stack = (decimal *) xmalloc (stacksize * sizeof (decimal));
stacktop = -1;
macrostacksize = 40;
macrostack = (decimal *) xmalloc (macrostacksize * sizeof (decimal));
macroindex = (int *) xmalloc (macrostacksize * sizeof (int));
macrolevel = 0;
/* Initialize for reading input files if any */
open_file = 0;
file_count = argc - 1;
next_file = argv + 1;
while (1)
{
execute ();
}
}
/* Read and execute one command from the current source of input */
void
execute ()
{
int c = fetch ();
if (c < 0) exit (0);
{
switch (c)
{
case '+': /* Arithmetic operators... */
binop (decimal_add);
break;
case '-':
binop (decimal_sub);
break;
case '*':
binop (decimal_mul_dc); /* Like decimal_mul but hairy
way of deciding precision to keep */
break;
case '/':
binop (decimal_div);
break;
case '%':
binop (decimal_rem);
break;
case '^':
binop (decimal_expt);
break;
case '_': /* Begin a negative decimal constant */
{
decimal tem = dec_read (stdin);
tem->sign = !tem->sign;
push (tem);
}
break;
case '.':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': /* All these begin decimal constants */
unfetch (c);
push (dec_read (stdin));
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
unfetch (c);
push (dec_read (stdin));
break;
case 'c': /* Clear the stack */
while (stacktop >= 0)
decref (stack[stacktop--]);
break;
case 'd': /* Duplicate top of stack */
if (stacktop < 0)
error ("stack empty", 0);
else push (stack[stacktop]);
break;
case 'f': /* Describe all registers and stack contents */
{
int regno;
int somereg = 0; /* set to 1 if we print any registers */
for (regno = 0; regno < 128; regno++)
{
if (regs[regno])
{
printf ("register %c: ", regno);
print_obj (regs[regno]);
somereg = 1;
printf ("\n");
}
}
if (somereg)
printf ("\n");
if (stacktop < 0)
printf ("stack empty\n");
else
{
int i;
printf ("stack:\n");
for (i = 0; i <= stacktop; i++)
{
print_obj (stack[stacktop - i]);
printf ("\n");
}
}
}
break;
case 'i': /* ibase <- top of stack */
popstack (setibase);
break;
case 'I': /* Push current ibase */
push (decimal_from_int (ibase));
break;
case 'k': /* like i, I but for precision instead of ibase */
popstack (setprecision);
break;
case 'K':
push (decimal_from_int (precision));
break;
case 'l': /* l<x> load register <x> onto stack */
{
char c1 = fetch ();
if (c1 < 0) exit (0);
if (!regs[c1])
error ("register %c empty", c1);
else
push (regs[c1]);
}
break;
case 'L': /* L<x> load register <x> to stack, pop <x>'s own stack */
{
char c1 = fetch ();
if (c1 < 0) exit (0);
if (!regstacks[c1])
error ("nothing pushed on register %c", c1);
else
{
regstack r = regstacks[c1];
if (!regs[c1])
error ("register %c empty after pop", c1);
else
push (regs[c1]);
regs[c1] = r->value;
regstacks[c1] = r->rest;
free_regstack (r);
}
}
break;
case 'o': /* o, O like i, I but for obase instead of ibase */
popstack (setobase);
break;
case 'O':
push (decimal_from_int (obase));
break;
case 'p': /* Print tos, don't pop, do print newline afterward */
if (stacktop < 0)
error ("stack empty", 0);
else
{
print_obj (stack[stacktop]);
printf ("\n");
}
break;
case 'P': /* Print tos, do pop, no newline afterward */
popstack (print_obj);
break;
case 'q': /* Exit */
if (macrolevel)
{ popmacro (); popmacro (); } /* decrease recursion level by 2 */
else
exit (0); /* If not in a macro, exit the program. */
break;
case 'Q': /* Tos says how many levels to exit */
popstack (popmacros);
break;
case 's': /* s<x> -- Pop stack and set register <x> */
if (stacktop < 0)
empty ();
else
{
int c1 = fetch ();
if (c1 < 0) exit (0);
if (regs[c1]) decref (regs[c1]);
regs[c1] = stack[stacktop--];
}
break;
case 'S': /* S<x> -- pop stack and push as new value of register <x> */
if (stacktop < 0)
empty ();
else
{
int c1 = fetch ();
if (c1 < 0) exit (0);
pushreg (c1);
regs[c1] = stack[stacktop--];
}
break;
case 'v': /* tos gets square root of tos */
popstack (pushsqrt);
break;
case 'x': /* pop stack , call as macro */
popstack (pushmacro);
break;
case 'X