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.

351 lignes
8.9KB

  1. /* dc.c - desktop calculator implementation
  2. Public domain.
  3. GMP based DC. Uses readline. No limitations, either.
  4. See README for more details.
  5. */
  6. #include <assert.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <stdint.h>
  10. #include <unistd.h>
  11. #include <math.h>
  12. #include <readline/readline.h>
  13. #include <readline/history.h>
  14. #include <gmp.h>
  15. #define DC_EXPORT static
  16. #define DC_EXPORT_VAR static
  17. #ifndef DC_COMPLY
  18. # define NS_REG_MAX 95
  19. # define NS_REG_OFF 32
  20. # define NS_REGCHAR_CLAMP(c) ((c) > NS_REG_MAX)
  21. #else
  22. # define NS_REG_MAX 256
  23. # define NS_REG_OFF 0
  24. # define NS_REGCHAR_CLAMP(c) 0
  25. #endif /* !DC_COMPLY */
  26. #include "config.h"
  27. #include "ns.c"
  28. #define REGCHAR() \
  29. do \
  30. { \
  31. if (!(i + 1 < len)) \
  32. { break; } \
  33. c = eval[i + 1]; \
  34. ++i; \
  35. } while (0)
  36. #define REGCLAMP() \
  37. if (c -= NS_REG_OFF, NS_REGCHAR_CLAMP(c)) \
  38. { \
  39. NS_REG_OOB(c, c); \
  40. break; \
  41. }
  42. #define AUTHOR "Emil Williams"
  43. #define VERSION_STRING "5"
  44. #define DC_EXIT (2 << 5)
  45. /* Command
  46. Note that I feel this should be ommited, as it doesn't serve
  47. any purpose except being able to run commands blindly.
  48. */
  49. static inline size_t
  50. commandn(char * command, size_t len)
  51. {
  52. size_t i = 0;
  53. char c;
  54. while ((command[i] != '\0' ||
  55. command[i] != '\n') &&
  56. i < len)
  57. { ++i; }
  58. c = command[i+1];
  59. command[i+1] = '\0';
  60. /* Why we're using system over anything else (direct quote):
  61. Will run the rest of the line as a SYSTEM command. */
  62. system(command);
  63. command[i+1] = c;
  64. return i;
  65. }
  66. static inline size_t
  67. command(char * command)
  68. { return commandn(command, strlen(command)); }
  69. /* File Helpers */
  70. static inline size_t
  71. frem(FILE * fp)
  72. {
  73. fseek(fp, 0, SEEK_END);
  74. return ftell(fp);
  75. }
  76. DC_EXPORT char *
  77. slurp(const char * fn, size_t * rlen)
  78. {
  79. FILE * fp = fopen(fn, "r");
  80. if (!fp)
  81. { PERROR_RETURN("fopen", NULL); }
  82. else
  83. {
  84. size_t len = frem(fp);
  85. char * buf = malloc(len + 1);
  86. rewind(fp);
  87. if (!buf)
  88. { PERROR_RETURN("malloc", NULL); }
  89. if (len != fread(buf, 1, len, fp))
  90. {
  91. free(buf);
  92. { PERROR_RETURN("fopen", NULL); }
  93. }
  94. *rlen = len;
  95. return buf;
  96. }
  97. }
  98. /* DC REPL and CLI->Eval */
  99. DC_EXPORT int
  100. dcevaln(ns_t * s, char * eval, size_t len)
  101. {
  102. size_t i;
  103. int comment = 0;
  104. int neg = 0;
  105. int lradix = g_iradix;
  106. uint8_t c = '\0';
  107. for (i = 0; i < len; ++i)
  108. {
  109. if (comment)
  110. {
  111. if (eval[i] == '\n')
  112. { comment = 0; }
  113. continue;
  114. }
  115. switch (eval[i])
  116. {
  117. case '\t': case '\v': case '\n': case '\r': case ' ':
  118. continue;
  119. case '_': neg = 1; continue;
  120. case '.':
  121. case 'A': case 'B': case 'C': case 'D': case 'E':
  122. case 'F':
  123. /* DC naturally respects numbers like A100 as their hexidecimal value, rather than respecting
  124. the base and dropping the number for an invalid numeric symbol. */
  125. #ifdef DC_COMPLY
  126. lradix = g_iradix;
  127. g_iradix = 16;
  128. #endif /* DC_COMPLY */
  129. /* fallthrough */
  130. case '0': case '1': case '2': case '3': case '4':
  131. case '5': case '6': case '7': case '8': case '9':
  132. i += ns_getnum(s, eval+i, len - i, g_iradix);
  133. if (neg)
  134. { mpf_neg(NS_PEEK(s), NS_PEEK(s)); }
  135. g_iradix = lradix;
  136. break;
  137. case '#': comment = 1; break;
  138. case '*': ns_mul(s); break;
  139. case '+': ns_add(s); break;
  140. case '-': ns_sub(s); break;
  141. case '/': ns_div(s); break;
  142. case '^': ns_exp(s); break;
  143. /* case '%': ns_mod(s); break; */
  144. /* case '~': ns_divrem(s); break; */
  145. /* case '|': modexp(s); break; */
  146. case 'c': ns_clear(s); break;
  147. case 'd': ns_dup(s); break;
  148. case 'f': ns_printline_all(s); break;
  149. case 'p': ns_printline_peek(s); break;
  150. case 'n': ns_print_peek(s); ns_pop(s); break;
  151. case 'q': return DC_EXIT;
  152. case 'r': ns_reverse(s); break;
  153. case 'R': ns_nrotate(s); break;
  154. case 'v': ns_sqrt(s); break;
  155. case 'z': ns_push_ui(s,s->top); break;
  156. case 's': REGCHAR(); REGCLAMP(); ns_reg_set(s, c); break;
  157. case 'l': REGCHAR(); REGCLAMP(); ns_reg_get(s, c); break;
  158. case 'S': REGCHAR(); REGCLAMP(); ns_reg_push(s, c); break;
  159. case 'L': REGCHAR(); REGCLAMP(); ns_reg_pop(s, c); break;
  160. case 'k': ns_pop_prec(s); break;
  161. case 'K': ns_push_prec(s); break;
  162. case 'i': ns_pop_iradix(s); break;
  163. case 'I': ns_push_iradix(s); break;
  164. case 'o': ns_pop_oradix(s); break;
  165. case 'O': ns_push_oradix(s); break;
  166. case ':': REGCHAR(); REGCLAMP(); ns_reg_push_index(s, c); break;
  167. case ';': REGCHAR(); REGCLAMP(); ns_reg_pop_index(s, c); break;
  168. case '!': i += command(eval + i + 1); break;
  169. #if 0
  170. case '?': /* take user input, and execute that
  171. as a slave to current level */ break;
  172. #endif /* 0 */
  173. /* New non-conflicting features not present in DC */
  174. case '@': ns_abs(s); break;
  175. case '"': ns_ceil(s); break;
  176. case '\'': ns_floor(s); break;
  177. #ifndef DC_COMPLY
  178. /*** CONFLICTION ***/
  179. case 'P': (void) ns_pop(s); break;
  180. #else
  181. case 'P':
  182. #endif /* !DC_COMPLY */
  183. /* Intended to be implemented */
  184. case 'Z': case 'X': case '?': case 'Q':
  185. default:
  186. if (31 < eval[i] &&
  187. eval[i] < 127)
  188. { fprintf(stderr, PROGN ": '%c' (%#o) unimplemented\n", (uint8_t) eval[i], (uint8_t) eval[i]); }
  189. else
  190. { fprintf(stderr, PROGN ": (%#o) unimplemented\n", (uint8_t) eval[i]); }
  191. }
  192. neg = 0;
  193. }
  194. return 0;
  195. }
  196. DC_EXPORT inline int
  197. dceval(ns_t * s, char * eval)
  198. {
  199. return dcevaln(s, eval, strlen(eval));
  200. }
  201. DC_EXPORT inline int
  202. dcfile(ns_t * s, char * fn)
  203. {
  204. int ret = 0;
  205. size_t sz;
  206. char * buf;
  207. buf = slurp(fn, &sz);
  208. if (!buf)
  209. { ret = 1; }
  210. else
  211. { ret += dcevaln(s, buf, sz); }
  212. free(buf);
  213. return ret;
  214. }
  215. /* DC CLI and ADDITIONAL INFORMATION */
  216. #ifndef OTHER_MAIN
  217. static inline void
  218. help(void)
  219. { fprintf(stderr,
  220. "Usage: " PROGN " [OPTION] [file ...]\n"
  221. "\t-e, --expression[=...] Evaluates an expression\n"
  222. "\t-f, --file[=...] Evaluates the contents of file\n"
  223. "\t-h, --help Displays this message and exits\n"
  224. "\t-V, --version Outputs version information and exits\n"); }
  225. static inline void
  226. version(void)
  227. { fprintf(stderr,
  228. PROGN " " VERSION_STRING "\n"
  229. "Copyright 2021, 2022, 2023 " AUTHOR "\n\n"
  230. PROGN " is free software: you can redistribute it and/or modify\n"
  231. "it under the terms of the GNU General Public License version 3 as\n"
  232. "published by the Free Software Foundation.\n\n"
  233. "See <https://www.gnu.org/licenses/gpl-3.0.txt>.\n"); }
  234. #define DC_EQOP(op,arg,off) \
  235. if (arg[off] == '=') \
  236. { op(s,arg+off+1); } \
  237. else if (arg[off] == '\0' && \
  238. argc > 1) \
  239. { \
  240. ret = op(s, argv[1]); \
  241. ++argv; --argc; \
  242. } \
  243. else \
  244. { goto help; }
  245. int
  246. main(int argc,
  247. char ** argv)
  248. {
  249. int ret = 0;
  250. ns_t sreal = (ns_t) {0, -1, NULL};
  251. ns_t * s = &sreal;
  252. mpf_set_default_prec(g_prec);
  253. ns_reg_init();
  254. if (!s)
  255. { ret = 1; }
  256. else if (argc > 1)
  257. {
  258. char * arg;
  259. while (++argv, --argc)
  260. {
  261. arg = *argv;
  262. if (arg[0] == '-')
  263. {
  264. if (arg[1] == '-')
  265. {
  266. if (strcmp(arg+2, "version") == 0)
  267. { goto version; }
  268. else if (strcmp(arg+2, "help") == 0)
  269. { goto help; }
  270. else if (strncmp(arg+2, "expression", 10) == 0)
  271. { DC_EQOP(dceval,arg,12) }
  272. else if (strncmp(arg+2, "file", 4) == 0)
  273. { DC_EQOP(dcfile,arg,6) }
  274. }
  275. else switch(arg[1])
  276. {
  277. case 'e': DC_EQOP(dceval,arg,2) break;
  278. case 'f': DC_EQOP(dcfile,arg,2) break;
  279. default:
  280. fprintf(stderr, PROGN ": invaild option -- '%c'\n", argv[0][1]);
  281. ret = 1;
  282. /* fallthrough */
  283. help: case 'h': help(); goto stop;
  284. version: case 'V': version(); goto stop;
  285. }
  286. }
  287. else
  288. { dcfile(s, *argv++); }
  289. }
  290. }
  291. else /* REPL */
  292. {
  293. char * input;
  294. while (!ret)
  295. {
  296. input = readline("");
  297. if (!input)
  298. {
  299. ret = 1;
  300. break;
  301. }
  302. add_history(input);
  303. ret = dceval(s,input);
  304. free(input);
  305. }
  306. clear_history();
  307. }
  308. stop:
  309. ns_reg_free();
  310. ns_free(s);
  311. return ret == DC_EXIT ? 0 : ret;
  312. }
  313. #endif /* !OTHER_MAIN */
  314. #undef REGCHAR
  315. #undef REGCLAMP