lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | /* vi: set sw=4 ts=4: */ |
| 2 | /* |
| 3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
| 4 | */ |
| 5 | |
| 6 | #include "libbb.h" |
| 7 | #include <math.h> |
| 8 | |
| 9 | //usage:#define dc_trivial_usage |
| 10 | //usage: "EXPRESSION..." |
| 11 | //usage: |
| 12 | //usage:#define dc_full_usage "\n\n" |
| 13 | //usage: "Tiny RPN calculator. Operations:\n" |
| 14 | //usage: "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, xor,\n" |
| 15 | //usage: "p - print top of the stack (without popping),\n" |
| 16 | //usage: "f - print entire stack,\n" |
| 17 | //usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n" |
| 18 | //usage: "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16" |
| 19 | //usage: |
| 20 | //usage:#define dc_example_usage |
| 21 | //usage: "$ dc 2 2 + p\n" |
| 22 | //usage: "4\n" |
| 23 | //usage: "$ dc 8 8 \\* 2 2 + / p\n" |
| 24 | //usage: "16\n" |
| 25 | //usage: "$ dc 0 1 and p\n" |
| 26 | //usage: "0\n" |
| 27 | //usage: "$ dc 0 1 or p\n" |
| 28 | //usage: "1\n" |
| 29 | //usage: "$ echo 72 9 div 8 mul p | dc\n" |
| 30 | //usage: "64\n" |
| 31 | |
| 32 | #if 0 |
| 33 | typedef unsigned data_t; |
| 34 | #define DATA_FMT "" |
| 35 | #elif 0 |
| 36 | typedef unsigned long data_t; |
| 37 | #define DATA_FMT "l" |
| 38 | #else |
| 39 | typedef unsigned long long data_t; |
| 40 | #define DATA_FMT "ll" |
| 41 | #endif |
| 42 | |
| 43 | |
| 44 | struct globals { |
| 45 | unsigned pointer; |
| 46 | unsigned base; |
| 47 | double stack[1]; |
| 48 | } FIX_ALIASING; |
| 49 | enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) }; |
| 50 | #define G (*(struct globals*)&bb_common_bufsiz1) |
| 51 | #define pointer (G.pointer ) |
| 52 | #define base (G.base ) |
| 53 | #define stack (G.stack ) |
| 54 | #define INIT_G() do { \ |
| 55 | base = 10; \ |
| 56 | } while (0) |
| 57 | |
| 58 | |
| 59 | static void push(double a) |
| 60 | { |
| 61 | if (pointer >= STACK_SIZE) |
| 62 | bb_error_msg_and_die("stack overflow"); |
| 63 | stack[pointer++] = a; |
| 64 | } |
| 65 | |
| 66 | static double pop(void) |
| 67 | { |
| 68 | if (pointer == 0) |
| 69 | bb_error_msg_and_die("stack underflow"); |
| 70 | return stack[--pointer]; |
| 71 | } |
| 72 | |
| 73 | static void add(void) |
| 74 | { |
| 75 | push(pop() + pop()); |
| 76 | } |
| 77 | |
| 78 | static void sub(void) |
| 79 | { |
| 80 | double subtrahend = pop(); |
| 81 | |
| 82 | push(pop() - subtrahend); |
| 83 | } |
| 84 | |
| 85 | static void mul(void) |
| 86 | { |
| 87 | push(pop() * pop()); |
| 88 | } |
| 89 | |
| 90 | #if ENABLE_FEATURE_DC_LIBM |
| 91 | static void power(void) |
| 92 | { |
| 93 | double topower = pop(); |
| 94 | |
| 95 | push(pow(pop(), topower)); |
| 96 | } |
| 97 | #endif |
| 98 | |
| 99 | static void divide(void) |
| 100 | { |
| 101 | double divisor = pop(); |
| 102 | |
| 103 | push(pop() / divisor); |
| 104 | } |
| 105 | |
| 106 | static void mod(void) |
| 107 | { |
| 108 | data_t d = pop(); |
| 109 | |
| 110 | push((data_t) pop() % d); |
| 111 | } |
| 112 | |
| 113 | static void and(void) |
| 114 | { |
| 115 | push((data_t) pop() & (data_t) pop()); |
| 116 | } |
| 117 | |
| 118 | static void or(void) |
| 119 | { |
| 120 | push((data_t) pop() | (data_t) pop()); |
| 121 | } |
| 122 | |
| 123 | static void eor(void) |
| 124 | { |
| 125 | push((data_t) pop() ^ (data_t) pop()); |
| 126 | } |
| 127 | |
| 128 | static void not(void) |
| 129 | { |
| 130 | push(~(data_t) pop()); |
| 131 | } |
| 132 | |
| 133 | static void set_output_base(void) |
| 134 | { |
| 135 | static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 }; |
| 136 | unsigned b = (unsigned)pop(); |
| 137 | |
| 138 | base = *strchrnul(bases, b); |
| 139 | if (base == 0) { |
| 140 | bb_error_msg("error, base %u is not supported", b); |
| 141 | base = 10; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | static void print_base(double print) |
| 146 | { |
| 147 | data_t x, i; |
| 148 | |
| 149 | x = (data_t) print; |
| 150 | if (base == 10) { |
| 151 | if (x == print) /* exactly representable as unsigned integer */ |
| 152 | printf("%"DATA_FMT"u\n", x); |
| 153 | else |
| 154 | printf("%g\n", print); |
| 155 | return; |
| 156 | } |
| 157 | |
| 158 | switch (base) { |
| 159 | case 16: |
| 160 | printf("%"DATA_FMT"x\n", x); |
| 161 | break; |
| 162 | case 8: |
| 163 | printf("%"DATA_FMT"o\n", x); |
| 164 | break; |
| 165 | default: /* base 2 */ |
| 166 | i = MAXINT(data_t) - (MAXINT(data_t) >> 1); |
| 167 | /* i is 100000...00000 */ |
| 168 | do { |
| 169 | if (x & i) |
| 170 | break; |
| 171 | i >>= 1; |
| 172 | } while (i > 1); |
| 173 | do { |
| 174 | bb_putchar('1' - !(x & i)); |
| 175 | i >>= 1; |
| 176 | } while (i); |
| 177 | bb_putchar('\n'); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | static void print_stack_no_pop(void) |
| 182 | { |
| 183 | unsigned i = pointer; |
| 184 | while (i) |
| 185 | print_base(stack[--i]); |
| 186 | } |
| 187 | |
| 188 | static void print_no_pop(void) |
| 189 | { |
| 190 | print_base(stack[pointer-1]); |
| 191 | } |
| 192 | |
| 193 | struct op { |
| 194 | const char name[4]; |
| 195 | void (*function) (void); |
| 196 | }; |
| 197 | |
| 198 | static const struct op operators[] = { |
| 199 | {"+", add}, |
| 200 | {"add", add}, |
| 201 | {"-", sub}, |
| 202 | {"sub", sub}, |
| 203 | {"*", mul}, |
| 204 | {"mul", mul}, |
| 205 | {"/", divide}, |
| 206 | {"div", divide}, |
| 207 | #if ENABLE_FEATURE_DC_LIBM |
| 208 | {"**", power}, |
| 209 | {"exp", power}, |
| 210 | {"pow", power}, |
| 211 | #endif |
| 212 | {"%", mod}, |
| 213 | {"mod", mod}, |
| 214 | {"and", and}, |
| 215 | {"or", or}, |
| 216 | {"not", not}, |
| 217 | {"eor", eor}, |
| 218 | {"xor", eor}, |
| 219 | {"p", print_no_pop}, |
| 220 | {"f", print_stack_no_pop}, |
| 221 | {"o", set_output_base}, |
| 222 | }; |
| 223 | |
| 224 | static void stack_machine(const char *argument) |
| 225 | { |
| 226 | char *end; |
| 227 | double d; |
| 228 | const struct op *o; |
| 229 | |
| 230 | d = strtod(argument, &end); |
| 231 | if (end != argument && *end == '\0') { |
| 232 | push(d); |
| 233 | return; |
| 234 | } |
| 235 | |
| 236 | o = operators; |
| 237 | do { |
| 238 | if (strcmp(o->name, argument) == 0) { |
| 239 | o->function(); |
| 240 | return; |
| 241 | } |
| 242 | o++; |
| 243 | } while (o != operators + ARRAY_SIZE(operators)); |
| 244 | |
| 245 | bb_error_msg_and_die("syntax error at '%s'", argument); |
| 246 | } |
| 247 | |
| 248 | int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 249 | int dc_main(int argc UNUSED_PARAM, char **argv) |
| 250 | { |
| 251 | INIT_G(); |
| 252 | |
| 253 | argv++; |
| 254 | if (!argv[0]) { |
| 255 | /* take stuff from stdin if no args are given */ |
| 256 | char *line; |
| 257 | char *cursor; |
| 258 | char *token; |
| 259 | while ((line = xmalloc_fgetline(stdin)) != NULL) { |
| 260 | cursor = line; |
| 261 | while (1) { |
| 262 | token = skip_whitespace(cursor); |
| 263 | if (*token == '\0') |
| 264 | break; |
| 265 | cursor = skip_non_whitespace(token); |
| 266 | if (*cursor != '\0') |
| 267 | *cursor++ = '\0'; |
| 268 | stack_machine(token); |
| 269 | } |
| 270 | free(line); |
| 271 | } |
| 272 | } else { |
| 273 | // why? it breaks "dc -2 2 + p" |
| 274 | //if (argv[0][0] == '-') |
| 275 | // bb_show_usage(); |
| 276 | do { |
| 277 | stack_machine(*argv); |
| 278 | } while (*++argv); |
| 279 | } |
| 280 | return EXIT_SUCCESS; |
| 281 | } |