It's a stack calculator for the unwise. Public Domain.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
Ce dépôt est archivé. Vous pouvez voir les fichiers et le cloner, mais vous ne pouvez pas pousser ni ouvrir de ticket/demande d'ajout.

279 lignes
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. }