This repository has been archived on 2024-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
dc/old-dc/dc.c

279 lines
6.2 KiB
C
Raw Normal View History

2023-09-23 13:26:21 -04:00
/* dc.c - desktop calculator implementation
Public domain.
-- Printing --
p - print top
n - print pop no newline
f - print all
-- Arithmetic --
n1 is the first popped value, and so on.
+/-*% such that n1 OP n2, pushes 1 result
^ n2 to the power of n1.
v n1's square root
-- Stack --
c clear
d duplicate
r reverse n1 and n2
R rotates top n1 items
Z pushes n1's digit count
X pushes n1's fraction digit count
z pushes top
-- Others --
b set the operational base, does not alter output
q quit
# a comment.
-- currently omitted --
marcos, strings, and registers.
TODO
Fix ns_expand
Fix CLI to act the same as GNU dc? place original code under DC_COMPLY &
implement unified stackframe option
Implement registers
Implement strings/macros
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <gmp.h>
#ifndef PROGN
# define PROGN "dc"
#endif
#include "ns.c"
#include "slurp.c"
#define AUTHOR "Emil Williams"
#define VERSION_STRING "3.0"
#define DC_EXIT (2 << 5)
static int
isnum(char c)
{
switch (c)
{
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return 1;
default:
return 0;
}
}
static size_t
getnum(ns_t * s, char * eval, size_t len, int base)
{
size_t i = 0;
char t;
while (isnum(eval[i]) && i < len) { ++i; }
t = eval[i];
eval[i] = '\0';
/* fprintf(stderr, "len: %3ld i: %4ld -- %12s --\n", len, i, eval); */
NS_EXPAND(s);
mpf_set_str(s->num[s->top], eval, base);
eval[i] = t;
return i-1;
}
static int
dcevaln(ns_t * s, char * eval, size_t len)
{
static int base = 10;
size_t i;
int comment = 0;
int neg = 0;
assert(s);
for (i = 0; i < len; ++i)
{
if (comment)
{
if (eval[i] == '\n')
{ comment = 0; }
continue;
}
switch (eval[i])
{
case '\t': case '\v': case '\n': case '\r': case ' ':
continue;
case '_': neg = 1; continue;
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
i += getnum(s, eval+i, len - i, base);
if (neg)
{ mpf_neg(NS_PEEK(s), NS_PEEK(s)); }
break;
case '#': comment = 1; break;
case '%': ns_mod(s); break;
case '*': ns_mul(s); break;
case '+': ns_add(s); break;
case '-': ns_sub(s); break;
case '/': ns_div(s); break;
case '^': ns_exp(s); break;
case 'b': base = (int) mpf_get_ui(*ns_pop(s)); break;
case 'c': ns_clear(s); break;
case 'D': ns_ndup(s); break;
case 'd': ns_dup(s); break;
case 'f': ns_printline_all(s); break;
case 'p': ns_printline_peek(s); break;
case 'n': ns_print_peek(s); ns_pop(s); break;
case 'q': return DC_EXIT;
case 'r': ns_reverse(s); break;
case 'v': ns_sqrt(s); break;
case 'z': NS_EXPAND(s); mpf_set_ui(s->num[s->top], (unsigned int) s->top); break;
#ifndef DC_COMPLY
/*** CONFLICTION ***/
case 'a': ns_abs(s); break;
case 'C': ns_ceil(s); break;
case 'F': ns_floor(s); break;
case 'P': (void) ns_pop(s); break;
#else
case 'P':
case 'a': case 'C': case 'F':
/* Params */
case 'i': case 'o': case 'k':
case 'I': case 'O': case 'K':
#endif /* DC_COMPLY */
default:
fprintf(stderr, PROGN ": '%c' (%04d) unimplemented\n", eval[i], eval[i]);
}
neg = 0;
}
fflush(stdout);
return 0;
}
static inline int
dceval(ns_t * s, char * eval)
{
size_t len = strlen(eval);
return dcevaln(s, eval, len);
}
static inline void
help(void)
{ fprintf(stderr,
"Usage: " PROGN " [OPTION] [file ...]\n"
"\t-e expr ..., evaluate expression\n"
"\t-f file ..., evaluate contents of file\n"
"\t-h, display this help message and exits\n"
"\t-V, output version information and exits\n"); }
static inline void
version(void)
{ fprintf(stderr,
PROGN " " VERSION_STRING "\n"
"Copyright 2022, 2023 " AUTHOR "\n\n"
"This program is free software: you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License version 3 as\n"
"published by the Free Software Foundation.\n\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License version 3 for more details.\n\n"
"You should have received a copy of the GNU General Public License\n"
"version 3 along with the source.\n\n"
"If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.\n"); }
int
dcfile(ns_t * s, int argc, char ** argv)
{
int ret = 0;
int i;
size_t sz;
char * buf;
for (i = 0; !ret && i < argc; ++i)
{
buf = slurp(argv[i], &sz);
if (!buf)
{ ret = 1; }
else
{ ret += dcevaln(s, buf, sz); }
free(buf);
}
return ret;
}
int
main(int argc,
char ** argv)
{
int ret = 0;
ns_t * s;
mpf_set_default_prec(2 << MAX_PREC);
s = ns_init(NS_DEFAULT);
if (!s)
{ return 1; }
else if (argc > 1)
{
if (argv[1][0] == '-')
{
if (argv[1][1] == '-')
{
if (strcmp(argv[1]+2, "version") == 0)
{ goto version; }
if (strcmp(argv[1]+2, "help") == 0)
{ goto help; }
}
else switch(argv[1][1])
{
int i;
case 'e':
for (i = 2; !ret && i < argc; ++i)
{ ret += dceval(s, argv[i]); }
goto stop;
case 'f':
dcfile(s, argc-=2, argv+=2);
goto stop;
default:
fprintf(stderr, PROGN ": invaild option -- '%c'\n", argv[1][1]);
ret = 1;
/* fallthrough */
help: case 'h': help(); goto stop;
version: case 'V': version(); goto stop;
}
}
else
{ dcfile(s, --argc, ++argv); }
}
else
{
char * input;
while (!ret)
{
input = readline("");
if (!input || (ret += dceval(s,input)))
{ ret = 1; }
free(input);
}
}
stop:
ns_free(s);
return ret == DC_EXIT ? 0 : ret;
}