/* 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 #include #include #include #include #include #include #include #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 .\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; }