It's a stack calculator for the unwise. Public Domain.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete nahrávat a vytvářet nové úkoly a požadavky na natažení.

279 řádky
6.2KB

  1. /* dc.c - desktop calculator implementation
  2. Public domain.
  3. -- Printing --
  4. p - print top
  5. n - print pop no newline
  6. f - print all
  7. -- Arithmetic --
  8. n1 is the first popped value, and so on.
  9. +/-*% such that n1 OP n2, pushes 1 result
  10. ^ n2 to the power of n1.
  11. v n1's square root
  12. -- Stack --
  13. c clear
  14. d duplicate
  15. r reverse n1 and n2
  16. R rotates top n1 items
  17. Z pushes n1's digit count
  18. X pushes n1's fraction digit count
  19. z pushes top
  20. -- Others --
  21. b set the operational base, does not alter output
  22. q quit
  23. # a comment.
  24. -- currently omitted --
  25. marcos, strings, and registers.
  26. TODO
  27. Fix ns_expand
  28. Fix CLI to act the same as GNU dc? place original code under DC_COMPLY &
  29. implement unified stackframe option
  30. Implement registers
  31. Implement strings/macros
  32. */
  33. #include <assert.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <unistd.h>
  37. #include <math.h>
  38. #include <readline/readline.h>
  39. #include <readline/history.h>
  40. #include <gmp.h>
  41. #ifndef PROGN
  42. # define PROGN "dc"
  43. #endif
  44. #include "ns.c"
  45. #include "slurp.c"
  46. #define AUTHOR "Emil Williams"
  47. #define VERSION_STRING "3.0"
  48. #define DC_EXIT (2 << 5)
  49. static int
  50. isnum(char c)
  51. {
  52. switch (c)
  53. {
  54. case '.':
  55. case '0': case '1': case '2': case '3': case '4':
  56. case '5': case '6': case '7': case '8': case '9':
  57. return 1;
  58. default:
  59. return 0;
  60. }
  61. }
  62. static size_t
  63. getnum(ns_t * s, char * eval, size_t len, int base)
  64. {
  65. size_t i = 0;
  66. char t;
  67. while (isnum(eval[i]) && i < len) { ++i; }
  68. t = eval[i];
  69. eval[i] = '\0';
  70. /* fprintf(stderr, "len: %3ld i: %4ld -- %12s --\n", len, i, eval); */
  71. NS_EXPAND(s);
  72. mpf_set_str(s->num[s->top], eval, base);
  73. eval[i] = t;
  74. return i-1;
  75. }
  76. static int
  77. dcevaln(ns_t * s, char * eval, size_t len)
  78. {
  79. static int base = 10;
  80. size_t i;
  81. int comment = 0;
  82. int neg = 0;
  83. assert(s);
  84. for (i = 0; i < len; ++i)
  85. {
  86. if (comment)
  87. {
  88. if (eval[i] == '\n')
  89. { comment = 0; }
  90. continue;
  91. }
  92. switch (eval[i])
  93. {
  94. case '\t': case '\v': case '\n': case '\r': case ' ':
  95. continue;
  96. case '_': neg = 1; continue;
  97. case '.':
  98. case '0': case '1': case '2': case '3': case '4':
  99. case '5': case '6': case '7': case '8': case '9':
  100. i += getnum(s, eval+i, len - i, base);
  101. if (neg)
  102. { mpf_neg(NS_PEEK(s), NS_PEEK(s)); }
  103. break;
  104. case '#': comment = 1; break;
  105. case '%': ns_mod(s); break;
  106. case '*': ns_mul(s); break;
  107. case '+': ns_add(s); break;
  108. case '-': ns_sub(s); break;
  109. case '/': ns_div(s); break;
  110. case '^': ns_exp(s); break;
  111. case 'b': base = (int) mpf_get_ui(*ns_pop(s)); break;
  112. case 'c': ns_clear(s); break;
  113. case 'D': ns_ndup(s); break;
  114. case 'd': ns_dup(s); break;
  115. case 'f': ns_printline_all(s); break;
  116. case 'p': ns_printline_peek(s); break;
  117. case 'n': ns_print_peek(s); ns_pop(s); break;
  118. case 'q': return DC_EXIT;
  119. case 'r': ns_reverse(s); break;
  120. case 'v': ns_sqrt(s); break;
  121. case 'z': NS_EXPAND(s); mpf_set_ui(s->num[s->top], (unsigned int) s->top); break;
  122. #ifndef DC_COMPLY
  123. /*** CONFLICTION ***/
  124. case 'a': ns_abs(s); break;
  125. case 'C': ns_ceil(s); break;
  126. case 'F': ns_floor(s); break;
  127. case 'P': (void) ns_pop(s); break;
  128. #else
  129. case 'P':
  130. case 'a': case 'C': case 'F':
  131. /* Params */
  132. case 'i': case 'o': case 'k':
  133. case 'I': case 'O': case 'K':
  134. #endif /* DC_COMPLY */
  135. default:
  136. fprintf(stderr, PROGN ": '%c' (%04d) unimplemented\n", eval[i], eval[i]);
  137. }
  138. neg = 0;
  139. }
  140. fflush(stdout);
  141. return 0;
  142. }
  143. static inline int
  144. dceval(ns_t * s, char * eval)
  145. {
  146. size_t len = strlen(eval);
  147. return dcevaln(s, eval, len);
  148. }
  149. static inline void
  150. help(void)
  151. { fprintf(stderr,
  152. "Usage: " PROGN " [OPTION] [file ...]\n"
  153. "\t-e expr ..., evaluate expression\n"
  154. "\t-f file ..., evaluate contents of file\n"
  155. "\t-h, display this help message and exits\n"
  156. "\t-V, output version information and exits\n"); }
  157. static inline void
  158. version(void)
  159. { fprintf(stderr,
  160. PROGN " " VERSION_STRING "\n"
  161. "Copyright 2022, 2023 " AUTHOR "\n\n"
  162. "This program is free software: you can redistribute it and/or modify\n"
  163. "it under the terms of the GNU General Public License version 3 as\n"
  164. "published by the Free Software Foundation.\n\n"
  165. "This program is distributed in the hope that it will be useful,\n"
  166. "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  167. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
  168. "GNU General Public License version 3 for more details.\n\n"
  169. "You should have received a copy of the GNU General Public License\n"
  170. "version 3 along with the source.\n\n"
  171. "If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.\n"); }
  172. int
  173. dcfile(ns_t * s, int argc, char ** argv)
  174. {
  175. int ret = 0;
  176. int i;
  177. size_t sz;
  178. char * buf;
  179. for (i = 0; !ret && i < argc; ++i)
  180. {
  181. buf = slurp(argv[i], &sz);
  182. if (!buf)
  183. { ret = 1; }
  184. else
  185. { ret += dcevaln(s, buf, sz); }
  186. free(buf);
  187. }
  188. return ret;
  189. }
  190. int
  191. main(int argc,
  192. char ** argv)
  193. {
  194. int ret = 0;
  195. ns_t * s;
  196. mpf_set_default_prec(2 << MAX_PREC);
  197. s = ns_init(NS_DEFAULT);
  198. if (!s)
  199. { return 1; }
  200. else if (argc > 1)
  201. {
  202. if (argv[1][0] == '-')
  203. {
  204. if (argv[1][1] == '-')
  205. {
  206. if (strcmp(argv[1]+2, "version") == 0)
  207. { goto version; }
  208. if (strcmp(argv[1]+2, "help") == 0)
  209. { goto help; }
  210. }
  211. else switch(argv[1][1])
  212. {
  213. int i;
  214. case 'e':
  215. for (i = 2; !ret && i < argc; ++i)
  216. { ret += dceval(s, argv[i]); }
  217. goto stop;
  218. case 'f':
  219. dcfile(s, argc-=2, argv+=2);
  220. goto stop;
  221. default:
  222. fprintf(stderr, PROGN ": invaild option -- '%c'\n", argv[1][1]);
  223. ret = 1;
  224. /* fallthrough */
  225. help: case 'h': help(); goto stop;
  226. version: case 'V': version(); goto stop;
  227. }
  228. }
  229. else
  230. { dcfile(s, --argc, ++argv); }
  231. }
  232. else
  233. {
  234. char * input;
  235. while (!ret)
  236. {
  237. input = readline("");
  238. if (!input || (ret += dceval(s,input)))
  239. { ret = 1; }
  240. free(input);
  241. }
  242. }
  243. stop:
  244. ns_free(s);
  245. return ret == DC_EXIT ? 0 : ret;
  246. }