blob: 6bcfbe2493a372ff3555e40c173af843567b3ee3
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 | } |
282 |