diff options
author | Ron Yorston <rmy@pobox.com> | 2019-01-10 08:38:15 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2019-01-10 08:38:15 +0000 |
commit | f99a280743e877c14ee90a3f9e93a34ca3476a27 (patch) | |
tree | 60ca3d17596e190c8c7cbca587168946598bee8a /miscutils | |
parent | 40d5dd07ea1f290eaed30a03fd598e33a8eaf495 (diff) | |
parent | 6ca8e347fed8c24655df692f22694baf7c572770 (diff) | |
download | busybox-w32-f99a280743e877c14ee90a3f9e93a34ca3476a27.tar.gz busybox-w32-f99a280743e877c14ee90a3f9e93a34ca3476a27.tar.bz2 busybox-w32-f99a280743e877c14ee90a3f9e93a34ca3476a27.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'miscutils')
36 files changed, 7501 insertions, 110 deletions
diff --git a/miscutils/adjtimex.c b/miscutils/adjtimex.c index c1718e909..8ca90d58a 100644 --- a/miscutils/adjtimex.c +++ b/miscutils/adjtimex.c | |||
@@ -11,7 +11,7 @@ | |||
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
12 | */ | 12 | */ |
13 | //config:config ADJTIMEX | 13 | //config:config ADJTIMEX |
14 | //config: bool "adjtimex (4.5 kb)" | 14 | //config: bool "adjtimex (4.7 kb)" |
15 | //config: default y | 15 | //config: default y |
16 | //config: select PLATFORM_LINUX | 16 | //config: select PLATFORM_LINUX |
17 | //config: help | 17 | //config: help |
diff --git a/miscutils/bc.c b/miscutils/bc.c new file mode 100644 index 000000000..7fecb264d --- /dev/null +++ b/miscutils/bc.c | |||
@@ -0,0 +1,7405 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
4 | * Adapted from https://github.com/gavinhoward/bc | ||
5 | * Original code copyright (c) 2018 Gavin D. Howard and contributors. | ||
6 | */ | ||
7 | //TODO: GNU extensions: | ||
8 | // support "define f(*param[])" - "pass array by reference" syntax | ||
9 | |||
10 | #define DEBUG_LEXER 0 | ||
11 | #define DEBUG_COMPILE 0 | ||
12 | #define DEBUG_EXEC 0 | ||
13 | // This can be left enabled for production as well: | ||
14 | #define SANITY_CHECKS 1 | ||
15 | |||
16 | //config:config BC | ||
17 | //config: bool "bc (45 kb)" | ||
18 | //config: default y | ||
19 | //config: select FEATURE_DC_BIG | ||
20 | //config: help | ||
21 | //config: bc is a command-line, arbitrary-precision calculator with a | ||
22 | //config: Turing-complete language. See the GNU bc manual | ||
23 | //config: (https://www.gnu.org/software/bc/manual/bc.html) and bc spec | ||
24 | //config: (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html). | ||
25 | //config: | ||
26 | //config: This bc has five differences to the GNU bc: | ||
27 | //config: 1) The period (.) is a shortcut for "last", as in the BSD bc. | ||
28 | //config: 2) Arrays are copied before being passed as arguments to | ||
29 | //config: functions. This behavior is required by the bc spec. | ||
30 | //config: 3) Arrays can be passed to the builtin "length" function to get | ||
31 | //config: the number of elements in the array. This prints "1": | ||
32 | //config: a[0] = 0; length(a[]) | ||
33 | //config: 4) The precedence of the boolean "not" operator (!) is equal to | ||
34 | //config: that of the unary minus (-) negation operator. This still | ||
35 | //config: allows POSIX-compliant scripts to work while somewhat | ||
36 | //config: preserving expected behavior (versus C) and making parsing | ||
37 | //config: easier. | ||
38 | //config: 5) "read()" accepts expressions, not only numeric literals. | ||
39 | //config: | ||
40 | //config:config DC | ||
41 | //config: bool "dc (36 kb)" | ||
42 | //config: default y | ||
43 | //config: help | ||
44 | //config: dc is a reverse-polish notation command-line calculator which | ||
45 | //config: supports unlimited precision arithmetic. See the FreeBSD man page | ||
46 | //config: (https://www.unix.com/man-page/FreeBSD/1/dc/) and GNU dc manual | ||
47 | //config: (https://www.gnu.org/software/bc/manual/dc-1.05/html_mono/dc.html). | ||
48 | //config: | ||
49 | //config: This dc has a few differences from the two above: | ||
50 | //config: 1) When printing a byte stream (command "P"), this dc follows what | ||
51 | //config: the FreeBSD dc does. | ||
52 | //config: 2) Implements the GNU extensions for divmod ("~") and | ||
53 | //config: modular exponentiation ("|"). | ||
54 | //config: 3) Implements all FreeBSD extensions, except for "J" and "M". | ||
55 | //config: 4) Like the FreeBSD dc, this dc supports extended registers. | ||
56 | //config: However, they are implemented differently. When it encounters | ||
57 | //config: whitespace where a register should be, it skips the whitespace. | ||
58 | //config: If the character following is not a lowercase letter, an error | ||
59 | //config: is issued. Otherwise, the register name is parsed by the | ||
60 | //config: following regex: [a-z][a-z0-9_]* | ||
61 | //config: This generally means that register names will be surrounded by | ||
62 | //config: whitespace. Examples: | ||
63 | //config: l idx s temp L index S temp2 < do_thing | ||
64 | //config: Also note that, like the FreeBSD dc, extended registers are not | ||
65 | //config: allowed unless the "-x" option is given. | ||
66 | //config: | ||
67 | //config:if BC || DC # for menuconfig indenting | ||
68 | //config: | ||
69 | //config:config FEATURE_DC_BIG | ||
70 | //config: bool "Use bc code base for dc (larger, more features)" | ||
71 | //config: default y | ||
72 | //config: | ||
73 | //config:config FEATURE_DC_LIBM | ||
74 | //config: bool "Enable power and exp functions (requires libm)" | ||
75 | //config: default y | ||
76 | //config: depends on DC && !BC && !FEATURE_DC_BIG | ||
77 | //config: help | ||
78 | //config: Enable power and exp functions. | ||
79 | //config: NOTE: This will require libm to be present for linking. | ||
80 | //config: | ||
81 | //config:config FEATURE_BC_INTERACTIVE | ||
82 | //config: bool "Interactive mode (+4kb)" | ||
83 | //config: default y | ||
84 | //config: depends on BC || (DC && FEATURE_DC_BIG) | ||
85 | //config: help | ||
86 | //config: Enable interactive mode: when started on a tty, | ||
87 | //config: ^C interrupts execution and returns to command line, | ||
88 | //config: errors also return to command line instead of exiting, | ||
89 | //config: line editing with history is available. | ||
90 | //config: | ||
91 | //config: With this option off, input can still be taken from tty, | ||
92 | //config: but all errors are fatal, ^C is fatal, | ||
93 | //config: tty is treated exactly the same as any other | ||
94 | //config: standard input (IOW: no line editing). | ||
95 | //config: | ||
96 | //config:config FEATURE_BC_LONG_OPTIONS | ||
97 | //config: bool "Enable bc/dc long options" | ||
98 | //config: default y | ||
99 | //config: depends on BC || (DC && FEATURE_DC_BIG) | ||
100 | //config: | ||
101 | //config:endif | ||
102 | |||
103 | //applet:IF_BC(APPLET(bc, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
104 | //applet:IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
105 | |||
106 | //kbuild:lib-$(CONFIG_BC) += bc.o | ||
107 | //kbuild:lib-$(CONFIG_DC) += bc.o | ||
108 | |||
109 | //See www.gnu.org/software/bc/manual/bc.html | ||
110 | //usage:#define bc_trivial_usage | ||
111 | //usage: "[-sqlw] FILE..." | ||
112 | //usage: | ||
113 | //usage:#define bc_full_usage "\n" | ||
114 | //usage: "\nArbitrary precision calculator" | ||
115 | //usage: "\n" | ||
116 | ///////: "\n -i Interactive" - has no effect for now | ||
117 | //usage: "\n -q Quiet" | ||
118 | //usage: "\n -l Load standard math library" | ||
119 | //usage: "\n -s Be POSIX compatible" | ||
120 | //usage: "\n -w Warn if extensions are used" | ||
121 | ///////: "\n -v Version" | ||
122 | //usage: "\n" | ||
123 | //usage: "\n$BC_LINE_LENGTH changes output width" | ||
124 | //usage: | ||
125 | //usage:#define bc_example_usage | ||
126 | //usage: "3 + 4.129\n" | ||
127 | //usage: "1903 - 2893\n" | ||
128 | //usage: "-129 * 213.28935\n" | ||
129 | //usage: "12 / -1932\n" | ||
130 | //usage: "12 % 12\n" | ||
131 | //usage: "34 ^ 189\n" | ||
132 | //usage: "scale = 13\n" | ||
133 | //usage: "ibase = 2\n" | ||
134 | //usage: "obase = A\n" | ||
135 | //usage: | ||
136 | //usage:#define dc_trivial_usage | ||
137 | //usage: IF_FEATURE_DC_BIG("[-x] ")"[-eSCRIPT]... [-fFILE]... [FILE]..." | ||
138 | //usage: | ||
139 | //usage:#define dc_full_usage "\n" | ||
140 | //usage: "\nTiny RPN calculator. Operations:" | ||
141 | //usage: "\n+, -, *, /, %, ~, ^," IF_FEATURE_DC_BIG(" |,") | ||
142 | //usage: "\np - print top of the stack without popping" | ||
143 | //usage: "\nf - print entire stack" | ||
144 | //usage: "\nk - pop the value and set the precision" | ||
145 | //usage: "\ni - pop the value and set input radix" | ||
146 | //usage: "\no - pop the value and set output radix" | ||
147 | //usage: "\nExamples: dc -e'2 2 + p' -> 4, dc -e'8 8 * 2 2 + / p' -> 16" | ||
148 | //usage: | ||
149 | //usage:#define dc_example_usage | ||
150 | //usage: "$ dc -e'2 2 + p'\n" | ||
151 | //usage: "4\n" | ||
152 | //usage: "$ dc -e'8 8 \\* 2 2 + / p'\n" | ||
153 | //usage: "16\n" | ||
154 | //usage: "$ dc -e'0 1 & p'\n" | ||
155 | //usage: "0\n" | ||
156 | //usage: "$ dc -e'0 1 | p'\n" | ||
157 | //usage: "1\n" | ||
158 | //usage: "$ echo '72 9 / 8 * p' | dc\n" | ||
159 | //usage: "64\n" | ||
160 | |||
161 | #include "libbb.h" | ||
162 | #include "common_bufsiz.h" | ||
163 | |||
164 | #if !ENABLE_BC && !ENABLE_FEATURE_DC_BIG | ||
165 | # include "dc.c" | ||
166 | #else | ||
167 | |||
168 | #if DEBUG_LEXER | ||
169 | static uint8_t lex_indent; | ||
170 | #define dbg_lex(...) \ | ||
171 | do { \ | ||
172 | fprintf(stderr, "%*s", lex_indent, ""); \ | ||
173 | bb_error_msg(__VA_ARGS__); \ | ||
174 | } while (0) | ||
175 | #define dbg_lex_enter(...) \ | ||
176 | do { \ | ||
177 | dbg_lex(__VA_ARGS__); \ | ||
178 | lex_indent++; \ | ||
179 | } while (0) | ||
180 | #define dbg_lex_done(...) \ | ||
181 | do { \ | ||
182 | lex_indent--; \ | ||
183 | dbg_lex(__VA_ARGS__); \ | ||
184 | } while (0) | ||
185 | #else | ||
186 | # define dbg_lex(...) ((void)0) | ||
187 | # define dbg_lex_enter(...) ((void)0) | ||
188 | # define dbg_lex_done(...) ((void)0) | ||
189 | #endif | ||
190 | |||
191 | #if DEBUG_COMPILE | ||
192 | # define dbg_compile(...) bb_error_msg(__VA_ARGS__) | ||
193 | #else | ||
194 | # define dbg_compile(...) ((void)0) | ||
195 | #endif | ||
196 | |||
197 | #if DEBUG_EXEC | ||
198 | # define dbg_exec(...) bb_error_msg(__VA_ARGS__) | ||
199 | #else | ||
200 | # define dbg_exec(...) ((void)0) | ||
201 | #endif | ||
202 | |||
203 | typedef enum BcStatus { | ||
204 | BC_STATUS_SUCCESS = 0, | ||
205 | BC_STATUS_FAILURE = 1, | ||
206 | } BcStatus; | ||
207 | |||
208 | #define BC_VEC_INVALID_IDX ((size_t) -1) | ||
209 | #define BC_VEC_START_CAP (1 << 5) | ||
210 | |||
211 | typedef void (*BcVecFree)(void *) FAST_FUNC; | ||
212 | |||
213 | typedef struct BcVec { | ||
214 | char *v; | ||
215 | size_t len; | ||
216 | size_t cap; | ||
217 | size_t size; | ||
218 | BcVecFree dtor; | ||
219 | } BcVec; | ||
220 | |||
221 | typedef signed char BcDig; | ||
222 | |||
223 | typedef struct BcNum { | ||
224 | BcDig *restrict num; | ||
225 | size_t rdx; | ||
226 | size_t len; | ||
227 | size_t cap; | ||
228 | bool neg; | ||
229 | } BcNum; | ||
230 | |||
231 | #define BC_NUM_MAX_IBASE 36 | ||
232 | // larger value might speed up BIGNUM calculations a bit: | ||
233 | #define BC_NUM_DEF_SIZE 16 | ||
234 | #define BC_NUM_PRINT_WIDTH 69 | ||
235 | |||
236 | #define BC_NUM_KARATSUBA_LEN 32 | ||
237 | |||
238 | typedef enum BcInst { | ||
239 | #if ENABLE_BC | ||
240 | BC_INST_INC_PRE, | ||
241 | BC_INST_DEC_PRE, | ||
242 | BC_INST_INC_POST, | ||
243 | BC_INST_DEC_POST, | ||
244 | #endif | ||
245 | XC_INST_NEG, // order | ||
246 | |||
247 | XC_INST_REL_EQ, // should | ||
248 | XC_INST_REL_LE, // match | ||
249 | XC_INST_REL_GE, // LEX | ||
250 | XC_INST_REL_NE, // constants | ||
251 | XC_INST_REL_LT, // for | ||
252 | XC_INST_REL_GT, // these | ||
253 | |||
254 | XC_INST_POWER, // operations | ||
255 | XC_INST_MULTIPLY, // | | ||
256 | XC_INST_DIVIDE, // | | ||
257 | XC_INST_MODULUS, // | | ||
258 | XC_INST_PLUS, // | | ||
259 | XC_INST_MINUS, // | | ||
260 | |||
261 | XC_INST_BOOL_NOT, // | | ||
262 | XC_INST_BOOL_OR, // | | ||
263 | XC_INST_BOOL_AND, // | | ||
264 | #if ENABLE_BC | ||
265 | BC_INST_ASSIGN_POWER, // | | ||
266 | BC_INST_ASSIGN_MULTIPLY,// | | ||
267 | BC_INST_ASSIGN_DIVIDE, // | | ||
268 | BC_INST_ASSIGN_MODULUS, // | | ||
269 | BC_INST_ASSIGN_PLUS, // | | ||
270 | BC_INST_ASSIGN_MINUS, // | | ||
271 | #endif | ||
272 | XC_INST_ASSIGN, // V | ||
273 | |||
274 | XC_INST_NUM, | ||
275 | XC_INST_VAR, | ||
276 | XC_INST_ARRAY_ELEM, | ||
277 | XC_INST_ARRAY, | ||
278 | XC_INST_SCALE_FUNC, | ||
279 | |||
280 | XC_INST_IBASE, // order of these constans should match other enums | ||
281 | XC_INST_OBASE, // order of these constans should match other enums | ||
282 | XC_INST_SCALE, // order of these constans should match other enums | ||
283 | IF_BC(BC_INST_LAST,) // order of these constans should match other enums | ||
284 | XC_INST_LENGTH, | ||
285 | XC_INST_READ, | ||
286 | XC_INST_SQRT, | ||
287 | |||
288 | XC_INST_PRINT, | ||
289 | XC_INST_PRINT_POP, | ||
290 | XC_INST_STR, | ||
291 | XC_INST_PRINT_STR, | ||
292 | |||
293 | #if ENABLE_BC | ||
294 | BC_INST_HALT, | ||
295 | BC_INST_JUMP, | ||
296 | BC_INST_JUMP_ZERO, | ||
297 | |||
298 | BC_INST_CALL, | ||
299 | BC_INST_RET0, | ||
300 | #endif | ||
301 | XC_INST_RET, | ||
302 | |||
303 | XC_INST_POP, | ||
304 | #if ENABLE_DC | ||
305 | DC_INST_POP_EXEC, | ||
306 | |||
307 | DC_INST_MODEXP, | ||
308 | DC_INST_DIVMOD, | ||
309 | |||
310 | DC_INST_EXECUTE, | ||
311 | DC_INST_EXEC_COND, | ||
312 | |||
313 | DC_INST_ASCIIFY, | ||
314 | DC_INST_PRINT_STREAM, | ||
315 | |||
316 | DC_INST_PRINT_STACK, | ||
317 | DC_INST_CLEAR_STACK, | ||
318 | DC_INST_STACK_LEN, | ||
319 | DC_INST_DUPLICATE, | ||
320 | DC_INST_SWAP, | ||
321 | |||
322 | DC_INST_LOAD, | ||
323 | DC_INST_PUSH_VAR, | ||
324 | DC_INST_PUSH_TO_VAR, | ||
325 | |||
326 | DC_INST_QUIT, | ||
327 | DC_INST_NQUIT, | ||
328 | |||
329 | DC_INST_INVALID = -1, | ||
330 | #endif | ||
331 | } BcInst; | ||
332 | |||
333 | typedef struct BcId { | ||
334 | char *name; | ||
335 | size_t idx; | ||
336 | } BcId; | ||
337 | |||
338 | typedef struct BcFunc { | ||
339 | BcVec code; | ||
340 | IF_BC(BcVec labels;) | ||
341 | IF_BC(BcVec autos;) | ||
342 | IF_BC(BcVec strs;) | ||
343 | IF_BC(BcVec consts;) | ||
344 | IF_BC(size_t nparams;) | ||
345 | IF_BC(bool voidfunc;) | ||
346 | } BcFunc; | ||
347 | |||
348 | typedef enum BcResultType { | ||
349 | XC_RESULT_TEMP, | ||
350 | IF_BC(BC_RESULT_VOID,) // same as TEMP, but INST_PRINT will ignore it | ||
351 | |||
352 | XC_RESULT_VAR, | ||
353 | XC_RESULT_ARRAY_ELEM, | ||
354 | XC_RESULT_ARRAY, | ||
355 | |||
356 | XC_RESULT_STR, | ||
357 | |||
358 | //code uses "inst - XC_INST_IBASE + XC_RESULT_IBASE" construct, | ||
359 | XC_RESULT_IBASE, // relative order should match for: XC_INST_IBASE | ||
360 | XC_RESULT_OBASE, // relative order should match for: XC_INST_OBASE | ||
361 | XC_RESULT_SCALE, // relative order should match for: XC_INST_SCALE | ||
362 | IF_BC(BC_RESULT_LAST,) // relative order should match for: BC_INST_LAST | ||
363 | XC_RESULT_CONSTANT, | ||
364 | IF_BC(BC_RESULT_ONE,) | ||
365 | } BcResultType; | ||
366 | |||
367 | typedef union BcResultData { | ||
368 | BcNum n; | ||
369 | BcVec v; | ||
370 | BcId id; | ||
371 | } BcResultData; | ||
372 | |||
373 | typedef struct BcResult { | ||
374 | BcResultType t; | ||
375 | BcResultData d; | ||
376 | } BcResult; | ||
377 | |||
378 | typedef struct BcInstPtr { | ||
379 | size_t func; | ||
380 | size_t inst_idx; | ||
381 | } BcInstPtr; | ||
382 | |||
383 | typedef enum BcLexType { | ||
384 | XC_LEX_EOF, | ||
385 | XC_LEX_INVALID, | ||
386 | |||
387 | XC_LEX_NLINE, | ||
388 | XC_LEX_WHITESPACE, | ||
389 | XC_LEX_STR, | ||
390 | XC_LEX_NAME, | ||
391 | XC_LEX_NUMBER, | ||
392 | |||
393 | XC_LEX_1st_op, | ||
394 | XC_LEX_NEG = XC_LEX_1st_op, // order | ||
395 | |||
396 | XC_LEX_OP_REL_EQ, // should | ||
397 | XC_LEX_OP_REL_LE, // match | ||
398 | XC_LEX_OP_REL_GE, // INST | ||
399 | XC_LEX_OP_REL_NE, // constants | ||
400 | XC_LEX_OP_REL_LT, // for | ||
401 | XC_LEX_OP_REL_GT, // these | ||
402 | |||
403 | XC_LEX_OP_POWER, // operations | ||
404 | XC_LEX_OP_MULTIPLY, // | | ||
405 | XC_LEX_OP_DIVIDE, // | | ||
406 | XC_LEX_OP_MODULUS, // | | ||
407 | XC_LEX_OP_PLUS, // | | ||
408 | XC_LEX_OP_MINUS, // | | ||
409 | XC_LEX_OP_last = XC_LEX_OP_MINUS, | ||
410 | #if ENABLE_BC | ||
411 | BC_LEX_OP_BOOL_NOT, // | | ||
412 | BC_LEX_OP_BOOL_OR, // | | ||
413 | BC_LEX_OP_BOOL_AND, // | | ||
414 | |||
415 | BC_LEX_OP_ASSIGN_POWER, // | | ||
416 | BC_LEX_OP_ASSIGN_MULTIPLY, // | | ||
417 | BC_LEX_OP_ASSIGN_DIVIDE, // | | ||
418 | BC_LEX_OP_ASSIGN_MODULUS, // | | ||
419 | BC_LEX_OP_ASSIGN_PLUS, // | | ||
420 | BC_LEX_OP_ASSIGN_MINUS, // | | ||
421 | |||
422 | BC_LEX_OP_ASSIGN, // V | ||
423 | |||
424 | BC_LEX_OP_INC, | ||
425 | BC_LEX_OP_DEC, | ||
426 | |||
427 | BC_LEX_LPAREN, // () are 0x28 and 0x29 | ||
428 | BC_LEX_RPAREN, // must be LPAREN+1: code uses (c - '(' + BC_LEX_LPAREN) | ||
429 | |||
430 | BC_LEX_LBRACKET, // [] are 0x5B and 0x5D | ||
431 | BC_LEX_COMMA, | ||
432 | BC_LEX_RBRACKET, // must be LBRACKET+2: code uses (c - '[' + BC_LEX_LBRACKET) | ||
433 | |||
434 | BC_LEX_LBRACE, // {} are 0x7B and 0x7D | ||
435 | BC_LEX_SCOLON, | ||
436 | BC_LEX_RBRACE, // must be LBRACE+2: code uses (c - '{' + BC_LEX_LBRACE) | ||
437 | |||
438 | BC_LEX_KEY_1st_keyword, | ||
439 | BC_LEX_KEY_AUTO = BC_LEX_KEY_1st_keyword, | ||
440 | BC_LEX_KEY_BREAK, | ||
441 | BC_LEX_KEY_CONTINUE, | ||
442 | BC_LEX_KEY_DEFINE, | ||
443 | BC_LEX_KEY_ELSE, | ||
444 | BC_LEX_KEY_FOR, | ||
445 | BC_LEX_KEY_HALT, | ||
446 | // code uses "type - BC_LEX_KEY_IBASE + XC_INST_IBASE" construct, | ||
447 | BC_LEX_KEY_IBASE, // relative order should match for: XC_INST_IBASE | ||
448 | BC_LEX_KEY_OBASE, // relative order should match for: XC_INST_OBASE | ||
449 | BC_LEX_KEY_IF, | ||
450 | BC_LEX_KEY_LAST, // relative order should match for: BC_INST_LAST | ||
451 | BC_LEX_KEY_LENGTH, | ||
452 | BC_LEX_KEY_LIMITS, | ||
453 | BC_LEX_KEY_PRINT, | ||
454 | BC_LEX_KEY_QUIT, | ||
455 | BC_LEX_KEY_READ, | ||
456 | BC_LEX_KEY_RETURN, | ||
457 | BC_LEX_KEY_SCALE, | ||
458 | BC_LEX_KEY_SQRT, | ||
459 | BC_LEX_KEY_WHILE, | ||
460 | #endif // ENABLE_BC | ||
461 | |||
462 | #if ENABLE_DC | ||
463 | DC_LEX_OP_BOOL_NOT = XC_LEX_OP_last + 1, | ||
464 | DC_LEX_OP_ASSIGN, | ||
465 | |||
466 | DC_LEX_LPAREN, | ||
467 | DC_LEX_SCOLON, | ||
468 | DC_LEX_READ, | ||
469 | DC_LEX_IBASE, | ||
470 | DC_LEX_SCALE, | ||
471 | DC_LEX_OBASE, | ||
472 | DC_LEX_LENGTH, | ||
473 | DC_LEX_PRINT, | ||
474 | DC_LEX_QUIT, | ||
475 | DC_LEX_SQRT, | ||
476 | DC_LEX_LBRACE, | ||
477 | |||
478 | DC_LEX_EQ_NO_REG, | ||
479 | DC_LEX_OP_MODEXP, | ||
480 | DC_LEX_OP_DIVMOD, | ||
481 | |||
482 | DC_LEX_COLON, | ||
483 | DC_LEX_ELSE, | ||
484 | DC_LEX_EXECUTE, | ||
485 | DC_LEX_PRINT_STACK, | ||
486 | DC_LEX_CLEAR_STACK, | ||
487 | DC_LEX_STACK_LEVEL, | ||
488 | DC_LEX_DUPLICATE, | ||
489 | DC_LEX_SWAP, | ||
490 | DC_LEX_POP, | ||
491 | |||
492 | DC_LEX_ASCIIFY, | ||
493 | DC_LEX_PRINT_STREAM, | ||
494 | |||
495 | // code uses "t - DC_LEX_STORE_IBASE + XC_INST_IBASE" construct, | ||
496 | DC_LEX_STORE_IBASE, // relative order should match for: XC_INST_IBASE | ||
497 | DC_LEX_STORE_OBASE, // relative order should match for: XC_INST_OBASE | ||
498 | DC_LEX_STORE_SCALE, // relative order should match for: XC_INST_SCALE | ||
499 | DC_LEX_LOAD, | ||
500 | DC_LEX_LOAD_POP, | ||
501 | DC_LEX_STORE_PUSH, | ||
502 | DC_LEX_PRINT_POP, | ||
503 | DC_LEX_NQUIT, | ||
504 | DC_LEX_SCALE_FACTOR, | ||
505 | #endif | ||
506 | } BcLexType; | ||
507 | // must match order of BC_LEX_KEY_foo etc above | ||
508 | #if ENABLE_BC | ||
509 | struct BcLexKeyword { | ||
510 | char name8[8]; | ||
511 | }; | ||
512 | #define LEX_KW_ENTRY(a, b) \ | ||
513 | { .name8 = a /*, .posix = b */ } | ||
514 | static const struct BcLexKeyword bc_lex_kws[20] = { | ||
515 | LEX_KW_ENTRY("auto" , 1), // 0 | ||
516 | LEX_KW_ENTRY("break" , 1), // 1 | ||
517 | LEX_KW_ENTRY("continue", 0), // 2 note: this one has no terminating NUL | ||
518 | LEX_KW_ENTRY("define" , 1), // 3 | ||
519 | LEX_KW_ENTRY("else" , 0), // 4 | ||
520 | LEX_KW_ENTRY("for" , 1), // 5 | ||
521 | LEX_KW_ENTRY("halt" , 0), // 6 | ||
522 | LEX_KW_ENTRY("ibase" , 1), // 7 | ||
523 | LEX_KW_ENTRY("obase" , 1), // 8 | ||
524 | LEX_KW_ENTRY("if" , 1), // 9 | ||
525 | LEX_KW_ENTRY("last" , 0), // 10 | ||
526 | LEX_KW_ENTRY("length" , 1), // 11 | ||
527 | LEX_KW_ENTRY("limits" , 0), // 12 | ||
528 | LEX_KW_ENTRY("print" , 0), // 13 | ||
529 | LEX_KW_ENTRY("quit" , 1), // 14 | ||
530 | LEX_KW_ENTRY("read" , 0), // 15 | ||
531 | LEX_KW_ENTRY("return" , 1), // 16 | ||
532 | LEX_KW_ENTRY("scale" , 1), // 17 | ||
533 | LEX_KW_ENTRY("sqrt" , 1), // 18 | ||
534 | LEX_KW_ENTRY("while" , 1), // 19 | ||
535 | }; | ||
536 | #undef LEX_KW_ENTRY | ||
537 | #define STRING_else (bc_lex_kws[4].name8) | ||
538 | #define STRING_for (bc_lex_kws[5].name8) | ||
539 | #define STRING_if (bc_lex_kws[9].name8) | ||
540 | #define STRING_while (bc_lex_kws[19].name8) | ||
541 | enum { | ||
542 | POSIX_KWORD_MASK = 0 | ||
543 | | (1 << 0) // 0 | ||
544 | | (1 << 1) // 1 | ||
545 | | (0 << 2) // 2 | ||
546 | | (1 << 3) // 3 | ||
547 | | (0 << 4) // 4 | ||
548 | | (1 << 5) // 5 | ||
549 | | (0 << 6) // 6 | ||
550 | | (1 << 7) // 7 | ||
551 | | (1 << 8) // 8 | ||
552 | | (1 << 9) // 9 | ||
553 | | (0 << 10) // 10 | ||
554 | | (1 << 11) // 11 | ||
555 | | (0 << 12) // 12 | ||
556 | | (0 << 13) // 13 | ||
557 | | (1 << 14) // 14 | ||
558 | | (0 << 15) // 15 | ||
559 | | (1 << 16) // 16 | ||
560 | | (1 << 17) // 17 | ||
561 | | (1 << 18) // 18 | ||
562 | | (1 << 19) // 19 | ||
563 | }; | ||
564 | #define keyword_is_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK) | ||
565 | |||
566 | // This is a bit array that corresponds to token types. An entry is | ||
567 | // true if the token is valid in an expression, false otherwise. | ||
568 | // Used to figure out when expr parsing should stop *without error message* | ||
569 | // - 0 element indicates this condition. 1 means "this token is to be eaten | ||
570 | // as part of the expression", it can then still be determined to be invalid | ||
571 | // by later processing. | ||
572 | enum { | ||
573 | #define EXBITS(a,b,c,d,e,f,g,h) \ | ||
574 | ((uint64_t)((a << 0)+(b << 1)+(c << 2)+(d << 3)+(e << 4)+(f << 5)+(g << 6)+(h << 7))) | ||
575 | BC_PARSE_EXPRS_BITS = 0 // corresponding BC_LEX_xyz: | ||
576 | + (EXBITS(0,0,0,0,0,1,1,1) << (0*8)) // 0: EOF INVAL NL WS STR NAME NUM - | ||
577 | + (EXBITS(1,1,1,1,1,1,1,1) << (1*8)) // 8: == <= >= != < > ^ * | ||
578 | + (EXBITS(1,1,1,1,1,1,1,1) << (2*8)) // 16: / % + - ! || && ^= | ||
579 | + (EXBITS(1,1,1,1,1,1,1,1) << (3*8)) // 24: *= /= %= += -= = ++ -- | ||
580 | + (EXBITS(1,1,0,0,0,0,0,0) << (4*8)) // 32: ( ) [ , ] { ; } | ||
581 | + (EXBITS(0,0,0,0,0,0,0,1) << (5*8)) // 40: auto break cont define else for halt ibase | ||
582 | + (EXBITS(1,0,1,1,0,0,0,1) << (6*8)) // 48: obase if last length limits print quit read | ||
583 | + (EXBITS(0,1,1,0,0,0,0,0) << (7*8)) // 56: return scale sqrt while | ||
584 | #undef EXBITS | ||
585 | }; | ||
586 | static ALWAYS_INLINE long lex_allowed_in_bc_expr(unsigned i) | ||
587 | { | ||
588 | #if ULONG_MAX > 0xffffffff | ||
589 | // 64-bit version (will not work correctly for 32-bit longs!) | ||
590 | return BC_PARSE_EXPRS_BITS & (1UL << i); | ||
591 | #else | ||
592 | // 32-bit version | ||
593 | unsigned long m = (uint32_t)BC_PARSE_EXPRS_BITS; | ||
594 | if (i >= 32) { | ||
595 | m = (uint32_t)(BC_PARSE_EXPRS_BITS >> 32); | ||
596 | i &= 31; | ||
597 | } | ||
598 | return m & (1UL << i); | ||
599 | #endif | ||
600 | } | ||
601 | |||
602 | // This is an array of data for operators that correspond to | ||
603 | // [XC_LEX_1st_op...] token types. | ||
604 | static const uint8_t bc_ops_prec_and_assoc[] ALIGN1 = { | ||
605 | #define OP(p,l) ((int)(l) * 0x10 + (p)) | ||
606 | OP(1, false), // neg | ||
607 | OP(6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), // == <= >= != < > | ||
608 | OP(2, false), // pow | ||
609 | OP(3, true ), OP( 3, true ), OP( 3, true ), // mul div mod | ||
610 | OP(4, true ), OP( 4, true ), // + - | ||
611 | OP(1, false), // not | ||
612 | OP(7, true ), OP( 7, true ), // or and | ||
613 | OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ), // ^= *= /= %= += | ||
614 | OP(5, false), OP( 5, false ), // -= = | ||
615 | OP(0, false), OP( 0, false ), // inc dec | ||
616 | #undef OP | ||
617 | }; | ||
618 | #define bc_operation_PREC(i) (bc_ops_prec_and_assoc[i] & 0x0f) | ||
619 | #define bc_operation_LEFT(i) (bc_ops_prec_and_assoc[i] & 0x10) | ||
620 | #endif // ENABLE_BC | ||
621 | |||
622 | #if ENABLE_DC | ||
623 | static const //BcLexType - should be this type | ||
624 | uint8_t | ||
625 | dc_char_to_LEX[] ALIGN1 = { | ||
626 | // %&'( | ||
627 | XC_LEX_OP_MODULUS, XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_LPAREN, | ||
628 | // )*+, | ||
629 | XC_LEX_INVALID, XC_LEX_OP_MULTIPLY, XC_LEX_OP_PLUS, XC_LEX_INVALID, | ||
630 | // -./ | ||
631 | XC_LEX_OP_MINUS, XC_LEX_INVALID, XC_LEX_OP_DIVIDE, | ||
632 | // 0123456789 | ||
633 | XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, | ||
634 | XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, | ||
635 | XC_LEX_INVALID, XC_LEX_INVALID, | ||
636 | // :;<=>?@ | ||
637 | DC_LEX_COLON, DC_LEX_SCOLON, XC_LEX_OP_REL_GT, XC_LEX_OP_REL_EQ, | ||
638 | XC_LEX_OP_REL_LT, DC_LEX_READ, XC_LEX_INVALID, | ||
639 | // ABCDEFGH | ||
640 | XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, | ||
641 | XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_EQ_NO_REG, XC_LEX_INVALID, | ||
642 | // IJKLMNOP | ||
643 | DC_LEX_IBASE, XC_LEX_INVALID, DC_LEX_SCALE, DC_LEX_LOAD_POP, | ||
644 | XC_LEX_INVALID, DC_LEX_OP_BOOL_NOT, DC_LEX_OBASE, DC_LEX_PRINT_STREAM, | ||
645 | // QRSTUVWX | ||
646 | DC_LEX_NQUIT, DC_LEX_POP, DC_LEX_STORE_PUSH, XC_LEX_INVALID, | ||
647 | XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_SCALE_FACTOR, | ||
648 | // YZ | ||
649 | XC_LEX_INVALID, DC_LEX_LENGTH, | ||
650 | // [\] | ||
651 | XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, | ||
652 | // ^_` | ||
653 | XC_LEX_OP_POWER, XC_LEX_NEG, XC_LEX_INVALID, | ||
654 | // abcdefgh | ||
655 | DC_LEX_ASCIIFY, XC_LEX_INVALID, DC_LEX_CLEAR_STACK, DC_LEX_DUPLICATE, | ||
656 | DC_LEX_ELSE, DC_LEX_PRINT_STACK, XC_LEX_INVALID, XC_LEX_INVALID, | ||
657 | // ijklmnop | ||
658 | DC_LEX_STORE_IBASE, XC_LEX_INVALID, DC_LEX_STORE_SCALE, DC_LEX_LOAD, | ||
659 | XC_LEX_INVALID, DC_LEX_PRINT_POP, DC_LEX_STORE_OBASE, DC_LEX_PRINT, | ||
660 | // qrstuvwx | ||
661 | DC_LEX_QUIT, DC_LEX_SWAP, DC_LEX_OP_ASSIGN, XC_LEX_INVALID, | ||
662 | XC_LEX_INVALID, DC_LEX_SQRT, XC_LEX_INVALID, DC_LEX_EXECUTE, | ||
663 | // yz | ||
664 | XC_LEX_INVALID, DC_LEX_STACK_LEVEL, | ||
665 | // {|}~ | ||
666 | DC_LEX_LBRACE, DC_LEX_OP_MODEXP, XC_LEX_INVALID, DC_LEX_OP_DIVMOD, | ||
667 | }; | ||
668 | static const //BcInst - should be this type. Using signed narrow type since DC_INST_INVALID is -1 | ||
669 | int8_t | ||
670 | dc_LEX_to_INST[] ALIGN1 = { //starts at XC_LEX_OP_POWER // corresponding XC/DC_LEX_xyz: | ||
671 | XC_INST_POWER, XC_INST_MULTIPLY, // XC_LEX_OP_POWER XC_LEX_OP_MULTIPLY | ||
672 | XC_INST_DIVIDE, XC_INST_MODULUS, // XC_LEX_OP_DIVIDE XC_LEX_OP_MODULUS | ||
673 | XC_INST_PLUS, XC_INST_MINUS, // XC_LEX_OP_PLUS XC_LEX_OP_MINUS | ||
674 | XC_INST_BOOL_NOT, // DC_LEX_OP_BOOL_NOT | ||
675 | DC_INST_INVALID, // DC_LEX_OP_ASSIGN | ||
676 | XC_INST_REL_GT, // DC_LEX_LPAREN | ||
677 | DC_INST_INVALID, // DC_LEX_SCOLON | ||
678 | DC_INST_INVALID, // DC_LEX_READ | ||
679 | XC_INST_IBASE, // DC_LEX_IBASE | ||
680 | XC_INST_SCALE, // DC_LEX_SCALE | ||
681 | XC_INST_OBASE, // DC_LEX_OBASE | ||
682 | XC_INST_LENGTH, // DC_LEX_LENGTH | ||
683 | XC_INST_PRINT, // DC_LEX_PRINT | ||
684 | DC_INST_QUIT, // DC_LEX_QUIT | ||
685 | XC_INST_SQRT, // DC_LEX_SQRT | ||
686 | XC_INST_REL_GE, // DC_LEX_LBRACE | ||
687 | XC_INST_REL_EQ, // DC_LEX_EQ_NO_REG | ||
688 | DC_INST_MODEXP, DC_INST_DIVMOD, // DC_LEX_OP_MODEXP DC_LEX_OP_DIVMOD | ||
689 | DC_INST_INVALID, DC_INST_INVALID, // DC_LEX_COLON DC_LEX_ELSE | ||
690 | DC_INST_EXECUTE, // DC_LEX_EXECUTE | ||
691 | DC_INST_PRINT_STACK, DC_INST_CLEAR_STACK, // DC_LEX_PRINT_STACK DC_LEX_CLEAR_STACK | ||
692 | DC_INST_STACK_LEN, DC_INST_DUPLICATE, // DC_LEX_STACK_LEVEL DC_LEX_DUPLICATE | ||
693 | DC_INST_SWAP, XC_INST_POP, // DC_LEX_SWAP DC_LEX_POP | ||
694 | DC_INST_ASCIIFY, DC_INST_PRINT_STREAM, // DC_LEX_ASCIIFY DC_LEX_PRINT_STREAM | ||
695 | DC_INST_INVALID, DC_INST_INVALID, // DC_LEX_STORE_IBASE DC_LEX_STORE_OBASE | ||
696 | DC_INST_INVALID, DC_INST_INVALID, // DC_LEX_STORE_SCALE DC_LEX_LOAD | ||
697 | DC_INST_INVALID, DC_INST_INVALID, // DC_LEX_LOAD_POP DC_LEX_STORE_PUSH | ||
698 | XC_INST_PRINT, DC_INST_NQUIT, // DC_LEX_PRINT_POP DC_LEX_NQUIT | ||
699 | XC_INST_SCALE_FUNC, // DC_LEX_SCALE_FACTOR | ||
700 | // DC_INST_INVALID in this table either means that corresponding LEX | ||
701 | // is not possible for dc, or that it does not compile one-to-one | ||
702 | // to a single INST. | ||
703 | }; | ||
704 | #endif // ENABLE_DC | ||
705 | |||
706 | typedef struct BcParse { | ||
707 | smallint lex; // was BcLexType // first member is most used | ||
708 | smallint lex_last; // was BcLexType | ||
709 | size_t lex_line; | ||
710 | const char *lex_inbuf; | ||
711 | const char *lex_next_at; // last lex_next() was called at this string | ||
712 | const char *lex_filename; | ||
713 | FILE *lex_input_fp; | ||
714 | BcVec lex_strnumbuf; | ||
715 | |||
716 | BcFunc *func; | ||
717 | size_t fidx; | ||
718 | IF_BC(size_t in_funcdef;) | ||
719 | IF_BC(BcVec exits;) | ||
720 | IF_BC(BcVec conds;) | ||
721 | IF_BC(BcVec ops;) | ||
722 | } BcParse; | ||
723 | |||
724 | typedef struct BcProgram { | ||
725 | size_t len; | ||
726 | size_t nchars; | ||
727 | |||
728 | size_t scale; | ||
729 | size_t ib_t; | ||
730 | size_t ob_t; | ||
731 | |||
732 | BcVec results; | ||
733 | BcVec exestack; | ||
734 | |||
735 | BcVec fns; | ||
736 | IF_BC(BcVec fn_map;) | ||
737 | |||
738 | BcVec vars; | ||
739 | BcVec var_map; | ||
740 | |||
741 | BcVec arrs; | ||
742 | BcVec arr_map; | ||
743 | |||
744 | IF_DC(BcVec strs;) | ||
745 | IF_DC(BcVec consts;) | ||
746 | |||
747 | BcNum zero; | ||
748 | IF_BC(BcNum one;) | ||
749 | IF_BC(BcNum last;) | ||
750 | } BcProgram; | ||
751 | |||
752 | struct globals { | ||
753 | BcParse prs; // first member is most used | ||
754 | |||
755 | // For error messages. Can be set to current parsed line, | ||
756 | // or [TODO] to current executing line (can be before last parsed one) | ||
757 | size_t err_line; | ||
758 | |||
759 | BcVec input_buffer; | ||
760 | |||
761 | IF_FEATURE_BC_INTERACTIVE(smallint ttyin;) | ||
762 | IF_FEATURE_CLEAN_UP(smallint exiting;) | ||
763 | |||
764 | BcProgram prog; | ||
765 | |||
766 | BcVec files; | ||
767 | |||
768 | char *env_args; | ||
769 | |||
770 | #if ENABLE_FEATURE_EDITING | ||
771 | line_input_t *line_input_state; | ||
772 | #endif | ||
773 | } FIX_ALIASING; | ||
774 | #define G (*ptr_to_globals) | ||
775 | #define INIT_G() do { \ | ||
776 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | ||
777 | } while (0) | ||
778 | #define FREE_G() do { \ | ||
779 | FREE_PTR_TO_GLOBALS(); \ | ||
780 | } while (0) | ||
781 | #define G_posix (ENABLE_BC && (option_mask32 & BC_FLAG_S)) | ||
782 | #define G_warn (ENABLE_BC && (option_mask32 & BC_FLAG_W)) | ||
783 | #define G_exreg (ENABLE_DC && (option_mask32 & DC_FLAG_X)) | ||
784 | #if ENABLE_FEATURE_BC_INTERACTIVE | ||
785 | # define G_interrupt bb_got_signal | ||
786 | # define G_ttyin G.ttyin | ||
787 | #else | ||
788 | # define G_interrupt 0 | ||
789 | # define G_ttyin 0 | ||
790 | #endif | ||
791 | #if ENABLE_FEATURE_CLEAN_UP | ||
792 | # define G_exiting G.exiting | ||
793 | #else | ||
794 | # define G_exiting 0 | ||
795 | #endif | ||
796 | #define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b')) | ||
797 | #define IS_DC (ENABLE_DC && (!ENABLE_BC || applet_name[0] != 'b')) | ||
798 | |||
799 | #if ENABLE_BC | ||
800 | # define BC_PARSE_REL (1 << 0) | ||
801 | # define BC_PARSE_PRINT (1 << 1) | ||
802 | # define BC_PARSE_ARRAY (1 << 2) | ||
803 | # define BC_PARSE_NOCALL (1 << 3) | ||
804 | #endif | ||
805 | |||
806 | #define BC_PROG_MAIN 0 | ||
807 | #define BC_PROG_READ 1 | ||
808 | #if ENABLE_DC | ||
809 | #define BC_PROG_REQ_FUNCS 2 | ||
810 | #endif | ||
811 | |||
812 | #define BC_FLAG_W (1 << 0) | ||
813 | #define BC_FLAG_V (1 << 1) | ||
814 | #define BC_FLAG_S (1 << 2) | ||
815 | #define BC_FLAG_Q (1 << 3) | ||
816 | #define BC_FLAG_L (1 << 4) | ||
817 | #define BC_FLAG_I ((1 << 5) * ENABLE_DC) | ||
818 | #define DC_FLAG_X ((1 << 6) * ENABLE_DC) | ||
819 | |||
820 | #define BC_MAX_OBASE ((unsigned) 999) | ||
821 | #define BC_MAX_DIM ((unsigned) INT_MAX) | ||
822 | #define BC_MAX_SCALE ((unsigned) UINT_MAX) | ||
823 | #define BC_MAX_STRING ((unsigned) UINT_MAX - 1) | ||
824 | #define BC_MAX_NUM BC_MAX_STRING | ||
825 | // Unused apart from "limits" message. Just show a "biggish number" there. | ||
826 | //#define BC_MAX_EXP ((unsigned long) LONG_MAX) | ||
827 | //#define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1) | ||
828 | #define BC_MAX_EXP_STR "999999999" | ||
829 | #define BC_MAX_VARS_STR "999999999" | ||
830 | |||
831 | #define BC_MAX_OBASE_STR "999" | ||
832 | |||
833 | #if INT_MAX == 2147483647 | ||
834 | # define BC_MAX_DIM_STR "2147483647" | ||
835 | #elif INT_MAX == 9223372036854775807 | ||
836 | # define BC_MAX_DIM_STR "9223372036854775807" | ||
837 | #else | ||
838 | # error Strange INT_MAX | ||
839 | #endif | ||
840 | |||
841 | #if UINT_MAX == 4294967295 | ||
842 | # define BC_MAX_SCALE_STR "4294967295" | ||
843 | # define BC_MAX_STRING_STR "4294967294" | ||
844 | #elif UINT_MAX == 18446744073709551615 | ||
845 | # define BC_MAX_SCALE_STR "18446744073709551615" | ||
846 | # define BC_MAX_STRING_STR "18446744073709551614" | ||
847 | #else | ||
848 | # error Strange UINT_MAX | ||
849 | #endif | ||
850 | #define BC_MAX_NUM_STR BC_MAX_STRING_STR | ||
851 | |||
852 | // In configurations where errors abort instead of propagating error | ||
853 | // return code up the call chain, functions returning BC_STATUS | ||
854 | // actually don't return anything, they always succeed and return "void". | ||
855 | // A macro wrapper is provided, which makes this statement work: | ||
856 | // s = zbc_func(...) | ||
857 | // and makes it visible to the compiler that s is always zero, | ||
858 | // allowing compiler to optimize dead code after the statement. | ||
859 | // | ||
860 | // To make code more readable, each such function has a "z" | ||
861 | // ("always returning zero") prefix, i.e. zbc_foo or zdc_foo. | ||
862 | // | ||
863 | #if ENABLE_FEATURE_BC_INTERACTIVE || ENABLE_FEATURE_CLEAN_UP | ||
864 | # define ERRORS_ARE_FATAL 0 | ||
865 | # define ERRORFUNC /*nothing*/ | ||
866 | # define IF_ERROR_RETURN_POSSIBLE(a) a | ||
867 | # define BC_STATUS BcStatus | ||
868 | # define RETURN_STATUS(v) return (v) | ||
869 | # define COMMA_SUCCESS /*nothing*/ | ||
870 | #else | ||
871 | # define ERRORS_ARE_FATAL 1 | ||
872 | # define ERRORFUNC NORETURN | ||
873 | # define IF_ERROR_RETURN_POSSIBLE(a) /*nothing*/ | ||
874 | # define BC_STATUS void | ||
875 | # define RETURN_STATUS(v) do { ((void)(v)); return; } while (0) | ||
876 | # define COMMA_SUCCESS ,BC_STATUS_SUCCESS | ||
877 | #endif | ||
878 | |||
879 | // | ||
880 | // Utility routines | ||
881 | // | ||
882 | |||
883 | #define BC_MAX(a, b) ((a) > (b) ? (a) : (b)) | ||
884 | #define BC_MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
885 | |||
886 | static void fflush_and_check(void) | ||
887 | { | ||
888 | fflush_all(); | ||
889 | if (ferror(stdout) || ferror(stderr)) | ||
890 | bb_perror_msg_and_die("output error"); | ||
891 | } | ||
892 | |||
893 | #if ENABLE_FEATURE_CLEAN_UP | ||
894 | #define QUIT_OR_RETURN_TO_MAIN \ | ||
895 | do { \ | ||
896 | IF_FEATURE_BC_INTERACTIVE(G_ttyin = 0;) /* do not loop in main loop anymore */ \ | ||
897 | G_exiting = 1; \ | ||
898 | return BC_STATUS_FAILURE; \ | ||
899 | } while (0) | ||
900 | #else | ||
901 | static void quit(void) NORETURN; | ||
902 | static void quit(void) | ||
903 | { | ||
904 | if (ferror(stdin)) | ||
905 | bb_perror_msg_and_die("input error"); | ||
906 | fflush_and_check(); | ||
907 | dbg_exec("quit(): exiting with exitcode SUCCESS"); | ||
908 | exit(0); | ||
909 | } | ||
910 | #define QUIT_OR_RETURN_TO_MAIN quit() | ||
911 | #endif | ||
912 | |||
913 | static void bc_verror_msg(const char *fmt, va_list p) | ||
914 | { | ||
915 | const char *sv = sv; // for compiler | ||
916 | if (G.prs.lex_filename) { | ||
917 | sv = applet_name; | ||
918 | applet_name = xasprintf("%s: %s:%lu", applet_name, | ||
919 | G.prs.lex_filename, (unsigned long)G.err_line | ||
920 | ); | ||
921 | } | ||
922 | bb_verror_msg(fmt, p, NULL); | ||
923 | if (G.prs.lex_filename) { | ||
924 | free((char*)applet_name); | ||
925 | applet_name = sv; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | static NOINLINE ERRORFUNC int bc_error_fmt(const char *fmt, ...) | ||
930 | { | ||
931 | va_list p; | ||
932 | |||
933 | va_start(p, fmt); | ||
934 | bc_verror_msg(fmt, p); | ||
935 | va_end(p); | ||
936 | |||
937 | if (ENABLE_FEATURE_CLEAN_UP || G_ttyin) | ||
938 | IF_ERROR_RETURN_POSSIBLE(return BC_STATUS_FAILURE); | ||
939 | exit(1); | ||
940 | } | ||
941 | |||
942 | #if ENABLE_BC | ||
943 | static NOINLINE BC_STATUS zbc_posix_error_fmt(const char *fmt, ...) | ||
944 | { | ||
945 | va_list p; | ||
946 | |||
947 | // Are non-POSIX constructs totally ok? | ||
948 | if (!(option_mask32 & (BC_FLAG_S|BC_FLAG_W))) | ||
949 | RETURN_STATUS(BC_STATUS_SUCCESS); // yes | ||
950 | |||
951 | va_start(p, fmt); | ||
952 | bc_verror_msg(fmt, p); | ||
953 | va_end(p); | ||
954 | |||
955 | // Do we treat non-POSIX constructs as errors? | ||
956 | if (!(option_mask32 & BC_FLAG_S)) | ||
957 | RETURN_STATUS(BC_STATUS_SUCCESS); // no, it's a warning | ||
958 | |||
959 | if (ENABLE_FEATURE_CLEAN_UP || G_ttyin) | ||
960 | RETURN_STATUS(BC_STATUS_FAILURE); | ||
961 | exit(1); | ||
962 | } | ||
963 | #define zbc_posix_error_fmt(...) (zbc_posix_error_fmt(__VA_ARGS__) COMMA_SUCCESS) | ||
964 | #endif | ||
965 | |||
966 | // We use error functions with "return bc_error(FMT[, PARAMS])" idiom. | ||
967 | // This idiom begs for tail-call optimization, but for it to work, | ||
968 | // function must not have caller-cleaned parameters on stack. | ||
969 | // Unfortunately, vararg function API does exactly that on most arches. | ||
970 | // Thus, use these shims for the cases when we have no vararg PARAMS: | ||
971 | static ERRORFUNC int bc_error(const char *msg) | ||
972 | { | ||
973 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg); | ||
974 | } | ||
975 | static ERRORFUNC int bc_error_at(const char *msg) | ||
976 | { | ||
977 | const char *err_at = G.prs.lex_next_at; | ||
978 | if (err_at) { | ||
979 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt( | ||
980 | "%s at '%.*s'", | ||
981 | msg, | ||
982 | (int)(strchrnul(err_at, '\n') - err_at), | ||
983 | err_at | ||
984 | ); | ||
985 | } | ||
986 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg); | ||
987 | } | ||
988 | static ERRORFUNC int bc_error_bad_character(char c) | ||
989 | { | ||
990 | if (!c) | ||
991 | IF_ERROR_RETURN_POSSIBLE(return) bc_error("NUL character"); | ||
992 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("bad character '%c'", c); | ||
993 | } | ||
994 | static ERRORFUNC int bc_error_bad_function_definition(void) | ||
995 | { | ||
996 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad function definition"); | ||
997 | } | ||
998 | static ERRORFUNC int bc_error_bad_expression(void) | ||
999 | { | ||
1000 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad expression"); | ||
1001 | } | ||
1002 | static ERRORFUNC int bc_error_bad_assignment(void) | ||
1003 | { | ||
1004 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_at( | ||
1005 | "bad assignment: left side must be variable or array element" | ||
1006 | ); | ||
1007 | } | ||
1008 | static ERRORFUNC int bc_error_bad_token(void) | ||
1009 | { | ||
1010 | IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad token"); | ||
1011 | } | ||
1012 | static ERRORFUNC int bc_error_stack_has_too_few_elements(void) | ||
1013 | { | ||
1014 | IF_ERROR_RETURN_POSSIBLE(return) bc_error("stack has too few elements"); | ||
1015 | } | ||
1016 | static ERRORFUNC int bc_error_variable_is_wrong_type(void) | ||
1017 | { | ||
1018 | IF_ERROR_RETURN_POSSIBLE(return) bc_error("variable is wrong type"); | ||
1019 | } | ||
1020 | #if ENABLE_BC | ||
1021 | static BC_STATUS zbc_POSIX_requires(const char *msg) | ||
1022 | { | ||
1023 | RETURN_STATUS(zbc_posix_error_fmt("POSIX requires %s", msg)); | ||
1024 | } | ||
1025 | #define zbc_POSIX_requires(...) (zbc_POSIX_requires(__VA_ARGS__) COMMA_SUCCESS) | ||
1026 | static BC_STATUS zbc_POSIX_does_not_allow(const char *msg) | ||
1027 | { | ||
1028 | RETURN_STATUS(zbc_posix_error_fmt("%s%s", "POSIX does not allow ", msg)); | ||
1029 | } | ||
1030 | #define zbc_POSIX_does_not_allow(...) (zbc_POSIX_does_not_allow(__VA_ARGS__) COMMA_SUCCESS) | ||
1031 | static BC_STATUS zbc_POSIX_does_not_allow_bool_ops_this_is_bad(const char *msg) | ||
1032 | { | ||
1033 | RETURN_STATUS(zbc_posix_error_fmt("%s%s %s", "POSIX does not allow ", "boolean operators; this is bad:", msg)); | ||
1034 | } | ||
1035 | #define zbc_POSIX_does_not_allow_bool_ops_this_is_bad(...) (zbc_POSIX_does_not_allow_bool_ops_this_is_bad(__VA_ARGS__) COMMA_SUCCESS) | ||
1036 | static BC_STATUS zbc_POSIX_does_not_allow_empty_X_expression_in_for(const char *msg) | ||
1037 | { | ||
1038 | RETURN_STATUS(zbc_posix_error_fmt("%san empty %s expression in 'for()'", "POSIX does not allow ", msg)); | ||
1039 | } | ||
1040 | #define zbc_POSIX_does_not_allow_empty_X_expression_in_for(...) (zbc_POSIX_does_not_allow_empty_X_expression_in_for(__VA_ARGS__) COMMA_SUCCESS) | ||
1041 | #endif | ||
1042 | |||
1043 | static void bc_vec_grow(BcVec *v, size_t n) | ||
1044 | { | ||
1045 | size_t cap = v->cap * 2; | ||
1046 | while (cap < v->len + n) cap *= 2; | ||
1047 | v->v = xrealloc(v->v, v->size * cap); | ||
1048 | v->cap = cap; | ||
1049 | } | ||
1050 | |||
1051 | static void bc_vec_init(BcVec *v, size_t esize, BcVecFree dtor) | ||
1052 | { | ||
1053 | v->size = esize; | ||
1054 | v->cap = BC_VEC_START_CAP; | ||
1055 | v->len = 0; | ||
1056 | v->dtor = dtor; | ||
1057 | v->v = xmalloc(esize * BC_VEC_START_CAP); | ||
1058 | } | ||
1059 | |||
1060 | static void bc_char_vec_init(BcVec *v) | ||
1061 | { | ||
1062 | bc_vec_init(v, sizeof(char), NULL); | ||
1063 | } | ||
1064 | |||
1065 | static void bc_vec_expand(BcVec *v, size_t req) | ||
1066 | { | ||
1067 | if (v->cap < req) { | ||
1068 | v->v = xrealloc(v->v, v->size * req); | ||
1069 | v->cap = req; | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | static void bc_vec_pop(BcVec *v) | ||
1074 | { | ||
1075 | v->len--; | ||
1076 | if (v->dtor) | ||
1077 | v->dtor(v->v + (v->size * v->len)); | ||
1078 | } | ||
1079 | |||
1080 | static void bc_vec_npop(BcVec *v, size_t n) | ||
1081 | { | ||
1082 | if (!v->dtor) | ||
1083 | v->len -= n; | ||
1084 | else { | ||
1085 | size_t len = v->len - n; | ||
1086 | while (v->len > len) v->dtor(v->v + (v->size * --v->len)); | ||
1087 | } | ||
1088 | } | ||
1089 | |||
1090 | static void bc_vec_pop_all(BcVec *v) | ||
1091 | { | ||
1092 | bc_vec_npop(v, v->len); | ||
1093 | } | ||
1094 | |||
1095 | static size_t bc_vec_push(BcVec *v, const void *data) | ||
1096 | { | ||
1097 | size_t len = v->len; | ||
1098 | if (len >= v->cap) bc_vec_grow(v, 1); | ||
1099 | memmove(v->v + (v->size * len), data, v->size); | ||
1100 | v->len++; | ||
1101 | return len; | ||
1102 | } | ||
1103 | |||
1104 | // G.prog.results often needs "pop old operand, push result" idiom. | ||
1105 | // Can do this without a few extra ops | ||
1106 | static size_t bc_result_pop_and_push(const void *data) | ||
1107 | { | ||
1108 | BcVec *v = &G.prog.results; | ||
1109 | char *last; | ||
1110 | size_t len = v->len - 1; | ||
1111 | |||
1112 | last = v->v + (v->size * len); | ||
1113 | if (v->dtor) | ||
1114 | v->dtor(last); | ||
1115 | memmove(last, data, v->size); | ||
1116 | return len; | ||
1117 | } | ||
1118 | |||
1119 | static size_t bc_vec_pushByte(BcVec *v, char data) | ||
1120 | { | ||
1121 | return bc_vec_push(v, &data); | ||
1122 | } | ||
1123 | |||
1124 | static size_t bc_vec_pushZeroByte(BcVec *v) | ||
1125 | { | ||
1126 | //return bc_vec_pushByte(v, '\0'); | ||
1127 | // better: | ||
1128 | return bc_vec_push(v, &const_int_0); | ||
1129 | } | ||
1130 | |||
1131 | static void bc_vec_pushAt(BcVec *v, const void *data, size_t idx) | ||
1132 | { | ||
1133 | if (idx == v->len) | ||
1134 | bc_vec_push(v, data); | ||
1135 | else { | ||
1136 | char *ptr; | ||
1137 | |||
1138 | if (v->len == v->cap) bc_vec_grow(v, 1); | ||
1139 | |||
1140 | ptr = v->v + v->size * idx; | ||
1141 | |||
1142 | memmove(ptr + v->size, ptr, v->size * (v->len++ - idx)); | ||
1143 | memmove(ptr, data, v->size); | ||
1144 | } | ||
1145 | } | ||
1146 | |||
1147 | static void bc_vec_string(BcVec *v, size_t len, const char *str) | ||
1148 | { | ||
1149 | bc_vec_pop_all(v); | ||
1150 | bc_vec_expand(v, len + 1); | ||
1151 | memcpy(v->v, str, len); | ||
1152 | v->len = len; | ||
1153 | |||
1154 | bc_vec_pushZeroByte(v); | ||
1155 | } | ||
1156 | |||
1157 | static void *bc_vec_item(const BcVec *v, size_t idx) | ||
1158 | { | ||
1159 | return v->v + v->size * idx; | ||
1160 | } | ||
1161 | |||
1162 | static void *bc_vec_item_rev(const BcVec *v, size_t idx) | ||
1163 | { | ||
1164 | return v->v + v->size * (v->len - idx - 1); | ||
1165 | } | ||
1166 | |||
1167 | static void *bc_vec_top(const BcVec *v) | ||
1168 | { | ||
1169 | return v->v + v->size * (v->len - 1); | ||
1170 | } | ||
1171 | |||
1172 | static FAST_FUNC void bc_vec_free(void *vec) | ||
1173 | { | ||
1174 | BcVec *v = (BcVec *) vec; | ||
1175 | bc_vec_pop_all(v); | ||
1176 | free(v->v); | ||
1177 | } | ||
1178 | |||
1179 | static BcFunc* xc_program_func(size_t idx) | ||
1180 | { | ||
1181 | return bc_vec_item(&G.prog.fns, idx); | ||
1182 | } | ||
1183 | // BC_PROG_MAIN is zeroth element, so: | ||
1184 | #define xc_program_func_BC_PROG_MAIN() ((BcFunc*)(G.prog.fns.v)) | ||
1185 | |||
1186 | #if ENABLE_BC | ||
1187 | static BcFunc* bc_program_current_func(void) | ||
1188 | { | ||
1189 | BcInstPtr *ip = bc_vec_top(&G.prog.exestack); | ||
1190 | BcFunc *func = xc_program_func(ip->func); | ||
1191 | return func; | ||
1192 | } | ||
1193 | #endif | ||
1194 | |||
1195 | static char** xc_program_str(size_t idx) | ||
1196 | { | ||
1197 | #if ENABLE_BC | ||
1198 | if (IS_BC) { | ||
1199 | BcFunc *func = bc_program_current_func(); | ||
1200 | return bc_vec_item(&func->strs, idx); | ||
1201 | } | ||
1202 | #endif | ||
1203 | IF_DC(return bc_vec_item(&G.prog.strs, idx);) | ||
1204 | } | ||
1205 | |||
1206 | static char** xc_program_const(size_t idx) | ||
1207 | { | ||
1208 | #if ENABLE_BC | ||
1209 | if (IS_BC) { | ||
1210 | BcFunc *func = bc_program_current_func(); | ||
1211 | return bc_vec_item(&func->consts, idx); | ||
1212 | } | ||
1213 | #endif | ||
1214 | IF_DC(return bc_vec_item(&G.prog.consts, idx);) | ||
1215 | } | ||
1216 | |||
1217 | static int bc_id_cmp(const void *e1, const void *e2) | ||
1218 | { | ||
1219 | return strcmp(((const BcId *) e1)->name, ((const BcId *) e2)->name); | ||
1220 | } | ||
1221 | |||
1222 | static FAST_FUNC void bc_id_free(void *id) | ||
1223 | { | ||
1224 | free(((BcId *) id)->name); | ||
1225 | } | ||
1226 | |||
1227 | static size_t bc_map_find_ge(const BcVec *v, const void *ptr) | ||
1228 | { | ||
1229 | size_t low = 0, high = v->len; | ||
1230 | |||
1231 | while (low < high) { | ||
1232 | size_t mid = (low + high) / 2; | ||
1233 | BcId *id = bc_vec_item(v, mid); | ||
1234 | int result = bc_id_cmp(ptr, id); | ||
1235 | |||
1236 | if (result == 0) | ||
1237 | return mid; | ||
1238 | if (result < 0) | ||
1239 | high = mid; | ||
1240 | else | ||
1241 | low = mid + 1; | ||
1242 | } | ||
1243 | |||
1244 | return low; | ||
1245 | } | ||
1246 | |||
1247 | static int bc_map_insert(BcVec *v, const void *ptr, size_t *i) | ||
1248 | { | ||
1249 | size_t n = *i = bc_map_find_ge(v, ptr); | ||
1250 | |||
1251 | if (n == v->len) | ||
1252 | bc_vec_push(v, ptr); | ||
1253 | else if (!bc_id_cmp(ptr, bc_vec_item(v, n))) | ||
1254 | return 0; // "was not inserted" | ||
1255 | else | ||
1256 | bc_vec_pushAt(v, ptr, n); | ||
1257 | return 1; // "was inserted" | ||
1258 | } | ||
1259 | |||
1260 | #if ENABLE_BC | ||
1261 | static size_t bc_map_find_exact(const BcVec *v, const void *ptr) | ||
1262 | { | ||
1263 | size_t i = bc_map_find_ge(v, ptr); | ||
1264 | if (i >= v->len) return BC_VEC_INVALID_IDX; | ||
1265 | return bc_id_cmp(ptr, bc_vec_item(v, i)) ? BC_VEC_INVALID_IDX : i; | ||
1266 | } | ||
1267 | #endif | ||
1268 | |||
1269 | static void bc_num_setToZero(BcNum *n, size_t scale) | ||
1270 | { | ||
1271 | n->len = 0; | ||
1272 | n->neg = false; | ||
1273 | n->rdx = scale; | ||
1274 | } | ||
1275 | |||
1276 | static void bc_num_zero(BcNum *n) | ||
1277 | { | ||
1278 | bc_num_setToZero(n, 0); | ||
1279 | } | ||
1280 | |||
1281 | static void bc_num_one(BcNum *n) | ||
1282 | { | ||
1283 | bc_num_setToZero(n, 0); | ||
1284 | n->len = 1; | ||
1285 | n->num[0] = 1; | ||
1286 | } | ||
1287 | |||
1288 | // Note: this also sets BcNum to zero | ||
1289 | static void bc_num_init(BcNum *n, size_t req) | ||
1290 | { | ||
1291 | req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; | ||
1292 | //memset(n, 0, sizeof(BcNum)); - cleared by assignments below | ||
1293 | n->num = xmalloc(req); | ||
1294 | n->cap = req; | ||
1295 | n->rdx = 0; | ||
1296 | n->len = 0; | ||
1297 | n->neg = false; | ||
1298 | } | ||
1299 | |||
1300 | static void bc_num_init_DEF_SIZE(BcNum *n) | ||
1301 | { | ||
1302 | bc_num_init(n, BC_NUM_DEF_SIZE); | ||
1303 | } | ||
1304 | |||
1305 | static void bc_num_expand(BcNum *n, size_t req) | ||
1306 | { | ||
1307 | req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; | ||
1308 | if (req > n->cap) { | ||
1309 | n->num = xrealloc(n->num, req); | ||
1310 | n->cap = req; | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1314 | static FAST_FUNC void bc_num_free(void *num) | ||
1315 | { | ||
1316 | free(((BcNum *) num)->num); | ||
1317 | } | ||
1318 | |||
1319 | static void bc_num_copy(BcNum *d, BcNum *s) | ||
1320 | { | ||
1321 | if (d != s) { | ||
1322 | bc_num_expand(d, s->cap); | ||
1323 | d->len = s->len; | ||
1324 | d->neg = s->neg; | ||
1325 | d->rdx = s->rdx; | ||
1326 | memcpy(d->num, s->num, sizeof(BcDig) * d->len); | ||
1327 | } | ||
1328 | } | ||
1329 | |||
1330 | static BC_STATUS zbc_num_ulong_abs(BcNum *n, unsigned long *result_p) | ||
1331 | { | ||
1332 | size_t i; | ||
1333 | unsigned long result; | ||
1334 | |||
1335 | result = 0; | ||
1336 | i = n->len; | ||
1337 | while (i > n->rdx) { | ||
1338 | unsigned long prev = result; | ||
1339 | result = result * 10 + n->num[--i]; | ||
1340 | // Even overflowed N*10 can still satisfy N*10>=N. For example, | ||
1341 | // 0x1ff00000 * 10 is 0x13f600000, | ||
1342 | // or 0x3f600000 truncated to 32 bits. Which is larger. | ||
1343 | // However, (N*10)/8 < N check is always correct. | ||
1344 | if ((result / 8) < prev) | ||
1345 | RETURN_STATUS(bc_error("overflow")); | ||
1346 | } | ||
1347 | *result_p = result; | ||
1348 | |||
1349 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1350 | } | ||
1351 | #define zbc_num_ulong_abs(...) (zbc_num_ulong_abs(__VA_ARGS__) COMMA_SUCCESS) | ||
1352 | |||
1353 | static BC_STATUS zbc_num_ulong(BcNum *n, unsigned long *result_p) | ||
1354 | { | ||
1355 | if (n->neg) RETURN_STATUS(bc_error("negative number")); | ||
1356 | |||
1357 | RETURN_STATUS(zbc_num_ulong_abs(n, result_p)); | ||
1358 | } | ||
1359 | #define zbc_num_ulong(...) (zbc_num_ulong(__VA_ARGS__) COMMA_SUCCESS) | ||
1360 | |||
1361 | #if ULONG_MAX == 0xffffffffUL // 10 digits: 4294967295 | ||
1362 | # define ULONG_NUM_BUFSIZE (10 > BC_NUM_DEF_SIZE ? 10 : BC_NUM_DEF_SIZE) | ||
1363 | #elif ULONG_MAX == 0xffffffffffffffffULL // 20 digits: 18446744073709551615 | ||
1364 | # define ULONG_NUM_BUFSIZE (20 > BC_NUM_DEF_SIZE ? 20 : BC_NUM_DEF_SIZE) | ||
1365 | #endif | ||
1366 | // minimum BC_NUM_DEF_SIZE, so that bc_num_expand() in bc_num_ulong2num() | ||
1367 | // would not hit realloc() code path - not good if num[] is not malloced | ||
1368 | |||
1369 | static void bc_num_ulong2num(BcNum *n, unsigned long val) | ||
1370 | { | ||
1371 | BcDig *ptr; | ||
1372 | |||
1373 | bc_num_zero(n); | ||
1374 | |||
1375 | if (val == 0) return; | ||
1376 | |||
1377 | bc_num_expand(n, ULONG_NUM_BUFSIZE); | ||
1378 | |||
1379 | ptr = n->num; | ||
1380 | for (;;) { | ||
1381 | n->len++; | ||
1382 | *ptr++ = val % 10; | ||
1383 | val /= 10; | ||
1384 | if (val == 0) break; | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | static void bc_num_subArrays(BcDig *restrict a, BcDig *restrict b, size_t len) | ||
1389 | { | ||
1390 | size_t i, j; | ||
1391 | for (i = 0; i < len; ++i) { | ||
1392 | a[i] -= b[i]; | ||
1393 | for (j = i; a[j] < 0;) { | ||
1394 | a[j++] += 10; | ||
1395 | a[j] -= 1; | ||
1396 | } | ||
1397 | } | ||
1398 | } | ||
1399 | |||
1400 | static ssize_t bc_num_compare(BcDig *restrict a, BcDig *restrict b, size_t len) | ||
1401 | { | ||
1402 | size_t i = len; | ||
1403 | for (;;) { | ||
1404 | int c; | ||
1405 | if (i == 0) | ||
1406 | return 0; | ||
1407 | i--; | ||
1408 | c = a[i] - b[i]; | ||
1409 | if (c != 0) { | ||
1410 | i++; | ||
1411 | if (c < 0) | ||
1412 | return -i; | ||
1413 | return i; | ||
1414 | } | ||
1415 | } | ||
1416 | } | ||
1417 | |||
1418 | #define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg)) | ||
1419 | #define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1) | ||
1420 | #define BC_NUM_INT(n) ((n)->len - (n)->rdx) | ||
1421 | //#define BC_NUM_AREQ(a, b) (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1) | ||
1422 | static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b) | ||
1423 | { | ||
1424 | return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1; | ||
1425 | } | ||
1426 | //#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1) | ||
1427 | static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale) | ||
1428 | { | ||
1429 | return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1; | ||
1430 | } | ||
1431 | |||
1432 | static ssize_t bc_num_cmp(BcNum *a, BcNum *b) | ||
1433 | { | ||
1434 | size_t i, min, a_int, b_int, diff; | ||
1435 | BcDig *max_num, *min_num; | ||
1436 | bool a_max, neg; | ||
1437 | ssize_t cmp; | ||
1438 | |||
1439 | if (a == b) return 0; | ||
1440 | if (a->len == 0) return BC_NUM_NEG(!!b->len, !b->neg); | ||
1441 | if (b->len == 0) return BC_NUM_NEG(1, a->neg); | ||
1442 | |||
1443 | if (a->neg != b->neg) // signs of a and b differ | ||
1444 | // +a,-b = a>b = 1 or -a,+b = a<b = -1 | ||
1445 | return (int)b->neg - (int)a->neg; | ||
1446 | neg = a->neg; // 1 if both negative, 0 if both positive | ||
1447 | |||
1448 | a_int = BC_NUM_INT(a); | ||
1449 | b_int = BC_NUM_INT(b); | ||
1450 | a_int -= b_int; | ||
1451 | |||
1452 | if (a_int != 0) return (ssize_t) a_int; | ||
1453 | |||
1454 | a_max = (a->rdx > b->rdx); | ||
1455 | if (a_max) { | ||
1456 | min = b->rdx; | ||
1457 | diff = a->rdx - b->rdx; | ||
1458 | max_num = a->num + diff; | ||
1459 | min_num = b->num; | ||
1460 | // neg = (a_max == neg); - NOP (maps 1->1 and 0->0) | ||
1461 | } else { | ||
1462 | min = a->rdx; | ||
1463 | diff = b->rdx - a->rdx; | ||
1464 | max_num = b->num + diff; | ||
1465 | min_num = a->num; | ||
1466 | neg = !neg; // same as "neg = (a_max == neg)" | ||
1467 | } | ||
1468 | |||
1469 | cmp = bc_num_compare(max_num, min_num, b_int + min); | ||
1470 | if (cmp != 0) return BC_NUM_NEG(cmp, neg); | ||
1471 | |||
1472 | for (max_num -= diff, i = diff - 1; i < diff; --i) { | ||
1473 | if (max_num[i]) return BC_NUM_NEG(1, neg); | ||
1474 | } | ||
1475 | |||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1479 | static void bc_num_truncate(BcNum *n, size_t places) | ||
1480 | { | ||
1481 | if (places == 0) return; | ||
1482 | |||
1483 | n->rdx -= places; | ||
1484 | |||
1485 | if (n->len != 0) { | ||
1486 | n->len -= places; | ||
1487 | memmove(n->num, n->num + places, n->len * sizeof(BcDig)); | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | static void bc_num_extend(BcNum *n, size_t places) | ||
1492 | { | ||
1493 | size_t len = n->len + places; | ||
1494 | |||
1495 | if (places != 0) { | ||
1496 | if (n->cap < len) bc_num_expand(n, len); | ||
1497 | |||
1498 | memmove(n->num + places, n->num, sizeof(BcDig) * n->len); | ||
1499 | memset(n->num, 0, sizeof(BcDig) * places); | ||
1500 | |||
1501 | n->len += places; | ||
1502 | n->rdx += places; | ||
1503 | } | ||
1504 | } | ||
1505 | |||
1506 | static void bc_num_clean(BcNum *n) | ||
1507 | { | ||
1508 | while (n->len > 0 && n->num[n->len - 1] == 0) --n->len; | ||
1509 | if (n->len == 0) | ||
1510 | n->neg = false; | ||
1511 | else if (n->len < n->rdx) | ||
1512 | n->len = n->rdx; | ||
1513 | } | ||
1514 | |||
1515 | static void bc_num_retireMul(BcNum *n, size_t scale, bool neg1, bool neg2) | ||
1516 | { | ||
1517 | if (n->rdx < scale) | ||
1518 | bc_num_extend(n, scale - n->rdx); | ||
1519 | else | ||
1520 | bc_num_truncate(n, n->rdx - scale); | ||
1521 | |||
1522 | bc_num_clean(n); | ||
1523 | if (n->len != 0) n->neg = !neg1 != !neg2; | ||
1524 | } | ||
1525 | |||
1526 | static void bc_num_split(BcNum *restrict n, size_t idx, BcNum *restrict a, | ||
1527 | BcNum *restrict b) | ||
1528 | { | ||
1529 | if (idx < n->len) { | ||
1530 | b->len = n->len - idx; | ||
1531 | a->len = idx; | ||
1532 | a->rdx = b->rdx = 0; | ||
1533 | |||
1534 | memcpy(b->num, n->num + idx, b->len * sizeof(BcDig)); | ||
1535 | memcpy(a->num, n->num, idx * sizeof(BcDig)); | ||
1536 | } else { | ||
1537 | bc_num_zero(b); | ||
1538 | bc_num_copy(a, n); | ||
1539 | } | ||
1540 | |||
1541 | bc_num_clean(a); | ||
1542 | bc_num_clean(b); | ||
1543 | } | ||
1544 | |||
1545 | static BC_STATUS zbc_num_shift(BcNum *n, size_t places) | ||
1546 | { | ||
1547 | if (places == 0 || n->len == 0) RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1548 | |||
1549 | // This check makes sense only if size_t is (much) larger than BC_MAX_NUM. | ||
1550 | if (SIZE_MAX > (BC_MAX_NUM | 0xff)) { | ||
1551 | if (places + n->len > BC_MAX_NUM) | ||
1552 | RETURN_STATUS(bc_error("number too long: must be [1,"BC_MAX_NUM_STR"]")); | ||
1553 | } | ||
1554 | |||
1555 | if (n->rdx >= places) | ||
1556 | n->rdx -= places; | ||
1557 | else { | ||
1558 | bc_num_extend(n, places - n->rdx); | ||
1559 | n->rdx = 0; | ||
1560 | } | ||
1561 | |||
1562 | bc_num_clean(n); | ||
1563 | |||
1564 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1565 | } | ||
1566 | #define zbc_num_shift(...) (zbc_num_shift(__VA_ARGS__) COMMA_SUCCESS) | ||
1567 | |||
1568 | typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC; | ||
1569 | |||
1570 | static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, | ||
1571 | BcNumBinaryOp op, size_t req) | ||
1572 | { | ||
1573 | BcStatus s; | ||
1574 | BcNum num2, *ptr_a, *ptr_b; | ||
1575 | bool init = false; | ||
1576 | |||
1577 | if (c == a) { | ||
1578 | ptr_a = &num2; | ||
1579 | memcpy(ptr_a, c, sizeof(BcNum)); | ||
1580 | init = true; | ||
1581 | } else | ||
1582 | ptr_a = a; | ||
1583 | |||
1584 | if (c == b) { | ||
1585 | ptr_b = &num2; | ||
1586 | if (c != a) { | ||
1587 | memcpy(ptr_b, c, sizeof(BcNum)); | ||
1588 | init = true; | ||
1589 | } | ||
1590 | } else | ||
1591 | ptr_b = b; | ||
1592 | |||
1593 | if (init) | ||
1594 | bc_num_init(c, req); | ||
1595 | else | ||
1596 | bc_num_expand(c, req); | ||
1597 | |||
1598 | s = BC_STATUS_SUCCESS; | ||
1599 | IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale); | ||
1600 | |||
1601 | if (init) bc_num_free(&num2); | ||
1602 | |||
1603 | RETURN_STATUS(s); | ||
1604 | } | ||
1605 | #define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS) | ||
1606 | |||
1607 | static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1608 | static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1609 | static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1610 | static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1611 | static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1612 | static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); | ||
1613 | |||
1614 | static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1615 | { | ||
1616 | BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s; | ||
1617 | (void) scale; | ||
1618 | RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b))); | ||
1619 | } | ||
1620 | |||
1621 | static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1622 | { | ||
1623 | BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a; | ||
1624 | (void) scale; | ||
1625 | RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b))); | ||
1626 | } | ||
1627 | |||
1628 | static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1629 | { | ||
1630 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
1631 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req)); | ||
1632 | } | ||
1633 | |||
1634 | static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1635 | { | ||
1636 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
1637 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req)); | ||
1638 | } | ||
1639 | |||
1640 | static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1641 | { | ||
1642 | size_t req = BC_NUM_MREQ(a, b, scale); | ||
1643 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req)); | ||
1644 | } | ||
1645 | |||
1646 | static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) | ||
1647 | { | ||
1648 | RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1)); | ||
1649 | } | ||
1650 | |||
1651 | static const BcNumBinaryOp zxc_program_ops[] = { | ||
1652 | zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub, | ||
1653 | }; | ||
1654 | #define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS) | ||
1655 | #define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS) | ||
1656 | #define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS) | ||
1657 | #define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS) | ||
1658 | #define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS) | ||
1659 | #define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS) | ||
1660 | |||
1661 | static BC_STATUS zbc_num_inv(BcNum *a, BcNum *b, size_t scale) | ||
1662 | { | ||
1663 | BcNum one; | ||
1664 | BcDig num[2]; | ||
1665 | |||
1666 | one.cap = 2; | ||
1667 | one.num = num; | ||
1668 | bc_num_one(&one); | ||
1669 | |||
1670 | RETURN_STATUS(zbc_num_div(&one, a, b, scale)); | ||
1671 | } | ||
1672 | #define zbc_num_inv(...) (zbc_num_inv(__VA_ARGS__) COMMA_SUCCESS) | ||
1673 | |||
1674 | static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) | ||
1675 | { | ||
1676 | BcDig *ptr, *ptr_a, *ptr_b, *ptr_c; | ||
1677 | size_t i, max, min_rdx, min_int, diff, a_int, b_int; | ||
1678 | unsigned carry; | ||
1679 | |||
1680 | // Because this function doesn't need to use scale (per the bc spec), | ||
1681 | // I am hijacking it to say whether it's doing an add or a subtract. | ||
1682 | |||
1683 | if (a->len == 0) { | ||
1684 | bc_num_copy(c, b); | ||
1685 | if (sub && c->len) c->neg = !c->neg; | ||
1686 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1687 | } | ||
1688 | if (b->len == 0) { | ||
1689 | bc_num_copy(c, a); | ||
1690 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1691 | } | ||
1692 | |||
1693 | c->neg = a->neg; | ||
1694 | c->rdx = BC_MAX(a->rdx, b->rdx); | ||
1695 | min_rdx = BC_MIN(a->rdx, b->rdx); | ||
1696 | c->len = 0; | ||
1697 | |||
1698 | if (a->rdx > b->rdx) { | ||
1699 | diff = a->rdx - b->rdx; | ||
1700 | ptr = a->num; | ||
1701 | ptr_a = a->num + diff; | ||
1702 | ptr_b = b->num; | ||
1703 | } else { | ||
1704 | diff = b->rdx - a->rdx; | ||
1705 | ptr = b->num; | ||
1706 | ptr_a = a->num; | ||
1707 | ptr_b = b->num + diff; | ||
1708 | } | ||
1709 | |||
1710 | ptr_c = c->num; | ||
1711 | for (i = 0; i < diff; ++i, ++c->len) | ||
1712 | ptr_c[i] = ptr[i]; | ||
1713 | |||
1714 | ptr_c += diff; | ||
1715 | a_int = BC_NUM_INT(a); | ||
1716 | b_int = BC_NUM_INT(b); | ||
1717 | |||
1718 | if (a_int > b_int) { | ||
1719 | min_int = b_int; | ||
1720 | max = a_int; | ||
1721 | ptr = ptr_a; | ||
1722 | } else { | ||
1723 | min_int = a_int; | ||
1724 | max = b_int; | ||
1725 | ptr = ptr_b; | ||
1726 | } | ||
1727 | |||
1728 | carry = 0; | ||
1729 | for (i = 0; i < min_rdx + min_int; ++i) { | ||
1730 | unsigned in = (unsigned)ptr_a[i] + (unsigned)ptr_b[i] + carry; | ||
1731 | carry = in / 10; | ||
1732 | ptr_c[i] = (BcDig)(in % 10); | ||
1733 | } | ||
1734 | for (; i < max + min_rdx; ++i) { | ||
1735 | unsigned in = (unsigned)ptr[i] + carry; | ||
1736 | carry = in / 10; | ||
1737 | ptr_c[i] = (BcDig)(in % 10); | ||
1738 | } | ||
1739 | c->len += i; | ||
1740 | |||
1741 | if (carry != 0) c->num[c->len++] = (BcDig) carry; | ||
1742 | |||
1743 | RETURN_STATUS(BC_STATUS_SUCCESS); // can't make void, see zbc_num_binary() | ||
1744 | } | ||
1745 | |||
1746 | static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) | ||
1747 | { | ||
1748 | ssize_t cmp; | ||
1749 | BcNum *minuend, *subtrahend; | ||
1750 | size_t start; | ||
1751 | bool aneg, bneg, neg; | ||
1752 | |||
1753 | // Because this function doesn't need to use scale (per the bc spec), | ||
1754 | // I am hijacking it to say whether it's doing an add or a subtract. | ||
1755 | |||
1756 | if (a->len == 0) { | ||
1757 | bc_num_copy(c, b); | ||
1758 | if (sub && c->len) c->neg = !c->neg; | ||
1759 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1760 | } | ||
1761 | if (b->len == 0) { | ||
1762 | bc_num_copy(c, a); | ||
1763 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1764 | } | ||
1765 | |||
1766 | aneg = a->neg; | ||
1767 | bneg = b->neg; | ||
1768 | a->neg = b->neg = false; | ||
1769 | |||
1770 | cmp = bc_num_cmp(a, b); | ||
1771 | |||
1772 | a->neg = aneg; | ||
1773 | b->neg = bneg; | ||
1774 | |||
1775 | if (cmp == 0) { | ||
1776 | bc_num_setToZero(c, BC_MAX(a->rdx, b->rdx)); | ||
1777 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1778 | } | ||
1779 | if (cmp > 0) { | ||
1780 | neg = a->neg; | ||
1781 | minuend = a; | ||
1782 | subtrahend = b; | ||
1783 | } else { | ||
1784 | neg = b->neg; | ||
1785 | if (sub) neg = !neg; | ||
1786 | minuend = b; | ||
1787 | subtrahend = a; | ||
1788 | } | ||
1789 | |||
1790 | bc_num_copy(c, minuend); | ||
1791 | c->neg = neg; | ||
1792 | |||
1793 | if (c->rdx < subtrahend->rdx) { | ||
1794 | bc_num_extend(c, subtrahend->rdx - c->rdx); | ||
1795 | start = 0; | ||
1796 | } else | ||
1797 | start = c->rdx - subtrahend->rdx; | ||
1798 | |||
1799 | bc_num_subArrays(c->num + start, subtrahend->num, subtrahend->len); | ||
1800 | |||
1801 | bc_num_clean(c); | ||
1802 | |||
1803 | RETURN_STATUS(BC_STATUS_SUCCESS); // can't make void, see zbc_num_binary() | ||
1804 | } | ||
1805 | |||
1806 | static FAST_FUNC BC_STATUS zbc_num_k(BcNum *restrict a, BcNum *restrict b, | ||
1807 | BcNum *restrict c) | ||
1808 | #define zbc_num_k(...) (zbc_num_k(__VA_ARGS__) COMMA_SUCCESS) | ||
1809 | { | ||
1810 | BcStatus s; | ||
1811 | size_t max = BC_MAX(a->len, b->len), max2 = (max + 1) / 2; | ||
1812 | BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp; | ||
1813 | bool aone; | ||
1814 | |||
1815 | if (a->len == 0 || b->len == 0) { | ||
1816 | bc_num_zero(c); | ||
1817 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1818 | } | ||
1819 | aone = BC_NUM_ONE(a); | ||
1820 | if (aone || BC_NUM_ONE(b)) { | ||
1821 | bc_num_copy(c, aone ? b : a); | ||
1822 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1823 | } | ||
1824 | |||
1825 | if (a->len + b->len < BC_NUM_KARATSUBA_LEN | ||
1826 | || a->len < BC_NUM_KARATSUBA_LEN | ||
1827 | || b->len < BC_NUM_KARATSUBA_LEN | ||
1828 | ) { | ||
1829 | size_t i, j, len; | ||
1830 | |||
1831 | bc_num_expand(c, a->len + b->len + 1); | ||
1832 | |||
1833 | memset(c->num, 0, sizeof(BcDig) * c->cap); | ||
1834 | c->len = len = 0; | ||
1835 | |||
1836 | for (i = 0; i < b->len; ++i) { | ||
1837 | unsigned carry = 0; | ||
1838 | for (j = 0; j < a->len; ++j) { | ||
1839 | unsigned in = c->num[i + j]; | ||
1840 | in += (unsigned)a->num[j] * (unsigned)b->num[i] + carry; | ||
1841 | // note: compilers prefer _unsigned_ div/const | ||
1842 | carry = in / 10; | ||
1843 | c->num[i + j] = (BcDig)(in % 10); | ||
1844 | } | ||
1845 | |||
1846 | c->num[i + j] += (BcDig) carry; | ||
1847 | len = BC_MAX(len, i + j + !!carry); | ||
1848 | |||
1849 | #if ENABLE_FEATURE_BC_INTERACTIVE | ||
1850 | // a=2^1000000 | ||
1851 | // a*a <- without check below, this will not be interruptible | ||
1852 | if (G_interrupt) return BC_STATUS_FAILURE; | ||
1853 | #endif | ||
1854 | } | ||
1855 | |||
1856 | c->len = len; | ||
1857 | |||
1858 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1859 | } | ||
1860 | |||
1861 | bc_num_init(&l1, max); | ||
1862 | bc_num_init(&h1, max); | ||
1863 | bc_num_init(&l2, max); | ||
1864 | bc_num_init(&h2, max); | ||
1865 | bc_num_init(&m1, max); | ||
1866 | bc_num_init(&m2, max); | ||
1867 | bc_num_init(&z0, max); | ||
1868 | bc_num_init(&z1, max); | ||
1869 | bc_num_init(&z2, max); | ||
1870 | bc_num_init(&temp, max + max); | ||
1871 | |||
1872 | bc_num_split(a, max2, &l1, &h1); | ||
1873 | bc_num_split(b, max2, &l2, &h2); | ||
1874 | |||
1875 | s = zbc_num_add(&h1, &l1, &m1, 0); | ||
1876 | if (s) goto err; | ||
1877 | s = zbc_num_add(&h2, &l2, &m2, 0); | ||
1878 | if (s) goto err; | ||
1879 | |||
1880 | s = zbc_num_k(&h1, &h2, &z0); | ||
1881 | if (s) goto err; | ||
1882 | s = zbc_num_k(&m1, &m2, &z1); | ||
1883 | if (s) goto err; | ||
1884 | s = zbc_num_k(&l1, &l2, &z2); | ||
1885 | if (s) goto err; | ||
1886 | |||
1887 | s = zbc_num_sub(&z1, &z0, &temp, 0); | ||
1888 | if (s) goto err; | ||
1889 | s = zbc_num_sub(&temp, &z2, &z1, 0); | ||
1890 | if (s) goto err; | ||
1891 | |||
1892 | s = zbc_num_shift(&z0, max2 * 2); | ||
1893 | if (s) goto err; | ||
1894 | s = zbc_num_shift(&z1, max2); | ||
1895 | if (s) goto err; | ||
1896 | s = zbc_num_add(&z0, &z1, &temp, 0); | ||
1897 | if (s) goto err; | ||
1898 | s = zbc_num_add(&temp, &z2, c, 0); | ||
1899 | err: | ||
1900 | bc_num_free(&temp); | ||
1901 | bc_num_free(&z2); | ||
1902 | bc_num_free(&z1); | ||
1903 | bc_num_free(&z0); | ||
1904 | bc_num_free(&m2); | ||
1905 | bc_num_free(&m1); | ||
1906 | bc_num_free(&h2); | ||
1907 | bc_num_free(&l2); | ||
1908 | bc_num_free(&h1); | ||
1909 | bc_num_free(&l1); | ||
1910 | RETURN_STATUS(s); | ||
1911 | } | ||
1912 | |||
1913 | static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) | ||
1914 | { | ||
1915 | BcStatus s; | ||
1916 | BcNum cpa, cpb; | ||
1917 | size_t maxrdx = BC_MAX(a->rdx, b->rdx); | ||
1918 | |||
1919 | scale = BC_MAX(scale, a->rdx); | ||
1920 | scale = BC_MAX(scale, b->rdx); | ||
1921 | scale = BC_MIN(a->rdx + b->rdx, scale); | ||
1922 | maxrdx = BC_MAX(maxrdx, scale); | ||
1923 | |||
1924 | bc_num_init(&cpa, a->len); | ||
1925 | bc_num_init(&cpb, b->len); | ||
1926 | |||
1927 | bc_num_copy(&cpa, a); | ||
1928 | bc_num_copy(&cpb, b); | ||
1929 | cpa.neg = cpb.neg = false; | ||
1930 | |||
1931 | s = zbc_num_shift(&cpa, maxrdx); | ||
1932 | if (s) goto err; | ||
1933 | s = zbc_num_shift(&cpb, maxrdx); | ||
1934 | if (s) goto err; | ||
1935 | s = zbc_num_k(&cpa, &cpb, c); | ||
1936 | if (s) goto err; | ||
1937 | |||
1938 | maxrdx += scale; | ||
1939 | bc_num_expand(c, c->len + maxrdx); | ||
1940 | |||
1941 | if (c->len < maxrdx) { | ||
1942 | memset(c->num + c->len, 0, (c->cap - c->len) * sizeof(BcDig)); | ||
1943 | c->len += maxrdx; | ||
1944 | } | ||
1945 | |||
1946 | c->rdx = maxrdx; | ||
1947 | bc_num_retireMul(c, scale, a->neg, b->neg); | ||
1948 | err: | ||
1949 | bc_num_free(&cpb); | ||
1950 | bc_num_free(&cpa); | ||
1951 | RETURN_STATUS(s); | ||
1952 | } | ||
1953 | #define zbc_num_m(...) (zbc_num_m(__VA_ARGS__) COMMA_SUCCESS) | ||
1954 | |||
1955 | static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) | ||
1956 | { | ||
1957 | BcStatus s; | ||
1958 | size_t len, end, i; | ||
1959 | BcNum cp; | ||
1960 | |||
1961 | if (b->len == 0) | ||
1962 | RETURN_STATUS(bc_error("divide by zero")); | ||
1963 | if (a->len == 0) { | ||
1964 | bc_num_setToZero(c, scale); | ||
1965 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1966 | } | ||
1967 | if (BC_NUM_ONE(b)) { | ||
1968 | bc_num_copy(c, a); | ||
1969 | bc_num_retireMul(c, scale, a->neg, b->neg); | ||
1970 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
1971 | } | ||
1972 | |||
1973 | bc_num_init(&cp, BC_NUM_MREQ(a, b, scale)); | ||
1974 | bc_num_copy(&cp, a); | ||
1975 | len = b->len; | ||
1976 | |||
1977 | if (len > cp.len) { | ||
1978 | bc_num_expand(&cp, len + 2); | ||
1979 | bc_num_extend(&cp, len - cp.len); | ||
1980 | } | ||
1981 | |||
1982 | if (b->rdx > cp.rdx) bc_num_extend(&cp, b->rdx - cp.rdx); | ||
1983 | cp.rdx -= b->rdx; | ||
1984 | if (scale > cp.rdx) bc_num_extend(&cp, scale - cp.rdx); | ||
1985 | |||
1986 | if (b->rdx == b->len) { | ||
1987 | for (;;) { | ||
1988 | if (len == 0) break; | ||
1989 | len--; | ||
1990 | if (b->num[len] != 0) | ||
1991 | break; | ||
1992 | } | ||
1993 | len++; | ||
1994 | } | ||
1995 | |||
1996 | if (cp.cap == cp.len) bc_num_expand(&cp, cp.len + 1); | ||
1997 | |||
1998 | // We want an extra zero in front to make things simpler. | ||
1999 | cp.num[cp.len++] = 0; | ||
2000 | end = cp.len - len; | ||
2001 | |||
2002 | bc_num_expand(c, cp.len); | ||
2003 | |||
2004 | bc_num_zero(c); | ||
2005 | memset(c->num + end, 0, (c->cap - end) * sizeof(BcDig)); | ||
2006 | c->rdx = cp.rdx; | ||
2007 | c->len = cp.len; | ||
2008 | |||
2009 | s = BC_STATUS_SUCCESS; | ||
2010 | for (i = end - 1; i < end; --i) { | ||
2011 | BcDig *n, q; | ||
2012 | n = cp.num + i; | ||
2013 | for (q = 0; n[len] != 0 || bc_num_compare(n, b->num, len) >= 0; ++q) | ||
2014 | bc_num_subArrays(n, b->num, len); | ||
2015 | c->num[i] = q; | ||
2016 | #if ENABLE_FEATURE_BC_INTERACTIVE | ||
2017 | // a=2^100000 | ||
2018 | // scale=40000 | ||
2019 | // 1/a <- without check below, this will not be interruptible | ||
2020 | if (G_interrupt) { | ||
2021 | s = BC_STATUS_FAILURE; | ||
2022 | break; | ||
2023 | } | ||
2024 | #endif | ||
2025 | } | ||
2026 | |||
2027 | bc_num_retireMul(c, scale, a->neg, b->neg); | ||
2028 | bc_num_free(&cp); | ||
2029 | |||
2030 | RETURN_STATUS(s); | ||
2031 | } | ||
2032 | #define zbc_num_d(...) (zbc_num_d(__VA_ARGS__) COMMA_SUCCESS) | ||
2033 | |||
2034 | static FAST_FUNC BC_STATUS zbc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, | ||
2035 | BcNum *restrict d, size_t scale, size_t ts) | ||
2036 | { | ||
2037 | BcStatus s; | ||
2038 | BcNum temp; | ||
2039 | bool neg; | ||
2040 | |||
2041 | if (b->len == 0) | ||
2042 | RETURN_STATUS(bc_error("divide by zero")); | ||
2043 | |||
2044 | if (a->len == 0) { | ||
2045 | bc_num_setToZero(d, ts); | ||
2046 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2047 | } | ||
2048 | |||
2049 | bc_num_init(&temp, d->cap); | ||
2050 | s = zbc_num_d(a, b, c, scale); | ||
2051 | if (s) goto err; | ||
2052 | |||
2053 | if (scale != 0) scale = ts; | ||
2054 | |||
2055 | s = zbc_num_m(c, b, &temp, scale); | ||
2056 | if (s) goto err; | ||
2057 | s = zbc_num_sub(a, &temp, d, scale); | ||
2058 | if (s) goto err; | ||
2059 | |||
2060 | if (ts > d->rdx && d->len) bc_num_extend(d, ts - d->rdx); | ||
2061 | |||
2062 | neg = d->neg; | ||
2063 | bc_num_retireMul(d, ts, a->neg, b->neg); | ||
2064 | d->neg = neg; | ||
2065 | err: | ||
2066 | bc_num_free(&temp); | ||
2067 | RETURN_STATUS(s); | ||
2068 | } | ||
2069 | #define zbc_num_r(...) (zbc_num_r(__VA_ARGS__) COMMA_SUCCESS) | ||
2070 | |||
2071 | static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) | ||
2072 | { | ||
2073 | BcStatus s; | ||
2074 | BcNum c1; | ||
2075 | size_t ts = BC_MAX(scale + b->rdx, a->rdx), len = BC_NUM_MREQ(a, b, ts); | ||
2076 | |||
2077 | bc_num_init(&c1, len); | ||
2078 | s = zbc_num_r(a, b, &c1, c, scale, ts); | ||
2079 | bc_num_free(&c1); | ||
2080 | |||
2081 | RETURN_STATUS(s); | ||
2082 | } | ||
2083 | #define zbc_num_rem(...) (zbc_num_rem(__VA_ARGS__) COMMA_SUCCESS) | ||
2084 | |||
2085 | static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) | ||
2086 | { | ||
2087 | BcStatus s = BC_STATUS_SUCCESS; | ||
2088 | BcNum copy; | ||
2089 | unsigned long pow; | ||
2090 | size_t i, powrdx, resrdx; | ||
2091 | bool neg; | ||
2092 | |||
2093 | // GNU bc does not allow 2^2.0 - we do | ||
2094 | for (i = 0; i < b->rdx; i++) | ||
2095 | if (b->num[i] != 0) | ||
2096 | RETURN_STATUS(bc_error("not an integer")); | ||
2097 | |||
2098 | if (b->len == 0) { | ||
2099 | bc_num_one(c); | ||
2100 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2101 | } | ||
2102 | if (a->len == 0) { | ||
2103 | bc_num_setToZero(c, scale); | ||
2104 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2105 | } | ||
2106 | if (BC_NUM_ONE(b)) { | ||
2107 | if (!b->neg) | ||
2108 | bc_num_copy(c, a); | ||
2109 | else | ||
2110 | s = zbc_num_inv(a, c, scale); | ||
2111 | RETURN_STATUS(s); | ||
2112 | } | ||
2113 | |||
2114 | neg = b->neg; | ||
2115 | s = zbc_num_ulong_abs(b, &pow); | ||
2116 | if (s) RETURN_STATUS(s); | ||
2117 | // b is not used beyond this point | ||
2118 | |||
2119 | bc_num_init(©, a->len); | ||
2120 | bc_num_copy(©, a); | ||
2121 | |||
2122 | if (!neg) { | ||
2123 | if (a->rdx > scale) | ||
2124 | scale = a->rdx; | ||
2125 | if (a->rdx * pow < scale) | ||
2126 | scale = a->rdx * pow; | ||
2127 | } | ||
2128 | |||
2129 | |||
2130 | for (powrdx = a->rdx; !(pow & 1); pow >>= 1) { | ||
2131 | powrdx <<= 1; | ||
2132 | s = zbc_num_mul(©, ©, ©, powrdx); | ||
2133 | if (s) goto err; | ||
2134 | // Not needed: zbc_num_mul() has a check for ^C: | ||
2135 | //if (G_interrupt) { | ||
2136 | // s = BC_STATUS_FAILURE; | ||
2137 | // goto err; | ||
2138 | //} | ||
2139 | } | ||
2140 | |||
2141 | bc_num_copy(c, ©); | ||
2142 | |||
2143 | for (resrdx = powrdx, pow >>= 1; pow != 0; pow >>= 1) { | ||
2144 | powrdx <<= 1; | ||
2145 | s = zbc_num_mul(©, ©, ©, powrdx); | ||
2146 | if (s) goto err; | ||
2147 | |||
2148 | if (pow & 1) { | ||
2149 | resrdx += powrdx; | ||
2150 | s = zbc_num_mul(c, ©, c, resrdx); | ||
2151 | if (s) goto err; | ||
2152 | } | ||
2153 | // Not needed: zbc_num_mul() has a check for ^C: | ||
2154 | //if (G_interrupt) { | ||
2155 | // s = BC_STATUS_FAILURE; | ||
2156 | // goto err; | ||
2157 | //} | ||
2158 | } | ||
2159 | |||
2160 | if (neg) { | ||
2161 | s = zbc_num_inv(c, c, scale); | ||
2162 | if (s) goto err; | ||
2163 | } | ||
2164 | |||
2165 | if (c->rdx > scale) bc_num_truncate(c, c->rdx - scale); | ||
2166 | |||
2167 | // We can't use bc_num_clean() here. | ||
2168 | for (i = 0; i < c->len; ++i) | ||
2169 | if (c->num[i] != 0) | ||
2170 | goto skip; | ||
2171 | bc_num_setToZero(c, scale); | ||
2172 | skip: | ||
2173 | |||
2174 | err: | ||
2175 | bc_num_free(©); | ||
2176 | RETURN_STATUS(s); | ||
2177 | } | ||
2178 | #define zbc_num_p(...) (zbc_num_p(__VA_ARGS__) COMMA_SUCCESS) | ||
2179 | |||
2180 | static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale) | ||
2181 | { | ||
2182 | BcStatus s; | ||
2183 | BcNum num1, num2, half, f, fprime, *x0, *x1, *temp; | ||
2184 | BcDig half_digs[1]; | ||
2185 | size_t pow, len, digs, digs1, resrdx, req, times = 0; | ||
2186 | ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX; | ||
2187 | |||
2188 | req = BC_MAX(scale, a->rdx) + ((BC_NUM_INT(a) + 1) >> 1) + 1; | ||
2189 | bc_num_expand(b, req); | ||
2190 | |||
2191 | if (a->len == 0) { | ||
2192 | bc_num_setToZero(b, scale); | ||
2193 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2194 | } | ||
2195 | if (a->neg) { | ||
2196 | RETURN_STATUS(bc_error("negative number")); | ||
2197 | } | ||
2198 | if (BC_NUM_ONE(a)) { | ||
2199 | bc_num_one(b); | ||
2200 | bc_num_extend(b, scale); | ||
2201 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2202 | } | ||
2203 | |||
2204 | scale = BC_MAX(scale, a->rdx) + 1; | ||
2205 | len = a->len + scale; | ||
2206 | |||
2207 | bc_num_init(&num1, len); | ||
2208 | bc_num_init(&num2, len); | ||
2209 | |||
2210 | half.cap = ARRAY_SIZE(half_digs); | ||
2211 | half.num = half_digs; | ||
2212 | bc_num_one(&half); | ||
2213 | half_digs[0] = 5; | ||
2214 | half.rdx = 1; | ||
2215 | |||
2216 | bc_num_init(&f, len); | ||
2217 | bc_num_init(&fprime, len); | ||
2218 | |||
2219 | x0 = &num1; | ||
2220 | x1 = &num2; | ||
2221 | |||
2222 | bc_num_one(x0); | ||
2223 | pow = BC_NUM_INT(a); | ||
2224 | |||
2225 | if (pow) { | ||
2226 | if (pow & 1) | ||
2227 | x0->num[0] = 2; | ||
2228 | else | ||
2229 | x0->num[0] = 6; | ||
2230 | |||
2231 | pow -= 2 - (pow & 1); | ||
2232 | |||
2233 | bc_num_extend(x0, pow); | ||
2234 | |||
2235 | // Make sure to move the radix back. | ||
2236 | x0->rdx -= pow; | ||
2237 | } | ||
2238 | |||
2239 | x0->rdx = digs = digs1 = 0; | ||
2240 | resrdx = scale + 2; | ||
2241 | len = BC_NUM_INT(x0) + resrdx - 1; | ||
2242 | |||
2243 | while (cmp != 0 || digs < len) { | ||
2244 | s = zbc_num_div(a, x0, &f, resrdx); | ||
2245 | if (s) goto err; | ||
2246 | s = zbc_num_add(x0, &f, &fprime, resrdx); | ||
2247 | if (s) goto err; | ||
2248 | s = zbc_num_mul(&fprime, &half, x1, resrdx); | ||
2249 | if (s) goto err; | ||
2250 | |||
2251 | cmp = bc_num_cmp(x1, x0); | ||
2252 | digs = x1->len - (unsigned long long) llabs(cmp); | ||
2253 | |||
2254 | if (cmp == cmp2 && digs == digs1) | ||
2255 | times += 1; | ||
2256 | else | ||
2257 | times = 0; | ||
2258 | |||
2259 | resrdx += times > 4; | ||
2260 | |||
2261 | cmp2 = cmp1; | ||
2262 | cmp1 = cmp; | ||
2263 | digs1 = digs; | ||
2264 | |||
2265 | temp = x0; | ||
2266 | x0 = x1; | ||
2267 | x1 = temp; | ||
2268 | } | ||
2269 | |||
2270 | bc_num_copy(b, x0); | ||
2271 | scale -= 1; | ||
2272 | if (b->rdx > scale) bc_num_truncate(b, b->rdx - scale); | ||
2273 | err: | ||
2274 | bc_num_free(&fprime); | ||
2275 | bc_num_free(&f); | ||
2276 | bc_num_free(&num2); | ||
2277 | bc_num_free(&num1); | ||
2278 | RETURN_STATUS(s); | ||
2279 | } | ||
2280 | #define zbc_num_sqrt(...) (zbc_num_sqrt(__VA_ARGS__) COMMA_SUCCESS) | ||
2281 | |||
2282 | static BC_STATUS zbc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, | ||
2283 | size_t scale) | ||
2284 | { | ||
2285 | BcStatus s; | ||
2286 | BcNum num2, *ptr_a; | ||
2287 | bool init = false; | ||
2288 | size_t ts = BC_MAX(scale + b->rdx, a->rdx), len = BC_NUM_MREQ(a, b, ts); | ||
2289 | |||
2290 | if (c == a) { | ||
2291 | memcpy(&num2, c, sizeof(BcNum)); | ||
2292 | ptr_a = &num2; | ||
2293 | bc_num_init(c, len); | ||
2294 | init = true; | ||
2295 | } else { | ||
2296 | ptr_a = a; | ||
2297 | bc_num_expand(c, len); | ||
2298 | } | ||
2299 | |||
2300 | s = zbc_num_r(ptr_a, b, c, d, scale, ts); | ||
2301 | |||
2302 | if (init) bc_num_free(&num2); | ||
2303 | |||
2304 | RETURN_STATUS(s); | ||
2305 | } | ||
2306 | #define zbc_num_divmod(...) (zbc_num_divmod(__VA_ARGS__) COMMA_SUCCESS) | ||
2307 | |||
2308 | #if ENABLE_DC | ||
2309 | static BC_STATUS zdc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) | ||
2310 | { | ||
2311 | BcStatus s; | ||
2312 | BcNum base, exp, two, temp; | ||
2313 | BcDig two_digs[1]; | ||
2314 | |||
2315 | if (c->len == 0) | ||
2316 | RETURN_STATUS(bc_error("divide by zero")); | ||
2317 | if (a->rdx || b->rdx || c->rdx) | ||
2318 | RETURN_STATUS(bc_error("not an integer")); | ||
2319 | if (b->neg) | ||
2320 | RETURN_STATUS(bc_error("negative number")); | ||
2321 | |||
2322 | bc_num_expand(d, c->len); | ||
2323 | bc_num_init(&base, c->len); | ||
2324 | bc_num_init(&exp, b->len); | ||
2325 | bc_num_init(&temp, b->len); | ||
2326 | |||
2327 | two.cap = ARRAY_SIZE(two_digs); | ||
2328 | two.num = two_digs; | ||
2329 | bc_num_one(&two); | ||
2330 | two_digs[0] = 2; | ||
2331 | |||
2332 | bc_num_one(d); | ||
2333 | |||
2334 | s = zbc_num_rem(a, c, &base, 0); | ||
2335 | if (s) goto err; | ||
2336 | bc_num_copy(&exp, b); | ||
2337 | |||
2338 | while (exp.len != 0) { | ||
2339 | s = zbc_num_divmod(&exp, &two, &exp, &temp, 0); | ||
2340 | if (s) goto err; | ||
2341 | |||
2342 | if (BC_NUM_ONE(&temp)) { | ||
2343 | s = zbc_num_mul(d, &base, &temp, 0); | ||
2344 | if (s) goto err; | ||
2345 | s = zbc_num_rem(&temp, c, d, 0); | ||
2346 | if (s) goto err; | ||
2347 | } | ||
2348 | |||
2349 | s = zbc_num_mul(&base, &base, &temp, 0); | ||
2350 | if (s) goto err; | ||
2351 | s = zbc_num_rem(&temp, c, &base, 0); | ||
2352 | if (s) goto err; | ||
2353 | } | ||
2354 | err: | ||
2355 | bc_num_free(&temp); | ||
2356 | bc_num_free(&exp); | ||
2357 | bc_num_free(&base); | ||
2358 | RETURN_STATUS(s); | ||
2359 | } | ||
2360 | #define zdc_num_modexp(...) (zdc_num_modexp(__VA_ARGS__) COMMA_SUCCESS) | ||
2361 | #endif // ENABLE_DC | ||
2362 | |||
2363 | static FAST_FUNC void bc_string_free(void *string) | ||
2364 | { | ||
2365 | free(*(char**)string); | ||
2366 | } | ||
2367 | |||
2368 | static void bc_func_init(BcFunc *f) | ||
2369 | { | ||
2370 | bc_char_vec_init(&f->code); | ||
2371 | IF_BC(bc_vec_init(&f->labels, sizeof(size_t), NULL);) | ||
2372 | IF_BC(bc_vec_init(&f->autos, sizeof(BcId), bc_id_free);) | ||
2373 | IF_BC(bc_vec_init(&f->strs, sizeof(char *), bc_string_free);) | ||
2374 | IF_BC(bc_vec_init(&f->consts, sizeof(char *), bc_string_free);) | ||
2375 | IF_BC(f->nparams = 0;) | ||
2376 | } | ||
2377 | |||
2378 | static FAST_FUNC void bc_func_free(void *func) | ||
2379 | { | ||
2380 | BcFunc *f = (BcFunc *) func; | ||
2381 | bc_vec_free(&f->code); | ||
2382 | IF_BC(bc_vec_free(&f->labels);) | ||
2383 | IF_BC(bc_vec_free(&f->autos);) | ||
2384 | IF_BC(bc_vec_free(&f->strs);) | ||
2385 | IF_BC(bc_vec_free(&f->consts);) | ||
2386 | } | ||
2387 | |||
2388 | static void bc_array_expand(BcVec *a, size_t len); | ||
2389 | |||
2390 | static void bc_array_init(BcVec *a, bool nums) | ||
2391 | { | ||
2392 | if (nums) | ||
2393 | bc_vec_init(a, sizeof(BcNum), bc_num_free); | ||
2394 | else | ||
2395 | bc_vec_init(a, sizeof(BcVec), bc_vec_free); | ||
2396 | bc_array_expand(a, 1); | ||
2397 | } | ||
2398 | |||
2399 | static void bc_array_expand(BcVec *a, size_t len) | ||
2400 | { | ||
2401 | if (a->dtor == bc_num_free | ||
2402 | // && a->size == sizeof(BcNum) - always true | ||
2403 | ) { | ||
2404 | BcNum n; | ||
2405 | while (len > a->len) { | ||
2406 | bc_num_init_DEF_SIZE(&n); | ||
2407 | bc_vec_push(a, &n); | ||
2408 | } | ||
2409 | } else { | ||
2410 | BcVec v; | ||
2411 | while (len > a->len) { | ||
2412 | bc_array_init(&v, true); | ||
2413 | bc_vec_push(a, &v); | ||
2414 | } | ||
2415 | } | ||
2416 | } | ||
2417 | |||
2418 | static void bc_array_copy(BcVec *d, const BcVec *s) | ||
2419 | { | ||
2420 | BcNum *dnum, *snum; | ||
2421 | size_t i; | ||
2422 | |||
2423 | bc_vec_pop_all(d); | ||
2424 | bc_vec_expand(d, s->cap); | ||
2425 | d->len = s->len; | ||
2426 | |||
2427 | dnum = (void*)d->v; | ||
2428 | snum = (void*)s->v; | ||
2429 | for (i = 0; i < s->len; i++, dnum++, snum++) { | ||
2430 | bc_num_init(dnum, snum->len); | ||
2431 | bc_num_copy(dnum, snum); | ||
2432 | } | ||
2433 | } | ||
2434 | |||
2435 | #if ENABLE_DC | ||
2436 | static void dc_result_copy(BcResult *d, BcResult *src) | ||
2437 | { | ||
2438 | d->t = src->t; | ||
2439 | |||
2440 | switch (d->t) { | ||
2441 | case XC_RESULT_TEMP: | ||
2442 | case XC_RESULT_IBASE: | ||
2443 | case XC_RESULT_SCALE: | ||
2444 | case XC_RESULT_OBASE: | ||
2445 | bc_num_init(&d->d.n, src->d.n.len); | ||
2446 | bc_num_copy(&d->d.n, &src->d.n); | ||
2447 | break; | ||
2448 | case XC_RESULT_VAR: | ||
2449 | case XC_RESULT_ARRAY: | ||
2450 | case XC_RESULT_ARRAY_ELEM: | ||
2451 | d->d.id.name = xstrdup(src->d.id.name); | ||
2452 | break; | ||
2453 | case XC_RESULT_CONSTANT: | ||
2454 | case XC_RESULT_STR: | ||
2455 | memcpy(&d->d.n, &src->d.n, sizeof(BcNum)); | ||
2456 | break; | ||
2457 | default: // placate compiler | ||
2458 | // BC_RESULT_VOID, BC_RESULT_LAST, BC_RESULT_ONE - do not happen | ||
2459 | break; | ||
2460 | } | ||
2461 | } | ||
2462 | #endif // ENABLE_DC | ||
2463 | |||
2464 | static FAST_FUNC void bc_result_free(void *result) | ||
2465 | { | ||
2466 | BcResult *r = (BcResult *) result; | ||
2467 | |||
2468 | switch (r->t) { | ||
2469 | case XC_RESULT_TEMP: | ||
2470 | IF_BC(case BC_RESULT_VOID:) | ||
2471 | case XC_RESULT_IBASE: | ||
2472 | case XC_RESULT_SCALE: | ||
2473 | case XC_RESULT_OBASE: | ||
2474 | bc_num_free(&r->d.n); | ||
2475 | break; | ||
2476 | case XC_RESULT_VAR: | ||
2477 | case XC_RESULT_ARRAY: | ||
2478 | case XC_RESULT_ARRAY_ELEM: | ||
2479 | free(r->d.id.name); | ||
2480 | break; | ||
2481 | default: | ||
2482 | // Do nothing. | ||
2483 | break; | ||
2484 | } | ||
2485 | } | ||
2486 | |||
2487 | static int bad_input_byte(char c) | ||
2488 | { | ||
2489 | if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'? | ||
2490 | || c > 0x7e | ||
2491 | ) { | ||
2492 | bc_error_fmt("illegal character 0x%02x", c); | ||
2493 | return 1; | ||
2494 | } | ||
2495 | return 0; | ||
2496 | } | ||
2497 | |||
2498 | static void xc_read_line(BcVec *vec, FILE *fp) | ||
2499 | { | ||
2500 | again: | ||
2501 | bc_vec_pop_all(vec); | ||
2502 | fflush_and_check(); | ||
2503 | |||
2504 | #if ENABLE_FEATURE_BC_INTERACTIVE | ||
2505 | if (G_interrupt) { // ^C was pressed | ||
2506 | intr: | ||
2507 | if (fp != stdin) { | ||
2508 | // ^C while running a script (bc SCRIPT): die. | ||
2509 | // We do not return to interactive prompt: | ||
2510 | // user might be running us from a shell, | ||
2511 | // and SCRIPT might be intended to terminate | ||
2512 | // (e.g. contain a "halt" stmt). | ||
2513 | // ^C dropping user into a bc prompt instead of | ||
2514 | // the shell would be unexpected. | ||
2515 | xfunc_die(); | ||
2516 | } | ||
2517 | // ^C while interactive input | ||
2518 | G_interrupt = 0; | ||
2519 | // GNU bc says "interrupted execution." | ||
2520 | // GNU dc says "Interrupt!" | ||
2521 | fputs("\ninterrupted execution\n", stderr); | ||
2522 | } | ||
2523 | |||
2524 | # if ENABLE_FEATURE_EDITING | ||
2525 | if (G_ttyin && fp == stdin) { | ||
2526 | int n, i; | ||
2527 | # define line_buf bb_common_bufsiz1 | ||
2528 | n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE); | ||
2529 | if (n <= 0) { // read errors or EOF, or ^D, or ^C | ||
2530 | if (n == 0) // ^C | ||
2531 | goto intr; | ||
2532 | bc_vec_pushZeroByte(vec); // ^D or EOF (or error) | ||
2533 | return; | ||
2534 | } | ||
2535 | i = 0; | ||
2536 | for (;;) { | ||
2537 | char c = line_buf[i++]; | ||
2538 | if (c == '\0') break; | ||
2539 | if (bad_input_byte(c)) goto again; | ||
2540 | } | ||
2541 | bc_vec_string(vec, n, line_buf); | ||
2542 | # undef line_buf | ||
2543 | } else | ||
2544 | # endif | ||
2545 | #endif | ||
2546 | { | ||
2547 | int c; | ||
2548 | bool bad_chars = 0; | ||
2549 | |||
2550 | do { | ||
2551 | get_char: | ||
2552 | #if ENABLE_FEATURE_BC_INTERACTIVE | ||
2553 | if (G_interrupt) { | ||
2554 | // ^C was pressed: ignore entire line, get another one | ||
2555 | goto again; | ||
2556 | } | ||
2557 | #endif | ||
2558 | c = fgetc(fp); | ||
2559 | if (c == '\0') | ||
2560 | goto get_char; | ||
2561 | if (c == EOF) { | ||
2562 | if (ferror(fp)) | ||
2563 | bb_perror_msg_and_die("input error"); | ||
2564 | // Note: EOF does not append '\n' | ||
2565 | break; | ||
2566 | } | ||
2567 | bad_chars |= bad_input_byte(c); | ||
2568 | bc_vec_pushByte(vec, (char)c); | ||
2569 | } while (c != '\n'); | ||
2570 | |||
2571 | if (bad_chars) { | ||
2572 | // Bad chars on this line | ||
2573 | if (!G.prs.lex_filename) { // stdin | ||
2574 | // ignore entire line, get another one | ||
2575 | goto again; | ||
2576 | } | ||
2577 | bb_perror_msg_and_die("file '%s' is not text", G.prs.lex_filename); | ||
2578 | } | ||
2579 | bc_vec_pushZeroByte(vec); | ||
2580 | } | ||
2581 | } | ||
2582 | |||
2583 | // | ||
2584 | // Parsing routines | ||
2585 | // | ||
2586 | |||
2587 | // "Input numbers may contain the characters 0-9 and A-Z. | ||
2588 | // (Note: They must be capitals. Lower case letters are variable names.) | ||
2589 | // Single digit numbers always have the value of the digit regardless of | ||
2590 | // the value of ibase. (i.e. A = 10.) For multi-digit numbers, bc changes | ||
2591 | // all input digits greater or equal to ibase to the value of ibase-1. | ||
2592 | // This makes the number ZZZ always be the largest 3 digit number of the | ||
2593 | // input base." | ||
2594 | static bool xc_num_strValid(const char *val) | ||
2595 | { | ||
2596 | bool radix = false; | ||
2597 | for (;;) { | ||
2598 | BcDig c = *val++; | ||
2599 | if (c == '\0') | ||
2600 | break; | ||
2601 | if (c == '.') { | ||
2602 | if (radix) return false; | ||
2603 | radix = true; | ||
2604 | continue; | ||
2605 | } | ||
2606 | if ((c < '0' || c > '9') && (c < 'A' || c > 'Z')) | ||
2607 | return false; | ||
2608 | } | ||
2609 | return true; | ||
2610 | } | ||
2611 | |||
2612 | // Note: n is already "bc_num_zero()"ed, | ||
2613 | // leading zeroes in "val" are removed | ||
2614 | static void bc_num_parseDecimal(BcNum *n, const char *val) | ||
2615 | { | ||
2616 | size_t len, i; | ||
2617 | const char *ptr; | ||
2618 | |||
2619 | len = strlen(val); | ||
2620 | if (len == 0) | ||
2621 | return; | ||
2622 | |||
2623 | bc_num_expand(n, len + 1); // +1 for e.g. "A" converting into 10 | ||
2624 | |||
2625 | ptr = strchr(val, '.'); | ||
2626 | |||
2627 | n->rdx = 0; | ||
2628 | if (ptr != NULL) | ||
2629 | n->rdx = (size_t)((val + len) - (ptr + 1)); | ||
2630 | |||
2631 | for (i = 0; val[i]; ++i) { | ||
2632 | if (val[i] != '0' && val[i] != '.') { | ||
2633 | // Not entirely zero value - convert it, and exit | ||
2634 | if (len == 1) { | ||
2635 | unsigned c = val[0] - '0'; | ||
2636 | n->len = 1; | ||
2637 | if (c > 9) { // A-Z => 10-36 | ||
2638 | n->len = 2; | ||
2639 | c -= ('A' - '9' - 1); | ||
2640 | n->num[1] = c/10; | ||
2641 | c = c%10; | ||
2642 | } | ||
2643 | n->num[0] = c; | ||
2644 | break; | ||
2645 | } | ||
2646 | i = len - 1; | ||
2647 | for (;;) { | ||
2648 | char c = val[i] - '0'; | ||
2649 | if (c > 9) // A-Z => 9 | ||
2650 | c = 9; | ||
2651 | n->num[n->len] = c; | ||
2652 | n->len++; | ||
2653 | skip_dot: | ||
2654 | if (i == 0) break; | ||
2655 | if (val[--i] == '.') goto skip_dot; | ||
2656 | } | ||
2657 | break; | ||
2658 | } | ||
2659 | } | ||
2660 | // if for() exits without hitting if(), the value is entirely zero | ||
2661 | } | ||
2662 | |||
2663 | // Note: n is already "bc_num_zero()"ed, | ||
2664 | // leading zeroes in "val" are removed | ||
2665 | static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t) | ||
2666 | { | ||
2667 | BcStatus s; | ||
2668 | BcNum mult, result; | ||
2669 | BcNum temp; | ||
2670 | BcNum base; | ||
2671 | BcDig temp_digs[ULONG_NUM_BUFSIZE]; | ||
2672 | BcDig base_digs[ULONG_NUM_BUFSIZE]; | ||
2673 | size_t digits; | ||
2674 | |||
2675 | bc_num_init_DEF_SIZE(&mult); | ||
2676 | |||
2677 | temp.cap = ARRAY_SIZE(temp_digs); | ||
2678 | temp.num = temp_digs; | ||
2679 | |||
2680 | base.cap = ARRAY_SIZE(base_digs); | ||
2681 | base.num = base_digs; | ||
2682 | bc_num_ulong2num(&base, base_t); | ||
2683 | base_t--; | ||
2684 | |||
2685 | for (;;) { | ||
2686 | unsigned v; | ||
2687 | char c; | ||
2688 | |||
2689 | c = *val++; | ||
2690 | if (c == '\0') goto int_err; | ||
2691 | if (c == '.') break; | ||
2692 | |||
2693 | v = (unsigned)(c <= '9' ? c - '0' : c - 'A' + 10); | ||
2694 | if (v > base_t) v = base_t; | ||
2695 | |||
2696 | s = zbc_num_mul(n, &base, &mult, 0); | ||
2697 | if (s) goto int_err; | ||
2698 | bc_num_ulong2num(&temp, v); | ||
2699 | s = zbc_num_add(&mult, &temp, n, 0); | ||
2700 | if (s) goto int_err; | ||
2701 | } | ||
2702 | |||
2703 | bc_num_init(&result, base.len); | ||
2704 | //bc_num_zero(&result); - already is | ||
2705 | bc_num_one(&mult); | ||
2706 | |||
2707 | digits = 0; | ||
2708 | for (;;) { | ||
2709 | unsigned v; | ||
2710 | char c; | ||
2711 | |||
2712 | c = *val++; | ||
2713 | if (c == '\0') break; | ||
2714 | digits++; | ||
2715 | |||
2716 | v = (unsigned)(c <= '9' ? c - '0' : c - 'A' + 10); | ||
2717 | if (v > base_t) v = base_t; | ||
2718 | |||
2719 | s = zbc_num_mul(&result, &base, &result, 0); | ||
2720 | if (s) goto err; | ||
2721 | bc_num_ulong2num(&temp, v); | ||
2722 | s = zbc_num_add(&result, &temp, &result, 0); | ||
2723 | if (s) goto err; | ||
2724 | s = zbc_num_mul(&mult, &base, &mult, 0); | ||
2725 | if (s) goto err; | ||
2726 | } | ||
2727 | |||
2728 | s = zbc_num_div(&result, &mult, &result, digits); | ||
2729 | if (s) goto err; | ||
2730 | s = zbc_num_add(n, &result, n, digits); | ||
2731 | if (s) goto err; | ||
2732 | |||
2733 | if (n->len != 0) { | ||
2734 | if (n->rdx < digits) | ||
2735 | bc_num_extend(n, digits - n->rdx); | ||
2736 | } else | ||
2737 | bc_num_zero(n); | ||
2738 | err: | ||
2739 | bc_num_free(&result); | ||
2740 | int_err: | ||
2741 | bc_num_free(&mult); | ||
2742 | } | ||
2743 | |||
2744 | static BC_STATUS zxc_num_parse(BcNum *n, const char *val, unsigned base_t) | ||
2745 | { | ||
2746 | size_t i; | ||
2747 | |||
2748 | if (!xc_num_strValid(val)) | ||
2749 | RETURN_STATUS(bc_error("bad number string")); | ||
2750 | |||
2751 | bc_num_zero(n); | ||
2752 | while (*val == '0') | ||
2753 | val++; | ||
2754 | for (i = 0; ; ++i) { | ||
2755 | if (val[i] == '\0') | ||
2756 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2757 | if (val[i] != '.' && val[i] != '0') | ||
2758 | break; | ||
2759 | } | ||
2760 | |||
2761 | if (base_t == 10 || val[1] == '\0') | ||
2762 | // Decimal, or single-digit number | ||
2763 | bc_num_parseDecimal(n, val); | ||
2764 | else | ||
2765 | bc_num_parseBase(n, val, base_t); | ||
2766 | |||
2767 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2768 | } | ||
2769 | #define zxc_num_parse(...) (zxc_num_parse(__VA_ARGS__) COMMA_SUCCESS) | ||
2770 | |||
2771 | // p->lex_inbuf points to the current string to be parsed. | ||
2772 | // if p->lex_inbuf points to '\0', it's either EOF or it points after | ||
2773 | // last processed line's terminating '\n' (and more reading needs to be done | ||
2774 | // to get next character). | ||
2775 | // | ||
2776 | // If you are in a situation where that is a possibility, call peek_inbuf(). | ||
2777 | // If necessary, it performs more reading and changes p->lex_inbuf, | ||
2778 | // then it returns *p->lex_inbuf (which will be '\0' only if it's EOF). | ||
2779 | // After it, just referencing *p->lex_inbuf is valid, and if it wasn't '\0', | ||
2780 | // it's ok to do p->lex_inbuf++ once without end-of-buffer checking. | ||
2781 | // | ||
2782 | // eat_inbuf() is equvalent to "peek_inbuf(); if (c) p->lex_inbuf++": | ||
2783 | // it returns current char and advances the pointer (if not EOF). | ||
2784 | // After eat_inbuf(), referencing p->lex_inbuf[-1] and *p->lex_inbuf is valid. | ||
2785 | // | ||
2786 | // In many cases, you can use fast *p->lex_inbuf instead of peek_inbuf(): | ||
2787 | // unless prev char might have been '\n', *p->lex_inbuf is '\0' ONLY | ||
2788 | // on real EOF, not end-of-buffer. | ||
2789 | // | ||
2790 | // bc cases to test interactively: | ||
2791 | // 1 #comment\ - prints "1<newline>" at once (comment is not continued) | ||
2792 | // 1 #comment/* - prints "1<newline>" at once | ||
2793 | // 1 #comment" - prints "1<newline>" at once | ||
2794 | // 1\#comment - error at once (\ is not a line continuation) | ||
2795 | // 1 + /*"*/2 - prints "3<newline>" at once | ||
2796 | // 1 + /*#*/2 - prints "3<newline>" at once | ||
2797 | // "str\" - prints "str\" at once | ||
2798 | // "str#" - prints "str#" at once | ||
2799 | // "str/*" - prints "str/*" at once | ||
2800 | // "str#\ - waits for second line | ||
2801 | // end" - ...prints "str#\<newline>end" | ||
2802 | static char peek_inbuf(void) | ||
2803 | { | ||
2804 | if (*G.prs.lex_inbuf == '\0' | ||
2805 | && G.prs.lex_input_fp | ||
2806 | ) { | ||
2807 | xc_read_line(&G.input_buffer, G.prs.lex_input_fp); | ||
2808 | G.prs.lex_inbuf = G.input_buffer.v; | ||
2809 | if (G.input_buffer.len <= 1) // on EOF, len is 1 (NUL byte) | ||
2810 | G.prs.lex_input_fp = NULL; | ||
2811 | } | ||
2812 | return *G.prs.lex_inbuf; | ||
2813 | } | ||
2814 | static char eat_inbuf(void) | ||
2815 | { | ||
2816 | char c = peek_inbuf(); | ||
2817 | if (c) G.prs.lex_inbuf++; | ||
2818 | return c; | ||
2819 | } | ||
2820 | |||
2821 | static void xc_lex_lineComment(void) | ||
2822 | { | ||
2823 | BcParse *p = &G.prs; | ||
2824 | char c; | ||
2825 | |||
2826 | // Try: echo -n '#foo' | bc | ||
2827 | p->lex = XC_LEX_WHITESPACE; | ||
2828 | |||
2829 | // Not peek_inbuf(): we depend on input being done in whole lines: | ||
2830 | // '\0' which isn't the EOF can only be seen after '\n'. | ||
2831 | while ((c = *p->lex_inbuf) != '\n' && c != '\0') | ||
2832 | p->lex_inbuf++; | ||
2833 | } | ||
2834 | |||
2835 | static void xc_lex_whitespace(void) | ||
2836 | { | ||
2837 | BcParse *p = &G.prs; | ||
2838 | |||
2839 | p->lex = XC_LEX_WHITESPACE; | ||
2840 | for (;;) { | ||
2841 | // We depend here on input being done in whole lines: | ||
2842 | // '\0' which isn't the EOF can only be seen after '\n'. | ||
2843 | char c = *p->lex_inbuf; | ||
2844 | if (c == '\n') // this is XC_LEX_NLINE, not XC_LEX_WHITESPACE | ||
2845 | break; | ||
2846 | if (!isspace(c)) | ||
2847 | break; | ||
2848 | p->lex_inbuf++; | ||
2849 | } | ||
2850 | } | ||
2851 | |||
2852 | static BC_STATUS zxc_lex_number(char last) | ||
2853 | { | ||
2854 | BcParse *p = &G.prs; | ||
2855 | bool pt; | ||
2856 | char last_valid_ch; | ||
2857 | |||
2858 | bc_vec_pop_all(&p->lex_strnumbuf); | ||
2859 | bc_vec_pushByte(&p->lex_strnumbuf, last); | ||
2860 | |||
2861 | // bc: "Input numbers may contain the characters 0-9 and A-Z. | ||
2862 | // (Note: They must be capitals. Lower case letters are variable names.) | ||
2863 | // Single digit numbers always have the value of the digit regardless of | ||
2864 | // the value of ibase. (i.e. A = 10.) For multi-digit numbers, bc changes | ||
2865 | // all input digits greater or equal to ibase to the value of ibase-1. | ||
2866 | // This makes the number ZZZ always be the largest 3 digit number of the | ||
2867 | // input base." | ||
2868 | // dc only allows A-F, the rules about single-char and multi-char are the same. | ||
2869 | last_valid_ch = (IS_BC ? 'Z' : 'F'); | ||
2870 | pt = (last == '.'); | ||
2871 | p->lex = XC_LEX_NUMBER; | ||
2872 | for (;;) { | ||
2873 | // We depend here on input being done in whole lines: | ||
2874 | // '\0' which isn't the EOF can only be seen after '\n'. | ||
2875 | char c = *p->lex_inbuf; | ||
2876 | check_c: | ||
2877 | if (c == '\0') | ||
2878 | break; | ||
2879 | if (c == '\\' && p->lex_inbuf[1] == '\n') { | ||
2880 | p->lex_inbuf += 2; | ||
2881 | p->lex_line++; | ||
2882 | dbg_lex("++p->lex_line=%zd", p->lex_line); | ||
2883 | c = peek_inbuf(); // force next line to be read | ||
2884 | goto check_c; | ||
2885 | } | ||
2886 | if (!isdigit(c) && (c < 'A' || c > last_valid_ch)) { | ||
2887 | if (c != '.') break; | ||
2888 | // if '.' was already seen, stop on second one: | ||
2889 | if (pt) break; | ||
2890 | pt = true; | ||
2891 | } | ||
2892 | // c is one of "0-9A-Z." | ||
2893 | last = c; | ||
2894 | bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf); | ||
2895 | p->lex_inbuf++; | ||
2896 | } | ||
2897 | if (last == '.') // remove trailing '.' if any | ||
2898 | bc_vec_pop(&p->lex_strnumbuf); | ||
2899 | bc_vec_pushZeroByte(&p->lex_strnumbuf); | ||
2900 | |||
2901 | G.err_line = G.prs.lex_line; | ||
2902 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2903 | } | ||
2904 | #define zxc_lex_number(...) (zxc_lex_number(__VA_ARGS__) COMMA_SUCCESS) | ||
2905 | |||
2906 | static void xc_lex_name(void) | ||
2907 | { | ||
2908 | BcParse *p = &G.prs; | ||
2909 | size_t i; | ||
2910 | const char *buf; | ||
2911 | |||
2912 | p->lex = XC_LEX_NAME; | ||
2913 | |||
2914 | // Since names can't cross lines with \<newline>, | ||
2915 | // we depend on the fact that whole line is in the buffer | ||
2916 | i = 0; | ||
2917 | buf = p->lex_inbuf - 1; | ||
2918 | for (;;) { | ||
2919 | char c = buf[i]; | ||
2920 | if ((c < 'a' || c > 'z') && !isdigit(c) && c != '_') break; | ||
2921 | i++; | ||
2922 | } | ||
2923 | |||
2924 | #if 0 // We do not protect against people with gigabyte-long names | ||
2925 | // This check makes sense only if size_t is (much) larger than BC_MAX_STRING. | ||
2926 | if (SIZE_MAX > (BC_MAX_STRING | 0xff)) { | ||
2927 | if (i > BC_MAX_STRING) | ||
2928 | return bc_error("name too long: must be [1,"BC_MAX_STRING_STR"]"); | ||
2929 | } | ||
2930 | #endif | ||
2931 | bc_vec_string(&p->lex_strnumbuf, i, buf); | ||
2932 | |||
2933 | // Increment the index. We minus 1 because it has already been incremented. | ||
2934 | p->lex_inbuf += i - 1; | ||
2935 | |||
2936 | //return BC_STATUS_SUCCESS; | ||
2937 | } | ||
2938 | |||
2939 | IF_BC(static BC_STATUS zbc_lex_token(void);) | ||
2940 | IF_DC(static BC_STATUS zdc_lex_token(void);) | ||
2941 | #define zbc_lex_token(...) (zbc_lex_token(__VA_ARGS__) COMMA_SUCCESS) | ||
2942 | #define zdc_lex_token(...) (zdc_lex_token(__VA_ARGS__) COMMA_SUCCESS) | ||
2943 | |||
2944 | static BC_STATUS zxc_lex_next(void) | ||
2945 | { | ||
2946 | BcParse *p = &G.prs; | ||
2947 | BcStatus s; | ||
2948 | |||
2949 | G.err_line = p->lex_line; | ||
2950 | p->lex_last = p->lex; | ||
2951 | //why? | ||
2952 | // if (p->lex_last == XC_LEX_EOF) | ||
2953 | // RETURN_STATUS(bc_error("end of file")); | ||
2954 | |||
2955 | // Loop until failure or we don't have whitespace. This | ||
2956 | // is so the parser doesn't get inundated with whitespace. | ||
2957 | // Comments are also XC_LEX_WHITESPACE tokens and eaten here. | ||
2958 | s = BC_STATUS_SUCCESS; | ||
2959 | do { | ||
2960 | if (*p->lex_inbuf == '\0') { | ||
2961 | p->lex = XC_LEX_EOF; | ||
2962 | if (peek_inbuf() == '\0') | ||
2963 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2964 | } | ||
2965 | p->lex_next_at = p->lex_inbuf; | ||
2966 | dbg_lex("next string to parse:'%.*s'", | ||
2967 | (int)(strchrnul(p->lex_next_at, '\n') - p->lex_next_at), | ||
2968 | p->lex_next_at | ||
2969 | ); | ||
2970 | if (IS_BC) { | ||
2971 | IF_BC(s = zbc_lex_token()); | ||
2972 | } else { | ||
2973 | IF_DC(s = zdc_lex_token()); | ||
2974 | } | ||
2975 | } while (!s && p->lex == XC_LEX_WHITESPACE); | ||
2976 | dbg_lex("p->lex from string:%d", p->lex); | ||
2977 | |||
2978 | RETURN_STATUS(s); | ||
2979 | } | ||
2980 | #define zxc_lex_next(...) (zxc_lex_next(__VA_ARGS__) COMMA_SUCCESS) | ||
2981 | |||
2982 | #if ENABLE_BC | ||
2983 | static BC_STATUS zbc_lex_skip_if_at_NLINE(void) | ||
2984 | { | ||
2985 | if (G.prs.lex == XC_LEX_NLINE) | ||
2986 | RETURN_STATUS(zxc_lex_next()); | ||
2987 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2988 | } | ||
2989 | #define zbc_lex_skip_if_at_NLINE(...) (zbc_lex_skip_if_at_NLINE(__VA_ARGS__) COMMA_SUCCESS) | ||
2990 | |||
2991 | static BC_STATUS zbc_lex_next_and_skip_NLINE(void) | ||
2992 | { | ||
2993 | BcStatus s; | ||
2994 | s = zxc_lex_next(); | ||
2995 | if (s) RETURN_STATUS(s); | ||
2996 | // if(cond)<newline>stmt is accepted too (but not 2+ newlines) | ||
2997 | s = zbc_lex_skip_if_at_NLINE(); | ||
2998 | RETURN_STATUS(s); | ||
2999 | } | ||
3000 | #define zbc_lex_next_and_skip_NLINE(...) (zbc_lex_next_and_skip_NLINE(__VA_ARGS__) COMMA_SUCCESS) | ||
3001 | |||
3002 | static BC_STATUS zbc_lex_identifier(void) | ||
3003 | { | ||
3004 | BcParse *p = &G.prs; | ||
3005 | BcStatus s; | ||
3006 | unsigned i; | ||
3007 | const char *buf = p->lex_inbuf - 1; | ||
3008 | |||
3009 | for (i = 0; i < ARRAY_SIZE(bc_lex_kws); ++i) { | ||
3010 | const char *keyword8 = bc_lex_kws[i].name8; | ||
3011 | unsigned j = 0; | ||
3012 | while (buf[j] != '\0' && buf[j] == keyword8[j]) { | ||
3013 | j++; | ||
3014 | if (j == 8) goto match; | ||
3015 | } | ||
3016 | if (keyword8[j] != '\0') | ||
3017 | continue; | ||
3018 | match: | ||
3019 | // buf starts with keyword bc_lex_kws[i] | ||
3020 | if (isalnum(buf[j]) || buf[j]=='_') | ||
3021 | continue; // "ifz" does not match "if" keyword, "if." does | ||
3022 | p->lex = BC_LEX_KEY_1st_keyword + i; | ||
3023 | if (!keyword_is_POSIX(i)) { | ||
3024 | s = zbc_posix_error_fmt("%sthe '%.8s' keyword", "POSIX does not allow ", bc_lex_kws[i].name8); | ||
3025 | if (s) RETURN_STATUS(s); | ||
3026 | } | ||
3027 | |||
3028 | // We minus 1 because the index has already been incremented. | ||
3029 | p->lex_inbuf += j - 1; | ||
3030 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3031 | } | ||
3032 | |||
3033 | xc_lex_name(); | ||
3034 | s = BC_STATUS_SUCCESS; | ||
3035 | |||
3036 | if (p->lex_strnumbuf.len > 2) { | ||
3037 | // Prevent this: | ||
3038 | // >>> qwe=1 | ||
3039 | // bc: POSIX only allows one character names; this is bad: 'qwe=1 | ||
3040 | // ' | ||
3041 | unsigned len = strchrnul(buf, '\n') - buf; | ||
3042 | s = zbc_posix_error_fmt("POSIX only allows one character names; this is bad: '%.*s'", len, buf); | ||
3043 | } | ||
3044 | |||
3045 | RETURN_STATUS(s); | ||
3046 | } | ||
3047 | #define zbc_lex_identifier(...) (zbc_lex_identifier(__VA_ARGS__) COMMA_SUCCESS) | ||
3048 | |||
3049 | static BC_STATUS zbc_lex_string(void) | ||
3050 | { | ||
3051 | BcParse *p = &G.prs; | ||
3052 | |||
3053 | p->lex = XC_LEX_STR; | ||
3054 | bc_vec_pop_all(&p->lex_strnumbuf); | ||
3055 | for (;;) { | ||
3056 | char c = peek_inbuf(); // strings can cross lines | ||
3057 | if (c == '\0') { | ||
3058 | RETURN_STATUS(bc_error("unterminated string")); | ||
3059 | } | ||
3060 | if (c == '"') | ||
3061 | break; | ||
3062 | if (c == '\n') { | ||
3063 | p->lex_line++; | ||
3064 | dbg_lex("++p->lex_line=%zd", p->lex_line); | ||
3065 | } | ||
3066 | bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf); | ||
3067 | p->lex_inbuf++; | ||
3068 | } | ||
3069 | bc_vec_pushZeroByte(&p->lex_strnumbuf); | ||
3070 | p->lex_inbuf++; | ||
3071 | |||
3072 | G.err_line = p->lex_line; | ||
3073 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3074 | } | ||
3075 | #define zbc_lex_string(...) (zbc_lex_string(__VA_ARGS__) COMMA_SUCCESS) | ||
3076 | |||
3077 | static void parse_lex_by_checking_eq_sign(unsigned with_and_without) | ||
3078 | { | ||
3079 | BcParse *p = &G.prs; | ||
3080 | if (*p->lex_inbuf == '=') { | ||
3081 | // ^^^ not using peek_inbuf() since '==' etc can't be split across lines | ||
3082 | p->lex_inbuf++; | ||
3083 | with_and_without >>= 8; // store "with" value | ||
3084 | } // else store "without" value | ||
3085 | p->lex = (with_and_without & 0xff); | ||
3086 | } | ||
3087 | #define parse_lex_by_checking_eq_sign(with, without) \ | ||
3088 | parse_lex_by_checking_eq_sign(((with)<<8)|(without)) | ||
3089 | |||
3090 | static BC_STATUS zbc_lex_comment(void) | ||
3091 | { | ||
3092 | BcParse *p = &G.prs; | ||
3093 | |||
3094 | p->lex = XC_LEX_WHITESPACE; | ||
3095 | // here lex_inbuf is at '*' of opening comment delimiter | ||
3096 | for (;;) { | ||
3097 | char c; | ||
3098 | |||
3099 | p->lex_inbuf++; | ||
3100 | c = peek_inbuf(); | ||
3101 | check_star: | ||
3102 | if (c == '*') { | ||
3103 | p->lex_inbuf++; | ||
3104 | c = *p->lex_inbuf; // no need to peek_inbuf() | ||
3105 | if (c == '/') | ||
3106 | break; | ||
3107 | goto check_star; | ||
3108 | } | ||
3109 | if (c == '\0') { | ||
3110 | RETURN_STATUS(bc_error("unterminated comment")); | ||
3111 | } | ||
3112 | if (c == '\n') { | ||
3113 | p->lex_line++; | ||
3114 | dbg_lex("++p->lex_line=%zd", p->lex_line); | ||
3115 | } | ||
3116 | } | ||
3117 | p->lex_inbuf++; // skip trailing '/' | ||
3118 | |||
3119 | G.err_line = p->lex_line; | ||
3120 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3121 | } | ||
3122 | #define zbc_lex_comment(...) (zbc_lex_comment(__VA_ARGS__) COMMA_SUCCESS) | ||
3123 | |||
3124 | #undef zbc_lex_token | ||
3125 | static BC_STATUS zbc_lex_token(void) | ||
3126 | { | ||
3127 | BcParse *p = &G.prs; | ||
3128 | BcStatus s = BC_STATUS_SUCCESS; | ||
3129 | char c = eat_inbuf(); | ||
3130 | char c2; | ||
3131 | |||
3132 | // This is the workhorse of the lexer. | ||
3133 | switch (c) { | ||
3134 | // case '\0': // probably never reached | ||
3135 | // p->lex_inbuf--; | ||
3136 | // p->lex = XC_LEX_EOF; | ||
3137 | // break; | ||
3138 | case '\n': | ||
3139 | p->lex_line++; | ||
3140 | dbg_lex("++p->lex_line=%zd", p->lex_line); | ||
3141 | p->lex = XC_LEX_NLINE; | ||
3142 | break; | ||
3143 | case '\t': | ||
3144 | case '\v': | ||
3145 | case '\f': | ||
3146 | case '\r': | ||
3147 | case ' ': | ||
3148 | xc_lex_whitespace(); | ||
3149 | break; | ||
3150 | case '!': | ||
3151 | parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT); | ||
3152 | if (p->lex == BC_LEX_OP_BOOL_NOT) { | ||
3153 | s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("!"); | ||
3154 | if (s) RETURN_STATUS(s); | ||
3155 | } | ||
3156 | break; | ||
3157 | case '"': | ||
3158 | s = zbc_lex_string(); | ||
3159 | break; | ||
3160 | case '#': | ||
3161 | s = zbc_POSIX_does_not_allow("'#' script comments"); | ||
3162 | if (s) RETURN_STATUS(s); | ||
3163 | xc_lex_lineComment(); | ||
3164 | break; | ||
3165 | case '%': | ||
3166 | parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_MODULUS, XC_LEX_OP_MODULUS); | ||
3167 | break; | ||
3168 | case '&': | ||
3169 | c2 = *p->lex_inbuf; | ||
3170 | if (c2 == '&') { | ||
3171 | s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("&&"); | ||
3172 | if (s) RETURN_STATUS(s); | ||
3173 | p->lex_inbuf++; | ||
3174 | p->lex = BC_LEX_OP_BOOL_AND; | ||
3175 | } else { | ||
3176 | p->lex = XC_LEX_INVALID; | ||
3177 | s = bc_error_bad_character('&'); | ||
3178 | } | ||
3179 | break; | ||
3180 | case '(': | ||
3181 | case ')': | ||
3182 | p->lex = (BcLexType)(c - '(' + BC_LEX_LPAREN); | ||
3183 | break; | ||
3184 | case '*': | ||
3185 | parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_MULTIPLY, XC_LEX_OP_MULTIPLY); | ||
3186 | break; | ||
3187 | case '+': | ||
3188 | c2 = *p->lex_inbuf; | ||
3189 | if (c2 == '+') { | ||
3190 | p->lex_inbuf++; | ||
3191 | p->lex = BC_LEX_OP_INC; | ||
3192 | } else | ||
3193 | parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_PLUS, XC_LEX_OP_PLUS); | ||
3194 | break; | ||
3195 | case ',': | ||
3196 | p->lex = BC_LEX_COMMA; | ||
3197 | break; | ||
3198 | case '-': | ||
3199 | c2 = *p->lex_inbuf; | ||
3200 | if (c2 == '-') { | ||
3201 | p->lex_inbuf++; | ||
3202 | p->lex = BC_LEX_OP_DEC; | ||
3203 | } else | ||
3204 | parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_MINUS, XC_LEX_OP_MINUS); | ||
3205 | break; | ||
3206 | case '.': | ||
3207 | if (isdigit(*p->lex_inbuf)) | ||
3208 | s = zxc_lex_number(c); | ||
3209 | else { | ||
3210 | p->lex = BC_LEX_KEY_LAST; | ||
3211 | s = zbc_POSIX_does_not_allow("'.' as 'last'"); | ||
3212 | } | ||
3213 | break; | ||
3214 | case '/': | ||
3215 | c2 = *p->lex_inbuf; | ||
3216 | if (c2 == '*') | ||
3217 | s = zbc_lex_comment(); | ||
3218 | else | ||
3219 | parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_DIVIDE, XC_LEX_OP_DIVIDE); | ||
3220 | break; | ||
3221 | case '0': | ||
3222 | case '1': | ||
3223 | case '2': | ||
3224 | case '3': | ||
3225 | case '4': | ||
3226 | case '5': | ||
3227 | case '6': | ||
3228 | case '7': | ||
3229 | case '8': | ||
3230 | case '9': | ||
3231 | case 'A': | ||
3232 | case 'B': | ||
3233 | case 'C': | ||
3234 | case 'D': | ||
3235 | case 'E': | ||
3236 | case 'F': | ||
3237 | case 'G': | ||
3238 | case 'H': | ||
3239 | case 'I': | ||
3240 | case 'J': | ||
3241 | case 'K': | ||
3242 | case 'L': | ||
3243 | case 'M': | ||
3244 | case 'N': | ||
3245 | case 'O': | ||
3246 | case 'P': | ||
3247 | case 'Q': | ||
3248 | case 'R': | ||
3249 | case 'S': | ||
3250 | case 'T': | ||
3251 | case 'U': | ||
3252 | case 'V': | ||
3253 | case 'W': | ||
3254 | case 'X': | ||
3255 | case 'Y': | ||
3256 | case 'Z': | ||
3257 | s = zxc_lex_number(c); | ||
3258 | break; | ||
3259 | case ';': | ||
3260 | p->lex = BC_LEX_SCOLON; | ||
3261 | break; | ||
3262 | case '<': | ||
3263 | parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_LE, XC_LEX_OP_REL_LT); | ||
3264 | break; | ||
3265 | case '=': | ||
3266 | parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN); | ||
3267 | break; | ||
3268 | case '>': | ||
3269 | parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_GE, XC_LEX_OP_REL_GT); | ||
3270 | break; | ||
3271 | case '[': | ||
3272 | case ']': | ||
3273 | p->lex = (BcLexType)(c - '[' + BC_LEX_LBRACKET); | ||
3274 | break; | ||
3275 | case '\\': | ||
3276 | if (*p->lex_inbuf == '\n') { | ||
3277 | p->lex = XC_LEX_WHITESPACE; | ||
3278 | p->lex_inbuf++; | ||
3279 | } else | ||
3280 | s = bc_error_bad_character(c); | ||
3281 | break; | ||
3282 | case '^': | ||
3283 | parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_POWER, XC_LEX_OP_POWER); | ||
3284 | break; | ||
3285 | case 'a': | ||
3286 | case 'b': | ||
3287 | case 'c': | ||
3288 | case 'd': | ||
3289 | case 'e': | ||
3290 | case 'f': | ||
3291 | case 'g': | ||
3292 | case 'h': | ||
3293 | case 'i': | ||
3294 | case 'j': | ||
3295 | case 'k': | ||
3296 | case 'l': | ||
3297 | case 'm': | ||
3298 | case 'n': | ||
3299 | case 'o': | ||
3300 | case 'p': | ||
3301 | case 'q': | ||
3302 | case 'r': | ||
3303 | case 's': | ||
3304 | case 't': | ||
3305 | case 'u': | ||
3306 | case 'v': | ||
3307 | case 'w': | ||
3308 | case 'x': | ||
3309 | case 'y': | ||
3310 | case 'z': | ||
3311 | s = zbc_lex_identifier(); | ||
3312 | break; | ||
3313 | case '{': | ||
3314 | case '}': | ||
3315 | p->lex = (BcLexType)(c - '{' + BC_LEX_LBRACE); | ||
3316 | break; | ||
3317 | case '|': | ||
3318 | c2 = *p->lex_inbuf; | ||
3319 | if (c2 == '|') { | ||
3320 | s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("||"); | ||
3321 | if (s) RETURN_STATUS(s); | ||
3322 | p->lex_inbuf++; | ||
3323 | p->lex = BC_LEX_OP_BOOL_OR; | ||
3324 | } else { | ||
3325 | p->lex = XC_LEX_INVALID; | ||
3326 | s = bc_error_bad_character(c); | ||
3327 | } | ||
3328 | break; | ||
3329 | default: | ||
3330 | p->lex = XC_LEX_INVALID; | ||
3331 | s = bc_error_bad_character(c); | ||
3332 | break; | ||
3333 | } | ||
3334 | |||
3335 | RETURN_STATUS(s); | ||
3336 | } | ||
3337 | #define zbc_lex_token(...) (zbc_lex_token(__VA_ARGS__) COMMA_SUCCESS) | ||
3338 | #endif // ENABLE_BC | ||
3339 | |||
3340 | #if ENABLE_DC | ||
3341 | static BC_STATUS zdc_lex_register(void) | ||
3342 | { | ||
3343 | BcParse *p = &G.prs; | ||
3344 | if (G_exreg && isspace(*p->lex_inbuf)) { | ||
3345 | xc_lex_whitespace(); // eats whitespace (but not newline) | ||
3346 | p->lex_inbuf++; // xc_lex_name() expects this | ||
3347 | xc_lex_name(); | ||
3348 | } else { | ||
3349 | bc_vec_pop_all(&p->lex_strnumbuf); | ||
3350 | bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf++); | ||
3351 | bc_vec_pushZeroByte(&p->lex_strnumbuf); | ||
3352 | p->lex = XC_LEX_NAME; | ||
3353 | } | ||
3354 | |||
3355 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3356 | } | ||
3357 | #define zdc_lex_register(...) (zdc_lex_register(__VA_ARGS__) COMMA_SUCCESS) | ||
3358 | |||
3359 | static BC_STATUS zdc_lex_string(void) | ||
3360 | { | ||
3361 | BcParse *p = &G.prs; | ||
3362 | size_t depth; | ||
3363 | |||
3364 | p->lex = XC_LEX_STR; | ||
3365 | bc_vec_pop_all(&p->lex_strnumbuf); | ||
3366 | |||
3367 | depth = 1; | ||
3368 | for (;;) { | ||
3369 | char c = peek_inbuf(); | ||
3370 | if (c == '\0') { | ||
3371 | RETURN_STATUS(bc_error("unterminated string")); | ||
3372 | } | ||
3373 | if (c == '[') depth++; | ||
3374 | if (c == ']') | ||
3375 | if (--depth == 0) | ||
3376 | break; | ||
3377 | if (c == '\n') { | ||
3378 | p->lex_line++; | ||
3379 | dbg_lex("++p->lex_line=%zd", p->lex_line); | ||
3380 | } | ||
3381 | bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf); | ||
3382 | p->lex_inbuf++; | ||
3383 | } | ||
3384 | bc_vec_pushZeroByte(&p->lex_strnumbuf); | ||
3385 | p->lex_inbuf++; // skip trailing ']' | ||
3386 | |||
3387 | G.err_line = p->lex_line; | ||
3388 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3389 | } | ||
3390 | #define zdc_lex_string(...) (zdc_lex_string(__VA_ARGS__) COMMA_SUCCESS) | ||
3391 | |||
3392 | #undef zdc_lex_token | ||
3393 | static BC_STATUS zdc_lex_token(void) | ||
3394 | { | ||
3395 | static const //BcLexType - should be this type, but narrower type saves size: | ||
3396 | uint8_t | ||
3397 | dc_lex_regs[] ALIGN1 = { | ||
3398 | XC_LEX_OP_REL_EQ, XC_LEX_OP_REL_LE, XC_LEX_OP_REL_GE, XC_LEX_OP_REL_NE, | ||
3399 | XC_LEX_OP_REL_LT, XC_LEX_OP_REL_GT, DC_LEX_SCOLON, DC_LEX_COLON, | ||
3400 | DC_LEX_ELSE, DC_LEX_LOAD, DC_LEX_LOAD_POP, DC_LEX_OP_ASSIGN, | ||
3401 | DC_LEX_STORE_PUSH, | ||
3402 | }; | ||
3403 | |||
3404 | BcParse *p = &G.prs; | ||
3405 | BcStatus s; | ||
3406 | char c, c2; | ||
3407 | size_t i; | ||
3408 | |||
3409 | for (i = 0; i < ARRAY_SIZE(dc_lex_regs); ++i) { | ||
3410 | if (p->lex_last == dc_lex_regs[i]) | ||
3411 | RETURN_STATUS(zdc_lex_register()); | ||
3412 | } | ||
3413 | |||
3414 | s = BC_STATUS_SUCCESS; | ||
3415 | c = eat_inbuf(); | ||
3416 | if (c >= '%' && c <= '~' | ||
3417 | && (p->lex = dc_char_to_LEX[c - '%']) != XC_LEX_INVALID | ||
3418 | ) { | ||
3419 | RETURN_STATUS(s); | ||
3420 | } | ||
3421 | |||
3422 | // This is the workhorse of the lexer. | ||
3423 | switch (c) { | ||
3424 | // case '\0': // probably never reached | ||
3425 | // p->lex = XC_LEX_EOF; | ||
3426 | // break; | ||
3427 | case '\n': | ||
3428 | // '\n' is XC_LEX_NLINE, not XC_LEX_WHITESPACE | ||
3429 | // (and "case '\n':" is not just empty here) | ||
3430 | // only to allow interactive dc have a way to exit | ||
3431 | // "parse" stage of "parse,execute" loop | ||
3432 | // on <enter>, not on _next_ token (which would mean | ||
3433 | // commands are not executed on pressing <enter>). | ||
3434 | // IOW: typing "1p<enter>" should print "1" _at once_, | ||
3435 | // not after some more input. | ||
3436 | p->lex_line++; | ||
3437 | dbg_lex("++p->lex_line=%zd", p->lex_line); | ||
3438 | p->lex = XC_LEX_NLINE; | ||
3439 | break; | ||
3440 | case '\t': | ||
3441 | case '\v': | ||
3442 | case '\f': | ||
3443 | case '\r': | ||
3444 | case ' ': | ||
3445 | xc_lex_whitespace(); | ||
3446 | break; | ||
3447 | case '!': | ||
3448 | c2 = *p->lex_inbuf; | ||
3449 | if (c2 == '=') | ||
3450 | p->lex = XC_LEX_OP_REL_NE; | ||
3451 | else if (c2 == '<') | ||
3452 | p->lex = XC_LEX_OP_REL_LE; | ||
3453 | else if (c2 == '>') | ||
3454 | p->lex = XC_LEX_OP_REL_GE; | ||
3455 | else | ||
3456 | RETURN_STATUS(bc_error_bad_character(c)); | ||
3457 | p->lex_inbuf++; | ||
3458 | break; | ||
3459 | case '#': | ||
3460 | xc_lex_lineComment(); | ||
3461 | break; | ||
3462 | case '.': | ||
3463 | if (isdigit(*p->lex_inbuf)) | ||
3464 | s = zxc_lex_number(c); | ||
3465 | else | ||
3466 | s = bc_error_bad_character(c); | ||
3467 | break; | ||
3468 | case '0': | ||
3469 | case '1': | ||
3470 | case '2': | ||
3471 | case '3': | ||
3472 | case '4': | ||
3473 | case '5': | ||
3474 | case '6': | ||
3475 | case '7': | ||
3476 | case '8': | ||
3477 | case '9': | ||
3478 | case 'A': | ||
3479 | case 'B': | ||
3480 | case 'C': | ||
3481 | case 'D': | ||
3482 | case 'E': | ||
3483 | case 'F': | ||
3484 | s = zxc_lex_number(c); | ||
3485 | break; | ||
3486 | case '[': | ||
3487 | s = zdc_lex_string(); | ||
3488 | break; | ||
3489 | default: | ||
3490 | p->lex = XC_LEX_INVALID; | ||
3491 | s = bc_error_bad_character(c); | ||
3492 | break; | ||
3493 | } | ||
3494 | |||
3495 | RETURN_STATUS(s); | ||
3496 | } | ||
3497 | #define zdc_lex_token(...) (zdc_lex_token(__VA_ARGS__) COMMA_SUCCESS) | ||
3498 | #endif // ENABLE_DC | ||
3499 | |||
3500 | static void xc_parse_push(unsigned i) | ||
3501 | { | ||
3502 | BcVec *code = &G.prs.func->code; | ||
3503 | dbg_compile("%s:%d pushing bytecode %zd:%d", __func__, __LINE__, code->len, i); | ||
3504 | bc_vec_pushByte(code, (uint8_t)i); | ||
3505 | } | ||
3506 | |||
3507 | static void xc_parse_pushName(char *name) | ||
3508 | { | ||
3509 | #if 1 | ||
3510 | BcVec *code = &G.prs.func->code; | ||
3511 | size_t pos = code->len; | ||
3512 | size_t len = strlen(name) + 1; | ||
3513 | |||
3514 | bc_vec_expand(code, pos + len); | ||
3515 | strcpy(code->v + pos, name); | ||
3516 | code->len = pos + len; | ||
3517 | #else | ||
3518 | // Smaller code, but way slow: | ||
3519 | do { | ||
3520 | xc_parse_push(*name); | ||
3521 | } while (*name++); | ||
3522 | #endif | ||
3523 | } | ||
3524 | |||
3525 | // Indexes < 0xfc are encoded verbatim, else first byte is | ||
3526 | // 0xfc, 0xfd, 0xfe or 0xff, encoding "1..4 bytes", | ||
3527 | // followed by that many bytes, lsb first. | ||
3528 | // (The above describes 32-bit case). | ||
3529 | #define SMALL_INDEX_LIMIT (0x100 - sizeof(size_t)) | ||
3530 | |||
3531 | static void xc_parse_pushIndex(size_t idx) | ||
3532 | { | ||
3533 | size_t mask; | ||
3534 | unsigned amt; | ||
3535 | |||
3536 | dbg_lex("%s:%d pushing index %zd", __func__, __LINE__, idx); | ||
3537 | if (idx < SMALL_INDEX_LIMIT) { | ||
3538 | xc_parse_push(idx); | ||
3539 | return; | ||
3540 | } | ||
3541 | |||
3542 | mask = ((size_t)0xff) << (sizeof(idx) * 8 - 8); | ||
3543 | amt = sizeof(idx); | ||
3544 | for (;;) { | ||
3545 | if (idx & mask) break; | ||
3546 | mask >>= 8; | ||
3547 | amt--; | ||
3548 | } | ||
3549 | // amt is at least 1 here - "one byte of length data follows" | ||
3550 | |||
3551 | xc_parse_push((SMALL_INDEX_LIMIT - 1) + amt); | ||
3552 | |||
3553 | do { | ||
3554 | xc_parse_push((unsigned char)idx); | ||
3555 | idx >>= 8; | ||
3556 | } while (idx != 0); | ||
3557 | } | ||
3558 | |||
3559 | static void xc_parse_pushInst_and_Index(unsigned inst, size_t idx) | ||
3560 | { | ||
3561 | xc_parse_push(inst); | ||
3562 | xc_parse_pushIndex(idx); | ||
3563 | } | ||
3564 | |||
3565 | #if ENABLE_BC | ||
3566 | static void bc_parse_pushJUMP(size_t idx) | ||
3567 | { | ||
3568 | xc_parse_pushInst_and_Index(BC_INST_JUMP, idx); | ||
3569 | } | ||
3570 | |||
3571 | static void bc_parse_pushJUMP_ZERO(size_t idx) | ||
3572 | { | ||
3573 | xc_parse_pushInst_and_Index(BC_INST_JUMP_ZERO, idx); | ||
3574 | } | ||
3575 | |||
3576 | static BC_STATUS zbc_parse_pushSTR(void) | ||
3577 | { | ||
3578 | BcParse *p = &G.prs; | ||
3579 | char *str = xstrdup(p->lex_strnumbuf.v); | ||
3580 | |||
3581 | xc_parse_pushInst_and_Index(XC_INST_STR, p->func->strs.len); | ||
3582 | bc_vec_push(&p->func->strs, &str); | ||
3583 | |||
3584 | RETURN_STATUS(zxc_lex_next()); | ||
3585 | } | ||
3586 | #define zbc_parse_pushSTR(...) (zbc_parse_pushSTR(__VA_ARGS__) COMMA_SUCCESS) | ||
3587 | #endif | ||
3588 | |||
3589 | static void xc_parse_pushNUM(void) | ||
3590 | { | ||
3591 | BcParse *p = &G.prs; | ||
3592 | char *num = xstrdup(p->lex_strnumbuf.v); | ||
3593 | #if ENABLE_BC && ENABLE_DC | ||
3594 | size_t idx = bc_vec_push(IS_BC ? &p->func->consts : &G.prog.consts, &num); | ||
3595 | #elif ENABLE_BC | ||
3596 | size_t idx = bc_vec_push(&p->func->consts, &num); | ||
3597 | #else // DC | ||
3598 | size_t idx = bc_vec_push(&G.prog.consts, &num); | ||
3599 | #endif | ||
3600 | xc_parse_pushInst_and_Index(XC_INST_NUM, idx); | ||
3601 | } | ||
3602 | |||
3603 | static BC_STATUS zxc_parse_text_init(const char *text) | ||
3604 | { | ||
3605 | G.prs.func = xc_program_func(G.prs.fidx); | ||
3606 | G.prs.lex_inbuf = text; | ||
3607 | G.prs.lex = G.prs.lex_last = XC_LEX_INVALID; | ||
3608 | RETURN_STATUS(zxc_lex_next()); | ||
3609 | } | ||
3610 | #define zxc_parse_text_init(...) (zxc_parse_text_init(__VA_ARGS__) COMMA_SUCCESS) | ||
3611 | |||
3612 | // Called when parsing or execution detects a failure, | ||
3613 | // resets execution structures. | ||
3614 | static void xc_program_reset(void) | ||
3615 | { | ||
3616 | BcFunc *f; | ||
3617 | BcInstPtr *ip; | ||
3618 | |||
3619 | bc_vec_npop(&G.prog.exestack, G.prog.exestack.len - 1); | ||
3620 | bc_vec_pop_all(&G.prog.results); | ||
3621 | |||
3622 | f = xc_program_func_BC_PROG_MAIN(); | ||
3623 | ip = bc_vec_top(&G.prog.exestack); | ||
3624 | ip->inst_idx = f->code.len; | ||
3625 | } | ||
3626 | |||
3627 | // Called when parsing code detects a failure, | ||
3628 | // resets parsing structures. | ||
3629 | static void xc_parse_reset(void) | ||
3630 | { | ||
3631 | BcParse *p = &G.prs; | ||
3632 | if (p->fidx != BC_PROG_MAIN) { | ||
3633 | bc_func_free(p->func); | ||
3634 | bc_func_init(p->func); | ||
3635 | |||
3636 | p->fidx = BC_PROG_MAIN; | ||
3637 | p->func = xc_program_func_BC_PROG_MAIN(); | ||
3638 | } | ||
3639 | |||
3640 | p->lex_inbuf += strlen(p->lex_inbuf); | ||
3641 | p->lex = XC_LEX_EOF; | ||
3642 | |||
3643 | IF_BC(bc_vec_pop_all(&p->exits);) | ||
3644 | IF_BC(bc_vec_pop_all(&p->conds);) | ||
3645 | IF_BC(bc_vec_pop_all(&p->ops);) | ||
3646 | |||
3647 | xc_program_reset(); | ||
3648 | } | ||
3649 | |||
3650 | static void xc_parse_free(void) | ||
3651 | { | ||
3652 | IF_BC(bc_vec_free(&G.prs.exits);) | ||
3653 | IF_BC(bc_vec_free(&G.prs.conds);) | ||
3654 | IF_BC(bc_vec_free(&G.prs.ops);) | ||
3655 | bc_vec_free(&G.prs.lex_strnumbuf); | ||
3656 | } | ||
3657 | |||
3658 | static void xc_parse_create(size_t fidx) | ||
3659 | { | ||
3660 | BcParse *p = &G.prs; | ||
3661 | memset(p, 0, sizeof(BcParse)); | ||
3662 | |||
3663 | bc_char_vec_init(&p->lex_strnumbuf); | ||
3664 | IF_BC(bc_vec_init(&p->exits, sizeof(size_t), NULL);) | ||
3665 | IF_BC(bc_vec_init(&p->conds, sizeof(size_t), NULL);) | ||
3666 | IF_BC(bc_vec_init(&p->ops, sizeof(BcLexType), NULL);) | ||
3667 | |||
3668 | p->fidx = fidx; | ||
3669 | p->func = xc_program_func(fidx); | ||
3670 | } | ||
3671 | |||
3672 | static void xc_program_add_fn(void) | ||
3673 | { | ||
3674 | //size_t idx; | ||
3675 | BcFunc f; | ||
3676 | bc_func_init(&f); | ||
3677 | //idx = | ||
3678 | bc_vec_push(&G.prog.fns, &f); | ||
3679 | //return idx; | ||
3680 | } | ||
3681 | |||
3682 | #if ENABLE_BC | ||
3683 | |||
3684 | // Note: takes ownership of 'name' (must be malloced) | ||
3685 | static size_t bc_program_addFunc(char *name) | ||
3686 | { | ||
3687 | size_t idx; | ||
3688 | BcId entry, *entry_ptr; | ||
3689 | int inserted; | ||
3690 | |||
3691 | entry.name = name; | ||
3692 | entry.idx = G.prog.fns.len; | ||
3693 | |||
3694 | inserted = bc_map_insert(&G.prog.fn_map, &entry, &idx); | ||
3695 | if (!inserted) free(name); | ||
3696 | |||
3697 | entry_ptr = bc_vec_item(&G.prog.fn_map, idx); | ||
3698 | idx = entry_ptr->idx; | ||
3699 | |||
3700 | if (!inserted) { | ||
3701 | // There is already a function with this name. | ||
3702 | // It'll be redefined now, clear old definition. | ||
3703 | BcFunc *func = xc_program_func(entry_ptr->idx); | ||
3704 | bc_func_free(func); | ||
3705 | bc_func_init(func); | ||
3706 | } else { | ||
3707 | xc_program_add_fn(); | ||
3708 | } | ||
3709 | |||
3710 | return idx; | ||
3711 | } | ||
3712 | |||
3713 | #define BC_PARSE_TOP_OP(p) (*(BcLexType*)bc_vec_top(&(p)->ops)) | ||
3714 | // We can calculate the conversion between tokens and exprs by subtracting the | ||
3715 | // position of the first operator in the lex enum and adding the position of the | ||
3716 | // first in the expr enum. Note: This only works for binary operators. | ||
3717 | #define BC_TOKEN_2_INST(t) ((char) ((t) - XC_LEX_OP_POWER + XC_INST_POWER)) | ||
3718 | |||
3719 | static BC_STATUS zbc_parse_expr(uint8_t flags); | ||
3720 | #define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) | ||
3721 | |||
3722 | static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed); | ||
3723 | #define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__) COMMA_SUCCESS) | ||
3724 | |||
3725 | static BC_STATUS zbc_parse_stmt(void) | ||
3726 | { | ||
3727 | RETURN_STATUS(zbc_parse_stmt_possibly_auto(false)); | ||
3728 | } | ||
3729 | #define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__) COMMA_SUCCESS) | ||
3730 | |||
3731 | static BC_STATUS zbc_parse_stmt_allow_NLINE_before(const char *after_X) | ||
3732 | { | ||
3733 | BcParse *p = &G.prs; | ||
3734 | // "if(cond)<newline>stmt" is accepted too, but not 2+ newlines. | ||
3735 | // Same for "else", "while()", "for()". | ||
3736 | BcStatus s = zbc_lex_next_and_skip_NLINE(); | ||
3737 | if (s) RETURN_STATUS(s); | ||
3738 | if (p->lex == XC_LEX_NLINE) | ||
3739 | RETURN_STATUS(bc_error_fmt("no statement after '%s'", after_X)); | ||
3740 | |||
3741 | RETURN_STATUS(zbc_parse_stmt()); | ||
3742 | } | ||
3743 | #define zbc_parse_stmt_allow_NLINE_before(...) (zbc_parse_stmt_allow_NLINE_before(__VA_ARGS__) COMMA_SUCCESS) | ||
3744 | |||
3745 | static void bc_parse_operator(BcLexType type, size_t start, size_t *nexprs) | ||
3746 | { | ||
3747 | BcParse *p = &G.prs; | ||
3748 | char l, r = bc_operation_PREC(type - XC_LEX_1st_op); | ||
3749 | bool left = bc_operation_LEFT(type - XC_LEX_1st_op); | ||
3750 | |||
3751 | while (p->ops.len > start) { | ||
3752 | BcLexType t = BC_PARSE_TOP_OP(p); | ||
3753 | if (t == BC_LEX_LPAREN) break; | ||
3754 | |||
3755 | l = bc_operation_PREC(t - XC_LEX_1st_op); | ||
3756 | if (l >= r && (l != r || !left)) break; | ||
3757 | |||
3758 | xc_parse_push(BC_TOKEN_2_INST(t)); | ||
3759 | bc_vec_pop(&p->ops); | ||
3760 | *nexprs -= (t != BC_LEX_OP_BOOL_NOT && t != XC_LEX_NEG); | ||
3761 | } | ||
3762 | |||
3763 | bc_vec_push(&p->ops, &type); | ||
3764 | } | ||
3765 | |||
3766 | static BC_STATUS zbc_parse_rightParen(size_t ops_bgn, size_t *nexs) | ||
3767 | { | ||
3768 | BcParse *p = &G.prs; | ||
3769 | BcLexType top; | ||
3770 | |||
3771 | if (p->ops.len <= ops_bgn) | ||
3772 | RETURN_STATUS(bc_error_bad_expression()); | ||
3773 | top = BC_PARSE_TOP_OP(p); | ||
3774 | |||
3775 | while (top != BC_LEX_LPAREN) { | ||
3776 | xc_parse_push(BC_TOKEN_2_INST(top)); | ||
3777 | |||
3778 | bc_vec_pop(&p->ops); | ||
3779 | *nexs -= (top != BC_LEX_OP_BOOL_NOT && top != XC_LEX_NEG); | ||
3780 | |||
3781 | if (p->ops.len <= ops_bgn) | ||
3782 | RETURN_STATUS(bc_error_bad_expression()); | ||
3783 | top = BC_PARSE_TOP_OP(p); | ||
3784 | } | ||
3785 | |||
3786 | bc_vec_pop(&p->ops); | ||
3787 | |||
3788 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3789 | } | ||
3790 | #define zbc_parse_rightParen(...) (zbc_parse_rightParen(__VA_ARGS__) COMMA_SUCCESS) | ||
3791 | |||
3792 | static BC_STATUS zbc_parse_params(uint8_t flags) | ||
3793 | { | ||
3794 | BcParse *p = &G.prs; | ||
3795 | BcStatus s; | ||
3796 | size_t nparams; | ||
3797 | |||
3798 | dbg_lex("%s:%d p->lex:%d", __func__, __LINE__, p->lex); | ||
3799 | flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY; | ||
3800 | |||
3801 | s = zxc_lex_next(); | ||
3802 | if (s) RETURN_STATUS(s); | ||
3803 | |||
3804 | nparams = 0; | ||
3805 | if (p->lex != BC_LEX_RPAREN) { | ||
3806 | for (;;) { | ||
3807 | s = zbc_parse_expr(flags); | ||
3808 | if (s) RETURN_STATUS(s); | ||
3809 | nparams++; | ||
3810 | if (p->lex != BC_LEX_COMMA) { | ||
3811 | if (p->lex == BC_LEX_RPAREN) | ||
3812 | break; | ||
3813 | RETURN_STATUS(bc_error_bad_token()); | ||
3814 | } | ||
3815 | s = zxc_lex_next(); | ||
3816 | if (s) RETURN_STATUS(s); | ||
3817 | } | ||
3818 | } | ||
3819 | |||
3820 | xc_parse_pushInst_and_Index(BC_INST_CALL, nparams); | ||
3821 | |||
3822 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3823 | } | ||
3824 | #define zbc_parse_params(...) (zbc_parse_params(__VA_ARGS__) COMMA_SUCCESS) | ||
3825 | |||
3826 | // Note: takes ownership of 'name' (must be malloced) | ||
3827 | static BC_STATUS zbc_parse_call(char *name, uint8_t flags) | ||
3828 | { | ||
3829 | BcParse *p = &G.prs; | ||
3830 | BcStatus s; | ||
3831 | BcId entry, *entry_ptr; | ||
3832 | size_t idx; | ||
3833 | |||
3834 | entry.name = name; | ||
3835 | |||
3836 | s = zbc_parse_params(flags); | ||
3837 | if (s) goto err; | ||
3838 | |||
3839 | if (p->lex != BC_LEX_RPAREN) { | ||
3840 | s = bc_error_bad_token(); | ||
3841 | goto err; | ||
3842 | } | ||
3843 | |||
3844 | idx = bc_map_find_exact(&G.prog.fn_map, &entry); | ||
3845 | |||
3846 | if (idx == BC_VEC_INVALID_IDX) { | ||
3847 | // No such function exists, create an empty one | ||
3848 | bc_program_addFunc(name); | ||
3849 | idx = bc_map_find_exact(&G.prog.fn_map, &entry); | ||
3850 | } else | ||
3851 | free(name); | ||
3852 | |||
3853 | entry_ptr = bc_vec_item(&G.prog.fn_map, idx); | ||
3854 | xc_parse_pushIndex(entry_ptr->idx); | ||
3855 | |||
3856 | RETURN_STATUS(zxc_lex_next()); | ||
3857 | err: | ||
3858 | free(name); | ||
3859 | RETURN_STATUS(s); | ||
3860 | } | ||
3861 | #define zbc_parse_call(...) (zbc_parse_call(__VA_ARGS__) COMMA_SUCCESS) | ||
3862 | |||
3863 | static BC_STATUS zbc_parse_name(BcInst *type, uint8_t flags) | ||
3864 | { | ||
3865 | BcParse *p = &G.prs; | ||
3866 | BcStatus s; | ||
3867 | char *name; | ||
3868 | |||
3869 | name = xstrdup(p->lex_strnumbuf.v); | ||
3870 | s = zxc_lex_next(); | ||
3871 | if (s) goto err; | ||
3872 | |||
3873 | if (p->lex == BC_LEX_LBRACKET) { | ||
3874 | s = zxc_lex_next(); | ||
3875 | if (s) goto err; | ||
3876 | |||
3877 | if (p->lex == BC_LEX_RBRACKET) { | ||
3878 | if (!(flags & BC_PARSE_ARRAY)) { | ||
3879 | s = bc_error_bad_expression(); | ||
3880 | goto err; | ||
3881 | } | ||
3882 | *type = XC_INST_ARRAY; | ||
3883 | } else { | ||
3884 | *type = XC_INST_ARRAY_ELEM; | ||
3885 | flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); | ||
3886 | s = zbc_parse_expr(flags); | ||
3887 | if (s) goto err; | ||
3888 | } | ||
3889 | s = zxc_lex_next(); | ||
3890 | if (s) goto err; | ||
3891 | xc_parse_push(*type); | ||
3892 | xc_parse_pushName(name); | ||
3893 | free(name); | ||
3894 | } else if (p->lex == BC_LEX_LPAREN) { | ||
3895 | if (flags & BC_PARSE_NOCALL) { | ||
3896 | s = bc_error_bad_token(); | ||
3897 | goto err; | ||
3898 | } | ||
3899 | *type = BC_INST_CALL; | ||
3900 | s = zbc_parse_call(name, flags); | ||
3901 | } else { | ||
3902 | *type = XC_INST_VAR; | ||
3903 | xc_parse_push(XC_INST_VAR); | ||
3904 | xc_parse_pushName(name); | ||
3905 | free(name); | ||
3906 | } | ||
3907 | |||
3908 | RETURN_STATUS(s); | ||
3909 | err: | ||
3910 | free(name); | ||
3911 | RETURN_STATUS(s); | ||
3912 | } | ||
3913 | #define zbc_parse_name(...) (zbc_parse_name(__VA_ARGS__) COMMA_SUCCESS) | ||
3914 | |||
3915 | static BC_STATUS zbc_parse_read(void) | ||
3916 | { | ||
3917 | BcParse *p = &G.prs; | ||
3918 | BcStatus s; | ||
3919 | |||
3920 | s = zxc_lex_next(); | ||
3921 | if (s) RETURN_STATUS(s); | ||
3922 | if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
3923 | |||
3924 | s = zxc_lex_next(); | ||
3925 | if (s) RETURN_STATUS(s); | ||
3926 | if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
3927 | |||
3928 | xc_parse_push(XC_INST_READ); | ||
3929 | |||
3930 | RETURN_STATUS(s); | ||
3931 | } | ||
3932 | #define zbc_parse_read(...) (zbc_parse_read(__VA_ARGS__) COMMA_SUCCESS) | ||
3933 | |||
3934 | static BC_STATUS zbc_parse_builtin(BcLexType type, uint8_t flags, BcInst *prev) | ||
3935 | { | ||
3936 | BcParse *p = &G.prs; | ||
3937 | BcStatus s; | ||
3938 | |||
3939 | s = zxc_lex_next(); | ||
3940 | if (s) RETURN_STATUS(s); | ||
3941 | if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
3942 | |||
3943 | flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY; | ||
3944 | |||
3945 | s = zxc_lex_next(); | ||
3946 | if (s) RETURN_STATUS(s); | ||
3947 | |||
3948 | s = zbc_parse_expr(flags); | ||
3949 | if (s) RETURN_STATUS(s); | ||
3950 | |||
3951 | if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
3952 | |||
3953 | *prev = (type == BC_LEX_KEY_LENGTH) ? XC_INST_LENGTH : XC_INST_SQRT; | ||
3954 | xc_parse_push(*prev); | ||
3955 | |||
3956 | RETURN_STATUS(s); | ||
3957 | } | ||
3958 | #define zbc_parse_builtin(...) (zbc_parse_builtin(__VA_ARGS__) COMMA_SUCCESS) | ||
3959 | |||
3960 | static BC_STATUS zbc_parse_scale(BcInst *type, uint8_t flags) | ||
3961 | { | ||
3962 | BcParse *p = &G.prs; | ||
3963 | BcStatus s; | ||
3964 | |||
3965 | s = zxc_lex_next(); | ||
3966 | if (s) RETURN_STATUS(s); | ||
3967 | |||
3968 | if (p->lex != BC_LEX_LPAREN) { | ||
3969 | *type = XC_INST_SCALE; | ||
3970 | xc_parse_push(XC_INST_SCALE); | ||
3971 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
3972 | } | ||
3973 | |||
3974 | *type = XC_INST_SCALE_FUNC; | ||
3975 | flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); | ||
3976 | |||
3977 | s = zxc_lex_next(); | ||
3978 | if (s) RETURN_STATUS(s); | ||
3979 | |||
3980 | s = zbc_parse_expr(flags); | ||
3981 | if (s) RETURN_STATUS(s); | ||
3982 | if (p->lex != BC_LEX_RPAREN) | ||
3983 | RETURN_STATUS(bc_error_bad_token()); | ||
3984 | xc_parse_push(XC_INST_SCALE_FUNC); | ||
3985 | |||
3986 | RETURN_STATUS(zxc_lex_next()); | ||
3987 | } | ||
3988 | #define zbc_parse_scale(...) (zbc_parse_scale(__VA_ARGS__) COMMA_SUCCESS) | ||
3989 | |||
3990 | static BC_STATUS zbc_parse_incdec(BcInst *prev, size_t *nexs, uint8_t flags) | ||
3991 | { | ||
3992 | BcParse *p = &G.prs; | ||
3993 | BcStatus s; | ||
3994 | BcLexType type; | ||
3995 | char inst; | ||
3996 | BcInst etype = *prev; | ||
3997 | |||
3998 | if (etype == XC_INST_VAR || etype == XC_INST_ARRAY_ELEM | ||
3999 | || etype == XC_INST_SCALE || etype == BC_INST_LAST | ||
4000 | || etype == XC_INST_IBASE || etype == XC_INST_OBASE | ||
4001 | ) { | ||
4002 | *prev = inst = BC_INST_INC_POST + (p->lex != BC_LEX_OP_INC); | ||
4003 | xc_parse_push(inst); | ||
4004 | s = zxc_lex_next(); | ||
4005 | } else { | ||
4006 | *prev = inst = BC_INST_INC_PRE + (p->lex != BC_LEX_OP_INC); | ||
4007 | |||
4008 | s = zxc_lex_next(); | ||
4009 | if (s) RETURN_STATUS(s); | ||
4010 | type = p->lex; | ||
4011 | |||
4012 | // Because we parse the next part of the expression | ||
4013 | // right here, we need to increment this. | ||
4014 | *nexs = *nexs + 1; | ||
4015 | |||
4016 | switch (type) { | ||
4017 | case XC_LEX_NAME: | ||
4018 | s = zbc_parse_name(prev, flags | BC_PARSE_NOCALL); | ||
4019 | break; | ||
4020 | case BC_LEX_KEY_IBASE: | ||
4021 | case BC_LEX_KEY_LAST: | ||
4022 | case BC_LEX_KEY_OBASE: | ||
4023 | xc_parse_push(type - BC_LEX_KEY_IBASE + XC_INST_IBASE); | ||
4024 | s = zxc_lex_next(); | ||
4025 | break; | ||
4026 | case BC_LEX_KEY_SCALE: | ||
4027 | s = zxc_lex_next(); | ||
4028 | if (s) RETURN_STATUS(s); | ||
4029 | if (p->lex == BC_LEX_LPAREN) | ||
4030 | s = bc_error_bad_token(); | ||
4031 | else | ||
4032 | xc_parse_push(XC_INST_SCALE); | ||
4033 | break; | ||
4034 | default: | ||
4035 | s = bc_error_bad_token(); | ||
4036 | break; | ||
4037 | } | ||
4038 | |||
4039 | if (!s) xc_parse_push(inst); | ||
4040 | } | ||
4041 | |||
4042 | RETURN_STATUS(s); | ||
4043 | } | ||
4044 | #define zbc_parse_incdec(...) (zbc_parse_incdec(__VA_ARGS__) COMMA_SUCCESS) | ||
4045 | |||
4046 | static int bc_parse_inst_isLeaf(BcInst p) | ||
4047 | { | ||
4048 | return (p >= XC_INST_NUM && p <= XC_INST_SQRT) | ||
4049 | || p == BC_INST_INC_POST | ||
4050 | || p == BC_INST_DEC_POST | ||
4051 | ; | ||
4052 | } | ||
4053 | #define BC_PARSE_LEAF(prev, bin_last, rparen) \ | ||
4054 | (!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev))) | ||
4055 | |||
4056 | static BC_STATUS zbc_parse_minus(BcInst *prev, size_t ops_bgn, | ||
4057 | bool rparen, bool bin_last, size_t *nexprs) | ||
4058 | { | ||
4059 | BcParse *p = &G.prs; | ||
4060 | BcStatus s; | ||
4061 | BcLexType type; | ||
4062 | |||
4063 | s = zxc_lex_next(); | ||
4064 | if (s) RETURN_STATUS(s); | ||
4065 | |||
4066 | type = BC_PARSE_LEAF(*prev, bin_last, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG; | ||
4067 | *prev = BC_TOKEN_2_INST(type); | ||
4068 | |||
4069 | // We can just push onto the op stack because this is the largest | ||
4070 | // precedence operator that gets pushed. Inc/dec does not. | ||
4071 | if (type != XC_LEX_OP_MINUS) | ||
4072 | bc_vec_push(&p->ops, &type); | ||
4073 | else | ||
4074 | bc_parse_operator(type, ops_bgn, nexprs); | ||
4075 | |||
4076 | RETURN_STATUS(s); | ||
4077 | } | ||
4078 | #define zbc_parse_minus(...) (zbc_parse_minus(__VA_ARGS__) COMMA_SUCCESS) | ||
4079 | |||
4080 | static BC_STATUS zbc_parse_print(void) | ||
4081 | { | ||
4082 | BcParse *p = &G.prs; | ||
4083 | BcStatus s; | ||
4084 | BcLexType type; | ||
4085 | |||
4086 | for (;;) { | ||
4087 | s = zxc_lex_next(); | ||
4088 | if (s) RETURN_STATUS(s); | ||
4089 | type = p->lex; | ||
4090 | if (type == XC_LEX_STR) { | ||
4091 | s = zbc_parse_pushSTR(); | ||
4092 | } else { | ||
4093 | s = zbc_parse_expr(0); | ||
4094 | } | ||
4095 | if (s) RETURN_STATUS(s); | ||
4096 | xc_parse_push(XC_INST_PRINT_POP); | ||
4097 | if (p->lex != BC_LEX_COMMA) | ||
4098 | break; | ||
4099 | } | ||
4100 | |||
4101 | RETURN_STATUS(s); | ||
4102 | } | ||
4103 | #define zbc_parse_print(...) (zbc_parse_print(__VA_ARGS__) COMMA_SUCCESS) | ||
4104 | |||
4105 | static BC_STATUS zbc_parse_return(void) | ||
4106 | { | ||
4107 | BcParse *p = &G.prs; | ||
4108 | BcStatus s; | ||
4109 | BcLexType t; | ||
4110 | |||
4111 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4112 | s = zxc_lex_next(); | ||
4113 | if (s) RETURN_STATUS(s); | ||
4114 | |||
4115 | t = p->lex; | ||
4116 | if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE) | ||
4117 | xc_parse_push(BC_INST_RET0); | ||
4118 | else { | ||
4119 | //TODO: if (p->func->voidfunc) ERROR | ||
4120 | s = zbc_parse_expr(0); | ||
4121 | if (s) RETURN_STATUS(s); | ||
4122 | |||
4123 | if (t != BC_LEX_LPAREN // "return EXPR", no () | ||
4124 | || p->lex_last != BC_LEX_RPAREN // example: "return (a) + b" | ||
4125 | ) { | ||
4126 | s = zbc_POSIX_requires("parentheses around return expressions"); | ||
4127 | if (s) RETURN_STATUS(s); | ||
4128 | } | ||
4129 | |||
4130 | xc_parse_push(XC_INST_RET); | ||
4131 | } | ||
4132 | |||
4133 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4134 | RETURN_STATUS(s); | ||
4135 | } | ||
4136 | #define zbc_parse_return(...) (zbc_parse_return(__VA_ARGS__) COMMA_SUCCESS) | ||
4137 | |||
4138 | static void rewrite_label_to_current(size_t idx) | ||
4139 | { | ||
4140 | BcParse *p = &G.prs; | ||
4141 | size_t *label = bc_vec_item(&p->func->labels, idx); | ||
4142 | *label = p->func->code.len; | ||
4143 | } | ||
4144 | |||
4145 | static BC_STATUS zbc_parse_if(void) | ||
4146 | { | ||
4147 | BcParse *p = &G.prs; | ||
4148 | BcStatus s; | ||
4149 | size_t ip_idx; | ||
4150 | |||
4151 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4152 | s = zxc_lex_next(); | ||
4153 | if (s) RETURN_STATUS(s); | ||
4154 | if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
4155 | |||
4156 | s = zxc_lex_next(); | ||
4157 | if (s) RETURN_STATUS(s); | ||
4158 | s = zbc_parse_expr(BC_PARSE_REL); | ||
4159 | if (s) RETURN_STATUS(s); | ||
4160 | if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
4161 | |||
4162 | // Encode "if zero, jump to ..." | ||
4163 | // Pushed value (destination of the jump) is uninitialized, | ||
4164 | // will be rewritten to be address of "end of if()" or of "else". | ||
4165 | ip_idx = bc_vec_push(&p->func->labels, &ip_idx); | ||
4166 | bc_parse_pushJUMP_ZERO(ip_idx); | ||
4167 | |||
4168 | s = zbc_parse_stmt_allow_NLINE_before(STRING_if); | ||
4169 | if (s) RETURN_STATUS(s); | ||
4170 | |||
4171 | dbg_lex("%s:%d in if after stmt: p->lex:%d", __func__, __LINE__, p->lex); | ||
4172 | if (p->lex == BC_LEX_KEY_ELSE) { | ||
4173 | size_t ip2_idx; | ||
4174 | |||
4175 | // Encode "after then_stmt, jump to end of if()" | ||
4176 | ip2_idx = bc_vec_push(&p->func->labels, &ip2_idx); | ||
4177 | dbg_lex("%s:%d after if() then_stmt: BC_INST_JUMP to %zd", __func__, __LINE__, ip2_idx); | ||
4178 | bc_parse_pushJUMP(ip2_idx); | ||
4179 | |||
4180 | dbg_lex("%s:%d rewriting 'if_zero' label to jump to 'else'-> %zd", __func__, __LINE__, p->func->code.len); | ||
4181 | rewrite_label_to_current(ip_idx); | ||
4182 | |||
4183 | ip_idx = ip2_idx; | ||
4184 | |||
4185 | s = zbc_parse_stmt_allow_NLINE_before(STRING_else); | ||
4186 | if (s) RETURN_STATUS(s); | ||
4187 | } | ||
4188 | |||
4189 | dbg_lex("%s:%d rewriting label to jump after 'if' body-> %zd", __func__, __LINE__, p->func->code.len); | ||
4190 | rewrite_label_to_current(ip_idx); | ||
4191 | |||
4192 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4193 | RETURN_STATUS(s); | ||
4194 | } | ||
4195 | #define zbc_parse_if(...) (zbc_parse_if(__VA_ARGS__) COMMA_SUCCESS) | ||
4196 | |||
4197 | static BC_STATUS zbc_parse_while(void) | ||
4198 | { | ||
4199 | BcParse *p = &G.prs; | ||
4200 | BcStatus s; | ||
4201 | size_t cond_idx; | ||
4202 | size_t ip_idx; | ||
4203 | |||
4204 | s = zxc_lex_next(); | ||
4205 | if (s) RETURN_STATUS(s); | ||
4206 | if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
4207 | s = zxc_lex_next(); | ||
4208 | if (s) RETURN_STATUS(s); | ||
4209 | |||
4210 | cond_idx = bc_vec_push(&p->func->labels, &p->func->code.len); | ||
4211 | ip_idx = cond_idx + 1; | ||
4212 | bc_vec_push(&p->conds, &cond_idx); | ||
4213 | |||
4214 | bc_vec_push(&p->exits, &ip_idx); | ||
4215 | bc_vec_push(&p->func->labels, &ip_idx); | ||
4216 | |||
4217 | s = zbc_parse_expr(BC_PARSE_REL); | ||
4218 | if (s) RETURN_STATUS(s); | ||
4219 | if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
4220 | |||
4221 | bc_parse_pushJUMP_ZERO(ip_idx); | ||
4222 | |||
4223 | s = zbc_parse_stmt_allow_NLINE_before(STRING_while); | ||
4224 | if (s) RETURN_STATUS(s); | ||
4225 | |||
4226 | dbg_lex("%s:%d BC_INST_JUMP to %zd", __func__, __LINE__, cond_idx); | ||
4227 | bc_parse_pushJUMP(cond_idx); | ||
4228 | |||
4229 | dbg_lex("%s:%d rewriting label-> %zd", __func__, __LINE__, p->func->code.len); | ||
4230 | rewrite_label_to_current(ip_idx); | ||
4231 | |||
4232 | bc_vec_pop(&p->exits); | ||
4233 | bc_vec_pop(&p->conds); | ||
4234 | |||
4235 | RETURN_STATUS(s); | ||
4236 | } | ||
4237 | #define zbc_parse_while(...) (zbc_parse_while(__VA_ARGS__) COMMA_SUCCESS) | ||
4238 | |||
4239 | static BC_STATUS zbc_parse_for(void) | ||
4240 | { | ||
4241 | BcParse *p = &G.prs; | ||
4242 | BcStatus s; | ||
4243 | size_t cond_idx, exit_idx, body_idx, update_idx; | ||
4244 | |||
4245 | dbg_lex("%s:%d p->lex:%d", __func__, __LINE__, p->lex); | ||
4246 | s = zxc_lex_next(); | ||
4247 | if (s) RETURN_STATUS(s); | ||
4248 | if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
4249 | s = zxc_lex_next(); | ||
4250 | if (s) RETURN_STATUS(s); | ||
4251 | |||
4252 | if (p->lex != BC_LEX_SCOLON) { | ||
4253 | s = zbc_parse_expr(0); | ||
4254 | xc_parse_push(XC_INST_POP); | ||
4255 | if (s) RETURN_STATUS(s); | ||
4256 | } else { | ||
4257 | s = zbc_POSIX_does_not_allow_empty_X_expression_in_for("init"); | ||
4258 | if (s) RETURN_STATUS(s); | ||
4259 | } | ||
4260 | |||
4261 | if (p->lex != BC_LEX_SCOLON) RETURN_STATUS(bc_error_bad_token()); | ||
4262 | s = zxc_lex_next(); | ||
4263 | if (s) RETURN_STATUS(s); | ||
4264 | |||
4265 | cond_idx = bc_vec_push(&p->func->labels, &p->func->code.len); | ||
4266 | update_idx = cond_idx + 1; | ||
4267 | body_idx = update_idx + 1; | ||
4268 | exit_idx = body_idx + 1; | ||
4269 | |||
4270 | if (p->lex != BC_LEX_SCOLON) | ||
4271 | s = zbc_parse_expr(BC_PARSE_REL); | ||
4272 | else { | ||
4273 | // Set this for the next call to xc_parse_pushNUM(). | ||
4274 | // This is safe to set because the current token is a semicolon, | ||
4275 | // which has no string requirement. | ||
4276 | bc_vec_string(&p->lex_strnumbuf, 1, "1"); | ||
4277 | xc_parse_pushNUM(); | ||
4278 | s = zbc_POSIX_does_not_allow_empty_X_expression_in_for("condition"); | ||
4279 | } | ||
4280 | if (s) RETURN_STATUS(s); | ||
4281 | |||
4282 | if (p->lex != BC_LEX_SCOLON) RETURN_STATUS(bc_error_bad_token()); | ||
4283 | |||
4284 | s = zxc_lex_next(); | ||
4285 | if (s) RETURN_STATUS(s); | ||
4286 | |||
4287 | bc_parse_pushJUMP_ZERO(exit_idx); | ||
4288 | bc_parse_pushJUMP(body_idx); | ||
4289 | |||
4290 | bc_vec_push(&p->conds, &update_idx); | ||
4291 | bc_vec_push(&p->func->labels, &p->func->code.len); | ||
4292 | |||
4293 | if (p->lex != BC_LEX_RPAREN) { | ||
4294 | s = zbc_parse_expr(0); | ||
4295 | if (s) RETURN_STATUS(s); | ||
4296 | if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token()); | ||
4297 | xc_parse_push(XC_INST_POP); | ||
4298 | } else { | ||
4299 | s = zbc_POSIX_does_not_allow_empty_X_expression_in_for("update"); | ||
4300 | if (s) RETURN_STATUS(s); | ||
4301 | } | ||
4302 | |||
4303 | bc_parse_pushJUMP(cond_idx); | ||
4304 | bc_vec_push(&p->func->labels, &p->func->code.len); | ||
4305 | |||
4306 | bc_vec_push(&p->exits, &exit_idx); | ||
4307 | bc_vec_push(&p->func->labels, &exit_idx); | ||
4308 | |||
4309 | s = zbc_parse_stmt_allow_NLINE_before(STRING_for); | ||
4310 | if (s) RETURN_STATUS(s); | ||
4311 | |||
4312 | dbg_lex("%s:%d BC_INST_JUMP to %zd", __func__, __LINE__, update_idx); | ||
4313 | bc_parse_pushJUMP(update_idx); | ||
4314 | |||
4315 | dbg_lex("%s:%d rewriting label-> %zd", __func__, __LINE__, p->func->code.len); | ||
4316 | rewrite_label_to_current(exit_idx); | ||
4317 | |||
4318 | bc_vec_pop(&p->exits); | ||
4319 | bc_vec_pop(&p->conds); | ||
4320 | |||
4321 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
4322 | } | ||
4323 | #define zbc_parse_for(...) (zbc_parse_for(__VA_ARGS__) COMMA_SUCCESS) | ||
4324 | |||
4325 | static BC_STATUS zbc_parse_break_or_continue(BcLexType type) | ||
4326 | { | ||
4327 | BcParse *p = &G.prs; | ||
4328 | size_t i; | ||
4329 | |||
4330 | if (type == BC_LEX_KEY_BREAK) { | ||
4331 | if (p->exits.len == 0) // none of the enclosing blocks is a loop | ||
4332 | RETURN_STATUS(bc_error_bad_token()); | ||
4333 | i = *(size_t*)bc_vec_top(&p->exits); | ||
4334 | } else { | ||
4335 | i = *(size_t*)bc_vec_top(&p->conds); | ||
4336 | } | ||
4337 | bc_parse_pushJUMP(i); | ||
4338 | |||
4339 | RETURN_STATUS(zxc_lex_next()); | ||
4340 | } | ||
4341 | #define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS) | ||
4342 | |||
4343 | static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var) | ||
4344 | { | ||
4345 | BcId *autoid; | ||
4346 | BcId a; | ||
4347 | size_t i; | ||
4348 | |||
4349 | autoid = (void*)f->autos.v; | ||
4350 | for (i = 0; i < f->autos.len; i++, autoid++) { | ||
4351 | if (strcmp(name, autoid->name) == 0 | ||
4352 | && var == autoid->idx | ||
4353 | ) { | ||
4354 | RETURN_STATUS(bc_error("duplicate function parameter or auto name")); | ||
4355 | } | ||
4356 | } | ||
4357 | |||
4358 | a.idx = var; | ||
4359 | a.name = name; | ||
4360 | |||
4361 | bc_vec_push(&f->autos, &a); | ||
4362 | |||
4363 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
4364 | } | ||
4365 | #define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS) | ||
4366 | |||
4367 | static BC_STATUS zbc_parse_funcdef(void) | ||
4368 | { | ||
4369 | BcParse *p = &G.prs; | ||
4370 | BcStatus s; | ||
4371 | bool var, comma, voidfunc; | ||
4372 | char *name; | ||
4373 | |||
4374 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4375 | s = zxc_lex_next(); | ||
4376 | if (s) RETURN_STATUS(s); | ||
4377 | if (p->lex != XC_LEX_NAME) | ||
4378 | RETURN_STATUS(bc_error_bad_function_definition()); | ||
4379 | |||
4380 | // To be maximally both POSIX and GNU-compatible, | ||
4381 | // "void" is not treated as a normal keyword: | ||
4382 | // you can have variable named "void", and even a function | ||
4383 | // named "void": "define void() { return 6; }" is ok. | ||
4384 | // _Only_ "define void f() ..." syntax treats "void" | ||
4385 | // specially. | ||
4386 | voidfunc = (strcmp(p->lex_strnumbuf.v, "void") == 0); | ||
4387 | |||
4388 | s = zxc_lex_next(); | ||
4389 | if (s) RETURN_STATUS(s); | ||
4390 | |||
4391 | voidfunc = (voidfunc && p->lex == XC_LEX_NAME); | ||
4392 | if (voidfunc) { | ||
4393 | s = zxc_lex_next(); | ||
4394 | if (s) RETURN_STATUS(s); | ||
4395 | } | ||
4396 | |||
4397 | if (p->lex != BC_LEX_LPAREN) | ||
4398 | RETURN_STATUS(bc_error_bad_function_definition()); | ||
4399 | |||
4400 | p->fidx = bc_program_addFunc(xstrdup(p->lex_strnumbuf.v)); | ||
4401 | p->func = xc_program_func(p->fidx); | ||
4402 | p->func->voidfunc = voidfunc; | ||
4403 | |||
4404 | s = zxc_lex_next(); | ||
4405 | if (s) RETURN_STATUS(s); | ||
4406 | |||
4407 | comma = false; | ||
4408 | while (p->lex != BC_LEX_RPAREN) { | ||
4409 | if (p->lex != XC_LEX_NAME) | ||
4410 | RETURN_STATUS(bc_error_bad_function_definition()); | ||
4411 | |||
4412 | ++p->func->nparams; | ||
4413 | |||
4414 | name = xstrdup(p->lex_strnumbuf.v); | ||
4415 | s = zxc_lex_next(); | ||
4416 | if (s) goto err; | ||
4417 | |||
4418 | var = p->lex != BC_LEX_LBRACKET; | ||
4419 | |||
4420 | if (!var) { | ||
4421 | s = zxc_lex_next(); | ||
4422 | if (s) goto err; | ||
4423 | |||
4424 | if (p->lex != BC_LEX_RBRACKET) { | ||
4425 | s = bc_error_bad_function_definition(); | ||
4426 | goto err; | ||
4427 | } | ||
4428 | |||
4429 | s = zxc_lex_next(); | ||
4430 | if (s) goto err; | ||
4431 | } | ||
4432 | |||
4433 | comma = p->lex == BC_LEX_COMMA; | ||
4434 | if (comma) { | ||
4435 | s = zxc_lex_next(); | ||
4436 | if (s) goto err; | ||
4437 | } | ||
4438 | |||
4439 | s = zbc_func_insert(p->func, name, var); | ||
4440 | if (s) goto err; | ||
4441 | } | ||
4442 | |||
4443 | if (comma) RETURN_STATUS(bc_error_bad_function_definition()); | ||
4444 | |||
4445 | s = zxc_lex_next(); | ||
4446 | if (s) RETURN_STATUS(s); | ||
4447 | |||
4448 | if (p->lex != BC_LEX_LBRACE) { | ||
4449 | s = zbc_POSIX_requires("the left brace be on the same line as the function header"); | ||
4450 | if (s) RETURN_STATUS(s); | ||
4451 | } | ||
4452 | |||
4453 | // Prevent "define z()<newline>" from being interpreted as function with empty stmt as body | ||
4454 | s = zbc_lex_skip_if_at_NLINE(); | ||
4455 | if (s) RETURN_STATUS(s); | ||
4456 | // GNU bc requires a {} block even if function body has single stmt, enforce this | ||
4457 | if (p->lex != BC_LEX_LBRACE) | ||
4458 | RETURN_STATUS(bc_error("function { body } expected")); | ||
4459 | |||
4460 | p->in_funcdef++; // to determine whether "return" stmt is allowed, and such | ||
4461 | s = zbc_parse_stmt_possibly_auto(true); | ||
4462 | p->in_funcdef--; | ||
4463 | if (s) RETURN_STATUS(s); | ||
4464 | |||
4465 | xc_parse_push(BC_INST_RET0); | ||
4466 | |||
4467 | // Subsequent code generation is into main program | ||
4468 | p->fidx = BC_PROG_MAIN; | ||
4469 | p->func = xc_program_func_BC_PROG_MAIN(); | ||
4470 | |||
4471 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4472 | RETURN_STATUS(s); | ||
4473 | err: | ||
4474 | dbg_lex_done("%s:%d done (error)", __func__, __LINE__); | ||
4475 | free(name); | ||
4476 | RETURN_STATUS(s); | ||
4477 | } | ||
4478 | #define zbc_parse_funcdef(...) (zbc_parse_funcdef(__VA_ARGS__) COMMA_SUCCESS) | ||
4479 | |||
4480 | static BC_STATUS zbc_parse_auto(void) | ||
4481 | { | ||
4482 | BcParse *p = &G.prs; | ||
4483 | BcStatus s; | ||
4484 | char *name; | ||
4485 | |||
4486 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4487 | s = zxc_lex_next(); | ||
4488 | if (s) RETURN_STATUS(s); | ||
4489 | |||
4490 | for (;;) { | ||
4491 | bool var; | ||
4492 | |||
4493 | if (p->lex != XC_LEX_NAME) | ||
4494 | RETURN_STATUS(bc_error_at("bad 'auto' syntax")); | ||
4495 | |||
4496 | name = xstrdup(p->lex_strnumbuf.v); | ||
4497 | s = zxc_lex_next(); | ||
4498 | if (s) goto err; | ||
4499 | |||
4500 | var = (p->lex != BC_LEX_LBRACKET); | ||
4501 | if (!var) { | ||
4502 | s = zxc_lex_next(); | ||
4503 | if (s) goto err; | ||
4504 | |||
4505 | if (p->lex != BC_LEX_RBRACKET) { | ||
4506 | s = bc_error_at("bad 'auto' syntax"); | ||
4507 | goto err; | ||
4508 | } | ||
4509 | s = zxc_lex_next(); | ||
4510 | if (s) goto err; | ||
4511 | } | ||
4512 | |||
4513 | s = zbc_func_insert(p->func, name, var); | ||
4514 | if (s) goto err; | ||
4515 | |||
4516 | if (p->lex == XC_LEX_NLINE | ||
4517 | || p->lex == BC_LEX_SCOLON | ||
4518 | //|| p->lex == BC_LEX_RBRACE // allow "define f() {auto a}" | ||
4519 | ) { | ||
4520 | break; | ||
4521 | } | ||
4522 | if (p->lex != BC_LEX_COMMA) | ||
4523 | RETURN_STATUS(bc_error_at("bad 'auto' syntax")); | ||
4524 | s = zxc_lex_next(); // skip comma | ||
4525 | if (s) RETURN_STATUS(s); | ||
4526 | } | ||
4527 | |||
4528 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4529 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
4530 | err: | ||
4531 | free(name); | ||
4532 | dbg_lex_done("%s:%d done (ERROR)", __func__, __LINE__); | ||
4533 | RETURN_STATUS(s); | ||
4534 | } | ||
4535 | #define zbc_parse_auto(...) (zbc_parse_auto(__VA_ARGS__) COMMA_SUCCESS) | ||
4536 | |||
4537 | #undef zbc_parse_stmt_possibly_auto | ||
4538 | static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed) | ||
4539 | { | ||
4540 | BcParse *p = &G.prs; | ||
4541 | BcStatus s = BC_STATUS_SUCCESS; | ||
4542 | |||
4543 | dbg_lex_enter("%s:%d entered, p->lex:%d", __func__, __LINE__, p->lex); | ||
4544 | |||
4545 | if (p->lex == XC_LEX_NLINE) { | ||
4546 | dbg_lex_done("%s:%d done (seen XC_LEX_NLINE)", __func__, __LINE__); | ||
4547 | RETURN_STATUS(s); | ||
4548 | } | ||
4549 | if (p->lex == BC_LEX_SCOLON) { | ||
4550 | dbg_lex_done("%s:%d done (seen BC_LEX_SCOLON)", __func__, __LINE__); | ||
4551 | RETURN_STATUS(s); | ||
4552 | } | ||
4553 | |||
4554 | if (p->lex == BC_LEX_LBRACE) { | ||
4555 | dbg_lex("%s:%d BC_LEX_LBRACE: (auto_allowed:%d)", __func__, __LINE__, auto_allowed); | ||
4556 | do { | ||
4557 | s = zxc_lex_next(); | ||
4558 | if (s) RETURN_STATUS(s); | ||
4559 | } while (p->lex == XC_LEX_NLINE); | ||
4560 | if (auto_allowed && p->lex == BC_LEX_KEY_AUTO) { | ||
4561 | dbg_lex("%s:%d calling zbc_parse_auto()", __func__, __LINE__); | ||
4562 | s = zbc_parse_auto(); | ||
4563 | if (s) RETURN_STATUS(s); | ||
4564 | } | ||
4565 | while (p->lex != BC_LEX_RBRACE) { | ||
4566 | dbg_lex("%s:%d block parsing loop", __func__, __LINE__); | ||
4567 | s = zbc_parse_stmt(); | ||
4568 | if (s) RETURN_STATUS(s); | ||
4569 | // Check that next token is a correct stmt delimiter - | ||
4570 | // disallows "print 1 print 2" and such. | ||
4571 | if (p->lex == BC_LEX_RBRACE) | ||
4572 | break; | ||
4573 | if (p->lex != BC_LEX_SCOLON | ||
4574 | && p->lex != XC_LEX_NLINE | ||
4575 | ) { | ||
4576 | RETURN_STATUS(bc_error_at("bad statement terminator")); | ||
4577 | } | ||
4578 | s = zxc_lex_next(); | ||
4579 | if (s) RETURN_STATUS(s); | ||
4580 | } | ||
4581 | s = zxc_lex_next(); | ||
4582 | dbg_lex_done("%s:%d done (seen BC_LEX_RBRACE)", __func__, __LINE__); | ||
4583 | RETURN_STATUS(s); | ||
4584 | } | ||
4585 | |||
4586 | dbg_lex("%s:%d p->lex:%d", __func__, __LINE__, p->lex); | ||
4587 | switch (p->lex) { | ||
4588 | case XC_LEX_OP_MINUS: | ||
4589 | case BC_LEX_OP_INC: | ||
4590 | case BC_LEX_OP_DEC: | ||
4591 | case BC_LEX_OP_BOOL_NOT: | ||
4592 | case BC_LEX_LPAREN: | ||
4593 | case XC_LEX_NAME: | ||
4594 | case XC_LEX_NUMBER: | ||
4595 | case BC_LEX_KEY_IBASE: | ||
4596 | case BC_LEX_KEY_LAST: | ||
4597 | case BC_LEX_KEY_LENGTH: | ||
4598 | case BC_LEX_KEY_OBASE: | ||
4599 | case BC_LEX_KEY_READ: | ||
4600 | case BC_LEX_KEY_SCALE: | ||
4601 | case BC_LEX_KEY_SQRT: | ||
4602 | s = zbc_parse_expr(BC_PARSE_PRINT); | ||
4603 | break; | ||
4604 | case XC_LEX_STR: | ||
4605 | s = zbc_parse_pushSTR(); | ||
4606 | xc_parse_push(XC_INST_PRINT_STR); | ||
4607 | break; | ||
4608 | case BC_LEX_KEY_BREAK: | ||
4609 | case BC_LEX_KEY_CONTINUE: | ||
4610 | s = zbc_parse_break_or_continue(p->lex); | ||
4611 | break; | ||
4612 | case BC_LEX_KEY_FOR: | ||
4613 | s = zbc_parse_for(); | ||
4614 | break; | ||
4615 | case BC_LEX_KEY_HALT: | ||
4616 | xc_parse_push(BC_INST_HALT); | ||
4617 | s = zxc_lex_next(); | ||
4618 | break; | ||
4619 | case BC_LEX_KEY_IF: | ||
4620 | s = zbc_parse_if(); | ||
4621 | break; | ||
4622 | case BC_LEX_KEY_LIMITS: | ||
4623 | // "limits" is a compile-time command, | ||
4624 | // the output is produced at _parse time_. | ||
4625 | printf( | ||
4626 | "BC_BASE_MAX = "BC_MAX_OBASE_STR "\n" | ||
4627 | "BC_DIM_MAX = "BC_MAX_DIM_STR "\n" | ||
4628 | "BC_SCALE_MAX = "BC_MAX_SCALE_STR "\n" | ||
4629 | "BC_STRING_MAX = "BC_MAX_STRING_STR"\n" | ||
4630 | // "BC_NUM_MAX = "BC_MAX_NUM_STR "\n" - GNU bc does not show this | ||
4631 | "MAX Exponent = "BC_MAX_EXP_STR "\n" | ||
4632 | "Number of vars = "BC_MAX_VARS_STR "\n" | ||
4633 | ); | ||
4634 | s = zxc_lex_next(); | ||
4635 | break; | ||
4636 | case BC_LEX_KEY_PRINT: | ||
4637 | s = zbc_parse_print(); | ||
4638 | break; | ||
4639 | case BC_LEX_KEY_QUIT: | ||
4640 | // "quit" is a compile-time command. For example, | ||
4641 | // "if (0 == 1) quit" terminates when parsing the statement, | ||
4642 | // not when it is executed | ||
4643 | QUIT_OR_RETURN_TO_MAIN; | ||
4644 | case BC_LEX_KEY_RETURN: | ||
4645 | if (!p->in_funcdef) | ||
4646 | RETURN_STATUS(bc_error("'return' not in a function")); | ||
4647 | s = zbc_parse_return(); | ||
4648 | break; | ||
4649 | case BC_LEX_KEY_WHILE: | ||
4650 | s = zbc_parse_while(); | ||
4651 | break; | ||
4652 | default: | ||
4653 | s = bc_error_bad_token(); | ||
4654 | break; | ||
4655 | } | ||
4656 | |||
4657 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4658 | RETURN_STATUS(s); | ||
4659 | } | ||
4660 | #define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__) COMMA_SUCCESS) | ||
4661 | |||
4662 | static BC_STATUS zbc_parse_stmt_or_funcdef(void) | ||
4663 | { | ||
4664 | BcParse *p = &G.prs; | ||
4665 | BcStatus s; | ||
4666 | |||
4667 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4668 | //why? | ||
4669 | // if (p->lex == XC_LEX_EOF) | ||
4670 | // s = bc_error("end of file"); | ||
4671 | // else | ||
4672 | if (p->lex == BC_LEX_KEY_DEFINE) { | ||
4673 | dbg_lex("%s:%d p->lex:BC_LEX_KEY_DEFINE", __func__, __LINE__); | ||
4674 | s = zbc_parse_funcdef(); | ||
4675 | } else { | ||
4676 | dbg_lex("%s:%d p->lex:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->lex); | ||
4677 | s = zbc_parse_stmt(); | ||
4678 | } | ||
4679 | |||
4680 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4681 | RETURN_STATUS(s); | ||
4682 | } | ||
4683 | #define zbc_parse_stmt_or_funcdef(...) (zbc_parse_stmt_or_funcdef(__VA_ARGS__) COMMA_SUCCESS) | ||
4684 | |||
4685 | #undef zbc_parse_expr | ||
4686 | static BC_STATUS zbc_parse_expr(uint8_t flags) | ||
4687 | { | ||
4688 | BcParse *p = &G.prs; | ||
4689 | BcInst prev = XC_INST_PRINT; | ||
4690 | size_t nexprs = 0, ops_bgn = p->ops.len; | ||
4691 | unsigned nparens, nrelops; | ||
4692 | bool paren_first, rprn, assign, bin_last, incdec; | ||
4693 | |||
4694 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4695 | paren_first = (p->lex == BC_LEX_LPAREN); | ||
4696 | nparens = nrelops = 0; | ||
4697 | rprn = assign = incdec = false; | ||
4698 | bin_last = true; | ||
4699 | |||
4700 | for (;;) { | ||
4701 | bool get_token; | ||
4702 | BcStatus s; | ||
4703 | BcLexType t = p->lex; | ||
4704 | |||
4705 | if (!lex_allowed_in_bc_expr(t)) | ||
4706 | break; | ||
4707 | |||
4708 | dbg_lex("%s:%d t:%d", __func__, __LINE__, t); | ||
4709 | get_token = false; | ||
4710 | s = BC_STATUS_SUCCESS; | ||
4711 | switch (t) { | ||
4712 | case BC_LEX_OP_INC: | ||
4713 | case BC_LEX_OP_DEC: | ||
4714 | dbg_lex("%s:%d LEX_OP_INC/DEC", __func__, __LINE__); | ||
4715 | if (incdec) RETURN_STATUS(bc_error_bad_assignment()); | ||
4716 | s = zbc_parse_incdec(&prev, &nexprs, flags); | ||
4717 | incdec = true; | ||
4718 | rprn = bin_last = false; | ||
4719 | //get_token = false; - already is | ||
4720 | break; | ||
4721 | case XC_LEX_OP_MINUS: | ||
4722 | dbg_lex("%s:%d LEX_OP_MINUS", __func__, __LINE__); | ||
4723 | s = zbc_parse_minus(&prev, ops_bgn, rprn, bin_last, &nexprs); | ||
4724 | rprn = false; | ||
4725 | //get_token = false; - already is | ||
4726 | bin_last = (prev == XC_INST_MINUS); | ||
4727 | if (bin_last) incdec = false; | ||
4728 | break; | ||
4729 | case BC_LEX_OP_ASSIGN_POWER: | ||
4730 | case BC_LEX_OP_ASSIGN_MULTIPLY: | ||
4731 | case BC_LEX_OP_ASSIGN_DIVIDE: | ||
4732 | case BC_LEX_OP_ASSIGN_MODULUS: | ||
4733 | case BC_LEX_OP_ASSIGN_PLUS: | ||
4734 | case BC_LEX_OP_ASSIGN_MINUS: | ||
4735 | case BC_LEX_OP_ASSIGN: | ||
4736 | dbg_lex("%s:%d LEX_ASSIGNxyz", __func__, __LINE__); | ||
4737 | if (prev != XC_INST_VAR && prev != XC_INST_ARRAY_ELEM | ||
4738 | && prev != XC_INST_SCALE && prev != XC_INST_IBASE | ||
4739 | && prev != XC_INST_OBASE && prev != BC_INST_LAST | ||
4740 | ) { | ||
4741 | RETURN_STATUS(bc_error_bad_assignment()); | ||
4742 | } | ||
4743 | // Fallthrough. | ||
4744 | case XC_LEX_OP_POWER: | ||
4745 | case XC_LEX_OP_MULTIPLY: | ||
4746 | case XC_LEX_OP_DIVIDE: | ||
4747 | case XC_LEX_OP_MODULUS: | ||
4748 | case XC_LEX_OP_PLUS: | ||
4749 | case XC_LEX_OP_REL_EQ: | ||
4750 | case XC_LEX_OP_REL_LE: | ||
4751 | case XC_LEX_OP_REL_GE: | ||
4752 | case XC_LEX_OP_REL_NE: | ||
4753 | case XC_LEX_OP_REL_LT: | ||
4754 | case XC_LEX_OP_REL_GT: | ||
4755 | case BC_LEX_OP_BOOL_NOT: | ||
4756 | case BC_LEX_OP_BOOL_OR: | ||
4757 | case BC_LEX_OP_BOOL_AND: | ||
4758 | dbg_lex("%s:%d LEX_OP_xyz", __func__, __LINE__); | ||
4759 | if (t == BC_LEX_OP_BOOL_NOT) { | ||
4760 | if (!bin_last && p->lex_last != BC_LEX_OP_BOOL_NOT) | ||
4761 | RETURN_STATUS(bc_error_bad_expression()); | ||
4762 | } else if (prev == XC_INST_BOOL_NOT) { | ||
4763 | RETURN_STATUS(bc_error_bad_expression()); | ||
4764 | } | ||
4765 | |||
4766 | nrelops += (t >= XC_LEX_OP_REL_EQ && t <= XC_LEX_OP_REL_GT); | ||
4767 | prev = BC_TOKEN_2_INST(t); | ||
4768 | bc_parse_operator(t, ops_bgn, &nexprs); | ||
4769 | rprn = incdec = false; | ||
4770 | get_token = true; | ||
4771 | bin_last = (t != BC_LEX_OP_BOOL_NOT); | ||
4772 | break; | ||
4773 | case BC_LEX_LPAREN: | ||
4774 | dbg_lex("%s:%d LEX_LPAREN", __func__, __LINE__); | ||
4775 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4776 | RETURN_STATUS(bc_error_bad_expression()); | ||
4777 | bc_vec_push(&p->ops, &t); | ||
4778 | nparens++; | ||
4779 | get_token = true; | ||
4780 | rprn = incdec = false; | ||
4781 | break; | ||
4782 | case BC_LEX_RPAREN: | ||
4783 | dbg_lex("%s:%d LEX_RPAREN", __func__, __LINE__); | ||
4784 | //why? | ||
4785 | // if (p->lex_last == BC_LEX_LPAREN) { | ||
4786 | // RETURN_STATUS(bc_error_at("empty expression")); | ||
4787 | // } | ||
4788 | if (bin_last || prev == XC_INST_BOOL_NOT) | ||
4789 | RETURN_STATUS(bc_error_bad_expression()); | ||
4790 | if (nparens == 0) { | ||
4791 | goto exit_loop; | ||
4792 | } | ||
4793 | s = zbc_parse_rightParen(ops_bgn, &nexprs); | ||
4794 | nparens--; | ||
4795 | get_token = true; | ||
4796 | rprn = true; | ||
4797 | bin_last = incdec = false; | ||
4798 | break; | ||
4799 | case XC_LEX_NAME: | ||
4800 | dbg_lex("%s:%d LEX_NAME", __func__, __LINE__); | ||
4801 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4802 | RETURN_STATUS(bc_error_bad_expression()); | ||
4803 | s = zbc_parse_name(&prev, flags & ~BC_PARSE_NOCALL); | ||
4804 | rprn = (prev == BC_INST_CALL); | ||
4805 | bin_last = false; | ||
4806 | //get_token = false; - already is | ||
4807 | nexprs++; | ||
4808 | break; | ||
4809 | case XC_LEX_NUMBER: | ||
4810 | dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__); | ||
4811 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4812 | RETURN_STATUS(bc_error_bad_expression()); | ||
4813 | xc_parse_pushNUM(); | ||
4814 | prev = XC_INST_NUM; | ||
4815 | get_token = true; | ||
4816 | rprn = bin_last = false; | ||
4817 | nexprs++; | ||
4818 | break; | ||
4819 | case BC_LEX_KEY_IBASE: | ||
4820 | case BC_LEX_KEY_LAST: | ||
4821 | case BC_LEX_KEY_OBASE: | ||
4822 | dbg_lex("%s:%d LEX_IBASE/LAST/OBASE", __func__, __LINE__); | ||
4823 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4824 | RETURN_STATUS(bc_error_bad_expression()); | ||
4825 | prev = (char) (t - BC_LEX_KEY_IBASE + XC_INST_IBASE); | ||
4826 | xc_parse_push((char) prev); | ||
4827 | get_token = true; | ||
4828 | rprn = bin_last = false; | ||
4829 | nexprs++; | ||
4830 | break; | ||
4831 | case BC_LEX_KEY_LENGTH: | ||
4832 | case BC_LEX_KEY_SQRT: | ||
4833 | dbg_lex("%s:%d LEX_LEN/SQRT", __func__, __LINE__); | ||
4834 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4835 | RETURN_STATUS(bc_error_bad_expression()); | ||
4836 | s = zbc_parse_builtin(t, flags, &prev); | ||
4837 | get_token = true; | ||
4838 | rprn = bin_last = incdec = false; | ||
4839 | nexprs++; | ||
4840 | break; | ||
4841 | case BC_LEX_KEY_READ: | ||
4842 | dbg_lex("%s:%d LEX_READ", __func__, __LINE__); | ||
4843 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4844 | RETURN_STATUS(bc_error_bad_expression()); | ||
4845 | s = zbc_parse_read(); | ||
4846 | prev = XC_INST_READ; | ||
4847 | get_token = true; | ||
4848 | rprn = bin_last = incdec = false; | ||
4849 | nexprs++; | ||
4850 | break; | ||
4851 | case BC_LEX_KEY_SCALE: | ||
4852 | dbg_lex("%s:%d LEX_SCALE", __func__, __LINE__); | ||
4853 | if (BC_PARSE_LEAF(prev, bin_last, rprn)) | ||
4854 | RETURN_STATUS(bc_error_bad_expression()); | ||
4855 | s = zbc_parse_scale(&prev, flags); | ||
4856 | //get_token = false; - already is | ||
4857 | rprn = bin_last = false; | ||
4858 | nexprs++; | ||
4859 | break; | ||
4860 | default: | ||
4861 | RETURN_STATUS(bc_error_bad_token()); | ||
4862 | } | ||
4863 | |||
4864 | if (s || G_interrupt) // error, or ^C: stop parsing | ||
4865 | RETURN_STATUS(BC_STATUS_FAILURE); | ||
4866 | if (get_token) { | ||
4867 | s = zxc_lex_next(); | ||
4868 | if (s) RETURN_STATUS(s); | ||
4869 | } | ||
4870 | } | ||
4871 | exit_loop: | ||
4872 | |||
4873 | while (p->ops.len > ops_bgn) { | ||
4874 | BcLexType top = BC_PARSE_TOP_OP(p); | ||
4875 | assign = (top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN); | ||
4876 | |||
4877 | if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN) | ||
4878 | RETURN_STATUS(bc_error_bad_expression()); | ||
4879 | |||
4880 | xc_parse_push(BC_TOKEN_2_INST(top)); | ||
4881 | |||
4882 | nexprs -= (top != BC_LEX_OP_BOOL_NOT && top != XC_LEX_NEG); | ||
4883 | bc_vec_pop(&p->ops); | ||
4884 | } | ||
4885 | |||
4886 | if (prev == XC_INST_BOOL_NOT || nexprs != 1) | ||
4887 | RETURN_STATUS(bc_error_bad_expression()); | ||
4888 | |||
4889 | if (!(flags & BC_PARSE_REL) && nrelops) { | ||
4890 | BcStatus s; | ||
4891 | s = zbc_POSIX_does_not_allow("comparison operators outside if or loops"); | ||
4892 | if (s) RETURN_STATUS(s); | ||
4893 | } else if ((flags & BC_PARSE_REL) && nrelops > 1) { | ||
4894 | BcStatus s; | ||
4895 | s = zbc_POSIX_requires("exactly one comparison operator per condition"); | ||
4896 | if (s) RETURN_STATUS(s); | ||
4897 | } | ||
4898 | |||
4899 | if (flags & BC_PARSE_PRINT) { | ||
4900 | if (paren_first || !assign) | ||
4901 | xc_parse_push(XC_INST_PRINT); | ||
4902 | xc_parse_push(XC_INST_POP); | ||
4903 | } | ||
4904 | |||
4905 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4906 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
4907 | } | ||
4908 | #define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) | ||
4909 | |||
4910 | #endif // ENABLE_BC | ||
4911 | |||
4912 | #if ENABLE_DC | ||
4913 | |||
4914 | static BC_STATUS zdc_parse_register(void) | ||
4915 | { | ||
4916 | BcParse *p = &G.prs; | ||
4917 | BcStatus s; | ||
4918 | |||
4919 | s = zxc_lex_next(); | ||
4920 | if (s) RETURN_STATUS(s); | ||
4921 | if (p->lex != XC_LEX_NAME) RETURN_STATUS(bc_error_bad_token()); | ||
4922 | |||
4923 | xc_parse_pushName(p->lex_strnumbuf.v); | ||
4924 | |||
4925 | RETURN_STATUS(s); | ||
4926 | } | ||
4927 | #define zdc_parse_register(...) (zdc_parse_register(__VA_ARGS__) COMMA_SUCCESS) | ||
4928 | |||
4929 | static void dc_parse_string(void) | ||
4930 | { | ||
4931 | BcParse *p = &G.prs; | ||
4932 | char *str; | ||
4933 | size_t len = G.prog.strs.len; | ||
4934 | |||
4935 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
4936 | |||
4937 | str = xstrdup(p->lex_strnumbuf.v); | ||
4938 | xc_parse_pushInst_and_Index(XC_INST_STR, len); | ||
4939 | bc_vec_push(&G.prog.strs, &str); | ||
4940 | |||
4941 | // Explanation needed here | ||
4942 | xc_program_add_fn(); | ||
4943 | p->func = xc_program_func(p->fidx); | ||
4944 | |||
4945 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
4946 | } | ||
4947 | |||
4948 | static BC_STATUS zdc_parse_mem(uint8_t inst, bool name, bool store) | ||
4949 | { | ||
4950 | BcStatus s; | ||
4951 | |||
4952 | xc_parse_push(inst); | ||
4953 | if (name) { | ||
4954 | s = zdc_parse_register(); | ||
4955 | if (s) RETURN_STATUS(s); | ||
4956 | } | ||
4957 | |||
4958 | if (store) { | ||
4959 | xc_parse_push(DC_INST_SWAP); | ||
4960 | xc_parse_push(XC_INST_ASSIGN); | ||
4961 | xc_parse_push(XC_INST_POP); | ||
4962 | } | ||
4963 | |||
4964 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
4965 | } | ||
4966 | #define zdc_parse_mem(...) (zdc_parse_mem(__VA_ARGS__) COMMA_SUCCESS) | ||
4967 | |||
4968 | static BC_STATUS zdc_parse_cond(uint8_t inst) | ||
4969 | { | ||
4970 | BcParse *p = &G.prs; | ||
4971 | BcStatus s; | ||
4972 | |||
4973 | xc_parse_push(inst); | ||
4974 | xc_parse_push(DC_INST_EXEC_COND); | ||
4975 | |||
4976 | s = zdc_parse_register(); | ||
4977 | if (s) RETURN_STATUS(s); | ||
4978 | |||
4979 | s = zxc_lex_next(); | ||
4980 | if (s) RETURN_STATUS(s); | ||
4981 | |||
4982 | // Note that 'else' part can not be on the next line: | ||
4983 | // echo -e '[1p]sa [2p]sb 2 1>a eb' | dc - OK, prints "2" | ||
4984 | // echo -e '[1p]sa [2p]sb 2 1>a\neb' | dc - parse error | ||
4985 | if (p->lex == DC_LEX_ELSE) { | ||
4986 | s = zdc_parse_register(); | ||
4987 | if (s) RETURN_STATUS(s); | ||
4988 | s = zxc_lex_next(); | ||
4989 | } else { | ||
4990 | xc_parse_push('\0'); | ||
4991 | } | ||
4992 | |||
4993 | RETURN_STATUS(s); | ||
4994 | } | ||
4995 | #define zdc_parse_cond(...) (zdc_parse_cond(__VA_ARGS__) COMMA_SUCCESS) | ||
4996 | |||
4997 | static BC_STATUS zdc_parse_token(BcLexType t) | ||
4998 | { | ||
4999 | BcStatus s; | ||
5000 | uint8_t inst; | ||
5001 | bool assign, get_token; | ||
5002 | |||
5003 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
5004 | s = BC_STATUS_SUCCESS; | ||
5005 | get_token = true; | ||
5006 | switch (t) { | ||
5007 | case XC_LEX_OP_REL_EQ: | ||
5008 | case XC_LEX_OP_REL_LE: | ||
5009 | case XC_LEX_OP_REL_GE: | ||
5010 | case XC_LEX_OP_REL_NE: | ||
5011 | case XC_LEX_OP_REL_LT: | ||
5012 | case XC_LEX_OP_REL_GT: | ||
5013 | dbg_lex("%s:%d LEX_OP_REL_xyz", __func__, __LINE__); | ||
5014 | s = zdc_parse_cond(t - XC_LEX_OP_REL_EQ + XC_INST_REL_EQ); | ||
5015 | get_token = false; | ||
5016 | break; | ||
5017 | case DC_LEX_SCOLON: | ||
5018 | case DC_LEX_COLON: | ||
5019 | dbg_lex("%s:%d LEX_[S]COLON", __func__, __LINE__); | ||
5020 | s = zdc_parse_mem(XC_INST_ARRAY_ELEM, true, t == DC_LEX_COLON); | ||
5021 | break; | ||
5022 | case XC_LEX_STR: | ||
5023 | dbg_lex("%s:%d LEX_STR", __func__, __LINE__); | ||
5024 | dc_parse_string(); | ||
5025 | break; | ||
5026 | case XC_LEX_NEG: | ||
5027 | dbg_lex("%s:%d LEX_NEG", __func__, __LINE__); | ||
5028 | s = zxc_lex_next(); | ||
5029 | if (s) RETURN_STATUS(s); | ||
5030 | if (G.prs.lex != XC_LEX_NUMBER) | ||
5031 | RETURN_STATUS(bc_error_bad_token()); | ||
5032 | xc_parse_pushNUM(); | ||
5033 | xc_parse_push(XC_INST_NEG); | ||
5034 | break; | ||
5035 | case XC_LEX_NUMBER: | ||
5036 | dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__); | ||
5037 | xc_parse_pushNUM(); | ||
5038 | break; | ||
5039 | case DC_LEX_READ: | ||
5040 | dbg_lex("%s:%d LEX_KEY_READ", __func__, __LINE__); | ||
5041 | xc_parse_push(XC_INST_READ); | ||
5042 | break; | ||
5043 | case DC_LEX_OP_ASSIGN: | ||
5044 | case DC_LEX_STORE_PUSH: | ||
5045 | dbg_lex("%s:%d LEX_OP_ASSIGN/STORE_PUSH", __func__, __LINE__); | ||
5046 | assign = (t == DC_LEX_OP_ASSIGN); | ||
5047 | inst = assign ? XC_INST_VAR : DC_INST_PUSH_TO_VAR; | ||
5048 | s = zdc_parse_mem(inst, true, assign); | ||
5049 | break; | ||
5050 | case DC_LEX_LOAD: | ||
5051 | case DC_LEX_LOAD_POP: | ||
5052 | dbg_lex("%s:%d LEX_OP_LOAD[_POP]", __func__, __LINE__); | ||
5053 | inst = t == DC_LEX_LOAD_POP ? DC_INST_PUSH_VAR : DC_INST_LOAD; | ||
5054 | s = zdc_parse_mem(inst, true, false); | ||
5055 | break; | ||
5056 | case DC_LEX_STORE_IBASE: | ||
5057 | case DC_LEX_STORE_SCALE: | ||
5058 | case DC_LEX_STORE_OBASE: | ||
5059 | dbg_lex("%s:%d LEX_OP_STORE_I/OBASE/SCALE", __func__, __LINE__); | ||
5060 | inst = t - DC_LEX_STORE_IBASE + XC_INST_IBASE; | ||
5061 | s = zdc_parse_mem(inst, false, true); | ||
5062 | break; | ||
5063 | default: | ||
5064 | dbg_lex_done("%s:%d done (bad token)", __func__, __LINE__); | ||
5065 | RETURN_STATUS(bc_error_bad_token()); | ||
5066 | } | ||
5067 | |||
5068 | if (!s && get_token) s = zxc_lex_next(); | ||
5069 | |||
5070 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
5071 | RETURN_STATUS(s); | ||
5072 | } | ||
5073 | #define zdc_parse_token(...) (zdc_parse_token(__VA_ARGS__) COMMA_SUCCESS) | ||
5074 | |||
5075 | static BC_STATUS zdc_parse_expr(void) | ||
5076 | { | ||
5077 | BcParse *p = &G.prs; | ||
5078 | int i; | ||
5079 | |||
5080 | if (p->lex == XC_LEX_NLINE) | ||
5081 | RETURN_STATUS(zxc_lex_next()); | ||
5082 | |||
5083 | i = (int)p->lex - (int)XC_LEX_OP_POWER; | ||
5084 | if (i >= 0) { | ||
5085 | BcInst inst = dc_LEX_to_INST[i]; | ||
5086 | if (inst != DC_INST_INVALID) { | ||
5087 | xc_parse_push(inst); | ||
5088 | RETURN_STATUS(zxc_lex_next()); | ||
5089 | } | ||
5090 | } | ||
5091 | RETURN_STATUS(zdc_parse_token(p->lex)); | ||
5092 | } | ||
5093 | #define zdc_parse_expr(...) (zdc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) | ||
5094 | |||
5095 | static BC_STATUS zdc_parse_exprs_until_eof(void) | ||
5096 | { | ||
5097 | BcParse *p = &G.prs; | ||
5098 | dbg_lex_enter("%s:%d entered, p->lex:%d", __func__, __LINE__, p->lex); | ||
5099 | while (p->lex != XC_LEX_EOF) { | ||
5100 | BcStatus s = zdc_parse_expr(); | ||
5101 | if (s) RETURN_STATUS(s); | ||
5102 | } | ||
5103 | |||
5104 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
5105 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
5106 | } | ||
5107 | #define zdc_parse_exprs_until_eof(...) (zdc_parse_exprs_until_eof(__VA_ARGS__) COMMA_SUCCESS) | ||
5108 | |||
5109 | #endif // ENABLE_DC | ||
5110 | |||
5111 | // | ||
5112 | // Execution engine | ||
5113 | // | ||
5114 | |||
5115 | #define BC_PROG_STR(n) (!(n)->num && !(n)->cap) | ||
5116 | #define BC_PROG_NUM(r, n) \ | ||
5117 | ((r)->t != XC_RESULT_ARRAY && (r)->t != XC_RESULT_STR && !BC_PROG_STR(n)) | ||
5118 | |||
5119 | #define STACK_HAS_MORE_THAN(s, n) ((s)->len > ((size_t)(n))) | ||
5120 | #define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n))) | ||
5121 | |||
5122 | static BcVec* xc_program_search(char *id, bool var) | ||
5123 | { | ||
5124 | BcId e, *ptr; | ||
5125 | BcVec *v, *map; | ||
5126 | size_t i; | ||
5127 | int new; | ||
5128 | |||
5129 | v = var ? &G.prog.vars : &G.prog.arrs; | ||
5130 | map = var ? &G.prog.var_map : &G.prog.arr_map; | ||
5131 | |||
5132 | e.name = id; | ||
5133 | e.idx = v->len; | ||
5134 | new = bc_map_insert(map, &e, &i); // 1 if insertion was successful | ||
5135 | |||
5136 | if (new) { | ||
5137 | BcVec v2; | ||
5138 | bc_array_init(&v2, var); | ||
5139 | bc_vec_push(v, &v2); | ||
5140 | } | ||
5141 | |||
5142 | ptr = bc_vec_item(map, i); | ||
5143 | if (new) ptr->name = xstrdup(e.name); | ||
5144 | return bc_vec_item(v, ptr->idx); | ||
5145 | } | ||
5146 | |||
5147 | // 'num' need not be initialized on entry | ||
5148 | static BC_STATUS zxc_program_num(BcResult *r, BcNum **num) | ||
5149 | { | ||
5150 | switch (r->t) { | ||
5151 | case XC_RESULT_STR: | ||
5152 | case XC_RESULT_TEMP: | ||
5153 | IF_BC(case BC_RESULT_VOID:) | ||
5154 | case XC_RESULT_IBASE: | ||
5155 | case XC_RESULT_SCALE: | ||
5156 | case XC_RESULT_OBASE: | ||
5157 | *num = &r->d.n; | ||
5158 | break; | ||
5159 | case XC_RESULT_CONSTANT: { | ||
5160 | BcStatus s; | ||
5161 | char *str; | ||
5162 | size_t len; | ||
5163 | |||
5164 | str = *xc_program_const(r->d.id.idx); | ||
5165 | len = strlen(str); | ||
5166 | |||
5167 | bc_num_init(&r->d.n, len); | ||
5168 | |||
5169 | s = zxc_num_parse(&r->d.n, str, G.prog.ib_t); | ||
5170 | if (s) { | ||
5171 | bc_num_free(&r->d.n); | ||
5172 | RETURN_STATUS(s); | ||
5173 | } | ||
5174 | *num = &r->d.n; | ||
5175 | r->t = XC_RESULT_TEMP; | ||
5176 | break; | ||
5177 | } | ||
5178 | case XC_RESULT_VAR: | ||
5179 | case XC_RESULT_ARRAY: | ||
5180 | case XC_RESULT_ARRAY_ELEM: { | ||
5181 | BcVec *v; | ||
5182 | void *p; | ||
5183 | v = xc_program_search(r->d.id.name, r->t == XC_RESULT_VAR); | ||
5184 | // dc variables are all stacks, so here we have this: | ||
5185 | p = bc_vec_top(v); | ||
5186 | // TODO: eliminate these stacks for bc-only config? | ||
5187 | if (r->t == XC_RESULT_ARRAY_ELEM) { | ||
5188 | v = p; | ||
5189 | if (v->len <= r->d.id.idx) | ||
5190 | bc_array_expand(v, r->d.id.idx + 1); | ||
5191 | *num = bc_vec_item(v, r->d.id.idx); | ||
5192 | } else { | ||
5193 | *num = p; | ||
5194 | } | ||
5195 | break; | ||
5196 | } | ||
5197 | #if ENABLE_BC | ||
5198 | case BC_RESULT_LAST: | ||
5199 | *num = &G.prog.last; | ||
5200 | break; | ||
5201 | case BC_RESULT_ONE: | ||
5202 | *num = &G.prog.one; | ||
5203 | break; | ||
5204 | #endif | ||
5205 | #if SANITY_CHECKS | ||
5206 | default: | ||
5207 | // Testing the theory that dc does not reach LAST/ONE | ||
5208 | bb_error_msg_and_die("BUG:%d", r->t); | ||
5209 | #endif | ||
5210 | } | ||
5211 | |||
5212 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
5213 | } | ||
5214 | #define zxc_program_num(...) (zxc_program_num(__VA_ARGS__) COMMA_SUCCESS) | ||
5215 | |||
5216 | static BC_STATUS zxc_program_binOpPrep(BcResult **l, BcNum **ln, | ||
5217 | BcResult **r, BcNum **rn, bool assign) | ||
5218 | { | ||
5219 | BcStatus s; | ||
5220 | BcResultType lt, rt; | ||
5221 | |||
5222 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 1)) | ||
5223 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
5224 | |||
5225 | *r = bc_vec_item_rev(&G.prog.results, 0); | ||
5226 | *l = bc_vec_item_rev(&G.prog.results, 1); | ||
5227 | |||
5228 | s = zxc_program_num(*l, ln); | ||
5229 | if (s) RETURN_STATUS(s); | ||
5230 | s = zxc_program_num(*r, rn); | ||
5231 | if (s) RETURN_STATUS(s); | ||
5232 | |||
5233 | lt = (*l)->t; | ||
5234 | rt = (*r)->t; | ||
5235 | |||
5236 | // We run this again under these conditions in case any vector has been | ||
5237 | // reallocated out from under the BcNums or arrays we had. | ||
5238 | if (lt == rt && (lt == XC_RESULT_VAR || lt == XC_RESULT_ARRAY_ELEM)) { | ||
5239 | s = zxc_program_num(*l, ln); | ||
5240 | if (s) RETURN_STATUS(s); | ||
5241 | } | ||
5242 | |||
5243 | if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != XC_RESULT_VAR)) | ||
5244 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
5245 | if (!assign && !BC_PROG_NUM((*r), (*ln))) | ||
5246 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
5247 | |||
5248 | RETURN_STATUS(s); | ||
5249 | } | ||
5250 | #define zxc_program_binOpPrep(...) (zxc_program_binOpPrep(__VA_ARGS__) COMMA_SUCCESS) | ||
5251 | |||
5252 | static void xc_program_binOpRetire(BcResult *r) | ||
5253 | { | ||
5254 | r->t = XC_RESULT_TEMP; | ||
5255 | bc_vec_pop(&G.prog.results); | ||
5256 | bc_result_pop_and_push(r); | ||
5257 | } | ||
5258 | |||
5259 | // Note: *r and *n need not be initialized by caller | ||
5260 | static BC_STATUS zxc_program_prep(BcResult **r, BcNum **n) | ||
5261 | { | ||
5262 | BcStatus s; | ||
5263 | |||
5264 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
5265 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
5266 | *r = bc_vec_top(&G.prog.results); | ||
5267 | |||
5268 | s = zxc_program_num(*r, n); | ||
5269 | if (s) RETURN_STATUS(s); | ||
5270 | |||
5271 | if (!BC_PROG_NUM((*r), (*n))) | ||
5272 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
5273 | |||
5274 | RETURN_STATUS(s); | ||
5275 | } | ||
5276 | #define zxc_program_prep(...) (zxc_program_prep(__VA_ARGS__) COMMA_SUCCESS) | ||
5277 | |||
5278 | static void xc_program_retire(BcResult *r, BcResultType t) | ||
5279 | { | ||
5280 | r->t = t; | ||
5281 | bc_result_pop_and_push(r); | ||
5282 | } | ||
5283 | |||
5284 | static BC_STATUS zxc_program_op(char inst) | ||
5285 | { | ||
5286 | BcStatus s; | ||
5287 | BcResult *opd1, *opd2, res; | ||
5288 | BcNum *n1, *n2; | ||
5289 | |||
5290 | s = zxc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false); | ||
5291 | if (s) RETURN_STATUS(s); | ||
5292 | bc_num_init_DEF_SIZE(&res.d.n); | ||
5293 | |||
5294 | s = BC_STATUS_SUCCESS; | ||
5295 | IF_ERROR_RETURN_POSSIBLE(s =) zxc_program_ops[inst - XC_INST_POWER](n1, n2, &res.d.n, G.prog.scale); | ||
5296 | if (s) goto err; | ||
5297 | xc_program_binOpRetire(&res); | ||
5298 | |||
5299 | RETURN_STATUS(s); | ||
5300 | err: | ||
5301 | bc_num_free(&res.d.n); | ||
5302 | RETURN_STATUS(s); | ||
5303 | } | ||
5304 | #define zxc_program_op(...) (zxc_program_op(__VA_ARGS__) COMMA_SUCCESS) | ||
5305 | |||
5306 | static BC_STATUS zxc_program_read(void) | ||
5307 | { | ||
5308 | BcStatus s; | ||
5309 | BcParse sv_parse; | ||
5310 | BcVec buf; | ||
5311 | BcInstPtr ip; | ||
5312 | BcFunc *f; | ||
5313 | |||
5314 | bc_char_vec_init(&buf); | ||
5315 | xc_read_line(&buf, stdin); | ||
5316 | |||
5317 | f = xc_program_func(BC_PROG_READ); | ||
5318 | bc_vec_pop_all(&f->code); | ||
5319 | |||
5320 | sv_parse = G.prs; // struct copy | ||
5321 | xc_parse_create(BC_PROG_READ); | ||
5322 | //G.err_line = G.prs.lex_line = 1; - not needed, error line info is not printed for read() | ||
5323 | |||
5324 | s = zxc_parse_text_init(buf.v); | ||
5325 | if (s) goto exec_err; | ||
5326 | if (IS_BC) { | ||
5327 | IF_BC(s = zbc_parse_expr(0)); | ||
5328 | } else { | ||
5329 | IF_DC(s = zdc_parse_exprs_until_eof()); | ||
5330 | } | ||
5331 | if (s) goto exec_err; | ||
5332 | if (G.prs.lex != XC_LEX_NLINE && G.prs.lex != XC_LEX_EOF) { | ||
5333 | s = bc_error_at("bad read() expression"); | ||
5334 | goto exec_err; | ||
5335 | } | ||
5336 | xc_parse_push(XC_INST_RET); | ||
5337 | |||
5338 | ip.func = BC_PROG_READ; | ||
5339 | ip.inst_idx = 0; | ||
5340 | bc_vec_push(&G.prog.exestack, &ip); | ||
5341 | |||
5342 | exec_err: | ||
5343 | xc_parse_free(); | ||
5344 | G.prs = sv_parse; // struct copy | ||
5345 | bc_vec_free(&buf); | ||
5346 | RETURN_STATUS(s); | ||
5347 | } | ||
5348 | #define zxc_program_read(...) (zxc_program_read(__VA_ARGS__) COMMA_SUCCESS) | ||
5349 | |||
5350 | static size_t xc_program_index(char *code, size_t *bgn) | ||
5351 | { | ||
5352 | unsigned char *bytes = (void*)(code + *bgn); | ||
5353 | unsigned amt; | ||
5354 | unsigned i; | ||
5355 | size_t res; | ||
5356 | |||
5357 | amt = *bytes++; | ||
5358 | if (amt < SMALL_INDEX_LIMIT) { | ||
5359 | *bgn += 1; | ||
5360 | return amt; | ||
5361 | } | ||
5362 | amt -= (SMALL_INDEX_LIMIT - 1); // amt is 1 or more here | ||
5363 | *bgn += amt + 1; | ||
5364 | |||
5365 | res = 0; | ||
5366 | i = 0; | ||
5367 | do { | ||
5368 | res |= (size_t)(*bytes++) << i; | ||
5369 | i += 8; | ||
5370 | } while (--amt != 0); | ||
5371 | |||
5372 | return res; | ||
5373 | } | ||
5374 | |||
5375 | static char *xc_program_name(char *code, size_t *bgn) | ||
5376 | { | ||
5377 | code += *bgn; | ||
5378 | *bgn += strlen(code) + 1; | ||
5379 | |||
5380 | return xstrdup(code); | ||
5381 | } | ||
5382 | |||
5383 | static void xc_program_printString(const char *str) | ||
5384 | { | ||
5385 | #if ENABLE_DC | ||
5386 | if (!str[0] && IS_DC) { | ||
5387 | // Example: echo '[]ap' | dc | ||
5388 | // should print two bytes: 0x00, 0x0A | ||
5389 | bb_putchar('\0'); | ||
5390 | return; | ||
5391 | } | ||
5392 | #endif | ||
5393 | while (*str) { | ||
5394 | char c = *str++; | ||
5395 | if (c == '\\') { | ||
5396 | static const char esc[] ALIGN1 = "nabfrt""e\\"; | ||
5397 | char *n; | ||
5398 | |||
5399 | c = *str++; | ||
5400 | n = strchr(esc, c); // note: c can be NUL | ||
5401 | if (!n) { | ||
5402 | // Just print the backslash and following character | ||
5403 | bb_putchar('\\'); | ||
5404 | ++G.prog.nchars; | ||
5405 | } else { | ||
5406 | if (n - esc == 0) // "\n" ? | ||
5407 | G.prog.nchars = SIZE_MAX; | ||
5408 | c = "\n\a\b\f\r\t""\\\\""\\"[n - esc]; | ||
5409 | // n a b f r t e \ \<end of line> | ||
5410 | } | ||
5411 | } | ||
5412 | putchar(c); | ||
5413 | ++G.prog.nchars; | ||
5414 | } | ||
5415 | } | ||
5416 | |||
5417 | static void bc_num_printNewline(void) | ||
5418 | { | ||
5419 | if (G.prog.nchars == G.prog.len - 1) { | ||
5420 | bb_putchar('\\'); | ||
5421 | bb_putchar('\n'); | ||
5422 | G.prog.nchars = 0; | ||
5423 | } | ||
5424 | } | ||
5425 | |||
5426 | #if ENABLE_DC | ||
5427 | static FAST_FUNC void dc_num_printChar(size_t num, size_t width, bool radix) | ||
5428 | { | ||
5429 | (void) radix; | ||
5430 | bb_putchar((char) num); | ||
5431 | G.prog.nchars += width; | ||
5432 | } | ||
5433 | #endif | ||
5434 | |||
5435 | static FAST_FUNC void bc_num_printDigits(size_t num, size_t width, bool radix) | ||
5436 | { | ||
5437 | size_t exp, pow; | ||
5438 | |||
5439 | bc_num_printNewline(); | ||
5440 | bb_putchar(radix ? '.' : ' '); | ||
5441 | ++G.prog.nchars; | ||
5442 | |||
5443 | bc_num_printNewline(); | ||
5444 | for (exp = 0, pow = 1; exp < width - 1; ++exp, pow *= 10) | ||
5445 | continue; | ||
5446 | |||
5447 | for (exp = 0; exp < width; pow /= 10, ++G.prog.nchars, ++exp) { | ||
5448 | size_t dig; | ||
5449 | bc_num_printNewline(); | ||
5450 | dig = num / pow; | ||
5451 | num -= dig * pow; | ||
5452 | bb_putchar(((char) dig) + '0'); | ||
5453 | } | ||
5454 | } | ||
5455 | |||
5456 | static FAST_FUNC void bc_num_printHex(size_t num, size_t width, bool radix) | ||
5457 | { | ||
5458 | if (radix) { | ||
5459 | bc_num_printNewline(); | ||
5460 | bb_putchar('.'); | ||
5461 | G.prog.nchars++; | ||
5462 | } | ||
5463 | |||
5464 | bc_num_printNewline(); | ||
5465 | bb_putchar(bb_hexdigits_upcase[num]); | ||
5466 | G.prog.nchars += width; | ||
5467 | } | ||
5468 | |||
5469 | static void bc_num_printDecimal(BcNum *n) | ||
5470 | { | ||
5471 | size_t i, rdx = n->rdx - 1; | ||
5472 | |||
5473 | if (n->neg) { | ||
5474 | bb_putchar('-'); | ||
5475 | G.prog.nchars++; | ||
5476 | } | ||
5477 | |||
5478 | for (i = n->len - 1; i < n->len; --i) | ||
5479 | bc_num_printHex((size_t) n->num[i], 1, i == rdx); | ||
5480 | } | ||
5481 | |||
5482 | typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC; | ||
5483 | |||
5484 | static BC_STATUS zxc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print) | ||
5485 | { | ||
5486 | BcStatus s; | ||
5487 | BcVec stack; | ||
5488 | BcNum base; | ||
5489 | BcDig base_digs[ULONG_NUM_BUFSIZE]; | ||
5490 | BcNum intp, fracp, digit, frac_len; | ||
5491 | unsigned long dig, *ptr; | ||
5492 | size_t i; | ||
5493 | bool radix; | ||
5494 | |||
5495 | if (n->len == 0) { | ||
5496 | print(0, width, false); | ||
5497 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
5498 | } | ||
5499 | |||
5500 | bc_vec_init(&stack, sizeof(long), NULL); | ||
5501 | bc_num_init(&intp, n->len); | ||
5502 | bc_num_init(&fracp, n->rdx); | ||
5503 | bc_num_init(&digit, width); | ||
5504 | bc_num_init(&frac_len, BC_NUM_INT(n)); | ||
5505 | bc_num_copy(&intp, n); | ||
5506 | bc_num_one(&frac_len); | ||
5507 | base.cap = ARRAY_SIZE(base_digs); | ||
5508 | base.num = base_digs; | ||
5509 | bc_num_ulong2num(&base, base_t); | ||
5510 | |||
5511 | bc_num_truncate(&intp, intp.rdx); | ||
5512 | s = zbc_num_sub(n, &intp, &fracp, 0); | ||
5513 | if (s) goto err; | ||
5514 | |||
5515 | while (intp.len != 0) { | ||
5516 | s = zbc_num_divmod(&intp, &base, &intp, &digit, 0); | ||
5517 | if (s) goto err; | ||
5518 | s = zbc_num_ulong(&digit, &dig); | ||
5519 | if (s) goto err; | ||
5520 | bc_vec_push(&stack, &dig); | ||
5521 | } | ||
5522 | |||
5523 | for (i = 0; i < stack.len; ++i) { | ||
5524 | ptr = bc_vec_item_rev(&stack, i); | ||
5525 | print(*ptr, width, false); | ||
5526 | } | ||
5527 | |||
5528 | if (!n->rdx) goto err; | ||
5529 | |||
5530 | for (radix = true; frac_len.len <= n->rdx; radix = false) { | ||
5531 | s = zbc_num_mul(&fracp, &base, &fracp, n->rdx); | ||
5532 | if (s) goto err; | ||
5533 | s = zbc_num_ulong(&fracp, &dig); | ||
5534 | if (s) goto err; | ||
5535 | bc_num_ulong2num(&intp, dig); | ||
5536 | s = zbc_num_sub(&fracp, &intp, &fracp, 0); | ||
5537 | if (s) goto err; | ||
5538 | print(dig, width, radix); | ||
5539 | s = zbc_num_mul(&frac_len, &base, &frac_len, 0); | ||
5540 | if (s) goto err; | ||
5541 | } | ||
5542 | err: | ||
5543 | bc_num_free(&frac_len); | ||
5544 | bc_num_free(&digit); | ||
5545 | bc_num_free(&fracp); | ||
5546 | bc_num_free(&intp); | ||
5547 | bc_vec_free(&stack); | ||
5548 | RETURN_STATUS(s); | ||
5549 | } | ||
5550 | #define zxc_num_printNum(...) (zxc_num_printNum(__VA_ARGS__) COMMA_SUCCESS) | ||
5551 | |||
5552 | static BC_STATUS zxc_num_printBase(BcNum *n) | ||
5553 | { | ||
5554 | BcStatus s; | ||
5555 | size_t width; | ||
5556 | BcNumDigitOp print; | ||
5557 | bool neg = n->neg; | ||
5558 | |||
5559 | if (neg) { | ||
5560 | bb_putchar('-'); | ||
5561 | G.prog.nchars++; | ||
5562 | } | ||
5563 | |||
5564 | n->neg = false; | ||
5565 | |||
5566 | if (G.prog.ob_t <= 16) { | ||
5567 | width = 1; | ||
5568 | print = bc_num_printHex; | ||
5569 | } else { | ||
5570 | unsigned i = G.prog.ob_t - 1; | ||
5571 | width = 0; | ||
5572 | for (;;) { | ||
5573 | width++; | ||
5574 | i /= 10; | ||
5575 | if (i == 0) | ||
5576 | break; | ||
5577 | } | ||
5578 | print = bc_num_printDigits; | ||
5579 | } | ||
5580 | |||
5581 | s = zxc_num_printNum(n, G.prog.ob_t, width, print); | ||
5582 | n->neg = neg; | ||
5583 | |||
5584 | RETURN_STATUS(s); | ||
5585 | } | ||
5586 | #define zxc_num_printBase(...) (zxc_num_printBase(__VA_ARGS__) COMMA_SUCCESS) | ||
5587 | |||
5588 | static BC_STATUS zxc_num_print(BcNum *n, bool newline) | ||
5589 | { | ||
5590 | BcStatus s = BC_STATUS_SUCCESS; | ||
5591 | |||
5592 | bc_num_printNewline(); | ||
5593 | |||
5594 | if (n->len == 0) { | ||
5595 | bb_putchar('0'); | ||
5596 | ++G.prog.nchars; | ||
5597 | } else if (G.prog.ob_t == 10) | ||
5598 | bc_num_printDecimal(n); | ||
5599 | else | ||
5600 | s = zxc_num_printBase(n); | ||
5601 | |||
5602 | if (newline) { | ||
5603 | bb_putchar('\n'); | ||
5604 | G.prog.nchars = 0; | ||
5605 | } | ||
5606 | |||
5607 | RETURN_STATUS(s); | ||
5608 | } | ||
5609 | #define zxc_num_print(...) (zxc_num_print(__VA_ARGS__) COMMA_SUCCESS) | ||
5610 | |||
5611 | #if !ENABLE_DC | ||
5612 | // for bc, idx is always 0 | ||
5613 | #define xc_program_print(inst, idx) \ | ||
5614 | xc_program_print(inst) | ||
5615 | #endif | ||
5616 | static BC_STATUS xc_program_print(char inst, size_t idx) | ||
5617 | { | ||
5618 | BcStatus s; | ||
5619 | BcResult *r; | ||
5620 | BcNum *num; | ||
5621 | IF_NOT_DC(size_t idx = 0); | ||
5622 | |||
5623 | if (!STACK_HAS_MORE_THAN(&G.prog.results, idx)) | ||
5624 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
5625 | |||
5626 | r = bc_vec_item_rev(&G.prog.results, idx); | ||
5627 | #if ENABLE_BC | ||
5628 | if (inst == XC_INST_PRINT && r->t == BC_RESULT_VOID) | ||
5629 | // void function's result on stack, ignore | ||
5630 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
5631 | #endif | ||
5632 | s = zxc_program_num(r, &num); | ||
5633 | if (s) RETURN_STATUS(s); | ||
5634 | |||
5635 | if (BC_PROG_NUM(r, num)) { | ||
5636 | s = zxc_num_print(num, /*newline:*/ inst == XC_INST_PRINT); | ||
5637 | #if ENABLE_BC | ||
5638 | if (!s && IS_BC) bc_num_copy(&G.prog.last, num); | ||
5639 | #endif | ||
5640 | } else { | ||
5641 | char *str; | ||
5642 | |||
5643 | idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : num->rdx; | ||
5644 | str = *xc_program_str(idx); | ||
5645 | |||
5646 | if (inst == XC_INST_PRINT_STR) { | ||
5647 | char *nl; | ||
5648 | G.prog.nchars += printf("%s", str); | ||
5649 | nl = strrchr(str, '\n'); | ||
5650 | if (nl) | ||
5651 | G.prog.nchars = strlen(nl + 1); | ||
5652 | } else { | ||
5653 | xc_program_printString(str); | ||
5654 | if (inst == XC_INST_PRINT) | ||
5655 | bb_putchar('\n'); | ||
5656 | } | ||
5657 | } | ||
5658 | |||
5659 | if (!s && inst != XC_INST_PRINT) bc_vec_pop(&G.prog.results); | ||
5660 | |||
5661 | RETURN_STATUS(s); | ||
5662 | } | ||
5663 | #define zxc_program_print(...) (xc_program_print(__VA_ARGS__) COMMA_SUCCESS) | ||
5664 | |||
5665 | static BC_STATUS zxc_program_negate(void) | ||
5666 | { | ||
5667 | BcStatus s; | ||
5668 | BcResult res, *ptr; | ||
5669 | BcNum *num; | ||
5670 | |||
5671 | s = zxc_program_prep(&ptr, &num); | ||
5672 | if (s) RETURN_STATUS(s); | ||
5673 | |||
5674 | bc_num_init(&res.d.n, num->len); | ||
5675 | bc_num_copy(&res.d.n, num); | ||
5676 | if (res.d.n.len) res.d.n.neg = !res.d.n.neg; | ||
5677 | |||
5678 | xc_program_retire(&res, XC_RESULT_TEMP); | ||
5679 | |||
5680 | RETURN_STATUS(s); | ||
5681 | } | ||
5682 | #define zxc_program_negate(...) (zxc_program_negate(__VA_ARGS__) COMMA_SUCCESS) | ||
5683 | |||
5684 | static BC_STATUS zxc_program_logical(char inst) | ||
5685 | { | ||
5686 | BcStatus s; | ||
5687 | BcResult *opd1, *opd2, res; | ||
5688 | BcNum *n1, *n2; | ||
5689 | ssize_t cond; | ||
5690 | |||
5691 | s = zxc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false); | ||
5692 | if (s) RETURN_STATUS(s); | ||
5693 | |||
5694 | bc_num_init_DEF_SIZE(&res.d.n); | ||
5695 | |||
5696 | if (inst == XC_INST_BOOL_AND) | ||
5697 | cond = bc_num_cmp(n1, &G.prog.zero) && bc_num_cmp(n2, &G.prog.zero); | ||
5698 | else if (inst == XC_INST_BOOL_OR) | ||
5699 | cond = bc_num_cmp(n1, &G.prog.zero) || bc_num_cmp(n2, &G.prog.zero); | ||
5700 | else { | ||
5701 | cond = bc_num_cmp(n1, n2); | ||
5702 | switch (inst) { | ||
5703 | case XC_INST_REL_EQ: | ||
5704 | cond = (cond == 0); | ||
5705 | break; | ||
5706 | case XC_INST_REL_LE: | ||
5707 | cond = (cond <= 0); | ||
5708 | break; | ||
5709 | case XC_INST_REL_GE: | ||
5710 | cond = (cond >= 0); | ||
5711 | break; | ||
5712 | case XC_INST_REL_LT: | ||
5713 | cond = (cond < 0); | ||
5714 | break; | ||
5715 | case XC_INST_REL_GT: | ||
5716 | cond = (cond > 0); | ||
5717 | break; | ||
5718 | default: // = case XC_INST_REL_NE: | ||
5719 | //cond = (cond != 0); - not needed | ||
5720 | break; | ||
5721 | } | ||
5722 | } | ||
5723 | |||
5724 | if (cond) bc_num_one(&res.d.n); | ||
5725 | //else bc_num_zero(&res.d.n); - already is | ||
5726 | |||
5727 | xc_program_binOpRetire(&res); | ||
5728 | |||
5729 | RETURN_STATUS(s); | ||
5730 | } | ||
5731 | #define zxc_program_logical(...) (zxc_program_logical(__VA_ARGS__) COMMA_SUCCESS) | ||
5732 | |||
5733 | #if ENABLE_DC | ||
5734 | static BC_STATUS zdc_program_assignStr(BcResult *r, BcVec *v, bool push) | ||
5735 | { | ||
5736 | BcNum n2; | ||
5737 | BcResult res; | ||
5738 | |||
5739 | memset(&n2, 0, sizeof(BcNum)); | ||
5740 | n2.rdx = res.d.id.idx = r->d.id.idx; | ||
5741 | res.t = XC_RESULT_STR; | ||
5742 | |||
5743 | if (!push) { | ||
5744 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 1)) | ||
5745 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
5746 | bc_vec_pop(v); | ||
5747 | bc_vec_pop(&G.prog.results); | ||
5748 | } | ||
5749 | |||
5750 | bc_result_pop_and_push(&res); | ||
5751 | bc_vec_push(v, &n2); | ||
5752 | |||
5753 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
5754 | } | ||
5755 | #define zdc_program_assignStr(...) (zdc_program_assignStr(__VA_ARGS__) COMMA_SUCCESS) | ||
5756 | #endif // ENABLE_DC | ||
5757 | |||
5758 | static BC_STATUS zxc_program_popResultAndCopyToVar(char *name, bool var) | ||
5759 | { | ||
5760 | BcStatus s; | ||
5761 | BcResult *ptr, r; | ||
5762 | BcVec *v; | ||
5763 | BcNum *n; | ||
5764 | |||
5765 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
5766 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
5767 | |||
5768 | ptr = bc_vec_top(&G.prog.results); | ||
5769 | if ((ptr->t == XC_RESULT_ARRAY) != !var) | ||
5770 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
5771 | v = xc_program_search(name, var); | ||
5772 | |||
5773 | #if ENABLE_DC | ||
5774 | if (ptr->t == XC_RESULT_STR && !var) | ||
5775 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
5776 | if (ptr->t == XC_RESULT_STR) | ||
5777 | RETURN_STATUS(zdc_program_assignStr(ptr, v, true)); | ||
5778 | #endif | ||
5779 | |||
5780 | s = zxc_program_num(ptr, &n); | ||
5781 | if (s) RETURN_STATUS(s); | ||
5782 | |||
5783 | // Do this once more to make sure that pointers were not invalidated. | ||
5784 | v = xc_program_search(name, var); | ||
5785 | |||
5786 | if (var) { | ||
5787 | bc_num_init_DEF_SIZE(&r.d.n); | ||
5788 | bc_num_copy(&r.d.n, n); | ||
5789 | } else { | ||
5790 | bc_array_init(&r.d.v, true); | ||
5791 | bc_array_copy(&r.d.v, (BcVec *) n); | ||
5792 | } | ||
5793 | |||
5794 | bc_vec_push(v, &r.d); | ||
5795 | bc_vec_pop(&G.prog.results); | ||
5796 | |||
5797 | RETURN_STATUS(s); | ||
5798 | } | ||
5799 | #define zxc_program_popResultAndCopyToVar(...) (zxc_program_popResultAndCopyToVar(__VA_ARGS__) COMMA_SUCCESS) | ||
5800 | |||
5801 | static BC_STATUS zxc_program_assign(char inst) | ||
5802 | { | ||
5803 | BcStatus s; | ||
5804 | BcResult *left, *right, res; | ||
5805 | BcNum *l, *r; | ||
5806 | bool assign = (inst == XC_INST_ASSIGN); | ||
5807 | bool ib, sc; | ||
5808 | |||
5809 | s = zxc_program_binOpPrep(&left, &l, &right, &r, assign); | ||
5810 | if (s) RETURN_STATUS(s); | ||
5811 | |||
5812 | ib = left->t == XC_RESULT_IBASE; | ||
5813 | sc = left->t == XC_RESULT_SCALE; | ||
5814 | |||
5815 | #if ENABLE_DC | ||
5816 | if (right->t == XC_RESULT_STR) { | ||
5817 | BcVec *v; | ||
5818 | |||
5819 | if (left->t != XC_RESULT_VAR) | ||
5820 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
5821 | v = xc_program_search(left->d.id.name, true); | ||
5822 | |||
5823 | RETURN_STATUS(zdc_program_assignStr(right, v, false)); | ||
5824 | } | ||
5825 | #endif | ||
5826 | |||
5827 | if (left->t == XC_RESULT_CONSTANT | ||
5828 | || left->t == XC_RESULT_TEMP | ||
5829 | IF_BC(|| left->t == BC_RESULT_VOID) | ||
5830 | ) { | ||
5831 | RETURN_STATUS(bc_error_bad_assignment()); | ||
5832 | } | ||
5833 | |||
5834 | #if ENABLE_BC | ||
5835 | if (assign) | ||
5836 | bc_num_copy(l, r); | ||
5837 | else { | ||
5838 | s = BC_STATUS_SUCCESS; | ||
5839 | IF_ERROR_RETURN_POSSIBLE(s =) zxc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, G.prog.scale); | ||
5840 | } | ||
5841 | if (s) RETURN_STATUS(s); | ||
5842 | #else | ||
5843 | bc_num_copy(l, r); | ||
5844 | #endif | ||
5845 | |||
5846 | if (ib || sc || left->t == XC_RESULT_OBASE) { | ||
5847 | static const char *const msg[] = { | ||
5848 | "bad ibase; must be [2,16]", //XC_RESULT_IBASE | ||
5849 | "bad obase; must be [2,"BC_MAX_OBASE_STR"]", //XC_RESULT_OBASE | ||
5850 | "bad scale; must be [0,"BC_MAX_SCALE_STR"]", //XC_RESULT_SCALE | ||
5851 | }; | ||
5852 | size_t *ptr; | ||
5853 | size_t max; | ||
5854 | unsigned long val; | ||
5855 | |||
5856 | s = zbc_num_ulong(l, &val); | ||
5857 | if (s) RETURN_STATUS(s); | ||
5858 | s = left->t - XC_RESULT_IBASE; | ||
5859 | if (sc) { | ||
5860 | max = BC_MAX_SCALE; | ||
5861 | ptr = &G.prog.scale; | ||
5862 | } else { | ||
5863 | if (val < 2) | ||
5864 | RETURN_STATUS(bc_error(msg[s])); | ||
5865 | max = ib ? BC_NUM_MAX_IBASE : BC_MAX_OBASE; | ||
5866 | ptr = ib ? &G.prog.ib_t : &G.prog.ob_t; | ||
5867 | } | ||
5868 | |||
5869 | if (val > max) | ||
5870 | RETURN_STATUS(bc_error(msg[s])); | ||
5871 | |||
5872 | *ptr = (size_t) val; | ||
5873 | s = BC_STATUS_SUCCESS; | ||
5874 | } | ||
5875 | |||
5876 | bc_num_init(&res.d.n, l->len); | ||
5877 | bc_num_copy(&res.d.n, l); | ||
5878 | xc_program_binOpRetire(&res); | ||
5879 | |||
5880 | RETURN_STATUS(s); | ||
5881 | } | ||
5882 | #define zxc_program_assign(...) (zxc_program_assign(__VA_ARGS__) COMMA_SUCCESS) | ||
5883 | |||
5884 | #if !ENABLE_DC | ||
5885 | #define xc_program_pushVar(code, bgn, pop, copy) \ | ||
5886 | xc_program_pushVar(code, bgn) | ||
5887 | // for bc, 'pop' and 'copy' are always false | ||
5888 | #endif | ||
5889 | static BC_STATUS xc_program_pushVar(char *code, size_t *bgn, | ||
5890 | bool pop, bool copy) | ||
5891 | { | ||
5892 | BcResult r; | ||
5893 | char *name = xc_program_name(code, bgn); | ||
5894 | |||
5895 | r.t = XC_RESULT_VAR; | ||
5896 | r.d.id.name = name; | ||
5897 | |||
5898 | #if ENABLE_DC | ||
5899 | if (pop || copy) { | ||
5900 | BcVec *v = xc_program_search(name, true); | ||
5901 | BcNum *num = bc_vec_top(v); | ||
5902 | |||
5903 | free(name); | ||
5904 | if (!STACK_HAS_MORE_THAN(v, 1 - copy)) { | ||
5905 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
5906 | } | ||
5907 | |||
5908 | if (!BC_PROG_STR(num)) { | ||
5909 | r.t = XC_RESULT_TEMP; | ||
5910 | bc_num_init_DEF_SIZE(&r.d.n); | ||
5911 | bc_num_copy(&r.d.n, num); | ||
5912 | } else { | ||
5913 | r.t = XC_RESULT_STR; | ||
5914 | r.d.id.idx = num->rdx; | ||
5915 | } | ||
5916 | |||
5917 | if (!copy) bc_vec_pop(v); | ||
5918 | } | ||
5919 | #endif // ENABLE_DC | ||
5920 | |||
5921 | bc_vec_push(&G.prog.results, &r); | ||
5922 | |||
5923 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
5924 | } | ||
5925 | #define zxc_program_pushVar(...) (xc_program_pushVar(__VA_ARGS__) COMMA_SUCCESS) | ||
5926 | |||
5927 | static BC_STATUS zbc_program_pushArray(char *code, size_t *bgn, char inst) | ||
5928 | { | ||
5929 | BcStatus s = BC_STATUS_SUCCESS; | ||
5930 | BcResult r; | ||
5931 | BcNum *num; | ||
5932 | |||
5933 | r.d.id.name = xc_program_name(code, bgn); | ||
5934 | |||
5935 | if (inst == XC_INST_ARRAY) { | ||
5936 | r.t = XC_RESULT_ARRAY; | ||
5937 | bc_vec_push(&G.prog.results, &r); | ||
5938 | } else { | ||
5939 | BcResult *operand; | ||
5940 | unsigned long temp; | ||
5941 | |||
5942 | s = zxc_program_prep(&operand, &num); | ||
5943 | if (s) goto err; | ||
5944 | s = zbc_num_ulong(num, &temp); | ||
5945 | if (s) goto err; | ||
5946 | |||
5947 | if (temp > BC_MAX_DIM) { | ||
5948 | s = bc_error("array too long; must be [1,"BC_MAX_DIM_STR"]"); | ||
5949 | goto err; | ||
5950 | } | ||
5951 | |||
5952 | r.d.id.idx = (size_t) temp; | ||
5953 | xc_program_retire(&r, XC_RESULT_ARRAY_ELEM); | ||
5954 | } | ||
5955 | err: | ||
5956 | if (s) free(r.d.id.name); | ||
5957 | RETURN_STATUS(s); | ||
5958 | } | ||
5959 | #define zbc_program_pushArray(...) (zbc_program_pushArray(__VA_ARGS__) COMMA_SUCCESS) | ||
5960 | |||
5961 | #if ENABLE_BC | ||
5962 | static BC_STATUS zbc_program_incdec(char inst) | ||
5963 | { | ||
5964 | BcStatus s; | ||
5965 | BcResult *ptr, res, copy; | ||
5966 | BcNum *num; | ||
5967 | char inst2 = inst; | ||
5968 | |||
5969 | s = zxc_program_prep(&ptr, &num); | ||
5970 | if (s) RETURN_STATUS(s); | ||
5971 | |||
5972 | if (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST) { | ||
5973 | copy.t = XC_RESULT_TEMP; | ||
5974 | bc_num_init(©.d.n, num->len); | ||
5975 | bc_num_copy(©.d.n, num); | ||
5976 | } | ||
5977 | |||
5978 | res.t = BC_RESULT_ONE; | ||
5979 | inst = (inst == BC_INST_INC_PRE || inst == BC_INST_INC_POST) | ||
5980 | ? BC_INST_ASSIGN_PLUS | ||
5981 | : BC_INST_ASSIGN_MINUS; | ||
5982 | |||
5983 | bc_vec_push(&G.prog.results, &res); | ||
5984 | s = zxc_program_assign(inst); | ||
5985 | if (s) RETURN_STATUS(s); | ||
5986 | |||
5987 | if (inst2 == BC_INST_INC_POST || inst2 == BC_INST_DEC_POST) { | ||
5988 | bc_result_pop_and_push(©); | ||
5989 | } | ||
5990 | |||
5991 | RETURN_STATUS(s); | ||
5992 | } | ||
5993 | #define zbc_program_incdec(...) (zbc_program_incdec(__VA_ARGS__) COMMA_SUCCESS) | ||
5994 | |||
5995 | static BC_STATUS zbc_program_call(char *code, size_t *idx) | ||
5996 | { | ||
5997 | BcInstPtr ip; | ||
5998 | size_t i, nparams; | ||
5999 | BcId *a; | ||
6000 | BcFunc *func; | ||
6001 | |||
6002 | nparams = xc_program_index(code, idx); | ||
6003 | ip.func = xc_program_index(code, idx); | ||
6004 | func = xc_program_func(ip.func); | ||
6005 | |||
6006 | if (func->code.len == 0) { | ||
6007 | RETURN_STATUS(bc_error("undefined function")); | ||
6008 | } | ||
6009 | if (nparams != func->nparams) { | ||
6010 | RETURN_STATUS(bc_error_fmt("function has %u parameters, but called with %u", func->nparams, nparams)); | ||
6011 | } | ||
6012 | ip.inst_idx = 0; | ||
6013 | |||
6014 | for (i = 0; i < nparams; ++i) { | ||
6015 | BcResult *arg; | ||
6016 | BcStatus s; | ||
6017 | |||
6018 | a = bc_vec_item(&func->autos, nparams - 1 - i); | ||
6019 | arg = bc_vec_top(&G.prog.results); | ||
6020 | |||
6021 | if ((!a->idx) != (arg->t == XC_RESULT_ARRAY) // array/variable mismatch | ||
6022 | // || arg->t == XC_RESULT_STR - impossible, f("str") is not a legal syntax (strings are not bc expressions) | ||
6023 | ) { | ||
6024 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
6025 | } | ||
6026 | s = zxc_program_popResultAndCopyToVar(a->name, a->idx); | ||
6027 | if (s) RETURN_STATUS(s); | ||
6028 | } | ||
6029 | |||
6030 | a = bc_vec_item(&func->autos, i); | ||
6031 | for (; i < func->autos.len; i++, a++) { | ||
6032 | BcVec *v; | ||
6033 | |||
6034 | v = xc_program_search(a->name, a->idx); | ||
6035 | if (a->idx) { | ||
6036 | BcNum n2; | ||
6037 | bc_num_init_DEF_SIZE(&n2); | ||
6038 | bc_vec_push(v, &n2); | ||
6039 | } else { | ||
6040 | BcVec v2; | ||
6041 | bc_array_init(&v2, true); | ||
6042 | bc_vec_push(v, &v2); | ||
6043 | } | ||
6044 | } | ||
6045 | |||
6046 | bc_vec_push(&G.prog.exestack, &ip); | ||
6047 | |||
6048 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
6049 | } | ||
6050 | #define zbc_program_call(...) (zbc_program_call(__VA_ARGS__) COMMA_SUCCESS) | ||
6051 | |||
6052 | static BC_STATUS zbc_program_return(char inst) | ||
6053 | { | ||
6054 | BcResult res; | ||
6055 | BcFunc *f; | ||
6056 | BcId *a; | ||
6057 | size_t i; | ||
6058 | BcInstPtr *ip = bc_vec_top(&G.prog.exestack); | ||
6059 | |||
6060 | f = xc_program_func(ip->func); | ||
6061 | |||
6062 | res.t = XC_RESULT_TEMP; | ||
6063 | if (inst == XC_INST_RET) { | ||
6064 | // bc needs this for e.g. RESULT_CONSTANT ("return 5") | ||
6065 | // because bc constants are per-function. | ||
6066 | // TODO: maybe avoid if value is already RESULT_TEMP? | ||
6067 | BcStatus s; | ||
6068 | BcNum *num; | ||
6069 | BcResult *operand = bc_vec_top(&G.prog.results); | ||
6070 | |||
6071 | s = zxc_program_num(operand, &num); | ||
6072 | if (s) RETURN_STATUS(s); | ||
6073 | bc_num_init(&res.d.n, num->len); | ||
6074 | bc_num_copy(&res.d.n, num); | ||
6075 | bc_vec_pop(&G.prog.results); | ||
6076 | } else { | ||
6077 | if (f->voidfunc) | ||
6078 | res.t = BC_RESULT_VOID; | ||
6079 | bc_num_init_DEF_SIZE(&res.d.n); | ||
6080 | //bc_num_zero(&res.d.n); - already is | ||
6081 | } | ||
6082 | bc_vec_push(&G.prog.results, &res); | ||
6083 | |||
6084 | bc_vec_pop(&G.prog.exestack); | ||
6085 | |||
6086 | // We need to pop arguments as well, so this takes that into account. | ||
6087 | a = (void*)f->autos.v; | ||
6088 | for (i = 0; i < f->autos.len; i++, a++) { | ||
6089 | BcVec *v; | ||
6090 | v = xc_program_search(a->name, a->idx); | ||
6091 | bc_vec_pop(v); | ||
6092 | } | ||
6093 | |||
6094 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
6095 | } | ||
6096 | #define zbc_program_return(...) (zbc_program_return(__VA_ARGS__) COMMA_SUCCESS) | ||
6097 | #endif // ENABLE_BC | ||
6098 | |||
6099 | static unsigned long xc_program_scale(BcNum *n) | ||
6100 | { | ||
6101 | return (unsigned long) n->rdx; | ||
6102 | } | ||
6103 | |||
6104 | static unsigned long xc_program_len(BcNum *n) | ||
6105 | { | ||
6106 | size_t len = n->len; | ||
6107 | |||
6108 | if (n->rdx != len) return len; | ||
6109 | for (;;) { | ||
6110 | if (len == 0) break; | ||
6111 | len--; | ||
6112 | if (n->num[len] != 0) break; | ||
6113 | } | ||
6114 | return len; | ||
6115 | } | ||
6116 | |||
6117 | static BC_STATUS zxc_program_builtin(char inst) | ||
6118 | { | ||
6119 | BcStatus s; | ||
6120 | BcResult *opnd; | ||
6121 | BcNum *num; | ||
6122 | BcResult res; | ||
6123 | bool len = (inst == XC_INST_LENGTH); | ||
6124 | |||
6125 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
6126 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6127 | opnd = bc_vec_top(&G.prog.results); | ||
6128 | |||
6129 | s = zxc_program_num(opnd, &num); | ||
6130 | if (s) RETURN_STATUS(s); | ||
6131 | |||
6132 | #if ENABLE_DC | ||
6133 | if (!BC_PROG_NUM(opnd, num) && !len) | ||
6134 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
6135 | #endif | ||
6136 | |||
6137 | bc_num_init_DEF_SIZE(&res.d.n); | ||
6138 | |||
6139 | if (inst == XC_INST_SQRT) | ||
6140 | s = zbc_num_sqrt(num, &res.d.n, G.prog.scale); | ||
6141 | #if ENABLE_BC | ||
6142 | else if (len != 0 && opnd->t == XC_RESULT_ARRAY) { | ||
6143 | bc_num_ulong2num(&res.d.n, (unsigned long) ((BcVec *) num)->len); | ||
6144 | } | ||
6145 | #endif | ||
6146 | #if ENABLE_DC | ||
6147 | else if (len != 0 && !BC_PROG_NUM(opnd, num)) { | ||
6148 | char **str; | ||
6149 | size_t idx = opnd->t == XC_RESULT_STR ? opnd->d.id.idx : num->rdx; | ||
6150 | |||
6151 | str = xc_program_str(idx); | ||
6152 | bc_num_ulong2num(&res.d.n, strlen(*str)); | ||
6153 | } | ||
6154 | #endif | ||
6155 | else { | ||
6156 | bc_num_ulong2num(&res.d.n, len ? xc_program_len(num) : xc_program_scale(num)); | ||
6157 | } | ||
6158 | |||
6159 | xc_program_retire(&res, XC_RESULT_TEMP); | ||
6160 | |||
6161 | RETURN_STATUS(s); | ||
6162 | } | ||
6163 | #define zxc_program_builtin(...) (zxc_program_builtin(__VA_ARGS__) COMMA_SUCCESS) | ||
6164 | |||
6165 | #if ENABLE_DC | ||
6166 | static BC_STATUS zdc_program_divmod(void) | ||
6167 | { | ||
6168 | BcStatus s; | ||
6169 | BcResult *opd1, *opd2, res, res2; | ||
6170 | BcNum *n1, *n2; | ||
6171 | |||
6172 | s = zxc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false); | ||
6173 | if (s) RETURN_STATUS(s); | ||
6174 | |||
6175 | bc_num_init_DEF_SIZE(&res.d.n); | ||
6176 | bc_num_init(&res2.d.n, n2->len); | ||
6177 | |||
6178 | s = zbc_num_divmod(n1, n2, &res2.d.n, &res.d.n, G.prog.scale); | ||
6179 | if (s) goto err; | ||
6180 | |||
6181 | xc_program_binOpRetire(&res2); | ||
6182 | res.t = XC_RESULT_TEMP; | ||
6183 | bc_vec_push(&G.prog.results, &res); | ||
6184 | |||
6185 | RETURN_STATUS(s); | ||
6186 | err: | ||
6187 | bc_num_free(&res2.d.n); | ||
6188 | bc_num_free(&res.d.n); | ||
6189 | RETURN_STATUS(s); | ||
6190 | } | ||
6191 | #define zdc_program_divmod(...) (zdc_program_divmod(__VA_ARGS__) COMMA_SUCCESS) | ||
6192 | |||
6193 | static BC_STATUS zdc_program_modexp(void) | ||
6194 | { | ||
6195 | BcStatus s; | ||
6196 | BcResult *r1, *r2, *r3, res; | ||
6197 | BcNum *n1, *n2, *n3; | ||
6198 | |||
6199 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 2)) | ||
6200 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6201 | s = zxc_program_binOpPrep(&r2, &n2, &r3, &n3, false); | ||
6202 | if (s) RETURN_STATUS(s); | ||
6203 | |||
6204 | r1 = bc_vec_item_rev(&G.prog.results, 2); | ||
6205 | s = zxc_program_num(r1, &n1); | ||
6206 | if (s) RETURN_STATUS(s); | ||
6207 | if (!BC_PROG_NUM(r1, n1)) | ||
6208 | RETURN_STATUS(bc_error_variable_is_wrong_type()); | ||
6209 | |||
6210 | // Make sure that the values have their pointers updated, if necessary. | ||
6211 | if (r1->t == XC_RESULT_VAR || r1->t == XC_RESULT_ARRAY_ELEM) { | ||
6212 | if (r1->t == r2->t) { | ||
6213 | s = zxc_program_num(r2, &n2); | ||
6214 | if (s) RETURN_STATUS(s); | ||
6215 | } | ||
6216 | if (r1->t == r3->t) { | ||
6217 | s = zxc_program_num(r3, &n3); | ||
6218 | if (s) RETURN_STATUS(s); | ||
6219 | } | ||
6220 | } | ||
6221 | |||
6222 | bc_num_init(&res.d.n, n3->len); | ||
6223 | s = zdc_num_modexp(n1, n2, n3, &res.d.n); | ||
6224 | if (s) goto err; | ||
6225 | |||
6226 | bc_vec_pop(&G.prog.results); | ||
6227 | xc_program_binOpRetire(&res); | ||
6228 | |||
6229 | RETURN_STATUS(s); | ||
6230 | err: | ||
6231 | bc_num_free(&res.d.n); | ||
6232 | RETURN_STATUS(s); | ||
6233 | } | ||
6234 | #define zdc_program_modexp(...) (zdc_program_modexp(__VA_ARGS__) COMMA_SUCCESS) | ||
6235 | |||
6236 | static void dc_program_stackLen(void) | ||
6237 | { | ||
6238 | BcResult res; | ||
6239 | size_t len = G.prog.results.len; | ||
6240 | |||
6241 | res.t = XC_RESULT_TEMP; | ||
6242 | |||
6243 | bc_num_init_DEF_SIZE(&res.d.n); | ||
6244 | bc_num_ulong2num(&res.d.n, len); | ||
6245 | bc_vec_push(&G.prog.results, &res); | ||
6246 | } | ||
6247 | |||
6248 | static BC_STATUS zdc_program_asciify(void) | ||
6249 | { | ||
6250 | BcStatus s; | ||
6251 | BcResult *r, res; | ||
6252 | BcNum *num, n; | ||
6253 | char **strs; | ||
6254 | char *str; | ||
6255 | char c; | ||
6256 | size_t idx; | ||
6257 | |||
6258 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
6259 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6260 | |||
6261 | r = bc_vec_top(&G.prog.results); | ||
6262 | s = zxc_program_num(r, &num); | ||
6263 | if (s) RETURN_STATUS(s); | ||
6264 | |||
6265 | if (BC_PROG_NUM(r, num)) { | ||
6266 | unsigned long val; | ||
6267 | BcNum strmb; | ||
6268 | BcDig strmb_digs[ULONG_NUM_BUFSIZE]; | ||
6269 | |||
6270 | bc_num_init_DEF_SIZE(&n); | ||
6271 | bc_num_copy(&n, num); | ||
6272 | bc_num_truncate(&n, n.rdx); | ||
6273 | |||
6274 | strmb.cap = ARRAY_SIZE(strmb_digs); | ||
6275 | strmb.num = strmb_digs; | ||
6276 | bc_num_ulong2num(&strmb, 0x100); | ||
6277 | |||
6278 | s = zbc_num_mod(&n, &strmb, &n, 0); | ||
6279 | if (s) goto num_err; | ||
6280 | s = zbc_num_ulong(&n, &val); | ||
6281 | if (s) goto num_err; | ||
6282 | |||
6283 | c = (char) val; | ||
6284 | |||
6285 | bc_num_free(&n); | ||
6286 | } else { | ||
6287 | char *sp; | ||
6288 | idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : num->rdx; | ||
6289 | sp = *xc_program_str(idx); | ||
6290 | c = sp[0]; | ||
6291 | } | ||
6292 | |||
6293 | strs = (void*)G.prog.strs.v; | ||
6294 | for (idx = 0; idx < G.prog.strs.len; idx++) { | ||
6295 | if (strs[idx][0] == c && strs[idx][1] == '\0') { | ||
6296 | goto dup; | ||
6297 | } | ||
6298 | } | ||
6299 | str = xzalloc(2); | ||
6300 | str[0] = c; | ||
6301 | //str[1] = '\0'; - already is | ||
6302 | bc_vec_push(&G.prog.strs, &str); | ||
6303 | dup: | ||
6304 | res.t = XC_RESULT_STR; | ||
6305 | res.d.id.idx = idx; | ||
6306 | bc_result_pop_and_push(&res); | ||
6307 | |||
6308 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
6309 | num_err: | ||
6310 | bc_num_free(&n); | ||
6311 | RETURN_STATUS(s); | ||
6312 | } | ||
6313 | #define zdc_program_asciify(...) (zdc_program_asciify(__VA_ARGS__) COMMA_SUCCESS) | ||
6314 | |||
6315 | static BC_STATUS zdc_program_printStream(void) | ||
6316 | { | ||
6317 | BcStatus s; | ||
6318 | BcResult *r; | ||
6319 | BcNum *n; | ||
6320 | size_t idx; | ||
6321 | |||
6322 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
6323 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6324 | r = bc_vec_top(&G.prog.results); | ||
6325 | |||
6326 | s = zxc_program_num(r, &n); | ||
6327 | if (s) RETURN_STATUS(s); | ||
6328 | |||
6329 | if (BC_PROG_NUM(r, n)) { | ||
6330 | s = zxc_num_printNum(n, 0x100, 1, dc_num_printChar); | ||
6331 | } else { | ||
6332 | char *str; | ||
6333 | idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : n->rdx; | ||
6334 | str = *xc_program_str(idx); | ||
6335 | fputs(str, stdout); | ||
6336 | } | ||
6337 | |||
6338 | RETURN_STATUS(s); | ||
6339 | } | ||
6340 | #define zdc_program_printStream(...) (zdc_program_printStream(__VA_ARGS__) COMMA_SUCCESS) | ||
6341 | |||
6342 | static BC_STATUS zdc_program_nquit(void) | ||
6343 | { | ||
6344 | BcStatus s; | ||
6345 | BcResult *opnd; | ||
6346 | BcNum *num; | ||
6347 | unsigned long val; | ||
6348 | |||
6349 | s = zxc_program_prep(&opnd, &num); | ||
6350 | if (s) RETURN_STATUS(s); | ||
6351 | s = zbc_num_ulong(num, &val); | ||
6352 | if (s) RETURN_STATUS(s); | ||
6353 | |||
6354 | bc_vec_pop(&G.prog.results); | ||
6355 | |||
6356 | if (G.prog.exestack.len < val) | ||
6357 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6358 | if (G.prog.exestack.len == val) { | ||
6359 | QUIT_OR_RETURN_TO_MAIN; | ||
6360 | } | ||
6361 | |||
6362 | bc_vec_npop(&G.prog.exestack, val); | ||
6363 | |||
6364 | RETURN_STATUS(s); | ||
6365 | } | ||
6366 | #define zdc_program_nquit(...) (zdc_program_nquit(__VA_ARGS__) COMMA_SUCCESS) | ||
6367 | |||
6368 | static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond) | ||
6369 | { | ||
6370 | BcStatus s = BC_STATUS_SUCCESS; | ||
6371 | BcResult *r; | ||
6372 | BcFunc *f; | ||
6373 | BcInstPtr ip; | ||
6374 | size_t fidx, sidx; | ||
6375 | |||
6376 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
6377 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6378 | |||
6379 | r = bc_vec_top(&G.prog.results); | ||
6380 | |||
6381 | if (cond) { | ||
6382 | BcNum *n = n; // for compiler | ||
6383 | bool exec; | ||
6384 | char *name; | ||
6385 | char *then_name = xc_program_name(code, bgn); | ||
6386 | char *else_name = NULL; | ||
6387 | |||
6388 | if (code[*bgn] == '\0') | ||
6389 | (*bgn) += 1; | ||
6390 | else | ||
6391 | else_name = xc_program_name(code, bgn); | ||
6392 | |||
6393 | exec = r->d.n.len != 0; | ||
6394 | name = then_name; | ||
6395 | if (!exec && else_name != NULL) { | ||
6396 | exec = true; | ||
6397 | name = else_name; | ||
6398 | } | ||
6399 | |||
6400 | if (exec) { | ||
6401 | BcVec *v; | ||
6402 | v = xc_program_search(name, true); | ||
6403 | n = bc_vec_top(v); | ||
6404 | } | ||
6405 | |||
6406 | free(then_name); | ||
6407 | free(else_name); | ||
6408 | |||
6409 | if (!exec) goto exit; | ||
6410 | if (!BC_PROG_STR(n)) { | ||
6411 | s = bc_error_variable_is_wrong_type(); | ||
6412 | goto exit; | ||
6413 | } | ||
6414 | |||
6415 | sidx = n->rdx; | ||
6416 | } else { | ||
6417 | if (r->t == XC_RESULT_STR) { | ||
6418 | sidx = r->d.id.idx; | ||
6419 | } else if (r->t == XC_RESULT_VAR) { | ||
6420 | BcNum *n; | ||
6421 | s = zxc_program_num(r, &n); | ||
6422 | if (s || !BC_PROG_STR(n)) goto exit; | ||
6423 | sidx = n->rdx; | ||
6424 | } else | ||
6425 | goto exit; | ||
6426 | } | ||
6427 | |||
6428 | fidx = sidx + BC_PROG_REQ_FUNCS; | ||
6429 | |||
6430 | f = xc_program_func(fidx); | ||
6431 | |||
6432 | if (f->code.len == 0) { | ||
6433 | BcParse sv_parse; | ||
6434 | char *str; | ||
6435 | |||
6436 | sv_parse = G.prs; // struct copy | ||
6437 | xc_parse_create(fidx); | ||
6438 | str = *xc_program_str(sidx); | ||
6439 | s = zxc_parse_text_init(str); | ||
6440 | if (s) goto err; | ||
6441 | |||
6442 | s = zdc_parse_exprs_until_eof(); | ||
6443 | if (s) goto err; | ||
6444 | xc_parse_push(DC_INST_POP_EXEC); | ||
6445 | if (G.prs.lex != XC_LEX_EOF) | ||
6446 | s = bc_error_bad_expression(); | ||
6447 | xc_parse_free(); | ||
6448 | G.prs = sv_parse; // struct copy | ||
6449 | if (s) { | ||
6450 | err: | ||
6451 | bc_vec_pop_all(&f->code); | ||
6452 | goto exit; | ||
6453 | } | ||
6454 | } | ||
6455 | |||
6456 | ip.inst_idx = 0; | ||
6457 | ip.func = fidx; | ||
6458 | |||
6459 | bc_vec_pop(&G.prog.results); | ||
6460 | bc_vec_push(&G.prog.exestack, &ip); | ||
6461 | |||
6462 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
6463 | exit: | ||
6464 | bc_vec_pop(&G.prog.results); | ||
6465 | RETURN_STATUS(s); | ||
6466 | } | ||
6467 | #define zdc_program_execStr(...) (zdc_program_execStr(__VA_ARGS__) COMMA_SUCCESS) | ||
6468 | #endif // ENABLE_DC | ||
6469 | |||
6470 | static void xc_program_pushGlobal(char inst) | ||
6471 | { | ||
6472 | BcResult res; | ||
6473 | unsigned long val; | ||
6474 | |||
6475 | res.t = inst - XC_INST_IBASE + XC_RESULT_IBASE; | ||
6476 | if (inst == XC_INST_IBASE) | ||
6477 | val = (unsigned long) G.prog.ib_t; | ||
6478 | else if (inst == XC_INST_SCALE) | ||
6479 | val = (unsigned long) G.prog.scale; | ||
6480 | else | ||
6481 | val = (unsigned long) G.prog.ob_t; | ||
6482 | |||
6483 | bc_num_init_DEF_SIZE(&res.d.n); | ||
6484 | bc_num_ulong2num(&res.d.n, val); | ||
6485 | bc_vec_push(&G.prog.results, &res); | ||
6486 | } | ||
6487 | |||
6488 | static BC_STATUS zxc_program_exec(void) | ||
6489 | { | ||
6490 | BcResult r, *ptr; | ||
6491 | BcInstPtr *ip = bc_vec_top(&G.prog.exestack); | ||
6492 | BcFunc *func = xc_program_func(ip->func); | ||
6493 | char *code = func->code.v; | ||
6494 | |||
6495 | dbg_exec("func:%zd bytes:%zd ip:%zd results.len:%d", | ||
6496 | ip->func, func->code.len, ip->inst_idx, G.prog.results.len); | ||
6497 | while (ip->inst_idx < func->code.len) { | ||
6498 | BcStatus s = BC_STATUS_SUCCESS; | ||
6499 | char inst = code[ip->inst_idx++]; | ||
6500 | |||
6501 | dbg_exec("inst at %zd:%d results.len:%d", ip->inst_idx - 1, inst, G.prog.results.len); | ||
6502 | switch (inst) { | ||
6503 | case XC_INST_RET: | ||
6504 | if (IS_DC) { // end of '?' reached | ||
6505 | bc_vec_pop(&G.prog.exestack); | ||
6506 | goto read_updated_ip; | ||
6507 | } | ||
6508 | // bc: fall through | ||
6509 | #if ENABLE_BC | ||
6510 | case BC_INST_RET0: | ||
6511 | dbg_exec("BC_INST_RET[0]:"); | ||
6512 | s = zbc_program_return(inst); | ||
6513 | goto read_updated_ip; | ||
6514 | case BC_INST_JUMP_ZERO: { | ||
6515 | BcNum *num; | ||
6516 | bool zero; | ||
6517 | dbg_exec("BC_INST_JUMP_ZERO:"); | ||
6518 | s = zxc_program_prep(&ptr, &num); | ||
6519 | if (s) RETURN_STATUS(s); | ||
6520 | zero = (bc_num_cmp(num, &G.prog.zero) == 0); | ||
6521 | bc_vec_pop(&G.prog.results); | ||
6522 | if (!zero) { | ||
6523 | xc_program_index(code, &ip->inst_idx); | ||
6524 | break; | ||
6525 | } | ||
6526 | // else: fall through | ||
6527 | } | ||
6528 | case BC_INST_JUMP: { | ||
6529 | size_t idx = xc_program_index(code, &ip->inst_idx); | ||
6530 | size_t *addr = bc_vec_item(&func->labels, idx); | ||
6531 | dbg_exec("BC_INST_JUMP: to %ld", (long)*addr); | ||
6532 | ip->inst_idx = *addr; | ||
6533 | break; | ||
6534 | } | ||
6535 | case BC_INST_CALL: | ||
6536 | dbg_exec("BC_INST_CALL:"); | ||
6537 | s = zbc_program_call(code, &ip->inst_idx); | ||
6538 | goto read_updated_ip; | ||
6539 | case BC_INST_INC_PRE: | ||
6540 | case BC_INST_DEC_PRE: | ||
6541 | case BC_INST_INC_POST: | ||
6542 | case BC_INST_DEC_POST: | ||
6543 | dbg_exec("BC_INST_INCDEC:"); | ||
6544 | s = zbc_program_incdec(inst); | ||
6545 | break; | ||
6546 | case BC_INST_HALT: | ||
6547 | dbg_exec("BC_INST_HALT:"); | ||
6548 | QUIT_OR_RETURN_TO_MAIN; | ||
6549 | break; | ||
6550 | case XC_INST_BOOL_OR: | ||
6551 | case XC_INST_BOOL_AND: | ||
6552 | #endif // ENABLE_BC | ||
6553 | case XC_INST_REL_EQ: | ||
6554 | case XC_INST_REL_LE: | ||
6555 | case XC_INST_REL_GE: | ||
6556 | case XC_INST_REL_NE: | ||
6557 | case XC_INST_REL_LT: | ||
6558 | case XC_INST_REL_GT: | ||
6559 | dbg_exec("BC_INST_BOOL:"); | ||
6560 | s = zxc_program_logical(inst); | ||
6561 | break; | ||
6562 | case XC_INST_READ: | ||
6563 | dbg_exec("XC_INST_READ:"); | ||
6564 | s = zxc_program_read(); | ||
6565 | goto read_updated_ip; | ||
6566 | case XC_INST_VAR: | ||
6567 | dbg_exec("XC_INST_VAR:"); | ||
6568 | s = zxc_program_pushVar(code, &ip->inst_idx, false, false); | ||
6569 | break; | ||
6570 | case XC_INST_ARRAY_ELEM: | ||
6571 | case XC_INST_ARRAY: | ||
6572 | dbg_exec("XC_INST_ARRAY[_ELEM]:"); | ||
6573 | s = zbc_program_pushArray(code, &ip->inst_idx, inst); | ||
6574 | break; | ||
6575 | #if ENABLE_BC | ||
6576 | case BC_INST_LAST: | ||
6577 | dbg_exec("BC_INST_LAST:"); | ||
6578 | r.t = BC_RESULT_LAST; | ||
6579 | bc_vec_push(&G.prog.results, &r); | ||
6580 | break; | ||
6581 | #endif | ||
6582 | case XC_INST_IBASE: | ||
6583 | case XC_INST_OBASE: | ||
6584 | case XC_INST_SCALE: | ||
6585 | dbg_exec("XC_INST_internalvar(%d):", inst - XC_INST_IBASE); | ||
6586 | xc_program_pushGlobal(inst); | ||
6587 | break; | ||
6588 | case XC_INST_SCALE_FUNC: | ||
6589 | case XC_INST_LENGTH: | ||
6590 | case XC_INST_SQRT: | ||
6591 | dbg_exec("BC_INST_builtin:"); | ||
6592 | s = zxc_program_builtin(inst); | ||
6593 | break; | ||
6594 | case XC_INST_NUM: | ||
6595 | dbg_exec("XC_INST_NUM:"); | ||
6596 | r.t = XC_RESULT_CONSTANT; | ||
6597 | r.d.id.idx = xc_program_index(code, &ip->inst_idx); | ||
6598 | bc_vec_push(&G.prog.results, &r); | ||
6599 | break; | ||
6600 | case XC_INST_POP: | ||
6601 | dbg_exec("XC_INST_POP:"); | ||
6602 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
6603 | s = bc_error_stack_has_too_few_elements(); | ||
6604 | else | ||
6605 | bc_vec_pop(&G.prog.results); | ||
6606 | break; | ||
6607 | case XC_INST_PRINT: | ||
6608 | case XC_INST_PRINT_POP: | ||
6609 | case XC_INST_PRINT_STR: | ||
6610 | dbg_exec("XC_INST_PRINTxyz(%d):", inst - XC_INST_PRINT); | ||
6611 | s = zxc_program_print(inst, 0); | ||
6612 | break; | ||
6613 | case XC_INST_STR: | ||
6614 | dbg_exec("XC_INST_STR:"); | ||
6615 | r.t = XC_RESULT_STR; | ||
6616 | r.d.id.idx = xc_program_index(code, &ip->inst_idx); | ||
6617 | bc_vec_push(&G.prog.results, &r); | ||
6618 | break; | ||
6619 | case XC_INST_POWER: | ||
6620 | case XC_INST_MULTIPLY: | ||
6621 | case XC_INST_DIVIDE: | ||
6622 | case XC_INST_MODULUS: | ||
6623 | case XC_INST_PLUS: | ||
6624 | case XC_INST_MINUS: | ||
6625 | dbg_exec("BC_INST_binaryop:"); | ||
6626 | s = zxc_program_op(inst); | ||
6627 | break; | ||
6628 | case XC_INST_BOOL_NOT: { | ||
6629 | BcNum *num; | ||
6630 | dbg_exec("XC_INST_BOOL_NOT:"); | ||
6631 | s = zxc_program_prep(&ptr, &num); | ||
6632 | if (s) RETURN_STATUS(s); | ||
6633 | bc_num_init_DEF_SIZE(&r.d.n); | ||
6634 | if (bc_num_cmp(num, &G.prog.zero) == 0) | ||
6635 | bc_num_one(&r.d.n); | ||
6636 | //else bc_num_zero(&r.d.n); - already is | ||
6637 | xc_program_retire(&r, XC_RESULT_TEMP); | ||
6638 | break; | ||
6639 | } | ||
6640 | case XC_INST_NEG: | ||
6641 | dbg_exec("XC_INST_NEG:"); | ||
6642 | s = zxc_program_negate(); | ||
6643 | break; | ||
6644 | #if ENABLE_BC | ||
6645 | case BC_INST_ASSIGN_POWER: | ||
6646 | case BC_INST_ASSIGN_MULTIPLY: | ||
6647 | case BC_INST_ASSIGN_DIVIDE: | ||
6648 | case BC_INST_ASSIGN_MODULUS: | ||
6649 | case BC_INST_ASSIGN_PLUS: | ||
6650 | case BC_INST_ASSIGN_MINUS: | ||
6651 | #endif | ||
6652 | case XC_INST_ASSIGN: | ||
6653 | dbg_exec("BC_INST_ASSIGNxyz:"); | ||
6654 | s = zxc_program_assign(inst); | ||
6655 | break; | ||
6656 | #if ENABLE_DC | ||
6657 | case DC_INST_POP_EXEC: | ||
6658 | dbg_exec("DC_INST_POP_EXEC:"); | ||
6659 | bc_vec_pop(&G.prog.exestack); | ||
6660 | goto read_updated_ip; | ||
6661 | case DC_INST_MODEXP: | ||
6662 | dbg_exec("DC_INST_MODEXP:"); | ||
6663 | s = zdc_program_modexp(); | ||
6664 | break; | ||
6665 | case DC_INST_DIVMOD: | ||
6666 | dbg_exec("DC_INST_DIVMOD:"); | ||
6667 | s = zdc_program_divmod(); | ||
6668 | break; | ||
6669 | case DC_INST_EXECUTE: | ||
6670 | case DC_INST_EXEC_COND: | ||
6671 | dbg_exec("DC_INST_EXEC[_COND]:"); | ||
6672 | s = zdc_program_execStr(code, &ip->inst_idx, inst == DC_INST_EXEC_COND); | ||
6673 | goto read_updated_ip; | ||
6674 | case DC_INST_PRINT_STACK: { | ||
6675 | size_t idx; | ||
6676 | dbg_exec("DC_INST_PRINT_STACK:"); | ||
6677 | for (idx = 0; idx < G.prog.results.len; ++idx) { | ||
6678 | s = zxc_program_print(XC_INST_PRINT, idx); | ||
6679 | if (s) break; | ||
6680 | } | ||
6681 | break; | ||
6682 | } | ||
6683 | case DC_INST_CLEAR_STACK: | ||
6684 | dbg_exec("DC_INST_CLEAR_STACK:"); | ||
6685 | bc_vec_pop_all(&G.prog.results); | ||
6686 | break; | ||
6687 | case DC_INST_STACK_LEN: | ||
6688 | dbg_exec("DC_INST_STACK_LEN:"); | ||
6689 | dc_program_stackLen(); | ||
6690 | break; | ||
6691 | case DC_INST_DUPLICATE: | ||
6692 | dbg_exec("DC_INST_DUPLICATE:"); | ||
6693 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) | ||
6694 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6695 | ptr = bc_vec_top(&G.prog.results); | ||
6696 | dc_result_copy(&r, ptr); | ||
6697 | bc_vec_push(&G.prog.results, &r); | ||
6698 | break; | ||
6699 | case DC_INST_SWAP: { | ||
6700 | BcResult *ptr2; | ||
6701 | dbg_exec("DC_INST_SWAP:"); | ||
6702 | if (!STACK_HAS_MORE_THAN(&G.prog.results, 1)) | ||
6703 | RETURN_STATUS(bc_error_stack_has_too_few_elements()); | ||
6704 | ptr = bc_vec_item_rev(&G.prog.results, 0); | ||
6705 | ptr2 = bc_vec_item_rev(&G.prog.results, 1); | ||
6706 | memcpy(&r, ptr, sizeof(BcResult)); | ||
6707 | memcpy(ptr, ptr2, sizeof(BcResult)); | ||
6708 | memcpy(ptr2, &r, sizeof(BcResult)); | ||
6709 | break; | ||
6710 | } | ||
6711 | case DC_INST_ASCIIFY: | ||
6712 | dbg_exec("DC_INST_ASCIIFY:"); | ||
6713 | s = zdc_program_asciify(); | ||
6714 | break; | ||
6715 | case DC_INST_PRINT_STREAM: | ||
6716 | dbg_exec("DC_INST_PRINT_STREAM:"); | ||
6717 | s = zdc_program_printStream(); | ||
6718 | break; | ||
6719 | case DC_INST_LOAD: | ||
6720 | case DC_INST_PUSH_VAR: { | ||
6721 | bool copy = inst == DC_INST_LOAD; | ||
6722 | s = zxc_program_pushVar(code, &ip->inst_idx, true, copy); | ||
6723 | break; | ||
6724 | } | ||
6725 | case DC_INST_PUSH_TO_VAR: { | ||
6726 | char *name = xc_program_name(code, &ip->inst_idx); | ||
6727 | s = zxc_program_popResultAndCopyToVar(name, true); | ||
6728 | free(name); | ||
6729 | break; | ||
6730 | } | ||
6731 | case DC_INST_QUIT: | ||
6732 | dbg_exec("DC_INST_QUIT:"); | ||
6733 | if (G.prog.exestack.len <= 2) | ||
6734 | QUIT_OR_RETURN_TO_MAIN; | ||
6735 | bc_vec_npop(&G.prog.exestack, 2); | ||
6736 | goto read_updated_ip; | ||
6737 | case DC_INST_NQUIT: | ||
6738 | dbg_exec("DC_INST_NQUIT:"); | ||
6739 | s = zdc_program_nquit(); | ||
6740 | //goto read_updated_ip; - just fall through to it | ||
6741 | #endif // ENABLE_DC | ||
6742 | read_updated_ip: | ||
6743 | // Instruction stack has changed, read new pointers | ||
6744 | ip = bc_vec_top(&G.prog.exestack); | ||
6745 | func = xc_program_func(ip->func); | ||
6746 | code = func->code.v; | ||
6747 | dbg_exec("func:%zd bytes:%zd ip:%zd", ip->func, func->code.len, ip->inst_idx); | ||
6748 | } | ||
6749 | |||
6750 | if (s || G_interrupt) { | ||
6751 | xc_program_reset(); | ||
6752 | RETURN_STATUS(s); | ||
6753 | } | ||
6754 | |||
6755 | fflush_and_check(); | ||
6756 | } | ||
6757 | |||
6758 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
6759 | } | ||
6760 | #define zxc_program_exec(...) (zxc_program_exec(__VA_ARGS__) COMMA_SUCCESS) | ||
6761 | |||
6762 | static unsigned xc_vm_envLen(const char *var) | ||
6763 | { | ||
6764 | char *lenv; | ||
6765 | unsigned len; | ||
6766 | |||
6767 | lenv = getenv(var); | ||
6768 | len = BC_NUM_PRINT_WIDTH; | ||
6769 | if (!lenv) return len; | ||
6770 | |||
6771 | len = bb_strtou(lenv, NULL, 10) - 1; | ||
6772 | if (errno || len < 2 || len >= INT_MAX) | ||
6773 | len = BC_NUM_PRINT_WIDTH; | ||
6774 | |||
6775 | return len; | ||
6776 | } | ||
6777 | |||
6778 | static BC_STATUS zxc_vm_process(const char *text) | ||
6779 | { | ||
6780 | BcStatus s; | ||
6781 | |||
6782 | dbg_lex_enter("%s:%d entered", __func__, __LINE__); | ||
6783 | s = zxc_parse_text_init(text); // does the first zxc_lex_next() | ||
6784 | if (s) RETURN_STATUS(s); | ||
6785 | |||
6786 | while (G.prs.lex != XC_LEX_EOF) { | ||
6787 | BcInstPtr *ip; | ||
6788 | BcFunc *f; | ||
6789 | |||
6790 | dbg_lex("%s:%d G.prs.lex:%d, parsing...", __func__, __LINE__, G.prs.lex); | ||
6791 | if (IS_BC) { | ||
6792 | #if ENABLE_BC | ||
6793 | s = zbc_parse_stmt_or_funcdef(); | ||
6794 | if (s) goto err; | ||
6795 | |||
6796 | // Check that next token is a correct stmt delimiter - | ||
6797 | // disallows "print 1 print 2" and such. | ||
6798 | if (G.prs.lex != BC_LEX_SCOLON | ||
6799 | && G.prs.lex != XC_LEX_NLINE | ||
6800 | && G.prs.lex != XC_LEX_EOF | ||
6801 | ) { | ||
6802 | bc_error_at("bad statement terminator"); | ||
6803 | goto err; | ||
6804 | } | ||
6805 | // The above logic is fragile. Check these examples: | ||
6806 | // - interactive read() still works | ||
6807 | #endif | ||
6808 | } else { | ||
6809 | #if ENABLE_DC | ||
6810 | s = zdc_parse_expr(); | ||
6811 | #endif | ||
6812 | } | ||
6813 | if (s || G_interrupt) { | ||
6814 | err: | ||
6815 | xc_parse_reset(); // includes xc_program_reset() | ||
6816 | RETURN_STATUS(BC_STATUS_FAILURE); | ||
6817 | } | ||
6818 | |||
6819 | dbg_lex("%s:%d executing...", __func__, __LINE__); | ||
6820 | s = zxc_program_exec(); | ||
6821 | if (s) { | ||
6822 | xc_program_reset(); | ||
6823 | break; | ||
6824 | } | ||
6825 | |||
6826 | ip = (void*)G.prog.exestack.v; | ||
6827 | #if SANITY_CHECKS | ||
6828 | if (G.prog.exestack.len != 1) // should have only main's IP | ||
6829 | bb_error_msg_and_die("BUG:call stack"); | ||
6830 | if (ip->func != BC_PROG_MAIN) | ||
6831 | bb_error_msg_and_die("BUG:not MAIN"); | ||
6832 | #endif | ||
6833 | f = xc_program_func_BC_PROG_MAIN(); | ||
6834 | // bc discards strings, constants and code after each | ||
6835 | // top-level statement in the "main program". | ||
6836 | // This prevents "yes 1 | bc" from growing its memory | ||
6837 | // without bound. This can be done because data stack | ||
6838 | // is empty and thus can't hold any references to | ||
6839 | // strings or constants, there is no generated code | ||
6840 | // which can hold references (after we discard one | ||
6841 | // we just executed). Code of functions can have references, | ||
6842 | // but bc stores function strings/constants in per-function | ||
6843 | // storage. | ||
6844 | if (IS_BC) { | ||
6845 | #if SANITY_CHECKS | ||
6846 | if (G.prog.results.len != 0) // should be empty | ||
6847 | bb_error_msg_and_die("BUG:data stack"); | ||
6848 | #endif | ||
6849 | IF_BC(bc_vec_pop_all(&f->strs);) | ||
6850 | IF_BC(bc_vec_pop_all(&f->consts);) | ||
6851 | // We are at SCOLON/NLINE, skip it: | ||
6852 | s = zxc_lex_next(); | ||
6853 | if (s) goto err; | ||
6854 | } else { | ||
6855 | if (G.prog.results.len == 0 | ||
6856 | && G.prog.vars.len == 0 | ||
6857 | ) { | ||
6858 | // If stack is empty and no registers exist (TODO: or they are all empty), | ||
6859 | // we can get rid of accumulated strings and constants. | ||
6860 | // In this example dc process should not grow | ||
6861 | // its memory consumption with time: | ||
6862 | // yes 1pc | dc | ||
6863 | IF_DC(bc_vec_pop_all(&G.prog.strs);) | ||
6864 | IF_DC(bc_vec_pop_all(&G.prog.consts);) | ||
6865 | } | ||
6866 | // The code is discarded always (below), thus this example | ||
6867 | // should also not grow its memory consumption with time, | ||
6868 | // even though its data stack is not empty: | ||
6869 | // { echo 1; yes dk; } | dc | ||
6870 | } | ||
6871 | // We drop generated and executed code for both bc and dc: | ||
6872 | bc_vec_pop_all(&f->code); | ||
6873 | ip->inst_idx = 0; | ||
6874 | } | ||
6875 | |||
6876 | dbg_lex_done("%s:%d done", __func__, __LINE__); | ||
6877 | RETURN_STATUS(s); | ||
6878 | } | ||
6879 | #define zxc_vm_process(...) (zxc_vm_process(__VA_ARGS__) COMMA_SUCCESS) | ||
6880 | |||
6881 | static BC_STATUS zxc_vm_execute_FILE(FILE *fp, const char *filename) | ||
6882 | { | ||
6883 | // So far bc/dc have no way to include a file from another file, | ||
6884 | // therefore we know G.prs.lex_filename == NULL on entry | ||
6885 | //const char *sv_file; | ||
6886 | BcStatus s; | ||
6887 | |||
6888 | G.prs.lex_filename = filename; | ||
6889 | G.prs.lex_input_fp = fp; | ||
6890 | G.err_line = G.prs.lex_line = 1; | ||
6891 | dbg_lex("p->lex_line reset to 1"); | ||
6892 | |||
6893 | do { | ||
6894 | s = zxc_vm_process(""); | ||
6895 | // We do not stop looping on errors here if reading stdin. | ||
6896 | // Example: start interactive bc and enter "return". | ||
6897 | // It should say "'return' not in a function" | ||
6898 | // but should not exit. | ||
6899 | } while (G.prs.lex_input_fp == stdin); | ||
6900 | G.prs.lex_filename = NULL; | ||
6901 | RETURN_STATUS(s); | ||
6902 | } | ||
6903 | #define zxc_vm_execute_FILE(...) (zxc_vm_execute_FILE(__VA_ARGS__) COMMA_SUCCESS) | ||
6904 | |||
6905 | static BC_STATUS zxc_vm_file(const char *file) | ||
6906 | { | ||
6907 | BcStatus s; | ||
6908 | FILE *fp; | ||
6909 | |||
6910 | fp = xfopen_for_read(file); | ||
6911 | s = zxc_vm_execute_FILE(fp, file); | ||
6912 | fclose(fp); | ||
6913 | |||
6914 | RETURN_STATUS(s); | ||
6915 | } | ||
6916 | #define zxc_vm_file(...) (zxc_vm_file(__VA_ARGS__) COMMA_SUCCESS) | ||
6917 | |||
6918 | #if ENABLE_BC | ||
6919 | static void bc_vm_info(void) | ||
6920 | { | ||
6921 | printf("%s "BB_VER"\n" | ||
6922 | "Adapted from https://github.com/gavinhoward/bc\n" | ||
6923 | "Original code (c) 2018 Gavin D. Howard and contributors\n" | ||
6924 | , applet_name); | ||
6925 | } | ||
6926 | |||
6927 | static void bc_args(char **argv) | ||
6928 | { | ||
6929 | unsigned opts; | ||
6930 | int i; | ||
6931 | |||
6932 | GETOPT_RESET(); | ||
6933 | #if ENABLE_FEATURE_BC_LONG_OPTIONS | ||
6934 | opts = option_mask32 |= getopt32long(argv, "wvsqli", | ||
6935 | "warn\0" No_argument "w" | ||
6936 | "version\0" No_argument "v" | ||
6937 | "standard\0" No_argument "s" | ||
6938 | "quiet\0" No_argument "q" | ||
6939 | "mathlib\0" No_argument "l" | ||
6940 | "interactive\0" No_argument "i" | ||
6941 | ); | ||
6942 | #else | ||
6943 | opts = option_mask32 |= getopt32(argv, "wvsqli"); | ||
6944 | #endif | ||
6945 | if (getenv("POSIXLY_CORRECT")) | ||
6946 | option_mask32 |= BC_FLAG_S; | ||
6947 | |||
6948 | if (opts & BC_FLAG_V) { | ||
6949 | bc_vm_info(); | ||
6950 | exit(0); | ||
6951 | } | ||
6952 | |||
6953 | for (i = optind; argv[i]; ++i) | ||
6954 | bc_vec_push(&G.files, argv + i); | ||
6955 | } | ||
6956 | |||
6957 | static void bc_vm_envArgs(void) | ||
6958 | { | ||
6959 | BcVec v; | ||
6960 | char *buf; | ||
6961 | char *env_args = getenv("BC_ENV_ARGS"); | ||
6962 | |||
6963 | if (!env_args) return; | ||
6964 | |||
6965 | G.env_args = xstrdup(env_args); | ||
6966 | buf = G.env_args; | ||
6967 | |||
6968 | bc_vec_init(&v, sizeof(char *), NULL); | ||
6969 | |||
6970 | while (*(buf = skip_whitespace(buf)) != '\0') { | ||
6971 | bc_vec_push(&v, &buf); | ||
6972 | buf = skip_non_whitespace(buf); | ||
6973 | if (!*buf) | ||
6974 | break; | ||
6975 | *buf++ = '\0'; | ||
6976 | } | ||
6977 | |||
6978 | // NULL terminate, and pass argv[] so that first arg is argv[1] | ||
6979 | if (sizeof(int) == sizeof(char*)) { | ||
6980 | bc_vec_push(&v, &const_int_0); | ||
6981 | } else { | ||
6982 | static char *const nullptr = NULL; | ||
6983 | bc_vec_push(&v, &nullptr); | ||
6984 | } | ||
6985 | bc_args(((char **)v.v) - 1); | ||
6986 | |||
6987 | bc_vec_free(&v); | ||
6988 | } | ||
6989 | |||
6990 | static const char bc_lib[] ALIGN1 = { | ||
6991 | "scale=20" | ||
6992 | "\n" "define e(x){" | ||
6993 | "\n" "auto b,s,n,r,d,i,p,f,v" | ||
6994 | ////////////////"if(x<0)return(1/e(-x))" // and drop 'n' and x<0 logic below | ||
6995 | //^^^^^^^^^^^^^^^^ this would work, and is even more precise than GNU bc: | ||
6996 | //e(-.998896): GNU:.36828580434569428695 | ||
6997 | // above code:.36828580434569428696 | ||
6998 | // actual value:.3682858043456942869594... | ||
6999 | // but for now let's be "GNU compatible" | ||
7000 | "\n" "b=ibase" | ||
7001 | "\n" "ibase=A" | ||
7002 | "\n" "if(x<0){" | ||
7003 | "\n" "n=1" | ||
7004 | "\n" "x=-x" | ||
7005 | "\n" "}" | ||
7006 | "\n" "s=scale" | ||
7007 | "\n" "r=6+s+.44*x" | ||
7008 | "\n" "scale=scale(x)+1" | ||
7009 | "\n" "while(x>1){" | ||
7010 | "\n" "d+=1" | ||
7011 | "\n" "x/=2" | ||
7012 | "\n" "scale+=1" | ||
7013 | "\n" "}" | ||
7014 | "\n" "scale=r" | ||
7015 | "\n" "r=x+1" | ||
7016 | "\n" "p=x" | ||
7017 | "\n" "f=v=1" | ||
7018 | "\n" "for(i=2;v;++i){" | ||
7019 | "\n" "p*=x" | ||
7020 | "\n" "f*=i" | ||
7021 | "\n" "v=p/f" | ||
7022 | "\n" "r+=v" | ||
7023 | "\n" "}" | ||
7024 | "\n" "while(d--)r*=r" | ||
7025 | "\n" "scale=s" | ||
7026 | "\n" "ibase=b" | ||
7027 | "\n" "if(n)return(1/r)" | ||
7028 | "\n" "return(r/1)" | ||
7029 | "\n" "}" | ||
7030 | "\n" "define l(x){" | ||
7031 | "\n" "auto b,s,r,p,a,q,i,v" | ||
7032 | "\n" "b=ibase" | ||
7033 | "\n" "ibase=A" | ||
7034 | "\n" "if(x<=0){" | ||
7035 | "\n" "r=(1-10^scale)/1" | ||
7036 | "\n" "ibase=b" | ||
7037 | "\n" "return(r)" | ||
7038 | "\n" "}" | ||
7039 | "\n" "s=scale" | ||
7040 | "\n" "scale+=6" | ||
7041 | "\n" "p=2" | ||
7042 | "\n" "while(x>=2){" | ||
7043 | "\n" "p*=2" | ||
7044 | "\n" "x=sqrt(x)" | ||
7045 | "\n" "}" | ||
7046 | "\n" "while(x<=.5){" | ||
7047 | "\n" "p*=2" | ||
7048 | "\n" "x=sqrt(x)" | ||
7049 | "\n" "}" | ||
7050 | "\n" "r=a=(x-1)/(x+1)" | ||
7051 | "\n" "q=a*a" | ||
7052 | "\n" "v=1" | ||
7053 | "\n" "for(i=3;v;i+=2){" | ||
7054 | "\n" "a*=q" | ||
7055 | "\n" "v=a/i" | ||
7056 | "\n" "r+=v" | ||
7057 | "\n" "}" | ||
7058 | "\n" "r*=p" | ||
7059 | "\n" "scale=s" | ||
7060 | "\n" "ibase=b" | ||
7061 | "\n" "return(r/1)" | ||
7062 | "\n" "}" | ||
7063 | "\n" "define s(x){" | ||
7064 | "\n" "auto b,s,r,a,q,i" | ||
7065 | "\n" "if(x<0)return(-s(-x))" | ||
7066 | "\n" "b=ibase" | ||
7067 | "\n" "ibase=A" | ||
7068 | "\n" "s=scale" | ||
7069 | "\n" "scale=1.1*s+2" | ||
7070 | "\n" "a=a(1)" | ||
7071 | "\n" "scale=0" | ||
7072 | "\n" "q=(x/a+2)/4" | ||
7073 | "\n" "x-=4*q*a" | ||
7074 | "\n" "if(q%2)x=-x" | ||
7075 | "\n" "scale=s+2" | ||
7076 | "\n" "r=a=x" | ||
7077 | "\n" "q=-x*x" | ||
7078 | "\n" "for(i=3;a;i+=2){" | ||
7079 | "\n" "a*=q/(i*(i-1))" | ||
7080 | "\n" "r+=a" | ||
7081 | "\n" "}" | ||
7082 | "\n" "scale=s" | ||
7083 | "\n" "ibase=b" | ||
7084 | "\n" "return(r/1)" | ||
7085 | "\n" "}" | ||
7086 | "\n" "define c(x){" | ||
7087 | "\n" "auto b,s" | ||
7088 | "\n" "b=ibase" | ||
7089 | "\n" "ibase=A" | ||
7090 | "\n" "s=scale" | ||
7091 | "\n" "scale*=1.2" | ||
7092 | "\n" "x=s(2*a(1)+x)" | ||
7093 | "\n" "scale=s" | ||
7094 | "\n" "ibase=b" | ||
7095 | "\n" "return(x/1)" | ||
7096 | "\n" "}" | ||
7097 | "\n" "define a(x){" | ||
7098 | "\n" "auto b,s,r,n,a,m,t,f,i,u" | ||
7099 | "\n" "b=ibase" | ||
7100 | "\n" "ibase=A" | ||
7101 | "\n" "n=1" | ||
7102 | "\n" "if(x<0){" | ||
7103 | "\n" "n=-1" | ||
7104 | "\n" "x=-x" | ||
7105 | "\n" "}" | ||
7106 | "\n" "if(scale<65){" | ||
7107 | "\n" "if(x==1)return(.7853981633974483096156608458198757210492923498437764552437361480/n)" | ||
7108 | "\n" "if(x==.2)return(.1973955598498807583700497651947902934475851037878521015176889402/n)" | ||
7109 | "\n" "}" | ||
7110 | "\n" "s=scale" | ||
7111 | "\n" "if(x>.2){" | ||
7112 | "\n" "scale+=5" | ||
7113 | "\n" "a=a(.2)" | ||
7114 | "\n" "}" | ||
7115 | "\n" "scale=s+3" | ||
7116 | "\n" "while(x>.2){" | ||
7117 | "\n" "m+=1" | ||
7118 | "\n" "x=(x-.2)/(1+.2*x)" | ||
7119 | "\n" "}" | ||
7120 | "\n" "r=u=x" | ||
7121 | "\n" "f=-x*x" | ||
7122 | "\n" "t=1" | ||
7123 | "\n" "for(i=3;t;i+=2){" | ||
7124 | "\n" "u*=f" | ||
7125 | "\n" "t=u/i" | ||
7126 | "\n" "r+=t" | ||
7127 | "\n" "}" | ||
7128 | "\n" "scale=s" | ||
7129 | "\n" "ibase=b" | ||
7130 | "\n" "return((m*a+r)/n)" | ||
7131 | "\n" "}" | ||
7132 | "\n" "define j(n,x){" | ||
7133 | "\n" "auto b,s,o,a,i,v,f" | ||
7134 | "\n" "b=ibase" | ||
7135 | "\n" "ibase=A" | ||
7136 | "\n" "s=scale" | ||
7137 | "\n" "scale=0" | ||
7138 | "\n" "n/=1" | ||
7139 | "\n" "if(n<0){" | ||
7140 | "\n" "n=-n" | ||
7141 | "\n" "o=n%2" | ||
7142 | "\n" "}" | ||
7143 | "\n" "a=1" | ||
7144 | "\n" "for(i=2;i<=n;++i)a*=i" | ||
7145 | "\n" "scale=1.5*s" | ||
7146 | "\n" "a=(x^n)/2^n/a" | ||
7147 | "\n" "r=v=1" | ||
7148 | "\n" "f=-x*x/4" | ||
7149 | "\n" "scale+=length(a)-scale(a)" | ||
7150 | "\n" "for(i=1;v;++i){" | ||
7151 | "\n" "v=v*f/i/(n+i)" | ||
7152 | "\n" "r+=v" | ||
7153 | "\n" "}" | ||
7154 | "\n" "scale=s" | ||
7155 | "\n" "ibase=b" | ||
7156 | "\n" "if(o)a=-a" | ||
7157 | "\n" "return(a*r/1)" | ||
7158 | "\n" "}" | ||
7159 | }; | ||
7160 | #endif // ENABLE_BC | ||
7161 | |||
7162 | static BC_STATUS zxc_vm_exec(void) | ||
7163 | { | ||
7164 | char **fname; | ||
7165 | BcStatus s; | ||
7166 | size_t i; | ||
7167 | |||
7168 | #if ENABLE_BC | ||
7169 | if (option_mask32 & BC_FLAG_L) { | ||
7170 | // We know that internal library is not buggy, | ||
7171 | // thus error checking is normally disabled. | ||
7172 | # define DEBUG_LIB 0 | ||
7173 | s = zxc_vm_process(bc_lib); | ||
7174 | if (DEBUG_LIB && s) RETURN_STATUS(s); | ||
7175 | } | ||
7176 | #endif | ||
7177 | |||
7178 | s = BC_STATUS_SUCCESS; | ||
7179 | fname = (void*)G.files.v; | ||
7180 | for (i = 0; i < G.files.len; i++) { | ||
7181 | s = zxc_vm_file(*fname++); | ||
7182 | if (ENABLE_FEATURE_CLEAN_UP && !G_ttyin && s) { | ||
7183 | // Debug config, non-interactive mode: | ||
7184 | // return all the way back to main. | ||
7185 | // Non-debug builds do not come here | ||
7186 | // in non-interactive mode, they exit. | ||
7187 | RETURN_STATUS(s); | ||
7188 | } | ||
7189 | } | ||
7190 | |||
7191 | if (IS_BC || (option_mask32 & BC_FLAG_I)) | ||
7192 | s = zxc_vm_execute_FILE(stdin, /*filename:*/ NULL); | ||
7193 | |||
7194 | RETURN_STATUS(s); | ||
7195 | } | ||
7196 | #define zxc_vm_exec(...) (zxc_vm_exec(__VA_ARGS__) COMMA_SUCCESS) | ||
7197 | |||
7198 | #if ENABLE_FEATURE_CLEAN_UP | ||
7199 | static void xc_program_free(void) | ||
7200 | { | ||
7201 | bc_vec_free(&G.prog.fns); | ||
7202 | IF_BC(bc_vec_free(&G.prog.fn_map);) | ||
7203 | bc_vec_free(&G.prog.vars); | ||
7204 | bc_vec_free(&G.prog.var_map); | ||
7205 | bc_vec_free(&G.prog.arrs); | ||
7206 | bc_vec_free(&G.prog.arr_map); | ||
7207 | IF_DC(bc_vec_free(&G.prog.strs);) | ||
7208 | IF_DC(bc_vec_free(&G.prog.consts);) | ||
7209 | bc_vec_free(&G.prog.results); | ||
7210 | bc_vec_free(&G.prog.exestack); | ||
7211 | IF_BC(bc_num_free(&G.prog.last);) | ||
7212 | //IF_BC(bc_num_free(&G.prog.zero);) | ||
7213 | IF_BC(bc_num_free(&G.prog.one);) | ||
7214 | bc_vec_free(&G.input_buffer); | ||
7215 | } | ||
7216 | #endif | ||
7217 | |||
7218 | static void xc_program_init(void) | ||
7219 | { | ||
7220 | BcInstPtr ip; | ||
7221 | |||
7222 | // memset(&G.prog, 0, sizeof(G.prog)); - already is | ||
7223 | memset(&ip, 0, sizeof(BcInstPtr)); | ||
7224 | |||
7225 | // G.prog.nchars = G.prog.scale = 0; - already is | ||
7226 | G.prog.ib_t = 10; | ||
7227 | G.prog.ob_t = 10; | ||
7228 | |||
7229 | IF_BC(bc_num_init_DEF_SIZE(&G.prog.last);) | ||
7230 | //IF_BC(bc_num_zero(&G.prog.last);) - already is | ||
7231 | |||
7232 | //bc_num_init_DEF_SIZE(&G.prog.zero); - not needed | ||
7233 | //bc_num_zero(&G.prog.zero); - already is | ||
7234 | |||
7235 | IF_BC(bc_num_init_DEF_SIZE(&G.prog.one);) | ||
7236 | IF_BC(bc_num_one(&G.prog.one);) | ||
7237 | |||
7238 | bc_vec_init(&G.prog.fns, sizeof(BcFunc), bc_func_free); | ||
7239 | IF_BC(bc_vec_init(&G.prog.fn_map, sizeof(BcId), bc_id_free);) | ||
7240 | |||
7241 | if (IS_BC) { | ||
7242 | // Names are chosen simply to be distinct and never match | ||
7243 | // a valid function name (and be short) | ||
7244 | IF_BC(bc_program_addFunc(xstrdup(""))); // func #0: main | ||
7245 | IF_BC(bc_program_addFunc(xstrdup("1"))); // func #1: for read() | ||
7246 | } else { | ||
7247 | // in dc, functions have no names | ||
7248 | xc_program_add_fn(); | ||
7249 | xc_program_add_fn(); | ||
7250 | } | ||
7251 | |||
7252 | bc_vec_init(&G.prog.vars, sizeof(BcVec), bc_vec_free); | ||
7253 | bc_vec_init(&G.prog.var_map, sizeof(BcId), bc_id_free); | ||
7254 | |||
7255 | bc_vec_init(&G.prog.arrs, sizeof(BcVec), bc_vec_free); | ||
7256 | bc_vec_init(&G.prog.arr_map, sizeof(BcId), bc_id_free); | ||
7257 | |||
7258 | IF_DC(bc_vec_init(&G.prog.strs, sizeof(char *), bc_string_free);) | ||
7259 | IF_DC(bc_vec_init(&G.prog.consts, sizeof(char *), bc_string_free);) | ||
7260 | bc_vec_init(&G.prog.results, sizeof(BcResult), bc_result_free); | ||
7261 | bc_vec_init(&G.prog.exestack, sizeof(BcInstPtr), NULL); | ||
7262 | bc_vec_push(&G.prog.exestack, &ip); | ||
7263 | |||
7264 | bc_char_vec_init(&G.input_buffer); | ||
7265 | } | ||
7266 | |||
7267 | static int xc_vm_init(const char *env_len) | ||
7268 | { | ||
7269 | G.prog.len = xc_vm_envLen(env_len); | ||
7270 | #if ENABLE_FEATURE_EDITING | ||
7271 | G.line_input_state = new_line_input_t(DO_HISTORY); | ||
7272 | #endif | ||
7273 | bc_vec_init(&G.files, sizeof(char *), NULL); | ||
7274 | |||
7275 | xc_program_init(); | ||
7276 | IF_BC(if (IS_BC) bc_vm_envArgs();) | ||
7277 | xc_parse_create(BC_PROG_MAIN); | ||
7278 | |||
7279 | //TODO: in GNU bc, the check is (isatty(0) && isatty(1)), | ||
7280 | //-i option unconditionally enables this regardless of isatty(): | ||
7281 | if (isatty(0)) { | ||
7282 | #if ENABLE_FEATURE_BC_INTERACTIVE | ||
7283 | G_ttyin = 1; | ||
7284 | // With SA_RESTART, most system calls will restart | ||
7285 | // (IOW: they won't fail with EINTR). | ||
7286 | // In particular, this means ^C won't cause | ||
7287 | // stdout to get into "error state" if SIGINT hits | ||
7288 | // within write() syscall. | ||
7289 | // | ||
7290 | // The downside is that ^C while tty input is taken | ||
7291 | // will only be handled after [Enter] since read() | ||
7292 | // from stdin is not interrupted by ^C either, | ||
7293 | // it restarts, thus fgetc() does not return on ^C. | ||
7294 | // (This problem manifests only if line editing is disabled) | ||
7295 | signal_SA_RESTART_empty_mask(SIGINT, record_signo); | ||
7296 | |||
7297 | // Without SA_RESTART, this exhibits a bug: | ||
7298 | // "while (1) print 1" and try ^C-ing it. | ||
7299 | // Intermittently, instead of returning to input line, | ||
7300 | // you'll get "output error: Interrupted system call" | ||
7301 | // and exit. | ||
7302 | //signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); | ||
7303 | #endif | ||
7304 | return 1; // "tty" | ||
7305 | } | ||
7306 | return 0; // "not a tty" | ||
7307 | } | ||
7308 | |||
7309 | static BcStatus xc_vm_run(void) | ||
7310 | { | ||
7311 | BcStatus st = zxc_vm_exec(); | ||
7312 | #if ENABLE_FEATURE_CLEAN_UP | ||
7313 | if (G_exiting) // it was actually "halt" or "quit" | ||
7314 | st = EXIT_SUCCESS; | ||
7315 | |||
7316 | bc_vec_free(&G.files); | ||
7317 | xc_program_free(); | ||
7318 | xc_parse_free(); | ||
7319 | free(G.env_args); | ||
7320 | # if ENABLE_FEATURE_EDITING | ||
7321 | free_line_input_t(G.line_input_state); | ||
7322 | # endif | ||
7323 | FREE_G(); | ||
7324 | #endif | ||
7325 | dbg_exec("exiting with exitcode %d", st); | ||
7326 | return st; | ||
7327 | } | ||
7328 | |||
7329 | #if ENABLE_BC | ||
7330 | int bc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
7331 | int bc_main(int argc UNUSED_PARAM, char **argv) | ||
7332 | { | ||
7333 | int is_tty; | ||
7334 | |||
7335 | INIT_G(); | ||
7336 | |||
7337 | is_tty = xc_vm_init("BC_LINE_LENGTH"); | ||
7338 | |||
7339 | bc_args(argv); | ||
7340 | |||
7341 | if (is_tty && !(option_mask32 & BC_FLAG_Q)) | ||
7342 | bc_vm_info(); | ||
7343 | |||
7344 | return xc_vm_run(); | ||
7345 | } | ||
7346 | #endif | ||
7347 | |||
7348 | #if ENABLE_DC | ||
7349 | int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
7350 | int dc_main(int argc UNUSED_PARAM, char **argv) | ||
7351 | { | ||
7352 | int noscript; | ||
7353 | |||
7354 | INIT_G(); | ||
7355 | |||
7356 | // TODO: dc (GNU bc 1.07.1) 1.4.1 seems to use width | ||
7357 | // 1 char wider than bc from the same package. | ||
7358 | // Both default width, and xC_LINE_LENGTH=N are wider: | ||
7359 | // "DC_LINE_LENGTH=5 dc -e'123456 p'" prints: | ||
7360 | // |1234\ | | ||
7361 | // |56 | | ||
7362 | // "echo '123456' | BC_LINE_LENGTH=5 bc" prints: | ||
7363 | // |123\ | | ||
7364 | // |456 | | ||
7365 | // Do the same, or it's a bug? | ||
7366 | xc_vm_init("DC_LINE_LENGTH"); | ||
7367 | |||
7368 | // Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs | ||
7369 | noscript = BC_FLAG_I; | ||
7370 | for (;;) { | ||
7371 | int n = getopt(argc, argv, "e:f:x"); | ||
7372 | if (n <= 0) | ||
7373 | break; | ||
7374 | switch (n) { | ||
7375 | case 'e': | ||
7376 | noscript = 0; | ||
7377 | n = zxc_vm_process(optarg); | ||
7378 | if (n) return n; | ||
7379 | break; | ||
7380 | case 'f': | ||
7381 | noscript = 0; | ||
7382 | n = zxc_vm_file(optarg); | ||
7383 | if (n) return n; | ||
7384 | break; | ||
7385 | case 'x': | ||
7386 | option_mask32 |= DC_FLAG_X; | ||
7387 | break; | ||
7388 | default: | ||
7389 | bb_show_usage(); | ||
7390 | } | ||
7391 | } | ||
7392 | argv += optind; | ||
7393 | |||
7394 | while (*argv) { | ||
7395 | noscript = 0; | ||
7396 | bc_vec_push(&G.files, argv++); | ||
7397 | } | ||
7398 | |||
7399 | option_mask32 |= noscript; // set BC_FLAG_I if we need to interpret stdin | ||
7400 | |||
7401 | return xc_vm_run(); | ||
7402 | } | ||
7403 | #endif | ||
7404 | |||
7405 | #endif // DC_BIG | ||
diff --git a/miscutils/beep.c b/miscutils/beep.c index 0c8a8225e..92faa1cd5 100644 --- a/miscutils/beep.c +++ b/miscutils/beep.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config BEEP | 9 | //config:config BEEP |
10 | //config: bool "beep (3 kb)" | 10 | //config: bool "beep (2.4 kb)" |
11 | //config: default y | 11 | //config: default y |
12 | //config: select PLATFORM_LINUX | 12 | //config: select PLATFORM_LINUX |
13 | //config: help | 13 | //config: help |
diff --git a/miscutils/chat.c b/miscutils/chat.c index 2dfe52c4f..5183d1369 100644 --- a/miscutils/chat.c +++ b/miscutils/chat.c | |||
@@ -8,7 +8,7 @@ | |||
8 | * Licensed under GPLv2, see file LICENSE in this source tree. | 8 | * Licensed under GPLv2, see file LICENSE in this source tree. |
9 | */ | 9 | */ |
10 | //config:config CHAT | 10 | //config:config CHAT |
11 | //config: bool "chat (6.6 kb)" | 11 | //config: bool "chat (6.3 kb)" |
12 | //config: default y | 12 | //config: default y |
13 | //config: help | 13 | //config: help |
14 | //config: Simple chat utility. | 14 | //config: Simple chat utility. |
diff --git a/miscutils/crond.c b/miscutils/crond.c index f6580a9d4..2e36c406b 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
10 | */ | 10 | */ |
11 | //config:config CROND | 11 | //config:config CROND |
12 | //config: bool "crond (13 kb)" | 12 | //config: bool "crond (14 kb)" |
13 | //config: default y | 13 | //config: default y |
14 | //config: select FEATURE_SYSLOG | 14 | //config: select FEATURE_SYSLOG |
15 | //config: help | 15 | //config: help |
diff --git a/miscutils/crontab.c b/miscutils/crontab.c index 4787fa08f..96dc4741a 100644 --- a/miscutils/crontab.c +++ b/miscutils/crontab.c | |||
@@ -10,7 +10,7 @@ | |||
10 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 10 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
11 | */ | 11 | */ |
12 | //config:config CRONTAB | 12 | //config:config CRONTAB |
13 | //config: bool "crontab (9.7 kb)" | 13 | //config: bool "crontab (10 kb)" |
14 | //config: default y | 14 | //config: default y |
15 | //config: help | 15 | //config: help |
16 | //config: Crontab manipulates the crontab for a particular user. Only | 16 | //config: Crontab manipulates the crontab for a particular user. Only |
diff --git a/miscutils/dc.c b/miscutils/dc.c index f752a1377..0d09f5e2b 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c | |||
@@ -2,50 +2,11 @@ | |||
2 | /* | 2 | /* |
3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
4 | */ | 4 | */ |
5 | //config:config DC | 5 | |
6 | //config: bool "dc (4.2 kb)" | 6 | /* config/applet/usage bits are in bc.c */ |
7 | //config: default y | 7 | |
8 | //config: help | 8 | //#include "libbb.h" |
9 | //config: Dc is a reverse-polish desk calculator which supports unlimited | 9 | //#include "common_bufsiz.h" |
10 | //config: precision arithmetic. | ||
11 | //config: | ||
12 | //config:config FEATURE_DC_LIBM | ||
13 | //config: bool "Enable power and exp functions (requires libm)" | ||
14 | //config: default y | ||
15 | //config: depends on DC | ||
16 | //config: help | ||
17 | //config: Enable power and exp functions. | ||
18 | //config: NOTE: This will require libm to be present for linking. | ||
19 | |||
20 | //applet:IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
21 | |||
22 | //kbuild:lib-$(CONFIG_DC) += dc.o | ||
23 | |||
24 | //usage:#define dc_trivial_usage | ||
25 | //usage: "EXPRESSION..." | ||
26 | //usage: | ||
27 | //usage:#define dc_full_usage "\n\n" | ||
28 | //usage: "Tiny RPN calculator. Operations:\n" | ||
29 | //usage: "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, xor,\n" | ||
30 | //usage: "p - print top of the stack (without popping),\n" | ||
31 | //usage: "f - print entire stack,\n" | ||
32 | //usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n" | ||
33 | //usage: "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16" | ||
34 | //usage: | ||
35 | //usage:#define dc_example_usage | ||
36 | //usage: "$ dc 2 2 + p\n" | ||
37 | //usage: "4\n" | ||
38 | //usage: "$ dc 8 8 \\* 2 2 + / p\n" | ||
39 | //usage: "16\n" | ||
40 | //usage: "$ dc 0 1 and p\n" | ||
41 | //usage: "0\n" | ||
42 | //usage: "$ dc 0 1 or p\n" | ||
43 | //usage: "1\n" | ||
44 | //usage: "$ echo 72 9 div 8 mul p | dc\n" | ||
45 | //usage: "64\n" | ||
46 | |||
47 | #include "libbb.h" | ||
48 | #include "common_bufsiz.h" | ||
49 | #include <math.h> | 10 | #include <math.h> |
50 | 11 | ||
51 | #if 0 | 12 | #if 0 |
@@ -59,7 +20,6 @@ typedef unsigned long long data_t; | |||
59 | #define DATA_FMT LL_FMT | 20 | #define DATA_FMT LL_FMT |
60 | #endif | 21 | #endif |
61 | 22 | ||
62 | |||
63 | struct globals { | 23 | struct globals { |
64 | unsigned pointer; | 24 | unsigned pointer; |
65 | unsigned base; | 25 | unsigned base; |
@@ -75,7 +35,6 @@ enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof( | |||
75 | base = 10; \ | 35 | base = 10; \ |
76 | } while (0) | 36 | } while (0) |
77 | 37 | ||
78 | |||
79 | static void check_under(void) | 38 | static void check_under(void) |
80 | { | 39 | { |
81 | if (pointer == 0) | 40 | if (pointer == 0) |
@@ -223,25 +182,25 @@ struct op { | |||
223 | 182 | ||
224 | static const struct op operators[] = { | 183 | static const struct op operators[] = { |
225 | #if ENABLE_FEATURE_DC_LIBM | 184 | #if ENABLE_FEATURE_DC_LIBM |
226 | {"**", power}, | 185 | {"^", power}, |
227 | {"exp", power}, | 186 | // {"exp", power}, |
228 | {"pow", power}, | 187 | // {"pow", power}, |
229 | #endif | 188 | #endif |
230 | {"%", mod}, | 189 | {"%", mod}, |
231 | {"mod", mod}, | 190 | // {"mod", mod}, |
191 | // logic ops are not standard, remove? | ||
232 | {"and", and}, | 192 | {"and", and}, |
233 | {"or", or}, | 193 | {"or", or}, |
234 | {"not", not}, | 194 | {"not", not}, |
235 | {"eor", eor}, | ||
236 | {"xor", eor}, | 195 | {"xor", eor}, |
237 | {"+", add}, | 196 | {"+", add}, |
238 | {"add", add}, | 197 | // {"add", add}, |
239 | {"-", sub}, | 198 | {"-", sub}, |
240 | {"sub", sub}, | 199 | // {"sub", sub}, |
241 | {"*", mul}, | 200 | {"*", mul}, |
242 | {"mul", mul}, | 201 | // {"mul", mul}, |
243 | {"/", divide}, | 202 | {"/", divide}, |
244 | {"div", divide}, | 203 | // {"div", divide}, |
245 | {"p", print_no_pop}, | 204 | {"p", print_no_pop}, |
246 | {"f", print_stack_no_pop}, | 205 | {"f", print_stack_no_pop}, |
247 | {"o", set_output_base}, | 206 | {"o", set_output_base}, |
@@ -282,23 +241,50 @@ static void stack_machine(const char *argument) | |||
282 | bb_error_msg_and_die("syntax error at '%s'", argument); | 241 | bb_error_msg_and_die("syntax error at '%s'", argument); |
283 | } | 242 | } |
284 | 243 | ||
244 | static void process_file(FILE *fp) | ||
245 | { | ||
246 | char *line; | ||
247 | while ((line = xmalloc_fgetline(fp)) != NULL) { | ||
248 | stack_machine(line); | ||
249 | free(line); | ||
250 | } | ||
251 | } | ||
252 | |||
285 | int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 253 | int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
286 | int dc_main(int argc UNUSED_PARAM, char **argv) | 254 | int dc_main(int argc UNUSED_PARAM, char **argv) |
287 | { | 255 | { |
256 | bool script = 0; | ||
257 | |||
288 | INIT_G(); | 258 | INIT_G(); |
289 | 259 | ||
290 | argv++; | 260 | /* Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs */ |
291 | if (!argv[0]) { | 261 | for (;;) { |
292 | /* take stuff from stdin if no args are given */ | 262 | int n = getopt(argc, argv, "e:f:"); |
293 | char *line; | 263 | if (n <= 0) |
294 | while ((line = xmalloc_fgetline(stdin)) != NULL) { | 264 | break; |
295 | stack_machine(line); | 265 | switch (n) { |
296 | free(line); | 266 | case 'e': |
267 | script = 1; | ||
268 | stack_machine(optarg); | ||
269 | break; | ||
270 | case 'f': | ||
271 | script = 1; | ||
272 | process_file(xfopen_for_read(optarg)); | ||
273 | break; | ||
274 | default: | ||
275 | bb_show_usage(); | ||
297 | } | 276 | } |
298 | } else { | ||
299 | do { | ||
300 | stack_machine(*argv); | ||
301 | } while (*++argv); | ||
302 | } | 277 | } |
278 | argv += optind; | ||
279 | |||
280 | if (*argv) { | ||
281 | do | ||
282 | process_file(xfopen_for_read(*argv++)); | ||
283 | while (*argv); | ||
284 | } else if (!script) { | ||
285 | /* Take stuff from stdin if no args are given */ | ||
286 | process_file(stdin); | ||
287 | } | ||
288 | |||
303 | return EXIT_SUCCESS; | 289 | return EXIT_SUCCESS; |
304 | } | 290 | } |
diff --git a/miscutils/devfsd.c b/miscutils/devfsd.c index 814714f53..3bf06b965 100644 --- a/miscutils/devfsd.c +++ b/miscutils/devfsd.c | |||
@@ -694,7 +694,7 @@ static void process_config_line(const char *line, unsigned long *event_mask) | |||
694 | return; | 694 | return; |
695 | 695 | ||
696 | process_config_line_err: | 696 | process_config_line_err: |
697 | msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg , line); | 697 | msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg, line); |
698 | } /* End Function process_config_line */ | 698 | } /* End Function process_config_line */ |
699 | 699 | ||
700 | static int do_servicing(int fd, unsigned long event_mask) | 700 | static int do_servicing(int fd, unsigned long event_mask) |
diff --git a/miscutils/fbsplash.c b/miscutils/fbsplash.c index bc3c61055..bba22d6d1 100644 --- a/miscutils/fbsplash.c +++ b/miscutils/fbsplash.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * "exit" (or just close fifo) - well you guessed it. | 21 | * "exit" (or just close fifo) - well you guessed it. |
22 | */ | 22 | */ |
23 | //config:config FBSPLASH | 23 | //config:config FBSPLASH |
24 | //config: bool "fbsplash (27 kb)" | 24 | //config: bool "fbsplash (26 kb)" |
25 | //config: default y | 25 | //config: default y |
26 | //config: select PLATFORM_LINUX | 26 | //config: select PLATFORM_LINUX |
27 | //config: help | 27 | //config: help |
diff --git a/miscutils/flash_eraseall.c b/miscutils/flash_eraseall.c index 8e93060ca..a3dabdadb 100644 --- a/miscutils/flash_eraseall.c +++ b/miscutils/flash_eraseall.c | |||
@@ -11,7 +11,7 @@ | |||
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
12 | */ | 12 | */ |
13 | //config:config FLASH_ERASEALL | 13 | //config:config FLASH_ERASEALL |
14 | //config: bool "flash_eraseall (5.5 kb)" | 14 | //config: bool "flash_eraseall (5.9 kb)" |
15 | //config: default n # doesn't build on Ubuntu 8.04 | 15 | //config: default n # doesn't build on Ubuntu 8.04 |
16 | //config: help | 16 | //config: help |
17 | //config: The flash_eraseall binary from mtd-utils as of git head c4c6a59eb. | 17 | //config: The flash_eraseall binary from mtd-utils as of git head c4c6a59eb. |
diff --git a/miscutils/flashcp.c b/miscutils/flashcp.c index 858cee194..1ca9d158d 100644 --- a/miscutils/flashcp.c +++ b/miscutils/flashcp.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config FLASHCP | 9 | //config:config FLASHCP |
10 | //config: bool "flashcp (5.4 kb)" | 10 | //config: bool "flashcp (5.3 kb)" |
11 | //config: default n # doesn't build on Ubuntu 8.04 | 11 | //config: default n # doesn't build on Ubuntu 8.04 |
12 | //config: help | 12 | //config: help |
13 | //config: The flashcp binary, inspired by mtd-utils as of git head 5eceb74f7. | 13 | //config: The flashcp binary, inspired by mtd-utils as of git head 5eceb74f7. |
diff --git a/miscutils/hdparm.c b/miscutils/hdparm.c index 0bbe6ca10..342e240fa 100644 --- a/miscutils/hdparm.c +++ b/miscutils/hdparm.c | |||
@@ -12,7 +12,7 @@ | |||
12 | * - by Mark Lord (C) 1994-2002 -- freely distributable | 12 | * - by Mark Lord (C) 1994-2002 -- freely distributable |
13 | */ | 13 | */ |
14 | //config:config HDPARM | 14 | //config:config HDPARM |
15 | //config: bool "hdparm (23 kb)" | 15 | //config: bool "hdparm (25 kb)" |
16 | //config: default y | 16 | //config: default y |
17 | //config: select PLATFORM_LINUX | 17 | //config: select PLATFORM_LINUX |
18 | //config: help | 18 | //config: help |
diff --git a/miscutils/hexedit.c b/miscutils/hexedit.c index 298eb8149..5c2f4a555 100644 --- a/miscutils/hexedit.c +++ b/miscutils/hexedit.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Licensed under GPLv2, see file LICENSE in this source tree. | 4 | * Licensed under GPLv2, see file LICENSE in this source tree. |
5 | */ | 5 | */ |
6 | //config:config HEXEDIT | 6 | //config:config HEXEDIT |
7 | //config: bool "hexedit (20 kb)" | 7 | //config: bool "hexedit (21 kb)" |
8 | //config: default y | 8 | //config: default y |
9 | //config: help | 9 | //config: help |
10 | //config: Edit file in hexadecimal. | 10 | //config: Edit file in hexadecimal. |
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c index 6a2134063..610fed5d6 100644 --- a/miscutils/i2c_tools.c +++ b/miscutils/i2c_tools.c | |||
@@ -9,28 +9,28 @@ | |||
9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
10 | */ | 10 | */ |
11 | //config:config I2CGET | 11 | //config:config I2CGET |
12 | //config: bool "i2cget (5.6 kb)" | 12 | //config: bool "i2cget (5.5 kb)" |
13 | //config: default y | 13 | //config: default y |
14 | //config: select PLATFORM_LINUX | 14 | //config: select PLATFORM_LINUX |
15 | //config: help | 15 | //config: help |
16 | //config: Read from I2C/SMBus chip registers. | 16 | //config: Read from I2C/SMBus chip registers. |
17 | //config: | 17 | //config: |
18 | //config:config I2CSET | 18 | //config:config I2CSET |
19 | //config: bool "i2cset (6.9 kb)" | 19 | //config: bool "i2cset (6.7 kb)" |
20 | //config: default y | 20 | //config: default y |
21 | //config: select PLATFORM_LINUX | 21 | //config: select PLATFORM_LINUX |
22 | //config: help | 22 | //config: help |
23 | //config: Set I2C registers. | 23 | //config: Set I2C registers. |
24 | //config: | 24 | //config: |
25 | //config:config I2CDUMP | 25 | //config:config I2CDUMP |
26 | //config: bool "i2cdump (7.2 kb)" | 26 | //config: bool "i2cdump (7.1 kb)" |
27 | //config: default y | 27 | //config: default y |
28 | //config: select PLATFORM_LINUX | 28 | //config: select PLATFORM_LINUX |
29 | //config: help | 29 | //config: help |
30 | //config: Examine I2C registers. | 30 | //config: Examine I2C registers. |
31 | //config: | 31 | //config: |
32 | //config:config I2CDETECT | 32 | //config:config I2CDETECT |
33 | //config: bool "i2cdetect (7.2 kb)" | 33 | //config: bool "i2cdetect (7.1 kb)" |
34 | //config: default y | 34 | //config: default y |
35 | //config: select PLATFORM_LINUX | 35 | //config: select PLATFORM_LINUX |
36 | //config: help | 36 | //config: help |
diff --git a/miscutils/inotifyd.c b/miscutils/inotifyd.c index 0060797ed..ec0321941 100644 --- a/miscutils/inotifyd.c +++ b/miscutils/inotifyd.c | |||
@@ -27,7 +27,7 @@ | |||
27 | * See below for mask names explanation. | 27 | * See below for mask names explanation. |
28 | */ | 28 | */ |
29 | //config:config INOTIFYD | 29 | //config:config INOTIFYD |
30 | //config: bool "inotifyd (3.5 kb)" | 30 | //config: bool "inotifyd (3.6 kb)" |
31 | //config: default n # doesn't build on Knoppix 5 | 31 | //config: default n # doesn't build on Knoppix 5 |
32 | //config: help | 32 | //config: help |
33 | //config: Simple inotify daemon. Reports filesystem changes. Requires | 33 | //config: Simple inotify daemon. Reports filesystem changes. Requires |
diff --git a/miscutils/less.c b/miscutils/less.c index 6b5c8c2dd..ad23b7d0d 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -20,7 +20,7 @@ | |||
20 | * redirected input has been read from stdin | 20 | * redirected input has been read from stdin |
21 | */ | 21 | */ |
22 | //config:config LESS | 22 | //config:config LESS |
23 | //config: bool "less (15 kb)" | 23 | //config: bool "less (16 kb)" |
24 | //config: default y | 24 | //config: default y |
25 | //config: help | 25 | //config: help |
26 | //config: 'less' is a pager, meaning that it displays text files. It possesses | 26 | //config: 'less' is a pager, meaning that it displays text files. It possesses |
diff --git a/miscutils/lsscsi.c b/miscutils/lsscsi.c index 0aaa01ded..f737d33d9 100644 --- a/miscutils/lsscsi.c +++ b/miscutils/lsscsi.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config LSSCSI | 9 | //config:config LSSCSI |
10 | //config: bool "lsscsi (2.4 kb)" | 10 | //config: bool "lsscsi (2.5 kb)" |
11 | //config: default y | 11 | //config: default y |
12 | //config: #select PLATFORM_LINUX | 12 | //config: #select PLATFORM_LINUX |
13 | //config: help | 13 | //config: help |
diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c index 80975c652..93c550042 100644 --- a/miscutils/makedevs.c +++ b/miscutils/makedevs.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * known bugs: can't deal with alpha ranges | 7 | * known bugs: can't deal with alpha ranges |
8 | */ | 8 | */ |
9 | //config:config MAKEDEVS | 9 | //config:config MAKEDEVS |
10 | //config: bool "makedevs (9.3 kb)" | 10 | //config: bool "makedevs (9.2 kb)" |
11 | //config: default y | 11 | //config: default y |
12 | //config: help | 12 | //config: help |
13 | //config: 'makedevs' is a utility used to create a batch of devices with | 13 | //config: 'makedevs' is a utility used to create a batch of devices with |
diff --git a/miscutils/man.c b/miscutils/man.c index 4ff58a9a0..fd5d90c1a 100644 --- a/miscutils/man.c +++ b/miscutils/man.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * Licensed under GPLv2, see file LICENSE in this source tree. | 3 | * Licensed under GPLv2, see file LICENSE in this source tree. |
4 | */ | 4 | */ |
5 | //config:config MAN | 5 | //config:config MAN |
6 | //config: bool "man (27 kb)" | 6 | //config: bool "man (26 kb)" |
7 | //config: default y | 7 | //config: default y |
8 | //config: help | 8 | //config: help |
9 | //config: Format and display manual pages. | 9 | //config: Format and display manual pages. |
diff --git a/miscutils/microcom.c b/miscutils/microcom.c index fa090057e..399d4cf7f 100644 --- a/miscutils/microcom.c +++ b/miscutils/microcom.c | |||
@@ -8,7 +8,7 @@ | |||
8 | * Licensed under GPLv2, see file LICENSE in this source tree. | 8 | * Licensed under GPLv2, see file LICENSE in this source tree. |
9 | */ | 9 | */ |
10 | //config:config MICROCOM | 10 | //config:config MICROCOM |
11 | //config: bool "microcom (5.6 kb)" | 11 | //config: bool "microcom (5.7 kb)" |
12 | //config: default y | 12 | //config: default y |
13 | //config: help | 13 | //config: help |
14 | //config: The poor man's minicom utility for chatting with serial port devices. | 14 | //config: The poor man's minicom utility for chatting with serial port devices. |
diff --git a/miscutils/mt.c b/miscutils/mt.c index fad656e95..9f1aecfca 100644 --- a/miscutils/mt.c +++ b/miscutils/mt.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
4 | */ | 4 | */ |
5 | //config:config MT | 5 | //config:config MT |
6 | //config: bool "mt (2.6 kb)" | 6 | //config: bool "mt (2.5 kb)" |
7 | //config: default y | 7 | //config: default y |
8 | //config: help | 8 | //config: help |
9 | //config: mt is used to control tape devices. You can use the mt utility | 9 | //config: mt is used to control tape devices. You can use the mt utility |
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c index 29c800612..09bcaaf63 100644 --- a/miscutils/nandwrite.c +++ b/miscutils/nandwrite.c | |||
@@ -8,14 +8,14 @@ | |||
8 | * TODO: add support for large (>4GB) MTD devices | 8 | * TODO: add support for large (>4GB) MTD devices |
9 | */ | 9 | */ |
10 | //config:config NANDWRITE | 10 | //config:config NANDWRITE |
11 | //config: bool "nandwrite (5.9 kb)" | 11 | //config: bool "nandwrite (4.8 kb)" |
12 | //config: default y | 12 | //config: default y |
13 | //config: select PLATFORM_LINUX | 13 | //config: select PLATFORM_LINUX |
14 | //config: help | 14 | //config: help |
15 | //config: Write to the specified MTD device, with bad blocks awareness | 15 | //config: Write to the specified MTD device, with bad blocks awareness |
16 | //config: | 16 | //config: |
17 | //config:config NANDDUMP | 17 | //config:config NANDDUMP |
18 | //config: bool "nanddump (6.3 kb)" | 18 | //config: bool "nanddump (5.2 kb)" |
19 | //config: default y | 19 | //config: default y |
20 | //config: select PLATFORM_LINUX | 20 | //config: select PLATFORM_LINUX |
21 | //config: help | 21 | //config: help |
diff --git a/miscutils/partprobe.c b/miscutils/partprobe.c index d1ae27348..0abed6ff1 100644 --- a/miscutils/partprobe.c +++ b/miscutils/partprobe.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * Licensed under GPLv2, see file LICENSE in this source tree. | 5 | * Licensed under GPLv2, see file LICENSE in this source tree. |
6 | */ | 6 | */ |
7 | //config:config PARTPROBE | 7 | //config:config PARTPROBE |
8 | //config: bool "partprobe (3.6 kb)" | 8 | //config: bool "partprobe (3.5 kb)" |
9 | //config: default y | 9 | //config: default y |
10 | //config: select PLATFORM_LINUX | 10 | //config: select PLATFORM_LINUX |
11 | //config: help | 11 | //config: help |
diff --git a/miscutils/raidautorun.c b/miscutils/raidautorun.c index d315c2734..39816ab1f 100644 --- a/miscutils/raidautorun.c +++ b/miscutils/raidautorun.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config RAIDAUTORUN | 9 | //config:config RAIDAUTORUN |
10 | //config: bool "raidautorun (1.4 kb)" | 10 | //config: bool "raidautorun (1.3 kb)" |
11 | //config: default y | 11 | //config: default y |
12 | //config: select PLATFORM_LINUX | 12 | //config: select PLATFORM_LINUX |
13 | //config: help | 13 | //config: help |
diff --git a/miscutils/readahead.c b/miscutils/readahead.c index 972302a18..cc0ba5ba3 100644 --- a/miscutils/readahead.c +++ b/miscutils/readahead.c | |||
@@ -10,7 +10,7 @@ | |||
10 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 10 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
11 | */ | 11 | */ |
12 | //config:config READAHEAD | 12 | //config:config READAHEAD |
13 | //config: bool "readahead (2 kb)" | 13 | //config: bool "readahead (1.5 kb)" |
14 | //config: default y | 14 | //config: default y |
15 | //config: depends on LFS | 15 | //config: depends on LFS |
16 | //config: select PLATFORM_LINUX | 16 | //config: select PLATFORM_LINUX |
diff --git a/miscutils/rfkill.c b/miscutils/rfkill.c index ae38c182d..766bad8c7 100644 --- a/miscutils/rfkill.c +++ b/miscutils/rfkill.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config RFKILL | 9 | //config:config RFKILL |
10 | //config: bool "rfkill (5.3 kb)" | 10 | //config: bool "rfkill (4.4 kb)" |
11 | //config: default n # doesn't build on Ubuntu 9.04 | 11 | //config: default n # doesn't build on Ubuntu 9.04 |
12 | //config: select PLATFORM_LINUX | 12 | //config: select PLATFORM_LINUX |
13 | //config: help | 13 | //config: help |
diff --git a/miscutils/runlevel.c b/miscutils/runlevel.c index 0b2098564..2f1581ead 100644 --- a/miscutils/runlevel.c +++ b/miscutils/runlevel.c | |||
@@ -12,11 +12,11 @@ | |||
12 | * initially busyboxified by Bernhard Reutner-Fischer | 12 | * initially busyboxified by Bernhard Reutner-Fischer |
13 | */ | 13 | */ |
14 | //config:config RUNLEVEL | 14 | //config:config RUNLEVEL |
15 | //config: bool "runlevel (518 bytes)" | 15 | //config: bool "runlevel (559 bytes)" |
16 | //config: default y | 16 | //config: default y |
17 | //config: depends on FEATURE_UTMP | 17 | //config: depends on FEATURE_UTMP |
18 | //config: help | 18 | //config: help |
19 | //config: find the current and previous system runlevel. | 19 | //config: Find the current and previous system runlevel. |
20 | //config: | 20 | //config: |
21 | //config: This applet uses utmp but does not rely on busybox supporing | 21 | //config: This applet uses utmp but does not rely on busybox supporing |
22 | //config: utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc. | 22 | //config: utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc. |
diff --git a/miscutils/setfattr.c b/miscutils/setfattr.c index 12eebc56e..9792c2660 100644 --- a/miscutils/setfattr.c +++ b/miscutils/setfattr.c | |||
@@ -6,7 +6,7 @@ | |||
6 | * Licensed under GPLv2, see file LICENSE in this source tree. | 6 | * Licensed under GPLv2, see file LICENSE in this source tree. |
7 | */ | 7 | */ |
8 | //config:config SETFATTR | 8 | //config:config SETFATTR |
9 | //config: bool "setfattr (3.6 kb)" | 9 | //config: bool "setfattr (3.7 kb)" |
10 | //config: default y | 10 | //config: default y |
11 | //config: help | 11 | //config: help |
12 | //config: Set/delete extended attributes on files | 12 | //config: Set/delete extended attributes on files |
diff --git a/miscutils/setserial.c b/miscutils/setserial.c index fd88ed106..71b274568 100644 --- a/miscutils/setserial.c +++ b/miscutils/setserial.c | |||
@@ -8,7 +8,7 @@ | |||
8 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 8 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
9 | */ | 9 | */ |
10 | //config:config SETSERIAL | 10 | //config:config SETSERIAL |
11 | //config: bool "setserial (6.6 kb)" | 11 | //config: bool "setserial (6.9 kb)" |
12 | //config: default y | 12 | //config: default y |
13 | //config: select PLATFORM_LINUX | 13 | //config: select PLATFORM_LINUX |
14 | //config: help | 14 | //config: help |
diff --git a/miscutils/strings.c b/miscutils/strings.c index ccb05f6d4..51412f401 100644 --- a/miscutils/strings.c +++ b/miscutils/strings.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config STRINGS | 9 | //config:config STRINGS |
10 | //config: bool "strings (4.3 kb)" | 10 | //config: bool "strings (4.6 kb)" |
11 | //config: default y | 11 | //config: default y |
12 | //config: help | 12 | //config: help |
13 | //config: strings prints the printable character sequences for each file | 13 | //config: strings prints the printable character sequences for each file |
diff --git a/miscutils/time.c b/miscutils/time.c index 7d6a7a29f..064888ab8 100644 --- a/miscutils/time.c +++ b/miscutils/time.c | |||
@@ -10,7 +10,7 @@ | |||
10 | * Heavily modified for busybox by Erik Andersen <andersen@codepoet.org> | 10 | * Heavily modified for busybox by Erik Andersen <andersen@codepoet.org> |
11 | */ | 11 | */ |
12 | //config:config TIME | 12 | //config:config TIME |
13 | //config: bool "time (7 kb)" | 13 | //config: bool "time (6.8 kb)" |
14 | //config: default y | 14 | //config: default y |
15 | //config: help | 15 | //config: help |
16 | //config: The time command runs the specified program with the given arguments. | 16 | //config: The time command runs the specified program with the given arguments. |
diff --git a/miscutils/ttysize.c b/miscutils/ttysize.c index 2c2d4ec33..d635b29ce 100644 --- a/miscutils/ttysize.c +++ b/miscutils/ttysize.c | |||
@@ -10,7 +10,7 @@ | |||
10 | * Licensed under GPLv2, see file LICENSE in this source tree. | 10 | * Licensed under GPLv2, see file LICENSE in this source tree. |
11 | */ | 11 | */ |
12 | //config:config TTYSIZE | 12 | //config:config TTYSIZE |
13 | //config: bool "ttysize (372 bytes)" | 13 | //config: bool "ttysize (432 bytes)" |
14 | //config: default y | 14 | //config: default y |
15 | //config: help | 15 | //config: help |
16 | //config: A replacement for "stty size". Unlike stty, can report only width, | 16 | //config: A replacement for "stty size". Unlike stty, can report only width, |
diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c index a947abab5..dc7af25a4 100644 --- a/miscutils/ubi_tools.c +++ b/miscutils/ubi_tools.c | |||
@@ -4,42 +4,42 @@ | |||
4 | * Licensed under GPLv2, see file LICENSE in this source tree. | 4 | * Licensed under GPLv2, see file LICENSE in this source tree. |
5 | */ | 5 | */ |
6 | //config:config UBIATTACH | 6 | //config:config UBIATTACH |
7 | //config: bool "ubiattach (4.7 kb)" | 7 | //config: bool "ubiattach (4.2 kb)" |
8 | //config: default y | 8 | //config: default y |
9 | //config: select PLATFORM_LINUX | 9 | //config: select PLATFORM_LINUX |
10 | //config: help | 10 | //config: help |
11 | //config: Attach MTD device to an UBI device. | 11 | //config: Attach MTD device to an UBI device. |
12 | //config: | 12 | //config: |
13 | //config:config UBIDETACH | 13 | //config:config UBIDETACH |
14 | //config: bool "ubidetach (4.6 kb)" | 14 | //config: bool "ubidetach (4.1 kb)" |
15 | //config: default y | 15 | //config: default y |
16 | //config: select PLATFORM_LINUX | 16 | //config: select PLATFORM_LINUX |
17 | //config: help | 17 | //config: help |
18 | //config: Detach MTD device from an UBI device. | 18 | //config: Detach MTD device from an UBI device. |
19 | //config: | 19 | //config: |
20 | //config:config UBIMKVOL | 20 | //config:config UBIMKVOL |
21 | //config: bool "ubimkvol (5.8 kb)" | 21 | //config: bool "ubimkvol (5.3 kb)" |
22 | //config: default y | 22 | //config: default y |
23 | //config: select PLATFORM_LINUX | 23 | //config: select PLATFORM_LINUX |
24 | //config: help | 24 | //config: help |
25 | //config: Create a UBI volume. | 25 | //config: Create a UBI volume. |
26 | //config: | 26 | //config: |
27 | //config:config UBIRMVOL | 27 | //config:config UBIRMVOL |
28 | //config: bool "ubirmvol (5.2 kb)" | 28 | //config: bool "ubirmvol (4.9 kb)" |
29 | //config: default y | 29 | //config: default y |
30 | //config: select PLATFORM_LINUX | 30 | //config: select PLATFORM_LINUX |
31 | //config: help | 31 | //config: help |
32 | //config: Delete a UBI volume. | 32 | //config: Delete a UBI volume. |
33 | //config: | 33 | //config: |
34 | //config:config UBIRSVOL | 34 | //config:config UBIRSVOL |
35 | //config: bool "ubirsvol (4.6 kb)" | 35 | //config: bool "ubirsvol (4.2 kb)" |
36 | //config: default y | 36 | //config: default y |
37 | //config: select PLATFORM_LINUX | 37 | //config: select PLATFORM_LINUX |
38 | //config: help | 38 | //config: help |
39 | //config: Resize a UBI volume. | 39 | //config: Resize a UBI volume. |
40 | //config: | 40 | //config: |
41 | //config:config UBIUPDATEVOL | 41 | //config:config UBIUPDATEVOL |
42 | //config: bool "ubiupdatevol (5.6 kb)" | 42 | //config: bool "ubiupdatevol (5.2 kb)" |
43 | //config: default y | 43 | //config: default y |
44 | //config: select PLATFORM_LINUX | 44 | //config: select PLATFORM_LINUX |
45 | //config: help | 45 | //config: help |
diff --git a/miscutils/ubirename.c b/miscutils/ubirename.c index ecc8fe137..21bd10111 100644 --- a/miscutils/ubirename.c +++ b/miscutils/ubirename.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | //config:config UBIRENAME | 9 | //config:config UBIRENAME |
10 | //config: bool "ubirename (2.2 kb)" | 10 | //config: bool "ubirename (2.4 kb)" |
11 | //config: default y | 11 | //config: default y |
12 | //config: select PLATFORM_LINUX | 12 | //config: select PLATFORM_LINUX |
13 | //config: help | 13 | //config: help |
diff --git a/miscutils/volname.c b/miscutils/volname.c index 6e6bbaa44..027d01272 100644 --- a/miscutils/volname.c +++ b/miscutils/volname.c | |||
@@ -28,7 +28,7 @@ | |||
28 | * Matthew Stoltenberg <d3matt@gmail.com> | 28 | * Matthew Stoltenberg <d3matt@gmail.com> |
29 | */ | 29 | */ |
30 | //config:config VOLNAME | 30 | //config:config VOLNAME |
31 | //config: bool "volname (1.7 kb)" | 31 | //config: bool "volname (1.6 kb)" |
32 | //config: default y | 32 | //config: default y |
33 | //config: help | 33 | //config: help |
34 | //config: Prints a CD-ROM volume name. | 34 | //config: Prints a CD-ROM volume name. |
diff --git a/miscutils/watchdog.c b/miscutils/watchdog.c index 392d05646..1e9ecc5e8 100644 --- a/miscutils/watchdog.c +++ b/miscutils/watchdog.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
10 | */ | 10 | */ |
11 | //config:config WATCHDOG | 11 | //config:config WATCHDOG |
12 | //config: bool "watchdog (5.1 kb)" | 12 | //config: bool "watchdog (5.3 kb)" |
13 | //config: default y | 13 | //config: default y |
14 | //config: select PLATFORM_LINUX | 14 | //config: select PLATFORM_LINUX |
15 | //config: help | 15 | //config: help |