1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *****************************************************************************
31 *
32 * Code to execute bc programs.
33 *
34 */
35
36 #include <assert.h>
37 #include <stdbool.h>
38 #include <string.h>
39
40 #include <setjmp.h>
41
42 #include <signal.h>
43
44 #include <time.h>
45
46 #include <read.h>
47 #include <parse.h>
48 #include <program.h>
49 #include <vm.h>
50
51 /**
52 * Quickly sets the const and strs vector pointers in the program. This is a
53 * convenience function.
54 * @param p The program.
55 * @param f The new function.
56 */
bc_program_setVecs(BcProgram * p,BcFunc * f)57 static inline void bc_program_setVecs(BcProgram *p, BcFunc *f) {
58 BC_SIG_ASSERT_LOCKED;
59 p->consts = &f->consts;
60 p->strs = &f->strs;
61 }
62
63 /**
64 * Does a type check for something that expects a number.
65 * @param r The result that will be checked.
66 * @param n The result's number.
67 */
bc_program_type_num(BcResult * r,BcNum * n)68 static inline void bc_program_type_num(BcResult *r, BcNum *n) {
69
70 #if BC_ENABLED
71
72 // This should have already been taken care of.
73 assert(r->t != BC_RESULT_VOID);
74
75 #endif // BC_ENABLED
76
77 if (BC_ERR(!BC_PROG_NUM(r, n))) bc_err(BC_ERR_EXEC_TYPE);
78 }
79
80 #if BC_ENABLED
81
82 /**
83 * Does a type check.
84 * @param r The result to check.
85 * @param t The type that the result should be.
86 */
bc_program_type_match(BcResult * r,BcType t)87 static void bc_program_type_match(BcResult *r, BcType t) {
88 if (BC_ERR((r->t != BC_RESULT_ARRAY) != (!t))) bc_err(BC_ERR_EXEC_TYPE);
89 }
90 #endif // BC_ENABLED
91
92 /**
93 * Pulls an index out of a bytecode vector and updates the index into the vector
94 * to point to the spot after the index. For more details on bytecode indices,
95 * see the development manual (manuals/development.md#bytecode-indices).
96 * @param code The bytecode vector.
97 * @param bgn An in/out parameter; the index into the vector that will be
98 * updated.
99 * @return The index at @a bgn in the bytecode vector.
100 */
bc_program_index(const char * restrict code,size_t * restrict bgn)101 static size_t bc_program_index(const char *restrict code, size_t *restrict bgn)
102 {
103 uchar amt = (uchar) code[(*bgn)++], i = 0;
104 size_t res = 0;
105
106 for (; i < amt; ++i, ++(*bgn)) {
107 size_t temp = ((size_t) ((int) (uchar) code[*bgn]) & UCHAR_MAX);
108 res |= (temp << (i * CHAR_BIT));
109 }
110
111 return res;
112 }
113
114 /**
115 * Returns a string from a result and its number.
116 * @param p The program.
117 * @param n The number tied to the result.
118 * @return The string corresponding to the result and number.
119 */
bc_program_string(BcProgram * p,const BcNum * n)120 static char* bc_program_string(BcProgram *p, const BcNum *n) {
121 BcFunc *f = bc_vec_item(&p->fns, n->rdx);
122 return *((char**) bc_vec_item(&f->strs, n->scale));
123 }
124
125 #if BC_ENABLED
126
127 /**
128 * Prepares the globals for a function call. This is only called when global
129 * stacks are on because it pushes a copy of the current globals onto each of
130 * their respective stacks.
131 * @param p The program.
132 */
bc_program_prepGlobals(BcProgram * p)133 static void bc_program_prepGlobals(BcProgram *p) {
134
135 size_t i;
136
137 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)
138 bc_vec_push(p->globals_v + i, p->globals + i);
139
140 #if BC_ENABLE_EXTRA_MATH
141 bc_rand_push(&p->rng);
142 #endif // BC_ENABLE_EXTRA_MATH
143 }
144
145 /**
146 * Pops globals stacks on returning from a function, or in the case of reset,
147 * pops all but one item on each global stack.
148 * @param p The program.
149 * @param reset True if all but one item on each stack should be popped, false
150 * otherwise.
151 */
bc_program_popGlobals(BcProgram * p,bool reset)152 static void bc_program_popGlobals(BcProgram *p, bool reset) {
153
154 size_t i;
155
156 BC_SIG_ASSERT_LOCKED;
157
158 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) {
159 BcVec *v = p->globals_v + i;
160 bc_vec_npop(v, reset ? v->len - 1 : 1);
161 p->globals[i] = BC_PROG_GLOBAL(v);
162 }
163
164 #if BC_ENABLE_EXTRA_MATH
165 bc_rand_pop(&p->rng, reset);
166 #endif // BC_ENABLE_EXTRA_MATH
167 }
168
169 /**
170 * Derefeneces an array reference and returns a pointer to the real array.
171 * @param p The program.
172 * @param vec The reference vector.
173 * @return A pointer to the desired array.
174 */
bc_program_dereference(const BcProgram * p,BcVec * vec)175 static BcVec* bc_program_dereference(const BcProgram *p, BcVec *vec) {
176
177 BcVec *v;
178 size_t vidx, nidx, i = 0;
179
180 // We want to be sure we have a reference vector.
181 assert(vec->size == sizeof(uchar));
182
183 // Get the index of the vector in arrs, then the index of the original
184 // referenced vector.
185 vidx = bc_program_index(vec->v, &i);
186 nidx = bc_program_index(vec->v, &i);
187
188 v = bc_vec_item(bc_vec_item(&p->arrs, vidx), nidx);
189
190 // We want to be sure we do *not* have a reference vector.
191 assert(v->size != sizeof(uchar));
192
193 return v;
194 }
195 #endif // BC_ENABLED
196
197 /**
198 * Creates a BcNum from a BcBigDig and pushes onto the results stack. This is a
199 * convenience function.
200 * @param p The program.
201 * @param dig The BcBigDig to push onto the results stack.
202 * @param type The type that the pushed result should be.
203 */
bc_program_pushBigdig(BcProgram * p,BcBigDig dig,BcResultType type)204 static void bc_program_pushBigdig(BcProgram *p, BcBigDig dig, BcResultType type)
205 {
206 BcResult res;
207
208 res.t = type;
209
210 BC_SIG_LOCK;
211
212 bc_num_createFromBigdig(&res.d.n, dig);
213 bc_vec_push(&p->results, &res);
214
215 BC_SIG_UNLOCK;
216 }
217
bc_program_addString(BcProgram * p,const char * str,size_t fidx)218 size_t bc_program_addString(BcProgram *p, const char *str, size_t fidx) {
219
220 BcFunc *f;
221 char **str_ptr;
222 BcVec *slabs;
223
224 BC_SIG_ASSERT_LOCKED;
225
226 // Push an empty string on the proper vector.
227 f = bc_vec_item(&p->fns, fidx);
228 str_ptr = bc_vec_pushEmpty(&f->strs);
229
230 // Figure out which slab vector to use.
231 slabs = fidx == BC_PROG_MAIN || fidx == BC_PROG_READ ?
232 &vm.main_slabs : &vm.other_slabs;
233
234 *str_ptr = bc_slabvec_strdup(slabs, str);
235
236 return f->strs.len - 1;
237 }
238
bc_program_search(BcProgram * p,const char * id,bool var)239 size_t bc_program_search(BcProgram *p, const char *id, bool var) {
240
241 BcVec *v, *map;
242 size_t i;
243
244 BC_SIG_ASSERT_LOCKED;
245
246 // Grab the right vector and map.
247 v = var ? &p->vars : &p->arrs;
248 map = var ? &p->var_map : &p->arr_map;
249
250 // We do an insert because the variable might not exist yet. This is because
251 // the parser calls this function. If the insert succeeds, we create a stack
252 // for the variable/array. But regardless, bc_map_insert() gives us the
253 // index of the item in i.
254 if (bc_map_insert(map, id, v->len, &i)) {
255 BcVec *temp = bc_vec_pushEmpty(v);
256 bc_array_init(temp, var);
257 }
258
259 return ((BcId*) bc_vec_item(map, i))->idx;
260 }
261
262 /**
263 * Returns the correct variable or array stack for the type.
264 * @param p The program.
265 * @param idx The index of the variable or array in the variable or array
266 * vector.
267 * @param type The type of vector to return.
268 * @return A pointer to the variable or array stack.
269 */
bc_program_vec(const BcProgram * p,size_t idx,BcType type)270 static inline BcVec* bc_program_vec(const BcProgram *p, size_t idx, BcType type)
271 {
272 const BcVec *v = (type == BC_TYPE_VAR) ? &p->vars : &p->arrs;
273 return bc_vec_item(v, idx);
274 }
275
276 /**
277 * Returns a pointer to the BcNum corresponding to the result. There is one
278 * case, however, where this returns a pointer to a BcVec: if the type of the
279 * result is array. In that case, the pointer is casted to a pointer to BcNum,
280 * but is never used. The function that calls this expecting an array casts the
281 * pointer back. This function is called a lot and needs to be as fast as
282 * possible.
283 * @param p The program.
284 * @param r The result whose number will be returned.
285 * @return The BcNum corresponding to the result.
286 */
bc_program_num(BcProgram * p,BcResult * r)287 static BcNum* bc_program_num(BcProgram *p, BcResult *r) {
288
289 BcNum *n;
290
291 #ifdef _WIN32
292 // Windows made it an error to not initialize this, so shut it up.
293 // I don't want to do this on other platforms because this procedure
294 // is one of the most heavily-used, and eliminating the initialization
295 // is a performance win.
296 n = NULL;
297 #endif // _WIN32
298
299 switch (r->t) {
300
301 case BC_RESULT_STR:
302 case BC_RESULT_TEMP:
303 case BC_RESULT_IBASE:
304 case BC_RESULT_SCALE:
305 case BC_RESULT_OBASE:
306 #if BC_ENABLE_EXTRA_MATH
307 case BC_RESULT_SEED:
308 #endif // BC_ENABLE_EXTRA_MATH
309 {
310 n = &r->d.n;
311 break;
312 }
313
314 case BC_RESULT_VAR:
315 case BC_RESULT_ARRAY:
316 case BC_RESULT_ARRAY_ELEM:
317 {
318 BcVec *v;
319 BcType type = (r->t == BC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY;
320
321 // Get the correct variable or array vector.
322 v = bc_program_vec(p, r->d.loc.loc, type);
323
324 // Surprisingly enough, the hard case is *not* returning an array;
325 // it's returning an array element. This is because we have to dig
326 // deeper to get *to* the element. That's what the code inside this
327 // if statement does.
328 if (r->t == BC_RESULT_ARRAY_ELEM) {
329
330 size_t idx = r->d.loc.idx;
331
332 v = bc_vec_top(v);
333
334 #if BC_ENABLED
335 // If this is true, we have a reference vector, so dereference
336 // it. The reason we don't need to worry about it for returning
337 // a straight array is because we only care about references
338 // when we access elements of an array that is a reference. That
339 // is this code, so in essence, this line takes care of arrays
340 // as well.
341 if (v->size == sizeof(uchar)) v = bc_program_dereference(p, v);
342 #endif // BC_ENABLED
343
344 // We want to be sure we got a valid array of numbers.
345 assert(v->size == sizeof(BcNum));
346
347 // The bc spec says that if an element is accessed that does not
348 // exist, it should be preinitialized to 0. Well, if we access
349 // an element *way* out there, we have to preinitialize all
350 // elements between the current last element and the actual
351 // accessed element.
352 if (v->len <= idx) {
353 BC_SIG_LOCK;
354 bc_array_expand(v, bc_vm_growSize(idx, 1));
355 BC_SIG_UNLOCK;
356 }
357
358 n = bc_vec_item(v, idx);
359 }
360 // This is either a number (for a var) or an array (for an array).
361 // Because bc_vec_top() returns a void*, we don't need to cast.
362 else n = bc_vec_top(v);
363
364 break;
365 }
366
367 case BC_RESULT_ZERO:
368 {
369 n = &vm.zero;
370 break;
371 }
372
373 case BC_RESULT_ONE:
374 {
375 n = &vm.one;
376 break;
377 }
378
379 #if BC_ENABLED
380 // We should never get here; this is taken care of earlier because a
381 // result is expected.
382 case BC_RESULT_VOID:
383 #ifndef NDEBUG
384 {
385 abort();
386 }
387 #endif // NDEBUG
388 // Fallthrough
389 case BC_RESULT_LAST:
390 {
391 n = &p->last;
392 break;
393 }
394 #endif // BC_ENABLED
395 }
396
397 return n;
398 }
399
400 /**
401 * Prepares an operand for use.
402 * @param p The program.
403 * @param r An out parameter; this is set to the pointer to the result that
404 * we care about.
405 * @param n An out parameter; this is set to the pointer to the number that
406 * we care about.
407 * @param idx The index of the result from the top of the results stack.
408 */
bc_program_operand(BcProgram * p,BcResult ** r,BcNum ** n,size_t idx)409 static void bc_program_operand(BcProgram *p, BcResult **r,
410 BcNum **n, size_t idx)
411 {
412 *r = bc_vec_item_rev(&p->results, idx);
413
414 #if BC_ENABLED
415 if (BC_ERR((*r)->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL);
416 #endif // BC_ENABLED
417
418 *n = bc_program_num(p, *r);
419 }
420
421 /**
422 * Prepares the operands of a binary operator.
423 * @param p The program.
424 * @param l An out parameter; this is set to the pointer to the result for
425 * the left operand.
426 * @param ln An out parameter; this is set to the pointer to the number for
427 * the left operand.
428 * @param r An out parameter; this is set to the pointer to the result for
429 * the right operand.
430 * @param rn An out parameter; this is set to the pointer to the number for
431 * the right operand.
432 * @param idx The starting index where the operands are in the results stack,
433 * starting from the top.
434 */
bc_program_binPrep(BcProgram * p,BcResult ** l,BcNum ** ln,BcResult ** r,BcNum ** rn,size_t idx)435 static void bc_program_binPrep(BcProgram *p, BcResult **l, BcNum **ln,
436 BcResult **r, BcNum **rn, size_t idx)
437 {
438 BcResultType lt;
439
440 assert(p != NULL && l != NULL && ln != NULL && r != NULL && rn != NULL);
441
442 #ifndef BC_PROG_NO_STACK_CHECK
443 // Check the stack for dc.
444 if (BC_IS_DC) {
445 if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 2)))
446 bc_err(BC_ERR_EXEC_STACK);
447 }
448 #endif // BC_PROG_NO_STACK_CHECK
449
450 assert(BC_PROG_STACK(&p->results, idx + 2));
451
452 // Get the operands.
453 bc_program_operand(p, l, ln, idx + 1);
454 bc_program_operand(p, r, rn, idx);
455
456 lt = (*l)->t;
457
458 #if BC_ENABLED
459 // bc_program_operand() checked these for us.
460 assert(lt != BC_RESULT_VOID && (*r)->t != BC_RESULT_VOID);
461 #endif // BC_ENABLED
462
463 // We run this again under these conditions in case any vector has been
464 // reallocated out from under the BcNums or arrays we had. In other words,
465 // this is to fix pointer invalidation.
466 if (lt == (*r)->t && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM))
467 *ln = bc_program_num(p, *l);
468
469 if (BC_ERR(lt == BC_RESULT_STR)) bc_err(BC_ERR_EXEC_TYPE);
470 }
471
472 /**
473 * Prepares the operands of a binary operator and type checks them. This is
474 * separate from bc_program_binPrep() because some places want this, others want
475 * bc_program_binPrep().
476 * @param p The program.
477 * @param l An out parameter; this is set to the pointer to the result for
478 * the left operand.
479 * @param ln An out parameter; this is set to the pointer to the number for
480 * the left operand.
481 * @param r An out parameter; this is set to the pointer to the result for
482 * the right operand.
483 * @param rn An out parameter; this is set to the pointer to the number for
484 * the right operand.
485 * @param idx The starting index where the operands are in the results stack,
486 * starting from the top.
487 */
bc_program_binOpPrep(BcProgram * p,BcResult ** l,BcNum ** ln,BcResult ** r,BcNum ** rn,size_t idx)488 static void bc_program_binOpPrep(BcProgram *p, BcResult **l, BcNum **ln,
489 BcResult **r, BcNum **rn, size_t idx)
490 {
491 bc_program_binPrep(p, l, ln, r, rn, idx);
492 bc_program_type_num(*l, *ln);
493 bc_program_type_num(*r, *rn);
494 }
495
496 /**
497 * Prepares the operands of an assignment operator.
498 * @param p The program.
499 * @param l An out parameter; this is set to the pointer to the result for the
500 * left operand.
501 * @param ln An out parameter; this is set to the pointer to the number for the
502 * left operand.
503 * @param r An out parameter; this is set to the pointer to the result for the
504 * right operand.
505 * @param rn An out parameter; this is set to the pointer to the number for the
506 * right operand.
507 */
bc_program_assignPrep(BcProgram * p,BcResult ** l,BcNum ** ln,BcResult ** r,BcNum ** rn)508 static void bc_program_assignPrep(BcProgram *p, BcResult **l, BcNum **ln,
509 BcResult **r, BcNum **rn)
510 {
511 BcResultType lt, min;
512
513 // This is the min non-allowable result type. dc allows strings.
514 min = BC_RESULT_TEMP - ((unsigned int) (BC_IS_BC));
515
516 // Prepare the operands.
517 bc_program_binPrep(p, l, ln, r, rn, 0);
518
519 lt = (*l)->t;
520
521 // Typecheck the left.
522 if (BC_ERR(lt >= min && lt <= BC_RESULT_ONE)) bc_err(BC_ERR_EXEC_TYPE);
523
524 // Strings can be assigned to variables. We are already good if we are
525 // assigning a string.
526 bool good = ((*r)->t == BC_RESULT_STR && lt <= BC_RESULT_ARRAY_ELEM);
527
528 assert(BC_PROG_STR(*rn) || (*r)->t != BC_RESULT_STR);
529
530 // If not, type check for a number.
531 if (!good) bc_program_type_num(*r, *rn);
532 }
533
534 /**
535 * Prepares a single operand and type checks it. This is separate from
536 * bc_program_operand() because different places want one or the other.
537 * @param p The program.
538 * @param r An out parameter; this is set to the pointer to the result that
539 * we care about.
540 * @param n An out parameter; this is set to the pointer to the number that
541 * we care about.
542 * @param idx The index of the result from the top of the results stack.
543 */
bc_program_prep(BcProgram * p,BcResult ** r,BcNum ** n,size_t idx)544 static void bc_program_prep(BcProgram *p, BcResult **r, BcNum **n, size_t idx) {
545
546 assert(p != NULL && r != NULL && n != NULL);
547
548 #ifndef BC_PROG_NO_STACK_CHECK
549 // Check the stack for dc.
550 if (BC_IS_DC) {
551 if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1)))
552 bc_err(BC_ERR_EXEC_STACK);
553 }
554 #endif // BC_PROG_NO_STACK_CHECK
555
556 assert(BC_PROG_STACK(&p->results, idx + 1));
557
558 bc_program_operand(p, r, n, idx);
559
560 // dc does not allow strings in this case.
561 bc_program_type_num(*r, *n);
562 }
563
564 /**
565 * Prepares and returns a clean result for the result of an operation.
566 * @param p The program.
567 * @return A clean result.
568 */
bc_program_prepResult(BcProgram * p)569 static BcResult* bc_program_prepResult(BcProgram *p) {
570
571 BcResult *res = bc_vec_pushEmpty(&p->results);
572
573 bc_result_clear(res);
574
575 return res;
576 }
577
578 /**
579 * Prepares a constant for use. This parses the constant into a number and then
580 * pushes that number onto the results stack.
581 * @param p The program.
582 * @param code The bytecode vector that we will pull the index of the constant
583 * from.
584 * @param bgn An in/out parameter; marks the start of the index in the
585 * bytecode vector and will be updated to point to after the index.
586 */
bc_program_const(BcProgram * p,const char * code,size_t * bgn)587 static void bc_program_const(BcProgram *p, const char *code, size_t *bgn) {
588
589 // I lied. I actually push the result first. I can do this because the
590 // result will be popped on error. I also get the constant itself.
591 BcResult *r = bc_program_prepResult(p);
592 BcConst *c = bc_vec_item(p->consts, bc_program_index(code, bgn));
593 BcBigDig base = BC_PROG_IBASE(p);
594
595 // Only reparse if the base changed.
596 if (c->base != base) {
597
598 // Allocate if we haven't yet.
599 if (c->num.num == NULL) {
600 BC_SIG_LOCK;
601 bc_num_init(&c->num, BC_NUM_RDX(strlen(c->val)));
602 BC_SIG_UNLOCK;
603 }
604
605 // bc_num_parse() should only do operations that cannot fail.
606 bc_num_parse(&c->num, c->val, base);
607
608 c->base = base;
609 }
610
611 BC_SIG_LOCK;
612
613 bc_num_createCopy(&r->d.n, &c->num);
614
615 BC_SIG_UNLOCK;
616 }
617
618 /**
619 * Executes a binary operator operation.
620 * @param p The program.
621 * @param inst The instruction corresponding to the binary operator to execute.
622 */
bc_program_op(BcProgram * p,uchar inst)623 static void bc_program_op(BcProgram *p, uchar inst) {
624
625 BcResult *opd1, *opd2, *res;
626 BcNum *n1, *n2;
627 size_t idx = inst - BC_INST_POWER;
628
629 res = bc_program_prepResult(p);
630
631 bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);
632
633 BC_SIG_LOCK;
634
635 // Initialize the number with enough space, using the correct
636 // BcNumBinaryOpReq function. This looks weird because it is executing an
637 // item of an array. Rest assured that item is a function.
638 bc_num_init(&res->d.n, bc_program_opReqs[idx](n1, n2, BC_PROG_SCALE(p)));
639
640 BC_SIG_UNLOCK;
641
642 assert(BC_NUM_RDX_VALID(n1));
643 assert(BC_NUM_RDX_VALID(n2));
644
645 // Run the operation. This also executes an item of an array.
646 bc_program_ops[idx](n1, n2, &res->d.n, BC_PROG_SCALE(p));
647
648 bc_program_retire(p, 1, 2);
649 }
650
651 /**
652 * Executes a read() or ? command.
653 * @param p The program.
654 */
bc_program_read(BcProgram * p)655 static void bc_program_read(BcProgram *p) {
656
657 BcStatus s;
658 BcInstPtr ip;
659 size_t i;
660 const char* file;
661 bool is_stdin;
662 BcFunc *f = bc_vec_item(&p->fns, BC_PROG_READ);
663
664 // If we are already executing a read, that is an error. So look for a read
665 // and barf.
666 for (i = 0; i < p->stack.len; ++i) {
667 BcInstPtr *ip_ptr = bc_vec_item(&p->stack, i);
668 if (ip_ptr->func == BC_PROG_READ) bc_err(BC_ERR_EXEC_REC_READ);
669 }
670
671 BC_SIG_LOCK;
672
673 // Save the filename because we are going to overwrite it.
674 file = vm.file;
675 is_stdin = vm.is_stdin;
676
677 // It is a parse error if there needs to be more than one line, so we unset
678 // this to tell the lexer to not request more. We set it back later.
679 vm.is_stdin = false;
680
681 if (!BC_PARSE_IS_INITED(&vm.read_prs, p)) {
682
683 // We need to parse, but we don't want to use the existing parser
684 // because it has state it needs to keep. (It could have a partial parse
685 // state.) So we create a new parser. This parser is in the BcVm struct
686 // so that it is not local, which means that a longjmp() could change
687 // it.
688 bc_parse_init(&vm.read_prs, p, BC_PROG_READ);
689
690 // We need a separate input buffer; that's why it is also in the BcVm
691 // struct.
692 bc_vec_init(&vm.read_buf, sizeof(char), BC_DTOR_NONE);
693 }
694 // This needs to be updated because the parser could have been used
695 // somewhere else
696 else bc_parse_updateFunc(&vm.read_prs, BC_PROG_READ);
697
698 BC_SETJMP_LOCKED(exec_err);
699
700 BC_SIG_UNLOCK;
701
702 // Set up the lexer and the read function.
703 bc_lex_file(&vm.read_prs.l, bc_program_stdin_name);
704 bc_vec_popAll(&f->code);
705
706 // Read a line.
707 if (!BC_R) s = bc_read_line(&vm.read_buf, "");
708 else s = bc_read_line(&vm.read_buf, BC_IS_BC ? "read> " : "?> ");
709
710 // We should *not* have run into EOF.
711 if (s == BC_STATUS_EOF) bc_err(BC_ERR_EXEC_READ_EXPR);
712
713 // Parse *one* expression.
714 bc_parse_text(&vm.read_prs, vm.read_buf.v, false);
715 BC_SIG_LOCK;
716 vm.expr(&vm.read_prs, BC_PARSE_NOREAD | BC_PARSE_NEEDVAL);
717 BC_SIG_UNLOCK;
718
719 // We *must* have a valid expression. A semicolon cannot end an expression,
720 // although EOF can.
721 if (BC_ERR(vm.read_prs.l.t != BC_LEX_NLINE &&
722 vm.read_prs.l.t != BC_LEX_EOF))
723 {
724 bc_err(BC_ERR_EXEC_READ_EXPR);
725 }
726
727 #if BC_ENABLED
728 // Push on the globals stack if necessary.
729 if (BC_G) bc_program_prepGlobals(p);
730 #endif // BC_ENABLED
731
732 // Set up a new BcInstPtr.
733 ip.func = BC_PROG_READ;
734 ip.idx = 0;
735 ip.len = p->results.len;
736
737 // Update this pointer, just in case.
738 f = bc_vec_item(&p->fns, BC_PROG_READ);
739
740 // We want a return instruction to simplify things.
741 bc_vec_pushByte(&f->code, vm.read_ret);
742
743 // This lock is here to make sure dc's tail calls are the same length.
744 BC_SIG_LOCK;
745 bc_vec_push(&p->stack, &ip);
746
747 #if DC_ENABLED
748 // We need a new tail call entry for dc.
749 if (BC_IS_DC) {
750 size_t temp = 0;
751 bc_vec_push(&p->tail_calls, &temp);
752 }
753 #endif // DC_ENABLED
754
755 exec_err:
756 BC_SIG_MAYLOCK;
757 vm.is_stdin = is_stdin;
758 vm.file = file;
759 BC_LONGJMP_CONT;
760 }
761
762 #if BC_ENABLE_EXTRA_MATH
763
764 /**
765 * Execute a rand().
766 * @param p The program.
767 */
bc_program_rand(BcProgram * p)768 static void bc_program_rand(BcProgram *p) {
769
770 BcRand rand = bc_rand_int(&p->rng);
771
772 bc_program_pushBigdig(p, (BcBigDig) rand, BC_RESULT_TEMP);
773
774 #ifndef NDEBUG
775 // This is just to ensure that the generated number is correct. I also use
776 // braces because I declare every local at the top of the scope.
777 {
778 BcResult *r = bc_vec_top(&p->results);
779 assert(BC_NUM_RDX_VALID_NP(r->d.n));
780 }
781 #endif // NDEBUG
782 }
783 #endif // BC_ENABLE_EXTRA_MATH
784
785 /**
786 * Prints a series of characters, without escapes.
787 * @param str The string (series of characters).
788 */
bc_program_printChars(const char * str)789 static void bc_program_printChars(const char *str) {
790
791 const char *nl;
792 size_t len = vm.nchars + strlen(str);
793 sig_atomic_t lock;
794
795 BC_SIG_TRYLOCK(lock);
796
797 bc_file_puts(&vm.fout, bc_flush_save, str);
798
799 // We need to update the number of characters, so we find the last newline
800 // and set the characters accordingly.
801 nl = strrchr(str, '\n');
802
803 if (nl != NULL) len = strlen(nl + 1);
804
805 vm.nchars = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len;
806
807 BC_SIG_TRYUNLOCK(lock);
808 }
809
810 /**
811 * Prints a string with escapes.
812 * @param str The string.
813 */
bc_program_printString(const char * restrict str)814 static void bc_program_printString(const char *restrict str) {
815
816 size_t i, len = strlen(str);
817
818 #if DC_ENABLED
819 // This is to ensure a nul byte is printed for dc's stream operation.
820 if (!len && BC_IS_DC) {
821 bc_vm_putchar('\0', bc_flush_save);
822 return;
823 }
824 #endif // DC_ENABLED
825
826 // Loop over the characters, processing escapes and printing the rest.
827 for (i = 0; i < len; ++i) {
828
829 int c = str[i];
830
831 // If we have an escape...
832 if (c == '\\' && i != len - 1) {
833
834 const char *ptr;
835
836 // Get the escape character and its companion.
837 c = str[++i];
838 ptr = strchr(bc_program_esc_chars, c);
839
840 // If we have a companion character...
841 if (ptr != NULL) {
842
843 // We need to specially handle a newline.
844 if (c == 'n') {
845 BC_SIG_LOCK;
846 vm.nchars = UINT16_MAX;
847 BC_SIG_UNLOCK;
848 }
849
850 // Grab the actual character.
851 c = bc_program_esc_seqs[(size_t) (ptr - bc_program_esc_chars)];
852 }
853 else {
854 // Just print the backslash if there is no companion character.
855 // The following character will be printed later after the outer
856 // if statement.
857 bc_vm_putchar('\\', bc_flush_save);
858 }
859 }
860
861 bc_vm_putchar(c, bc_flush_save);
862 }
863 }
864
865 /**
866 * Executes a print. This function handles all printing except streaming.
867 * @param p The program.
868 * @param inst The instruction for the type of print we are doing.
869 * @param idx The index of the result that we are printing.
870 */
bc_program_print(BcProgram * p,uchar inst,size_t idx)871 static void bc_program_print(BcProgram *p, uchar inst, size_t idx) {
872
873 BcResult *r;
874 char *str;
875 BcNum *n;
876 bool pop = (inst != BC_INST_PRINT);
877
878 assert(p != NULL);
879
880 #ifndef BC_PROG_NO_STACK_CHECK
881 if (BC_IS_DC) {
882 if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1)))
883 bc_err(BC_ERR_EXEC_STACK);
884 }
885 #endif // BC_PROG_NO_STACK_CHECK
886
887 assert(BC_PROG_STACK(&p->results, idx + 1));
888
889 r = bc_vec_item_rev(&p->results, idx);
890
891 #if BC_ENABLED
892 // If we have a void value, that's not necessarily an error. It is if pop is
893 // true because that means that we are executing a print statement, but
894 // attempting to do a print on a lone void value is allowed because that's
895 // exactly how we want void values used.
896 if (r->t == BC_RESULT_VOID) {
897 if (BC_ERR(pop)) bc_err(BC_ERR_EXEC_VOID_VAL);
898 bc_vec_pop(&p->results);
899 return;
900 }
901 #endif // BC_ENABLED
902
903 n = bc_program_num(p, r);
904
905 // If we have a number...
906 if (BC_PROG_NUM(r, n)) {
907
908 #if BC_ENABLED
909 assert(inst != BC_INST_PRINT_STR);
910 #endif // BC_ENABLED
911
912 // Print the number.
913 bc_num_print(n, BC_PROG_OBASE(p), !pop);
914
915 #if BC_ENABLED
916 // Need to store the number in last.
917 if (BC_IS_BC) bc_num_copy(&p->last, n);
918 #endif // BC_ENABLED
919 }
920 else {
921
922 // We want to flush any stuff in the stdout buffer first.
923 bc_file_flush(&vm.fout, bc_flush_save);
924 str = bc_program_string(p, n);
925
926 #if BC_ENABLED
927 if (inst == BC_INST_PRINT_STR) bc_program_printChars(str);
928 else
929 #endif // BC_ENABLED
930 {
931 bc_program_printString(str);
932
933 // Need to print a newline only in this case.
934 if (inst == BC_INST_PRINT)
935 bc_vm_putchar('\n', bc_flush_err);
936 }
937 }
938
939 // bc always pops.
940 if (BC_IS_BC || pop) bc_vec_pop(&p->results);
941 }
942
bc_program_negate(BcResult * r,BcNum * n)943 void bc_program_negate(BcResult *r, BcNum *n) {
944 bc_num_copy(&r->d.n, n);
945 if (BC_NUM_NONZERO(&r->d.n)) BC_NUM_NEG_TGL_NP(r->d.n);
946 }
947
bc_program_not(BcResult * r,BcNum * n)948 void bc_program_not(BcResult *r, BcNum *n) {
949 if (!bc_num_cmpZero(n)) bc_num_one(&r->d.n);
950 }
951
952 #if BC_ENABLE_EXTRA_MATH
bc_program_trunc(BcResult * r,BcNum * n)953 void bc_program_trunc(BcResult *r, BcNum *n) {
954 bc_num_copy(&r->d.n, n);
955 bc_num_truncate(&r->d.n, n->scale);
956 }
957 #endif // BC_ENABLE_EXTRA_MATH
958
959 /**
960 * Runs a unary operation.
961 * @param p The program.
962 * @param inst The unary operation.
963 */
bc_program_unary(BcProgram * p,uchar inst)964 static void bc_program_unary(BcProgram *p, uchar inst) {
965
966 BcResult *res, *ptr;
967 BcNum *num;
968
969 res = bc_program_prepResult(p);
970
971 bc_program_prep(p, &ptr, &num, 1);
972
973 BC_SIG_LOCK;
974
975 bc_num_init(&res->d.n, num->len);
976
977 BC_SIG_UNLOCK;
978
979 // This calls a function that is in an array.
980 bc_program_unarys[inst - BC_INST_NEG](res, num);
981 bc_program_retire(p, 1, 1);
982 }
983
984 /**
985 * Executes a logical operator.
986 * @param p The program.
987 * @param inst The operator.
988 */
bc_program_logical(BcProgram * p,uchar inst)989 static void bc_program_logical(BcProgram *p, uchar inst) {
990
991 BcResult *opd1, *opd2, *res;
992 BcNum *n1, *n2;
993 bool cond = 0;
994 ssize_t cmp;
995
996 res = bc_program_prepResult(p);
997
998 // All logical operators (except boolean not, which is taken care of by
999 // bc_program_unary()), are binary operators.
1000 bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);
1001
1002 // Boolean and and or are not short circuiting. This is why; they can be
1003 // implemented much easier this way.
1004 if (inst == BC_INST_BOOL_AND)
1005 cond = (bc_num_cmpZero(n1) && bc_num_cmpZero(n2));
1006 else if (inst == BC_INST_BOOL_OR)
1007 cond = (bc_num_cmpZero(n1) || bc_num_cmpZero(n2));
1008 else {
1009
1010 // We have a relational operator, so do a comparison.
1011 cmp = bc_num_cmp(n1, n2);
1012
1013 switch (inst) {
1014
1015 case BC_INST_REL_EQ:
1016 {
1017 cond = (cmp == 0);
1018 break;
1019 }
1020
1021 case BC_INST_REL_LE:
1022 {
1023 cond = (cmp <= 0);
1024 break;
1025 }
1026
1027 case BC_INST_REL_GE:
1028 {
1029 cond = (cmp >= 0);
1030 break;
1031 }
1032
1033 case BC_INST_REL_NE:
1034 {
1035 cond = (cmp != 0);
1036 break;
1037 }
1038
1039 case BC_INST_REL_LT:
1040 {
1041 cond = (cmp < 0);
1042 break;
1043 }
1044
1045 case BC_INST_REL_GT:
1046 {
1047 cond = (cmp > 0);
1048 break;
1049 }
1050 #ifndef NDEBUG
1051 default:
1052 {
1053 // There is a bug if we get here.
1054 abort();
1055 }
1056 #endif // NDEBUG
1057 }
1058 }
1059
1060 BC_SIG_LOCK;
1061
1062 bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);
1063
1064 BC_SIG_UNLOCK;
1065
1066 if (cond) bc_num_one(&res->d.n);
1067
1068 bc_program_retire(p, 1, 2);
1069 }
1070
1071 /**
1072 * Assigns a string to a variable.
1073 * @param p The program.
1074 * @param num The location of the string as a BcNum.
1075 * @param v The stack for the variable.
1076 * @param push Whether to push the string or not. To push means to move the
1077 * string from the results stack and push it onto the variable
1078 * stack.
1079 */
bc_program_assignStr(BcProgram * p,BcNum * num,BcVec * v,bool push)1080 static void bc_program_assignStr(BcProgram *p, BcNum *num, BcVec *v, bool push)
1081 {
1082 BcNum *n;
1083
1084 assert(BC_PROG_STACK(&p->results, 1 + !push));
1085 assert(num != NULL && num->num == NULL && num->cap == 0);
1086
1087 // If we are not pushing onto the variable stack, we need to replace the
1088 // top of the variable stack.
1089 if (!push) bc_vec_pop(v);
1090
1091 bc_vec_npop(&p->results, 1 + !push);
1092
1093 n = bc_vec_pushEmpty(v);
1094
1095 // We can just copy because the num should not have allocated anything.
1096 memcpy(n, num, sizeof(BcNum));
1097 }
1098
1099 /**
1100 * Copies a value to a variable. This is used for storing in dc as well as to
1101 * set function parameters to arguments in bc.
1102 * @param p The program.
1103 * @param idx The index of the variable or array to copy to.
1104 * @param t The type to copy to. This could be a variable or an array.
1105 * @param last Whether to grab the last item on the variable stack or not (for
1106 * bc function parameters). This is important because if a new
1107 * value has been pushed to the variable already, we need to grab
1108 * the value pushed before. This happens when you have a parameter
1109 * named something like "x", and a variable "x" is passed to
1110 * another parameter.
1111 */
bc_program_copyToVar(BcProgram * p,size_t idx,BcType t,bool last)1112 static void bc_program_copyToVar(BcProgram *p, size_t idx, BcType t, bool last)
1113 {
1114 BcResult *ptr = NULL, r;
1115 BcVec *vec;
1116 BcNum *n = NULL;
1117 bool var = (t == BC_TYPE_VAR);
1118
1119 #if DC_ENABLED
1120 // Check the stack for dc.
1121 if (BC_IS_DC) {
1122 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
1123 }
1124 #endif
1125
1126 assert(BC_PROG_STACK(&p->results, 1));
1127
1128 bc_program_operand(p, &ptr, &n, 0);
1129
1130 #if BC_ENABLED
1131 // Get the variable for a bc function call.
1132 if (BC_IS_BC)
1133 {
1134 // Type match the result.
1135 bc_program_type_match(ptr, t);
1136
1137 // Get the variable or array, taking care to get the real item. We take
1138 // care of last with arrays later.
1139 if (!last && var)
1140 n = bc_vec_item_rev(bc_program_vec(p, ptr->d.loc.loc, t), 1);
1141 }
1142 #endif // BC_ENABLED
1143
1144 vec = bc_program_vec(p, idx, t);
1145
1146 // We can shortcut in dc if it's assigning a string by using
1147 // bc_program_assignStr().
1148 if (ptr->t == BC_RESULT_STR) {
1149
1150 assert(BC_PROG_STR(n));
1151
1152 if (BC_ERR(!var)) bc_err(BC_ERR_EXEC_TYPE);
1153
1154 bc_program_assignStr(p, n, vec, true);
1155
1156 return;
1157 }
1158
1159 BC_SIG_LOCK;
1160
1161 // Just create and copy for a normal variable.
1162 if (var) {
1163 if (BC_PROG_STR(n)) memcpy(&r.d.n, n, sizeof(BcNum));
1164 else bc_num_createCopy(&r.d.n, n);
1165 }
1166 else {
1167
1168 // If we get here, we are handling an array. This is one place we need
1169 // to cast the number from bc_program_num() to a vector.
1170 BcVec *v = (BcVec*) n, *rv = &r.d.v;
1171
1172 #if BC_ENABLED
1173
1174 if (BC_IS_BC) {
1175
1176 BcVec *parent;
1177 bool ref, ref_size;
1178
1179 // We need to figure out if the parameter is a reference or not and
1180 // construct the reference vector, if necessary. So this gets the
1181 // parent stack for the array.
1182 parent = bc_program_vec(p, ptr->d.loc.loc, t);
1183 assert(parent != NULL);
1184
1185 // This takes care of last for arrays. Mostly.
1186 if (!last) v = bc_vec_item_rev(parent, !last);
1187 assert(v != NULL);
1188
1189 // True if we are using a reference.
1190 ref = (v->size == sizeof(BcNum) && t == BC_TYPE_REF);
1191
1192 // True if we already have a reference vector. This is slightly
1193 // (okay, a lot; it just doesn't look that way) different from
1194 // above. The above means that we need to construct a reference
1195 // vector, whereas this means that we have one and we might have to
1196 // *dereference* it.
1197 ref_size = (v->size == sizeof(uchar));
1198
1199 // If we *should* have a reference.
1200 if (ref || (ref_size && t == BC_TYPE_REF)) {
1201
1202 // Create a new reference vector.
1203 bc_vec_init(rv, sizeof(uchar), BC_DTOR_NONE);
1204
1205 // If this is true, then we need to construct a reference.
1206 if (ref) {
1207
1208 assert(parent->len >= (size_t) (!last + 1));
1209
1210 // Make sure the pointer was not invalidated.
1211 vec = bc_program_vec(p, idx, t);
1212
1213 // Push the indices onto the reference vector. This takes
1214 // care of last; it ensures the reference goes to the right
1215 // place.
1216 bc_vec_pushIndex(rv, ptr->d.loc.loc);
1217 bc_vec_pushIndex(rv, parent->len - !last - 1);
1218 }
1219 // If we get here, we are copying a ref to a ref. Just push a
1220 // copy of all of the bytes.
1221 else bc_vec_npush(rv, v->len * sizeof(uchar), v->v);
1222
1223 // Push the reference vector onto the array stack and pop the
1224 // source.
1225 bc_vec_push(vec, &r.d);
1226 bc_vec_pop(&p->results);
1227
1228 // We need to return early to avoid executing code that we must
1229 // not touch.
1230 BC_SIG_UNLOCK;
1231 return;
1232 }
1233 // If we get here, we have a reference, but we need an array, so
1234 // dereference the array.
1235 else if (ref_size && t != BC_TYPE_REF)
1236 v = bc_program_dereference(p, v);
1237 }
1238 #endif // BC_ENABLED
1239
1240 // If we get here, we need to copy the array because in bc, all
1241 // arguments are passed by value. Yes, this is expensive.
1242 bc_array_init(rv, true);
1243 bc_array_copy(rv, v);
1244 }
1245
1246 // Push the vector onto the array stack and pop the source.
1247 bc_vec_push(vec, &r.d);
1248 bc_vec_pop(&p->results);
1249
1250 BC_SIG_UNLOCK;
1251 }
1252
1253 /**
1254 * Executes an assignment operator.
1255 * @param p The program.
1256 * @param inst The assignment operator to execute.
1257 */
bc_program_assign(BcProgram * p,uchar inst)1258 static void bc_program_assign(BcProgram *p, uchar inst) {
1259
1260 // The local use_val is true when the assigned value needs to be copied.
1261 BcResult *left, *right, res;
1262 BcNum *l, *r;
1263 bool ob, sc, use_val = BC_INST_USE_VAL(inst);
1264
1265 bc_program_assignPrep(p, &left, &l, &right, &r);
1266
1267 // Assigning to a string should be impossible simply because of the parse.
1268 assert(left->t != BC_RESULT_STR);
1269
1270 // If we are assigning a string...
1271 if (right->t == BC_RESULT_STR) {
1272
1273 assert(BC_PROG_STR(r));
1274
1275 #if BC_ENABLED
1276 if (inst != BC_INST_ASSIGN && inst != BC_INST_ASSIGN_NO_VAL)
1277 bc_err(BC_ERR_EXEC_TYPE);
1278 #endif // BC_ENABLED
1279
1280 // If we are assigning to an array element...
1281 if (left->t == BC_RESULT_ARRAY_ELEM) {
1282
1283 BC_SIG_LOCK;
1284
1285 // We need to free the number and clear it.
1286 bc_num_free(l);
1287
1288 memcpy(l, r, sizeof(BcNum));
1289
1290 // Now we can pop the results.
1291 bc_vec_npop(&p->results, 2);
1292
1293 BC_SIG_UNLOCK;
1294 }
1295 else {
1296
1297 // If we get here, we are assigning to a variable, which we can use
1298 // bc_program_assignStr() for.
1299 BcVec *v = bc_program_vec(p, left->d.loc.loc, BC_TYPE_VAR);
1300 bc_program_assignStr(p, r, v, false);
1301 }
1302
1303 #if BC_ENABLED
1304
1305 // If this is true, the value is going to be used again, so we want to
1306 // push a temporary with the string.
1307 if (inst == BC_INST_ASSIGN) {
1308 res.t = BC_RESULT_STR;
1309 memcpy(&res.d.n, r, sizeof(BcNum));
1310 bc_vec_push(&p->results, &res);
1311 }
1312
1313 #endif // BC_ENABLED
1314
1315 // By using bc_program_assignStr(), we short-circuited this, so return.
1316 return;
1317 }
1318
1319 // If we have a normal assignment operator, not a math one...
1320 if (BC_INST_IS_ASSIGN(inst)) {
1321
1322 // Assigning to a variable that has a string here is fine because there
1323 // is no math done on it.
1324
1325 // BC_RESULT_TEMP, BC_RESULT_IBASE, BC_RESULT_OBASE, BC_RESULT_SCALE,
1326 // and BC_RESULT_SEED all have temporary copies. Because that's the
1327 // case, we can free the left and just move the value over. We set the
1328 // type of right to BC_RESULT_ZERO in order to prevent it from being
1329 // freed. We also don't have to worry about BC_RESULT_STR because it's
1330 // take care of above.
1331 if (right->t == BC_RESULT_TEMP || right->t >= BC_RESULT_IBASE) {
1332
1333 BC_SIG_LOCK;
1334
1335 bc_num_free(l);
1336 memcpy(l, r, sizeof(BcNum));
1337 right->t = BC_RESULT_ZERO;
1338
1339 BC_SIG_UNLOCK;
1340 }
1341 // Copy over.
1342 else bc_num_copy(l, r);
1343 }
1344 #if BC_ENABLED
1345 else {
1346
1347 // If we get here, we are doing a math assignment (+=, -=, etc.). So
1348 // we need to prepare for a binary operator.
1349 BcBigDig scale = BC_PROG_SCALE(p);
1350
1351 // At this point, the left side could still be a string because it could
1352 // be a variable that has the string. If that's the case, we have a type
1353 // error.
1354 if (BC_PROG_STR(l)) bc_err(BC_ERR_EXEC_TYPE);
1355
1356 // Get the right type of assignment operator, whether val is used or
1357 // NO_VAL for performance.
1358 if (!use_val)
1359 inst -= (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER);
1360
1361 assert(BC_NUM_RDX_VALID(l));
1362 assert(BC_NUM_RDX_VALID(r));
1363
1364 // Run the actual operation. We do not need worry about reallocating l
1365 // because bc_num_binary() does that behind the scenes for us.
1366 bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, scale);
1367 }
1368 #endif // BC_ENABLED
1369
1370 ob = (left->t == BC_RESULT_OBASE);
1371 sc = (left->t == BC_RESULT_SCALE);
1372
1373 // The globals need special handling, especially the non-seed ones. The
1374 // first part of the if statement handles them.
1375 if (ob || sc || left->t == BC_RESULT_IBASE) {
1376
1377 BcVec *v;
1378 BcBigDig *ptr, *ptr_t, val, max, min;
1379
1380 // Get the actual value.
1381 val = bc_num_bigdig(l);
1382
1383 // Scale needs handling separate from ibase and obase.
1384 if (sc) {
1385
1386 // Set the min and max.
1387 min = 0;
1388 max = vm.maxes[BC_PROG_GLOBALS_SCALE];
1389
1390 // Get a pointer to the stack and to the current value.
1391 v = p->globals_v + BC_PROG_GLOBALS_SCALE;
1392 ptr_t = p->globals + BC_PROG_GLOBALS_SCALE;
1393 }
1394 else {
1395
1396 // Set the min and max.
1397 min = BC_NUM_MIN_BASE;
1398 if (BC_ENABLE_EXTRA_MATH && ob && (BC_IS_DC || !BC_IS_POSIX))
1399 min = 0;
1400 max = vm.maxes[ob + BC_PROG_GLOBALS_IBASE];
1401
1402 // Get a pointer to the stack and to the current value.
1403 v = p->globals_v + BC_PROG_GLOBALS_IBASE + ob;
1404 ptr_t = p->globals + BC_PROG_GLOBALS_IBASE + ob;
1405 }
1406
1407 // Check for error.
1408 if (BC_ERR(val > max || val < min)) {
1409
1410 // This grabs the right error.
1411 BcErr e = left->t - BC_RESULT_IBASE + BC_ERR_EXEC_IBASE;
1412
1413 bc_verr(e, min, max);
1414 }
1415
1416 // Set the top of the stack and the actual global value.
1417 ptr = bc_vec_top(v);
1418 *ptr = val;
1419 *ptr_t = val;
1420 }
1421 #if BC_ENABLE_EXTRA_MATH
1422 // To assign to steed, let bc_num_rng() do its magic.
1423 else if (left->t == BC_RESULT_SEED) bc_num_rng(l, &p->rng);
1424 #endif // BC_ENABLE_EXTRA_MATH
1425
1426 BC_SIG_LOCK;
1427
1428 // If we needed to use the value, then we need to copy it. Otherwise, we can
1429 // pop indiscriminately. Oh, and the copy should be a BC_RESULT_TEMP.
1430 if (use_val) {
1431 bc_num_createCopy(&res.d.n, l);
1432 res.t = BC_RESULT_TEMP;
1433 bc_vec_npop(&p->results, 2);
1434 bc_vec_push(&p->results, &res);
1435 }
1436 else bc_vec_npop(&p->results, 2);
1437
1438 BC_SIG_UNLOCK;
1439 }
1440
1441 /**
1442 * Pushes a variable's value onto the results stack.
1443 * @param p The program.
1444 * @param code The bytecode vector to pull the variable's index out of.
1445 * @param bgn An in/out parameter; the start of the index in the bytecode
1446 * vector, and will be updated to point after the index on return.
1447 * @param pop True if the variable's value should be popped off its stack.
1448 * This is only used in dc.
1449 * @param copy True if the variable's value should be copied to the results
1450 * stack. This is only used in dc.
1451 */
bc_program_pushVar(BcProgram * p,const char * restrict code,size_t * restrict bgn,bool pop,bool copy)1452 static void bc_program_pushVar(BcProgram *p, const char *restrict code,
1453 size_t *restrict bgn, bool pop, bool copy)
1454 {
1455 BcResult r;
1456 size_t idx = bc_program_index(code, bgn);
1457
1458 // Set the result appropriately.
1459 r.t = BC_RESULT_VAR;
1460 r.d.loc.loc = idx;
1461
1462 #if DC_ENABLED
1463 // If this condition is true, then we have the hard case, where we have to
1464 // adjust dc registers.
1465 if (BC_IS_DC && (pop || copy)) {
1466
1467 // Get the stack for the variable and the number at the top.
1468 BcVec *v = bc_program_vec(p, idx, BC_TYPE_VAR);
1469 BcNum *num = bc_vec_top(v);
1470
1471 // Ensure there are enough elements on the stack.
1472 if (BC_ERR(!BC_PROG_STACK(v, 2 - copy))) {
1473 const char *name = bc_map_name(&p->var_map, idx);
1474 bc_verr(BC_ERR_EXEC_STACK_REGISTER, name);
1475 }
1476
1477 assert(BC_PROG_STACK(v, 2 - copy));
1478
1479 // If the top of the stack is actually a number...
1480 if (!BC_PROG_STR(num)) {
1481
1482 BC_SIG_LOCK;
1483
1484 // Create a copy to go onto the results stack as appropriate.
1485 r.t = BC_RESULT_TEMP;
1486 bc_num_createCopy(&r.d.n, num);
1487
1488 // If we are not actually copying, we need to do a replace, so pop.
1489 if (!copy) bc_vec_pop(v);
1490
1491 bc_vec_push(&p->results, &r);
1492
1493 BC_SIG_UNLOCK;
1494
1495 return;
1496 }
1497 else {
1498 // Set the string result. We can just memcpy because all of the
1499 // fields in the num should be cleared.
1500 memcpy(&r.d.n, num, sizeof(BcNum));
1501 r.t = BC_RESULT_STR;
1502 }
1503
1504 // If we are not actually copying, we need to do a replace, so pop.
1505 if (!copy) bc_vec_pop(v);
1506 }
1507 #endif // DC_ENABLED
1508
1509 bc_vec_push(&p->results, &r);
1510 }
1511
1512 /**
1513 * Pushes an array or an array element onto the results stack.
1514 * @param p The program.
1515 * @param code The bytecode vector to pull the variable's index out of.
1516 * @param bgn An in/out parameter; the start of the index in the bytecode
1517 * vector, and will be updated to point after the index on return.
1518 * @param inst The instruction; whether to push an array or an array element.
1519 */
bc_program_pushArray(BcProgram * p,const char * restrict code,size_t * restrict bgn,uchar inst)1520 static void bc_program_pushArray(BcProgram *p, const char *restrict code,
1521 size_t *restrict bgn, uchar inst)
1522 {
1523 BcResult r, *operand;
1524 BcNum *num;
1525 BcBigDig temp;
1526
1527 // Get the index of the array.
1528 r.d.loc.loc = bc_program_index(code, bgn);
1529
1530 // Doing an array is easy; just set the result type and finish.
1531 if (inst == BC_INST_ARRAY) {
1532 r.t = BC_RESULT_ARRAY;
1533 bc_vec_push(&p->results, &r);
1534 return;
1535 }
1536
1537 // Grab the top element of the results stack for the array index.
1538 bc_program_prep(p, &operand, &num, 0);
1539 temp = bc_num_bigdig(num);
1540
1541 // Set the result.
1542 r.t = BC_RESULT_ARRAY_ELEM;
1543 r.d.loc.idx = (size_t) temp;
1544
1545 BC_SIG_LOCK;
1546
1547 // Pop the index and push the element.
1548 bc_vec_pop(&p->results);
1549 bc_vec_push(&p->results, &r);
1550
1551 BC_SIG_UNLOCK;
1552 }
1553
1554 #if BC_ENABLED
1555
1556 /**
1557 * Executes an increment or decrement operator. This only handles postfix
1558 * inc/dec because the parser translates prefix inc/dec into an assignment where
1559 * the value is used.
1560 * @param p The program.
1561 * @param inst The instruction; whether to do an increment or decrement.
1562 */
bc_program_incdec(BcProgram * p,uchar inst)1563 static void bc_program_incdec(BcProgram *p, uchar inst) {
1564
1565 BcResult *ptr, res, copy;
1566 BcNum *num;
1567 uchar inst2;
1568
1569 bc_program_prep(p, &ptr, &num, 0);
1570
1571 BC_SIG_LOCK;
1572
1573 // We need a copy from *before* the operation.
1574 copy.t = BC_RESULT_TEMP;
1575 bc_num_createCopy(©.d.n, num);
1576
1577 BC_SETJMP_LOCKED(exit);
1578
1579 BC_SIG_UNLOCK;
1580
1581 // Create the proper assignment.
1582 res.t = BC_RESULT_ONE;
1583 inst2 = BC_INST_ASSIGN_PLUS_NO_VAL + (inst & 0x01);
1584
1585 bc_vec_push(&p->results, &res);
1586 bc_program_assign(p, inst2);
1587
1588 BC_SIG_LOCK;
1589
1590 bc_vec_push(&p->results, ©);
1591
1592 BC_UNSETJMP;
1593
1594 BC_SIG_UNLOCK;
1595
1596 // No need to free the copy here because we pushed it onto the stack.
1597 return;
1598
1599 exit:
1600 BC_SIG_MAYLOCK;
1601 bc_num_free(©.d.n);
1602 BC_LONGJMP_CONT;
1603 }
1604
1605 /**
1606 * Executes a function call for bc.
1607 * @param p The program.
1608 * @param code The bytecode vector to pull the number of arguments and the
1609 * function index out of.
1610 * @param bgn An in/out parameter; the start of the indices in the bytecode
1611 * vector, and will be updated to point after the indices on
1612 * return.
1613 */
bc_program_call(BcProgram * p,const char * restrict code,size_t * restrict bgn)1614 static void bc_program_call(BcProgram *p, const char *restrict code,
1615 size_t *restrict bgn)
1616 {
1617 BcInstPtr ip;
1618 size_t i, nargs;
1619 BcFunc *f;
1620 BcVec *v;
1621 BcAuto *a;
1622 BcResult *arg;
1623
1624 // Pull the number of arguments out of the bytecode vector.
1625 nargs = bc_program_index(code, bgn);
1626
1627 // Set up instruction pointer.
1628 ip.idx = 0;
1629 ip.func = bc_program_index(code, bgn);
1630 f = bc_vec_item(&p->fns, ip.func);
1631
1632 // Error checking.
1633 if (BC_ERR(!f->code.len)) bc_verr(BC_ERR_EXEC_UNDEF_FUNC, f->name);
1634 if (BC_ERR(nargs != f->nparams))
1635 bc_verr(BC_ERR_EXEC_PARAMS, f->nparams, nargs);
1636
1637 // Set the length of the results stack. We discount the argument, of course.
1638 ip.len = p->results.len - nargs;
1639
1640 assert(BC_PROG_STACK(&p->results, nargs));
1641
1642 // Prepare the globals' stacks.
1643 if (BC_G) bc_program_prepGlobals(p);
1644
1645 // Push the arguments onto the stacks of their respective parameters.
1646 for (i = 0; i < nargs; ++i) {
1647
1648 size_t j;
1649 bool last = true;
1650
1651 arg = bc_vec_top(&p->results);
1652 if (BC_ERR(arg->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL);
1653
1654 // Get the corresponding parameter.
1655 a = bc_vec_item(&f->autos, nargs - 1 - i);
1656
1657 // If I have already pushed to a var, I need to make sure I
1658 // get the previous version, not the already pushed one. This condition
1659 // must be true for that to even be possible.
1660 if (arg->t == BC_RESULT_VAR || arg->t == BC_RESULT_ARRAY) {
1661
1662 // Loop through all of the previous parameters.
1663 for (j = 0; j < i && last; ++j) {
1664
1665 BcAuto *aptr = bc_vec_item(&f->autos, nargs - 1 - j);
1666
1667 // This condition is true if there is a previous parameter with
1668 // the same name *and* type because variables and arrays do not
1669 // interfere with each other.
1670 last = (arg->d.loc.loc != aptr->idx ||
1671 (!aptr->type) != (arg->t == BC_RESULT_VAR));
1672 }
1673 }
1674
1675 // Actually push the value onto the parameter's stack.
1676 bc_program_copyToVar(p, a->idx, a->type, last);
1677 }
1678
1679 BC_SIG_LOCK;
1680
1681 // Push zeroes onto the stacks of the auto variables.
1682 for (; i < f->autos.len; ++i) {
1683
1684 // Get the auto and its stack.
1685 a = bc_vec_item(&f->autos, i);
1686 v = bc_program_vec(p, a->idx, a->type);
1687
1688 // If a variable, just push a 0; otherwise, push an array.
1689 if (a->type == BC_TYPE_VAR) {
1690 BcNum *n = bc_vec_pushEmpty(v);
1691 bc_num_init(n, BC_NUM_DEF_SIZE);
1692 }
1693 else {
1694
1695 BcVec *v2;
1696
1697 assert(a->type == BC_TYPE_ARRAY);
1698
1699 v2 = bc_vec_pushEmpty(v);
1700 bc_array_init(v2, true);
1701 }
1702 }
1703
1704 // Push the instruction pointer onto the execution stack.
1705 bc_vec_push(&p->stack, &ip);
1706
1707 BC_SIG_UNLOCK;
1708 }
1709
1710 /**
1711 * Executes a return instruction.
1712 * @param p The program.
1713 * @param inst The return instruction. bc can return void, and we need to know
1714 * if it is.
1715 */
bc_program_return(BcProgram * p,uchar inst)1716 static void bc_program_return(BcProgram *p, uchar inst) {
1717
1718 BcResult *res;
1719 BcFunc *f;
1720 BcInstPtr *ip;
1721 size_t i, nresults;
1722
1723 // Get the instruction pointer.
1724 ip = bc_vec_top(&p->stack);
1725
1726 // Get the difference between the actual number of results and the number of
1727 // results the caller expects.
1728 nresults = p->results.len - ip->len;
1729
1730 // If this isn't true, there was a missing call somewhere.
1731 assert(BC_PROG_STACK(&p->stack, 2));
1732
1733 // If this isn't true, the parser screwed by giving us no value when we
1734 // expected one, or giving us a value when we expected none.
1735 assert(BC_PROG_STACK(&p->results, ip->len + (inst == BC_INST_RET)));
1736
1737 // Get the function we are returning from.
1738 f = bc_vec_item(&p->fns, ip->func);
1739
1740 res = bc_program_prepResult(p);
1741
1742 // If we are returning normally...
1743 if (inst == BC_INST_RET) {
1744
1745 BcNum *num;
1746 BcResult *operand;
1747
1748 // Prepare and copy the return value.
1749 bc_program_operand(p, &operand, &num, 1);
1750
1751 if (BC_PROG_STR(num)) {
1752
1753 // We need to set this because otherwise, it will be a
1754 // BC_RESULT_TEMP, and BC_RESULT_TEMP needs an actual number to make
1755 // it easier to do type checking.
1756 res->t = BC_RESULT_STR;
1757
1758 memcpy(&res->d.n, num, sizeof(BcNum));
1759 }
1760 else {
1761
1762 BC_SIG_LOCK;
1763
1764 bc_num_createCopy(&res->d.n, num);
1765 }
1766 }
1767 // Void is easy; set the result.
1768 else if (inst == BC_INST_RET_VOID) res->t = BC_RESULT_VOID;
1769 else {
1770
1771 BC_SIG_LOCK;
1772
1773 // If we get here, the instruction is for returning a zero, so do that.
1774 bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);
1775 }
1776
1777 BC_SIG_MAYUNLOCK;
1778
1779 // We need to pop items off of the stacks of arguments and autos as well.
1780 for (i = 0; i < f->autos.len; ++i) {
1781
1782 BcAuto *a = bc_vec_item(&f->autos, i);
1783 BcVec *v = bc_program_vec(p, a->idx, a->type);
1784
1785 bc_vec_pop(v);
1786 }
1787
1788 BC_SIG_LOCK;
1789
1790 // When we retire, pop all of the unused results.
1791 bc_program_retire(p, 1, nresults);
1792
1793 // Pop the globals, if necessary.
1794 if (BC_G) bc_program_popGlobals(p, false);
1795
1796 // Pop the stack. This is what causes the function to actually "return."
1797 bc_vec_pop(&p->stack);
1798
1799 BC_SIG_UNLOCK;
1800 }
1801 #endif // BC_ENABLED
1802
1803 /**
1804 * Executes a builtin function.
1805 * @param p The program.
1806 * @param inst The builtin to execute.
1807 */
bc_program_builtin(BcProgram * p,uchar inst)1808 static void bc_program_builtin(BcProgram *p, uchar inst) {
1809
1810 BcResult *opd, *res;
1811 BcNum *num;
1812 bool len = (inst == BC_INST_LENGTH);
1813
1814 // Ensure we have a valid builtin.
1815 #if BC_ENABLE_EXTRA_MATH
1816 assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IRAND);
1817 #else // BC_ENABLE_EXTRA_MATH
1818 assert(inst >= BC_INST_LENGTH && inst <= BC_INST_ABS);
1819 #endif // BC_ENABLE_EXTRA_MATH
1820
1821 #ifndef BC_PROG_NO_STACK_CHECK
1822 // Check stack for dc.
1823 if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 1)))
1824 bc_err(BC_ERR_EXEC_STACK);
1825 #endif // BC_PROG_NO_STACK_CHECK
1826
1827 assert(BC_PROG_STACK(&p->results, 1));
1828
1829 res = bc_program_prepResult(p);
1830
1831 bc_program_operand(p, &opd, &num, 1);
1832
1833 assert(num != NULL);
1834
1835 // We need to ensure that strings and arrays aren't passed to most builtins.
1836 // The scale function can take strings in dc.
1837 if (!len && (inst != BC_INST_SCALE_FUNC || BC_IS_BC))
1838 bc_program_type_num(opd, num);
1839
1840 // Square root is easy.
1841 if (inst == BC_INST_SQRT) bc_num_sqrt(num, &res->d.n, BC_PROG_SCALE(p));
1842
1843 // Absolute value is easy.
1844 else if (inst == BC_INST_ABS) {
1845
1846 BC_SIG_LOCK;
1847
1848 bc_num_createCopy(&res->d.n, num);
1849
1850 BC_SIG_UNLOCK;
1851
1852 BC_NUM_NEG_CLR_NP(res->d.n);
1853 }
1854 #if BC_ENABLE_EXTRA_MATH
1855 // irand() is easy.
1856 else if (inst == BC_INST_IRAND) {
1857
1858 BC_SIG_LOCK;
1859
1860 bc_num_init(&res->d.n, num->len - BC_NUM_RDX_VAL(num));
1861
1862 BC_SIG_UNLOCK;
1863
1864 bc_num_irand(num, &res->d.n, &p->rng);
1865 }
1866 #endif // BC_ENABLE_EXTRA_MATH
1867
1868 // Everything else is...not easy.
1869 else {
1870
1871 BcBigDig val = 0;
1872
1873 // Well, scale() is easy, but length() is not.
1874 if (len) {
1875
1876 // If we are bc and we have an array...
1877 if (opd->t == BC_RESULT_ARRAY) {
1878
1879 // Yes, this is one place where we need to cast the number from
1880 // bc_program_num() to a vector.
1881 BcVec *v = (BcVec*) num;
1882
1883 #if BC_ENABLED
1884 // Dereference the array, if necessary.
1885 if (BC_IS_BC && v->size == sizeof(uchar))
1886 v = bc_program_dereference(p, v);
1887 #endif // BC_ENABLED
1888
1889 assert(v->size == sizeof(BcNum));
1890
1891 val = (BcBigDig) v->len;
1892 }
1893 else {
1894
1895 // If the item is a string...
1896 if (!BC_PROG_NUM(opd, num)) {
1897
1898 char *str;
1899
1900 // Get the string, then get the length.
1901 str = bc_program_string(p, num);
1902 val = (BcBigDig) strlen(str);
1903 }
1904 else
1905 {
1906 // Calculate the length of the number.
1907 val = (BcBigDig) bc_num_len(num);
1908 }
1909 }
1910 }
1911 // Like I said; scale() is actually easy. It just also needs the integer
1912 // conversion that length() does.
1913 else if (BC_IS_BC || BC_PROG_NUM(opd, num))
1914 val = (BcBigDig) bc_num_scale(num);
1915
1916 BC_SIG_LOCK;
1917
1918 // Create the result.
1919 bc_num_createFromBigdig(&res->d.n, val);
1920
1921 BC_SIG_UNLOCK;
1922 }
1923
1924 bc_program_retire(p, 1, 1);
1925 }
1926
1927 /**
1928 * Executes a divmod.
1929 * @param p The program.
1930 */
bc_program_divmod(BcProgram * p)1931 static void bc_program_divmod(BcProgram *p) {
1932
1933 BcResult *opd1, *opd2, *res, *res2;
1934 BcNum *n1, *n2;
1935 size_t req;
1936
1937 // We grow first to avoid pointer invalidation.
1938 bc_vec_grow(&p->results, 2);
1939
1940 // We don't need to update the pointer because
1941 // the capacity is enough due to the line above.
1942 res2 = bc_program_prepResult(p);
1943 res = bc_program_prepResult(p);
1944
1945 // Prepare the operands.
1946 bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 2);
1947
1948 req = bc_num_mulReq(n1, n2, BC_PROG_SCALE(p));
1949
1950 BC_SIG_LOCK;
1951
1952 // Initialize the results.
1953 bc_num_init(&res->d.n, req);
1954 bc_num_init(&res2->d.n, req);
1955
1956 BC_SIG_UNLOCK;
1957
1958 // Execute.
1959 bc_num_divmod(n1, n2, &res2->d.n, &res->d.n, BC_PROG_SCALE(p));
1960
1961 bc_program_retire(p, 2, 2);
1962 }
1963
1964 /**
1965 * Executes modular exponentiation.
1966 * @param p The program.
1967 */
bc_program_modexp(BcProgram * p)1968 static void bc_program_modexp(BcProgram *p) {
1969
1970 BcResult *r1, *r2, *r3, *res;
1971 BcNum *n1, *n2, *n3;
1972
1973 #if DC_ENABLED
1974
1975 // Check the stack.
1976 if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 3)))
1977 bc_err(BC_ERR_EXEC_STACK);
1978
1979 #endif // DC_ENABLED
1980
1981 assert(BC_PROG_STACK(&p->results, 3));
1982
1983 res = bc_program_prepResult(p);
1984
1985 // Get the first operand and typecheck.
1986 bc_program_operand(p, &r1, &n1, 3);
1987 bc_program_type_num(r1, n1);
1988
1989 // Get the last two operands.
1990 bc_program_binOpPrep(p, &r2, &n2, &r3, &n3, 1);
1991
1992 // Make sure that the values have their pointers updated, if necessary.
1993 // Only array elements are possible because this is dc.
1994 if (r1->t == BC_RESULT_ARRAY_ELEM && (r1->t == r2->t || r1->t == r3->t))
1995 n1 = bc_program_num(p, r1);
1996
1997 BC_SIG_LOCK;
1998
1999 bc_num_init(&res->d.n, n3->len);
2000
2001 BC_SIG_UNLOCK;
2002
2003 bc_num_modexp(n1, n2, n3, &res->d.n);
2004
2005 bc_program_retire(p, 1, 3);
2006 }
2007
2008 /**
2009 * Asciifies a number for dc. This is a helper for bc_program_asciify().
2010 * @param p The program.
2011 * @param n The number to asciify.
2012 */
bc_program_asciifyNum(BcProgram * p,BcNum * n)2013 static uchar bc_program_asciifyNum(BcProgram *p, BcNum *n) {
2014
2015 BcNum num;
2016 BcBigDig val;
2017
2018 #ifndef NDEBUG
2019 // This is entirely to satisfy a useless scan-build error.
2020 val = 0;
2021 #endif // NDEBUG
2022
2023 bc_num_clear(&num);
2024
2025 BC_SETJMP(num_err);
2026
2027 BC_SIG_LOCK;
2028
2029 bc_num_createCopy(&num, n);
2030
2031 BC_SIG_UNLOCK;
2032
2033 // We want to clear the scale and sign for easy mod later.
2034 bc_num_truncate(&num, num.scale);
2035 BC_NUM_NEG_CLR_NP(num);
2036
2037 // This is guaranteed to not have a divide by 0
2038 // because strmb is equal to 256.
2039 bc_num_mod(&num, &p->strmb, &num, 0);
2040
2041 // This is also guaranteed to not error because num is in the range
2042 // [0, UCHAR_MAX], which is definitely in range for a BcBigDig. And
2043 // it is not negative.
2044 val = bc_num_bigdig2(&num);
2045
2046 num_err:
2047 BC_SIG_MAYLOCK;
2048 bc_num_free(&num);
2049 BC_LONGJMP_CONT;
2050 return (uchar) val;
2051 }
2052
2053 /**
2054 * Executes the "asciify" command in dc.
2055 * @param p The program.
2056 * @param fidx The index of the current function.
2057 */
bc_program_asciify(BcProgram * p,size_t fidx)2058 static void bc_program_asciify(BcProgram *p, size_t fidx) {
2059
2060 BcResult *r, res;
2061 BcNum *n;
2062 char str[2], *str2;
2063 uchar c;
2064 size_t idx;
2065
2066 // Check the stack.
2067 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
2068
2069 assert(BC_PROG_STACK(&p->results, 1));
2070
2071 // Get the top of the results stack.
2072 bc_program_operand(p, &r, &n, 0);
2073
2074 assert(n != NULL);
2075
2076 // Asciify.
2077 if (BC_PROG_NUM(r, n)) c = bc_program_asciifyNum(p, n);
2078 else {
2079
2080 // Get the string itself, then the first character.
2081 str2 = bc_program_string(p, n);
2082 c = (uchar) str2[0];
2083 }
2084
2085 // Fill the resulting string.
2086 str[0] = (char) c;
2087 str[1] = '\0';
2088
2089 // Add the string to the data structures.
2090 BC_SIG_LOCK;
2091 idx = bc_program_addString(p, str, fidx);
2092 BC_SIG_UNLOCK;
2093
2094 // Set the result
2095 res.t = BC_RESULT_STR;
2096 bc_num_clear(&res.d.n);
2097 res.d.n.rdx = fidx;
2098 res.d.n.scale = idx;
2099
2100 // Pop and push.
2101 bc_vec_pop(&p->results);
2102 bc_vec_push(&p->results, &res);
2103 }
2104
2105 /**
2106 * Streams a number or a string to stdout.
2107 * @param p The program.
2108 */
bc_program_printStream(BcProgram * p)2109 static void bc_program_printStream(BcProgram *p) {
2110
2111 BcResult *r;
2112 BcNum *n;
2113
2114 // Check the stack.
2115 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
2116
2117 assert(BC_PROG_STACK(&p->results, 1));
2118
2119 // Get the top of the results stack.
2120 bc_program_operand(p, &r, &n, 0);
2121
2122 assert(n != NULL);
2123
2124 // Stream appropriately.
2125 if (BC_PROG_NUM(r, n)) bc_num_stream(n);
2126 else bc_program_printChars(bc_program_string(p, n));
2127
2128 // Pop the operand.
2129 bc_vec_pop(&p->results);
2130 }
2131
2132 #if DC_ENABLED
2133
2134 /**
2135 * Gets the length of a register in dc and pushes it onto the results stack.
2136 * @param p The program.
2137 * @param code The bytecode vector to pull the register's index out of.
2138 * @param bgn An in/out parameter; the start of the index in the bytecode
2139 * vector, and will be updated to point after the index on return.
2140 */
bc_program_regStackLen(BcProgram * p,const char * restrict code,size_t * restrict bgn)2141 static void bc_program_regStackLen(BcProgram *p, const char *restrict code,
2142 size_t *restrict bgn)
2143 {
2144 size_t idx = bc_program_index(code, bgn);
2145 BcVec *v = bc_program_vec(p, idx, BC_TYPE_VAR);
2146
2147 bc_program_pushBigdig(p, (BcBigDig) v->len, BC_RESULT_TEMP);
2148 }
2149
2150 /**
2151 * Pushes the length of the results stack onto the results stack.
2152 * @param p The program.
2153 */
bc_program_stackLen(BcProgram * p)2154 static void bc_program_stackLen(BcProgram *p) {
2155 bc_program_pushBigdig(p, (BcBigDig) p->results.len, BC_RESULT_TEMP);
2156 }
2157
2158 /**
2159 * Pops a certain number of elements off the execution stack.
2160 * @param p The program.
2161 * @param inst The instruction to tell us how many. There is one to pop up to
2162 * 2, and one to pop the amount equal to the number at the top of
2163 * the results stack.
2164 */
bc_program_nquit(BcProgram * p,uchar inst)2165 static void bc_program_nquit(BcProgram *p, uchar inst) {
2166
2167 BcResult *opnd;
2168 BcNum *num;
2169 BcBigDig val;
2170 size_t i;
2171
2172 // Ensure that the tail calls stack is correct.
2173 assert(p->stack.len == p->tail_calls.len);
2174
2175 // Get the number of executions to pop.
2176 if (inst == BC_INST_QUIT) val = 2;
2177 else {
2178
2179 bc_program_prep(p, &opnd, &num, 0);
2180 val = bc_num_bigdig(num);
2181
2182 bc_vec_pop(&p->results);
2183 }
2184
2185 // Loop over the tail call stack and adjust the quit value appropriately.
2186 for (i = 0; val && i < p->tail_calls.len; ++i) {
2187
2188 // Get the number of tail calls for this one.
2189 size_t calls = *((size_t*) bc_vec_item_rev(&p->tail_calls, i)) + 1;
2190
2191 // Adjust the value.
2192 if (calls >= val) val = 0;
2193 else val -= (BcBigDig) calls;
2194 }
2195
2196 // If we don't have enough executions, just quit.
2197 if (i == p->stack.len) {
2198 vm.status = BC_STATUS_QUIT;
2199 BC_JMP;
2200 }
2201 else {
2202 // We can always pop the last item we reached on the tail call stack
2203 // because these are for tail calls. That means that any executions that
2204 // we would not have quit in that position on the stack would have quit
2205 // anyway.
2206 BC_SIG_LOCK;
2207 bc_vec_npop(&p->stack, i);
2208 bc_vec_npop(&p->tail_calls, i);
2209 BC_SIG_UNLOCK;
2210 }
2211 }
2212
2213 /**
2214 * Pushes the depth of the execution stack onto the stack.
2215 * @param p The program.
2216 */
bc_program_execStackLen(BcProgram * p)2217 static void bc_program_execStackLen(BcProgram *p) {
2218
2219 size_t i, amt, len = p->tail_calls.len;
2220
2221 amt = len;
2222
2223 for (i = 0; i < len; ++i)
2224 amt += *((size_t*) bc_vec_item(&p->tail_calls, i));
2225
2226 bc_program_pushBigdig(p, (BcBigDig) amt, BC_RESULT_TEMP);
2227 }
2228
2229 /**
2230 *
2231 * @param p The program.
2232 * @param code The bytecode vector to pull the register's index out of.
2233 * @param bgn An in/out parameter; the start of the index in the bytecode
2234 * vector, and will be updated to point after the index on return.
2235 * @param cond True if the execution is conditional, false otherwise.
2236 * @param len The number of bytes in the bytecode vector.
2237 */
bc_program_execStr(BcProgram * p,const char * restrict code,size_t * restrict bgn,bool cond,size_t len)2238 static void bc_program_execStr(BcProgram *p, const char *restrict code,
2239 size_t *restrict bgn, bool cond, size_t len)
2240 {
2241 BcResult *r;
2242 char *str;
2243 BcFunc *f;
2244 BcInstPtr ip;
2245 size_t fidx;
2246 BcNum *n;
2247
2248 assert(p->stack.len == p->tail_calls.len);
2249
2250 // Check the stack.
2251 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
2252
2253 assert(BC_PROG_STACK(&p->results, 1));
2254
2255 // Get the operand.
2256 bc_program_operand(p, &r, &n, 0);
2257
2258 // If execution is conditional...
2259 if (cond) {
2260
2261 bool exec;
2262 size_t idx, then_idx, else_idx;
2263
2264 // Get the index of the "then" var and "else" var.
2265 then_idx = bc_program_index(code, bgn);
2266 else_idx = bc_program_index(code, bgn);
2267
2268 // Figure out if we should execute.
2269 exec = (r->d.n.len != 0);
2270
2271 idx = exec ? then_idx : else_idx;
2272
2273 BC_SIG_LOCK;
2274 BC_SETJMP_LOCKED(exit);
2275
2276 // If we are supposed to execute, execute. If else_idx == SIZE_MAX, that
2277 // means there was no else clause, so if execute is false and else does
2278 // not exist, we don't execute. The goto skips all of the setup for the
2279 // execution.
2280 if (exec || (else_idx != SIZE_MAX))
2281 n = bc_vec_top(bc_program_vec(p, idx, BC_TYPE_VAR));
2282 else goto exit;
2283
2284 if (BC_ERR(!BC_PROG_STR(n))) bc_err(BC_ERR_EXEC_TYPE);
2285
2286 BC_UNSETJMP;
2287 BC_SIG_UNLOCK;
2288 }
2289 else {
2290
2291 // In non-conditional situations, only the top of stack can be executed,
2292 // and in those cases, variables are not allowed to be "on the stack";
2293 // they are only put on the stack to be assigned to.
2294 assert(r->t != BC_RESULT_VAR);
2295
2296 if (r->t != BC_RESULT_STR) return;
2297 }
2298
2299 assert(BC_PROG_STR(n));
2300
2301 // Get the string.
2302 str = bc_program_string(p, n);
2303
2304 // Get the function index and function.
2305 BC_SIG_LOCK;
2306 fidx = bc_program_insertFunc(p, str);
2307 BC_SIG_UNLOCK;
2308 f = bc_vec_item(&p->fns, fidx);
2309
2310 // If the function has not been parsed yet...
2311 if (!f->code.len) {
2312
2313 BC_SIG_LOCK;
2314
2315 if (!BC_PARSE_IS_INITED(&vm.read_prs, p)) {
2316
2317 bc_parse_init(&vm.read_prs, p, fidx);
2318
2319 // Initialize this too because bc_vm_shutdown() expects them to be
2320 // initialized togther.
2321 bc_vec_init(&vm.read_buf, sizeof(char), BC_DTOR_NONE);
2322 }
2323 // This needs to be updated because the parser could have been used
2324 // somewhere else
2325 else bc_parse_updateFunc(&vm.read_prs, fidx);
2326
2327 bc_lex_file(&vm.read_prs.l, vm.file);
2328
2329 BC_SETJMP_LOCKED(err);
2330
2331 BC_SIG_UNLOCK;
2332
2333 // Parse.
2334 bc_parse_text(&vm.read_prs, str, false);
2335
2336 BC_SIG_LOCK;
2337 vm.expr(&vm.read_prs, BC_PARSE_NOCALL);
2338
2339 BC_UNSETJMP;
2340
2341 // We can just assert this here because
2342 // dc should parse everything until EOF.
2343 assert(vm.read_prs.l.t == BC_LEX_EOF);
2344
2345 BC_SIG_UNLOCK;
2346 }
2347
2348 // Set the instruction pointer.
2349 ip.idx = 0;
2350 ip.len = p->results.len;
2351 ip.func = fidx;
2352
2353 BC_SIG_LOCK;
2354
2355 // Pop the operand.
2356 bc_vec_pop(&p->results);
2357
2358 // Tail call processing. This condition means that there is more on the
2359 // execution stack, and we are at the end of the bytecode vector, and the
2360 // last instruction is just a BC_INST_POP_EXEC, which would return.
2361 if (p->stack.len > 1 && *bgn == len - 1 && code[*bgn] == BC_INST_POP_EXEC) {
2362
2363 size_t *call_ptr = bc_vec_top(&p->tail_calls);
2364
2365 // Add one to the tail call.
2366 *call_ptr += 1;
2367
2368 // Pop the execution stack before pushing the new instruction pointer
2369 // on.
2370 bc_vec_pop(&p->stack);
2371 }
2372 // If not a tail call, just push a new one.
2373 else bc_vec_push(&p->tail_calls, &ip.idx);
2374
2375 // Push the new function onto the execution stack and return.
2376 bc_vec_push(&p->stack, &ip);
2377
2378 BC_SIG_UNLOCK;
2379
2380 return;
2381
2382 err:
2383 BC_SIG_MAYLOCK;
2384
2385 f = bc_vec_item(&p->fns, fidx);
2386
2387 // Make sure to erase the bytecode vector so dc knows it is not parsed.
2388 bc_vec_popAll(&f->code);
2389
2390 exit:
2391 bc_vec_pop(&p->results);
2392 BC_LONGJMP_CONT;
2393 }
2394
2395 /**
2396 * Prints every item on the results stack, one per line.
2397 * @param p The program.
2398 */
bc_program_printStack(BcProgram * p)2399 static void bc_program_printStack(BcProgram *p) {
2400
2401 size_t idx;
2402
2403 for (idx = 0; idx < p->results.len; ++idx)
2404 bc_program_print(p, BC_INST_PRINT, idx);
2405 }
2406 #endif // DC_ENABLED
2407
2408 /**
2409 * Pushes the value of a global onto the results stack.
2410 * @param p The program.
2411 * @param inst Which global to push, as an instruction.
2412 */
bc_program_pushGlobal(BcProgram * p,uchar inst)2413 static void bc_program_pushGlobal(BcProgram *p, uchar inst) {
2414
2415 BcResultType t;
2416
2417 // Make sure the instruction is valid.
2418 assert(inst >= BC_INST_IBASE && inst <= BC_INST_SCALE);
2419
2420 // Push the global.
2421 t = inst - BC_INST_IBASE + BC_RESULT_IBASE;
2422 bc_program_pushBigdig(p, p->globals[inst - BC_INST_IBASE], t);
2423 }
2424
2425 /**
2426 * Pushes the value of a global setting onto the stack.
2427 * @param p The program.
2428 * @param inst Which global setting to push, as an instruction.
2429 */
bc_program_globalSetting(BcProgram * p,uchar inst)2430 static void bc_program_globalSetting(BcProgram *p, uchar inst) {
2431
2432 BcBigDig val;
2433
2434 // Make sure the instruction is valid.
2435 assert(inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO);
2436
2437 if (inst == BC_INST_LINE_LENGTH) val = (BcBigDig) vm.line_len;
2438 #if BC_ENABLED
2439 else if (inst == BC_INST_GLOBAL_STACKS) val = (BC_G != 0);
2440 #endif // BC_ENABLED
2441 else val = (BC_Z != 0);
2442
2443 // Push the global.
2444 bc_program_pushBigdig(p, val, BC_RESULT_TEMP);
2445 }
2446
2447 #if BC_ENABLE_EXTRA_MATH
2448
2449 /**
2450 * Pushes the value of seed on the stack.
2451 * @param p The program.
2452 */
bc_program_pushSeed(BcProgram * p)2453 static void bc_program_pushSeed(BcProgram *p) {
2454
2455 BcResult *res;
2456
2457 res = bc_program_prepResult(p);
2458 res->t = BC_RESULT_SEED;
2459
2460 BC_SIG_LOCK;
2461
2462 // We need 2*BC_RAND_NUM_SIZE because of the size of the state.
2463 bc_num_init(&res->d.n, 2 * BC_RAND_NUM_SIZE);
2464
2465 BC_SIG_UNLOCK;
2466
2467 bc_num_createFromRNG(&res->d.n, &p->rng);
2468 }
2469
2470 #endif // BC_ENABLE_EXTRA_MATH
2471
2472 /**
2473 * Adds a function to the fns array. The function's ID must have already been
2474 * inserted into the map.
2475 * @param p The program.
2476 * @param id_ptr The ID of the function as inserted into the map.
2477 */
bc_program_addFunc(BcProgram * p,BcId * id_ptr)2478 static void bc_program_addFunc(BcProgram *p, BcId *id_ptr) {
2479
2480 BcInstPtr *ip;
2481 BcFunc *f;
2482
2483 BC_SIG_ASSERT_LOCKED;
2484
2485 // Push and init.
2486 f = bc_vec_pushEmpty(&p->fns);
2487 bc_func_init(f, id_ptr->name);
2488
2489 // This is to make sure pointers are updated if the array was moved.
2490 if (p->stack.len) {
2491 ip = bc_vec_top(&p->stack);
2492 bc_program_setVecs(p, (BcFunc*) bc_vec_item(&p->fns, ip->func));
2493 }
2494 }
2495
bc_program_insertFunc(BcProgram * p,const char * name)2496 size_t bc_program_insertFunc(BcProgram *p, const char *name) {
2497
2498 BcId *id_ptr;
2499 bool new;
2500 size_t idx;
2501
2502 BC_SIG_ASSERT_LOCKED;
2503
2504 assert(p != NULL && name != NULL);
2505
2506 // Insert into the map and get the resulting ID.
2507 new = bc_map_insert(&p->fn_map, name, p->fns.len, &idx);
2508 id_ptr = (BcId*) bc_vec_item(&p->fn_map, idx);
2509 idx = id_ptr->idx;
2510
2511 // If the function is new...
2512 if (new) {
2513
2514 // Add the function to the fns array.
2515 bc_program_addFunc(p, id_ptr);
2516 }
2517 #if BC_ENABLED
2518 // bc has to reset the function because it's about to be redefined.
2519 else if (BC_IS_BC) {
2520 BcFunc *func = bc_vec_item(&p->fns, idx);
2521 bc_func_reset(func);
2522 }
2523 #endif // BC_ENABLED
2524
2525 return idx;
2526 }
2527
2528 #ifndef NDEBUG
bc_program_free(BcProgram * p)2529 void bc_program_free(BcProgram *p) {
2530
2531 size_t i;
2532
2533 BC_SIG_ASSERT_LOCKED;
2534
2535 assert(p != NULL);
2536
2537 // Free the globals stacks.
2538 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) bc_vec_free(p->globals_v + i);
2539
2540 bc_vec_free(&p->fns);
2541 bc_vec_free(&p->fn_map);
2542 bc_vec_free(&p->vars);
2543 bc_vec_free(&p->var_map);
2544 bc_vec_free(&p->arrs);
2545 bc_vec_free(&p->arr_map);
2546 bc_vec_free(&p->results);
2547 bc_vec_free(&p->stack);
2548
2549 #if BC_ENABLED
2550 if (BC_IS_BC) bc_num_free(&p->last);
2551 #endif // BC_ENABLED
2552
2553 #if BC_ENABLE_EXTRA_MATH
2554 bc_rand_free(&p->rng);
2555 #endif // BC_ENABLE_EXTRA_MATH
2556
2557 #if DC_ENABLED
2558 if (BC_IS_DC) bc_vec_free(&p->tail_calls);
2559 #endif // DC_ENABLED
2560 }
2561 #endif // NDEBUG
2562
bc_program_init(BcProgram * p)2563 void bc_program_init(BcProgram *p) {
2564
2565 BcInstPtr ip;
2566 size_t i;
2567
2568 BC_SIG_ASSERT_LOCKED;
2569
2570 assert(p != NULL);
2571
2572 // We want this clear.
2573 memset(&ip, 0, sizeof(BcInstPtr));
2574
2575 // Setup the globals stacks and the current values.
2576 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) {
2577
2578 BcBigDig val = i == BC_PROG_GLOBALS_SCALE ? 0 : BC_BASE;
2579
2580 bc_vec_init(p->globals_v + i, sizeof(BcBigDig), BC_DTOR_NONE);
2581 bc_vec_push(p->globals_v + i, &val);
2582
2583 p->globals[i] = val;
2584 }
2585
2586 #if DC_ENABLED
2587 // dc-only setup.
2588 if (BC_IS_DC) {
2589
2590 bc_vec_init(&p->tail_calls, sizeof(size_t), BC_DTOR_NONE);
2591
2592 // We want an item for the main function on the tail call stack.
2593 i = 0;
2594 bc_vec_push(&p->tail_calls, &i);
2595 }
2596 #endif // DC_ENABLED
2597
2598 bc_num_setup(&p->strmb, p->strmb_num, BC_NUM_BIGDIG_LOG10);
2599 bc_num_bigdig2num(&p->strmb, BC_NUM_STREAM_BASE);
2600
2601 #if BC_ENABLE_EXTRA_MATH
2602 // We need to initialize srand() just in case /dev/urandom and /dev/random
2603 // are not available.
2604 srand((unsigned int) time(NULL));
2605 bc_rand_init(&p->rng);
2606 #endif // BC_ENABLE_EXTRA_MATH
2607
2608 #if BC_ENABLED
2609 if (BC_IS_BC) bc_num_init(&p->last, BC_NUM_DEF_SIZE);
2610 #endif // BC_ENABLED
2611
2612 #ifndef NDEBUG
2613 bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_FUNC);
2614 #else // NDEBUG
2615 bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_NONE);
2616 #endif // NDEBUG
2617 bc_map_init(&p->fn_map);
2618 bc_program_insertFunc(p, bc_func_main);
2619 bc_program_insertFunc(p, bc_func_read);
2620
2621 bc_vec_init(&p->vars, sizeof(BcVec), BC_DTOR_VEC);
2622 bc_map_init(&p->var_map);
2623
2624 bc_vec_init(&p->arrs, sizeof(BcVec), BC_DTOR_VEC);
2625 bc_map_init(&p->arr_map);
2626
2627 bc_vec_init(&p->results, sizeof(BcResult), BC_DTOR_RESULT);
2628
2629 // Push the first instruction pointer onto the execution stack.
2630 bc_vec_init(&p->stack, sizeof(BcInstPtr), BC_DTOR_NONE);
2631 bc_vec_push(&p->stack, &ip);
2632
2633 // Make sure the pointers are properly set up.
2634 bc_program_setVecs(p, (BcFunc*) bc_vec_item(&p->fns, BC_PROG_MAIN));
2635
2636 assert(p->consts != NULL && p->strs != NULL);
2637 }
2638
bc_program_reset(BcProgram * p)2639 void bc_program_reset(BcProgram *p) {
2640
2641 BcFunc *f;
2642 BcInstPtr *ip;
2643
2644 BC_SIG_ASSERT_LOCKED;
2645
2646 // Pop all but the last execution and all results.
2647 bc_vec_npop(&p->stack, p->stack.len - 1);
2648 bc_vec_popAll(&p->results);
2649
2650 #if BC_ENABLED
2651 // Clear the globals' stacks.
2652 if (BC_G) bc_program_popGlobals(p, true);
2653 #endif // BC_ENABLED
2654
2655 // Clear the bytecode vector of the main function.
2656 f = bc_vec_item(&p->fns, BC_PROG_MAIN);
2657 bc_vec_npop(&f->code, f->code.len);
2658
2659 // Reset the instruction pointer.
2660 ip = bc_vec_top(&p->stack);
2661 bc_program_setVecs(p, f);
2662 memset(ip, 0, sizeof(BcInstPtr));
2663
2664 // Write the ready message for a signal, and clear the signal.
2665 if (vm.sig) {
2666 bc_file_write(&vm.fout, bc_flush_none, bc_program_ready_msg,
2667 bc_program_ready_msg_len);
2668 bc_file_flush(&vm.fout, bc_flush_err);
2669 vm.sig = 0;
2670 }
2671 }
2672
bc_program_exec(BcProgram * p)2673 void bc_program_exec(BcProgram *p) {
2674
2675 size_t idx;
2676 BcResult r, *ptr;
2677 BcInstPtr *ip;
2678 BcFunc *func;
2679 char *code;
2680 bool cond = false;
2681 uchar inst;
2682 #if BC_ENABLED
2683 BcNum *num;
2684 #endif // BC_ENABLED
2685 #if !BC_HAS_COMPUTED_GOTO
2686 #ifndef NDEBUG
2687 size_t jmp_bufs_len;
2688 #endif // NDEBUG
2689 #endif // !BC_HAS_COMPUTED_GOTO
2690
2691 #if BC_HAS_COMPUTED_GOTO
2692 BC_PROG_LBLS;
2693 BC_PROG_LBLS_ASSERT;
2694
2695 // BC_INST_INVALID is a marker for the end so that we don't have to have an
2696 // execution loop.
2697 func = (BcFunc*) bc_vec_item(&p->fns, BC_PROG_MAIN);
2698 bc_vec_pushByte(&func->code, BC_INST_INVALID);
2699 #endif // BC_HAS_COMPUTED_GOTO
2700
2701 ip = bc_vec_top(&p->stack);
2702 func = (BcFunc*) bc_vec_item(&p->fns, ip->func);
2703 code = func->code.v;
2704
2705 // Ensure the pointers are correct.
2706 BC_SIG_LOCK;
2707 bc_program_setVecs(p, func);
2708 BC_SIG_UNLOCK;
2709
2710 #if !BC_HAS_COMPUTED_GOTO
2711
2712 #ifndef NDEBUG
2713 jmp_bufs_len = vm.jmp_bufs.len;
2714 #endif // NDEBUG
2715
2716 // This loop is the heart of the execution engine. It *is* the engine. For
2717 // computed goto, it is ignored.
2718 while (ip->idx < func->code.len)
2719 #endif // !BC_HAS_COMPUTED_GOTO
2720 {
2721 BC_SIG_ASSERT_NOT_LOCKED;
2722
2723 #if BC_HAS_COMPUTED_GOTO
2724
2725 BC_PROG_JUMP(inst, code, ip);
2726
2727 #else // BC_HAS_COMPUTED_GOTO
2728
2729 // Get the next instruction and increment the index.
2730 inst = (uchar) code[(ip->idx)++];
2731
2732 #endif // BC_HAS_COMPUTED_GOTO
2733
2734 #if BC_DEBUG_CODE
2735 bc_file_printf(&vm.ferr, "inst: %s\n", bc_inst_names[inst]);
2736 bc_file_flush(&vm.ferr, bc_flush_none);
2737 #endif // BC_DEBUG_CODE
2738
2739 #if !BC_HAS_COMPUTED_GOTO
2740 switch (inst)
2741 #endif // !BC_HAS_COMPUTED_GOTO
2742 {
2743
2744 #if BC_ENABLED
2745 // This just sets up the condition for the unconditional jump below,
2746 // which checks the condition, if necessary.
2747 BC_PROG_LBL(BC_INST_JUMP_ZERO):
2748 {
2749 bc_program_prep(p, &ptr, &num, 0);
2750
2751 cond = !bc_num_cmpZero(num);
2752 bc_vec_pop(&p->results);
2753
2754 BC_PROG_DIRECT_JUMP(BC_INST_JUMP)
2755 }
2756 // Fallthrough.
2757 BC_PROG_FALLTHROUGH
2758
2759 BC_PROG_LBL(BC_INST_JUMP):
2760 {
2761 idx = bc_program_index(code, &ip->idx);
2762
2763 // If a jump is required...
2764 if (inst == BC_INST_JUMP || cond) {
2765
2766 // Get the address to jump to.
2767 size_t *addr = bc_vec_item(&func->labels, idx);
2768
2769 // If this fails, then the parser failed to set up the
2770 // labels correctly.
2771 assert(*addr != SIZE_MAX);
2772
2773 // Set the new address.
2774 ip->idx = *addr;
2775 }
2776
2777 BC_PROG_JUMP(inst, code, ip);
2778 }
2779
2780 BC_PROG_LBL(BC_INST_CALL):
2781 {
2782 assert(BC_IS_BC);
2783
2784 bc_program_call(p, code, &ip->idx);
2785
2786 // Because we changed the execution stack and where we are
2787 // executing, we have to update all of this.
2788 BC_SIG_LOCK;
2789 ip = bc_vec_top(&p->stack);
2790 func = bc_vec_item(&p->fns, ip->func);
2791 code = func->code.v;
2792 bc_program_setVecs(p, func);
2793 BC_SIG_UNLOCK;
2794
2795 BC_PROG_JUMP(inst, code, ip);
2796 }
2797
2798 BC_PROG_LBL(BC_INST_INC):
2799 BC_PROG_LBL(BC_INST_DEC):
2800 {
2801 bc_program_incdec(p, inst);
2802 BC_PROG_JUMP(inst, code, ip);
2803 }
2804
2805 BC_PROG_LBL(BC_INST_HALT):
2806 {
2807 vm.status = BC_STATUS_QUIT;
2808
2809 // Just jump out. The jump series will take care of everything.
2810 BC_JMP;
2811
2812 BC_PROG_JUMP(inst, code, ip);
2813 }
2814
2815 BC_PROG_LBL(BC_INST_RET):
2816 BC_PROG_LBL(BC_INST_RET0):
2817 BC_PROG_LBL(BC_INST_RET_VOID):
2818 {
2819 bc_program_return(p, inst);
2820
2821 // Because we changed the execution stack and where we are
2822 // executing, we have to update all of this.
2823 BC_SIG_LOCK;
2824 ip = bc_vec_top(&p->stack);
2825 func = bc_vec_item(&p->fns, ip->func);
2826 code = func->code.v;
2827 bc_program_setVecs(p, func);
2828 BC_SIG_UNLOCK;
2829
2830 BC_PROG_JUMP(inst, code, ip);
2831 }
2832 #endif // BC_ENABLED
2833
2834 BC_PROG_LBL(BC_INST_BOOL_OR):
2835 BC_PROG_LBL(BC_INST_BOOL_AND):
2836 BC_PROG_LBL(BC_INST_REL_EQ):
2837 BC_PROG_LBL(BC_INST_REL_LE):
2838 BC_PROG_LBL(BC_INST_REL_GE):
2839 BC_PROG_LBL(BC_INST_REL_NE):
2840 BC_PROG_LBL(BC_INST_REL_LT):
2841 BC_PROG_LBL(BC_INST_REL_GT):
2842 {
2843 bc_program_logical(p, inst);
2844 BC_PROG_JUMP(inst, code, ip);
2845 }
2846
2847 BC_PROG_LBL(BC_INST_READ):
2848 {
2849 // We want to flush output before
2850 // this in case there is a prompt.
2851 bc_file_flush(&vm.fout, bc_flush_save);
2852
2853 bc_program_read(p);
2854
2855 // Because we changed the execution stack and where we are
2856 // executing, we have to update all of this.
2857 BC_SIG_LOCK;
2858 ip = bc_vec_top(&p->stack);
2859 func = bc_vec_item(&p->fns, ip->func);
2860 code = func->code.v;
2861 bc_program_setVecs(p, func);
2862 BC_SIG_UNLOCK;
2863
2864 BC_PROG_JUMP(inst, code, ip);
2865 }
2866
2867 #if BC_ENABLE_EXTRA_MATH
2868 BC_PROG_LBL(BC_INST_RAND):
2869 {
2870 bc_program_rand(p);
2871 BC_PROG_JUMP(inst, code, ip);
2872 }
2873 #endif // BC_ENABLE_EXTRA_MATH
2874
2875 BC_PROG_LBL(BC_INST_MAXIBASE):
2876 BC_PROG_LBL(BC_INST_MAXOBASE):
2877 BC_PROG_LBL(BC_INST_MAXSCALE):
2878 #if BC_ENABLE_EXTRA_MATH
2879 BC_PROG_LBL(BC_INST_MAXRAND):
2880 #endif // BC_ENABLE_EXTRA_MATH
2881 {
2882 BcBigDig dig = vm.maxes[inst - BC_INST_MAXIBASE];
2883 bc_program_pushBigdig(p, dig, BC_RESULT_TEMP);
2884 BC_PROG_JUMP(inst, code, ip);
2885 }
2886
2887 BC_PROG_LBL(BC_INST_LINE_LENGTH):
2888 #if BC_ENABLED
2889 BC_PROG_LBL(BC_INST_GLOBAL_STACKS):
2890 #endif // BC_ENABLED
2891 BC_PROG_LBL(BC_INST_LEADING_ZERO):
2892 {
2893 bc_program_globalSetting(p, inst);
2894 BC_PROG_JUMP(inst, code, ip);
2895 }
2896
2897 BC_PROG_LBL(BC_INST_VAR):
2898 {
2899 bc_program_pushVar(p, code, &ip->idx, false, false);
2900 BC_PROG_JUMP(inst, code, ip);
2901 }
2902
2903 BC_PROG_LBL(BC_INST_ARRAY_ELEM):
2904 BC_PROG_LBL(BC_INST_ARRAY):
2905 {
2906 bc_program_pushArray(p, code, &ip->idx, inst);
2907 BC_PROG_JUMP(inst, code, ip);
2908 }
2909
2910 BC_PROG_LBL(BC_INST_IBASE):
2911 BC_PROG_LBL(BC_INST_SCALE):
2912 BC_PROG_LBL(BC_INST_OBASE):
2913 {
2914 bc_program_pushGlobal(p, inst);
2915 BC_PROG_JUMP(inst, code, ip);
2916 }
2917
2918 #if BC_ENABLE_EXTRA_MATH
2919 BC_PROG_LBL(BC_INST_SEED):
2920 {
2921 bc_program_pushSeed(p);
2922 BC_PROG_JUMP(inst, code, ip);
2923 }
2924 #endif // BC_ENABLE_EXTRA_MATH
2925
2926 BC_PROG_LBL(BC_INST_LENGTH):
2927 BC_PROG_LBL(BC_INST_SCALE_FUNC):
2928 BC_PROG_LBL(BC_INST_SQRT):
2929 BC_PROG_LBL(BC_INST_ABS):
2930 #if BC_ENABLE_EXTRA_MATH
2931 BC_PROG_LBL(BC_INST_IRAND):
2932 #endif // BC_ENABLE_EXTRA_MATH
2933 {
2934 bc_program_builtin(p, inst);
2935 BC_PROG_JUMP(inst, code, ip);
2936 }
2937
2938 BC_PROG_LBL(BC_INST_ASCIIFY):
2939 {
2940 bc_program_asciify(p, ip->func);
2941
2942 // Because we changed the execution stack and where we are
2943 // executing, we have to update all of this.
2944 BC_SIG_LOCK;
2945 ip = bc_vec_top(&p->stack);
2946 func = bc_vec_item(&p->fns, ip->func);
2947 code = func->code.v;
2948 bc_program_setVecs(p, func);
2949 BC_SIG_UNLOCK;
2950
2951 BC_PROG_JUMP(inst, code, ip);
2952 }
2953
2954 BC_PROG_LBL(BC_INST_NUM):
2955 {
2956 bc_program_const(p, code, &ip->idx);
2957 BC_PROG_JUMP(inst, code, ip);
2958 }
2959
2960 BC_PROG_LBL(BC_INST_ZERO):
2961 BC_PROG_LBL(BC_INST_ONE):
2962 #if BC_ENABLED
2963 BC_PROG_LBL(BC_INST_LAST):
2964 #endif // BC_ENABLED
2965 {
2966 r.t = BC_RESULT_ZERO + (inst - BC_INST_ZERO);
2967 bc_vec_push(&p->results, &r);
2968 BC_PROG_JUMP(inst, code, ip);
2969 }
2970
2971 BC_PROG_LBL(BC_INST_PRINT):
2972 BC_PROG_LBL(BC_INST_PRINT_POP):
2973 #if BC_ENABLED
2974 BC_PROG_LBL(BC_INST_PRINT_STR):
2975 #endif // BC_ENABLED
2976 {
2977 bc_program_print(p, inst, 0);
2978
2979 // We want to flush right away to save the output for history,
2980 // if history must preserve it when taking input.
2981 bc_file_flush(&vm.fout, bc_flush_save);
2982
2983 BC_PROG_JUMP(inst, code, ip);
2984 }
2985
2986 BC_PROG_LBL(BC_INST_STR):
2987 {
2988 // Set up the result and push.
2989 r.t = BC_RESULT_STR;
2990 bc_num_clear(&r.d.n);
2991 r.d.n.rdx = bc_program_index(code, &ip->idx);
2992 r.d.n.scale = bc_program_index(code, &ip->idx);
2993 bc_vec_push(&p->results, &r);
2994 BC_PROG_JUMP(inst, code, ip);
2995 }
2996
2997 BC_PROG_LBL(BC_INST_POWER):
2998 BC_PROG_LBL(BC_INST_MULTIPLY):
2999 BC_PROG_LBL(BC_INST_DIVIDE):
3000 BC_PROG_LBL(BC_INST_MODULUS):
3001 BC_PROG_LBL(BC_INST_PLUS):
3002 BC_PROG_LBL(BC_INST_MINUS):
3003 #if BC_ENABLE_EXTRA_MATH
3004 BC_PROG_LBL(BC_INST_PLACES):
3005 BC_PROG_LBL(BC_INST_LSHIFT):
3006 BC_PROG_LBL(BC_INST_RSHIFT):
3007 #endif // BC_ENABLE_EXTRA_MATH
3008 {
3009 bc_program_op(p, inst);
3010 BC_PROG_JUMP(inst, code, ip);
3011 }
3012
3013 BC_PROG_LBL(BC_INST_NEG):
3014 BC_PROG_LBL(BC_INST_BOOL_NOT):
3015 #if BC_ENABLE_EXTRA_MATH
3016 BC_PROG_LBL(BC_INST_TRUNC):
3017 #endif // BC_ENABLE_EXTRA_MATH
3018 {
3019 bc_program_unary(p, inst);
3020 BC_PROG_JUMP(inst, code, ip);
3021 }
3022
3023 #if BC_ENABLED
3024 BC_PROG_LBL(BC_INST_ASSIGN_POWER):
3025 BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY):
3026 BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE):
3027 BC_PROG_LBL(BC_INST_ASSIGN_MODULUS):
3028 BC_PROG_LBL(BC_INST_ASSIGN_PLUS):
3029 BC_PROG_LBL(BC_INST_ASSIGN_MINUS):
3030 #if BC_ENABLE_EXTRA_MATH
3031 BC_PROG_LBL(BC_INST_ASSIGN_PLACES):
3032 BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT):
3033 BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT):
3034 #endif // BC_ENABLE_EXTRA_MATH
3035 BC_PROG_LBL(BC_INST_ASSIGN):
3036 BC_PROG_LBL(BC_INST_ASSIGN_POWER_NO_VAL):
3037 BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY_NO_VAL):
3038 BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE_NO_VAL):
3039 BC_PROG_LBL(BC_INST_ASSIGN_MODULUS_NO_VAL):
3040 BC_PROG_LBL(BC_INST_ASSIGN_PLUS_NO_VAL):
3041 BC_PROG_LBL(BC_INST_ASSIGN_MINUS_NO_VAL):
3042 #if BC_ENABLE_EXTRA_MATH
3043 BC_PROG_LBL(BC_INST_ASSIGN_PLACES_NO_VAL):
3044 BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT_NO_VAL):
3045 BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT_NO_VAL):
3046 #endif // BC_ENABLE_EXTRA_MATH
3047 #endif // BC_ENABLED
3048 BC_PROG_LBL(BC_INST_ASSIGN_NO_VAL):
3049 {
3050 bc_program_assign(p, inst);
3051 BC_PROG_JUMP(inst, code, ip);
3052 }
3053
3054 BC_PROG_LBL(BC_INST_POP):
3055 {
3056 #ifndef BC_PROG_NO_STACK_CHECK
3057 // dc must do a stack check, but bc does not.
3058 if (BC_IS_DC) {
3059 if (BC_ERR(!BC_PROG_STACK(&p->results, 1)))
3060 bc_err(BC_ERR_EXEC_STACK);
3061 }
3062 #endif // BC_PROG_NO_STACK_CHECK
3063
3064 assert(BC_PROG_STACK(&p->results, 1));
3065
3066 bc_vec_pop(&p->results);
3067
3068 BC_PROG_JUMP(inst, code, ip);
3069 }
3070
3071 BC_PROG_LBL(BC_INST_SWAP):
3072 {
3073 BcResult *ptr2;
3074
3075 // Check the stack.
3076 if (BC_ERR(!BC_PROG_STACK(&p->results, 2)))
3077 bc_err(BC_ERR_EXEC_STACK);
3078
3079 assert(BC_PROG_STACK(&p->results, 2));
3080
3081 // Get the two items.
3082 ptr = bc_vec_item_rev(&p->results, 0);
3083 ptr2 = bc_vec_item_rev(&p->results, 1);
3084
3085 // Swap. It's just easiest to do it this way.
3086 memcpy(&r, ptr, sizeof(BcResult));
3087 memcpy(ptr, ptr2, sizeof(BcResult));
3088 memcpy(ptr2, &r, sizeof(BcResult));
3089
3090 BC_PROG_JUMP(inst, code, ip);
3091 }
3092
3093 BC_PROG_LBL(BC_INST_MODEXP):
3094 {
3095 bc_program_modexp(p);
3096 BC_PROG_JUMP(inst, code, ip);
3097 }
3098
3099 BC_PROG_LBL(BC_INST_DIVMOD):
3100 {
3101 bc_program_divmod(p);
3102 BC_PROG_JUMP(inst, code, ip);
3103 }
3104
3105 BC_PROG_LBL(BC_INST_PRINT_STREAM):
3106 {
3107 bc_program_printStream(p);
3108 BC_PROG_JUMP(inst, code, ip);
3109 }
3110
3111 #if DC_ENABLED
3112 BC_PROG_LBL(BC_INST_POP_EXEC):
3113 {
3114 // If this fails, the dc parser got something wrong.
3115 assert(BC_PROG_STACK(&p->stack, 2));
3116
3117 // Pop the execution stack and tail call stack.
3118 bc_vec_pop(&p->stack);
3119 bc_vec_pop(&p->tail_calls);
3120
3121 // Because we changed the execution stack and where we are
3122 // executing, we have to update all of this.
3123 BC_SIG_LOCK;
3124 ip = bc_vec_top(&p->stack);
3125 func = bc_vec_item(&p->fns, ip->func);
3126 code = func->code.v;
3127 bc_program_setVecs(p, func);
3128 BC_SIG_UNLOCK;
3129
3130 BC_PROG_JUMP(inst, code, ip);
3131 }
3132
3133 BC_PROG_LBL(BC_INST_EXECUTE):
3134 BC_PROG_LBL(BC_INST_EXEC_COND):
3135 {
3136 cond = (inst == BC_INST_EXEC_COND);
3137
3138 bc_program_execStr(p, code, &ip->idx, cond, func->code.len);
3139
3140 // Because we changed the execution stack and where we are
3141 // executing, we have to update all of this.
3142 BC_SIG_LOCK;
3143 ip = bc_vec_top(&p->stack);
3144 func = bc_vec_item(&p->fns, ip->func);
3145 code = func->code.v;
3146 bc_program_setVecs(p, func);
3147 BC_SIG_UNLOCK;
3148
3149 BC_PROG_JUMP(inst, code, ip);
3150 }
3151
3152 BC_PROG_LBL(BC_INST_PRINT_STACK):
3153 {
3154 bc_program_printStack(p);
3155 BC_PROG_JUMP(inst, code, ip);
3156 }
3157
3158 BC_PROG_LBL(BC_INST_CLEAR_STACK):
3159 {
3160 bc_vec_popAll(&p->results);
3161 BC_PROG_JUMP(inst, code, ip);
3162 }
3163
3164 BC_PROG_LBL(BC_INST_REG_STACK_LEN):
3165 {
3166 bc_program_regStackLen(p, code, &ip->idx);
3167 BC_PROG_JUMP(inst, code, ip);
3168 }
3169
3170 BC_PROG_LBL(BC_INST_STACK_LEN):
3171 {
3172 bc_program_stackLen(p);
3173 BC_PROG_JUMP(inst, code, ip);
3174 }
3175
3176 BC_PROG_LBL(BC_INST_DUPLICATE):
3177 {
3178 // Check the stack.
3179 if (BC_ERR(!BC_PROG_STACK(&p->results, 1)))
3180 bc_err(BC_ERR_EXEC_STACK);
3181
3182 assert(BC_PROG_STACK(&p->results, 1));
3183
3184 // Get the top of the stack.
3185 ptr = bc_vec_top(&p->results);
3186
3187 BC_SIG_LOCK;
3188
3189 // Copy and push.
3190 bc_result_copy(&r, ptr);
3191 bc_vec_push(&p->results, &r);
3192
3193 BC_SIG_UNLOCK;
3194
3195 BC_PROG_JUMP(inst, code, ip);
3196 }
3197
3198 BC_PROG_LBL(BC_INST_LOAD):
3199 BC_PROG_LBL(BC_INST_PUSH_VAR):
3200 {
3201 bool copy = (inst == BC_INST_LOAD);
3202 bc_program_pushVar(p, code, &ip->idx, true, copy);
3203 BC_PROG_JUMP(inst, code, ip);
3204 }
3205
3206 BC_PROG_LBL(BC_INST_PUSH_TO_VAR):
3207 {
3208 idx = bc_program_index(code, &ip->idx);
3209 bc_program_copyToVar(p, idx, BC_TYPE_VAR, true);
3210 BC_PROG_JUMP(inst, code, ip);
3211 }
3212
3213 BC_PROG_LBL(BC_INST_QUIT):
3214 BC_PROG_LBL(BC_INST_NQUIT):
3215 {
3216 bc_program_nquit(p, inst);
3217
3218 // Because we changed the execution stack and where we are
3219 // executing, we have to update all of this.
3220 BC_SIG_LOCK;
3221 ip = bc_vec_top(&p->stack);
3222 func = bc_vec_item(&p->fns, ip->func);
3223 code = func->code.v;
3224 bc_program_setVecs(p, func);
3225 BC_SIG_UNLOCK;
3226
3227 BC_PROG_JUMP(inst, code, ip);
3228 }
3229
3230 BC_PROG_LBL(BC_INST_EXEC_STACK_LEN):
3231 {
3232 bc_program_execStackLen(p);
3233 BC_PROG_JUMP(inst, code, ip);
3234 }
3235 #endif // DC_ENABLED
3236
3237 #if BC_HAS_COMPUTED_GOTO
3238 BC_PROG_LBL(BC_INST_INVALID):
3239 {
3240 return;
3241 }
3242 #else // BC_HAS_COMPUTED_GOTO
3243 default:
3244 {
3245 BC_UNREACHABLE
3246 #ifndef NDEBUG
3247 abort();
3248 #endif // NDEBUG
3249 }
3250 #endif // BC_HAS_COMPUTED_GOTO
3251 }
3252
3253 #if !BC_HAS_COMPUTED_GOTO
3254 #ifndef NDEBUG
3255 // This is to allow me to use a debugger to see the last instruction,
3256 // which will point to which function was the problem. But it's also a
3257 // good smoke test for error handling changes.
3258 assert(jmp_bufs_len == vm.jmp_bufs.len);
3259 #endif // NDEBUG
3260 #endif // !BC_HAS_COMPUTED_GOTO
3261 }
3262 }
3263
3264 #if BC_DEBUG_CODE
3265 #if BC_ENABLED && DC_ENABLED
bc_program_printStackDebug(BcProgram * p)3266 void bc_program_printStackDebug(BcProgram *p) {
3267 bc_file_puts(&vm.fout, bc_flush_err, "-------------- Stack ----------\n");
3268 bc_program_printStack(p);
3269 bc_file_puts(&vm.fout, bc_flush_err, "-------------- Stack End ------\n");
3270 }
3271
bc_program_printIndex(const char * restrict code,size_t * restrict bgn)3272 static void bc_program_printIndex(const char *restrict code,
3273 size_t *restrict bgn)
3274 {
3275 uchar byte, i, bytes = (uchar) code[(*bgn)++];
3276 ulong val = 0;
3277
3278 for (byte = 1, i = 0; byte && i < bytes; ++i) {
3279 byte = (uchar) code[(*bgn)++];
3280 if (byte) val |= ((ulong) byte) << (CHAR_BIT * i);
3281 }
3282
3283 bc_vm_printf(" (%lu) ", val);
3284 }
3285
bc_program_printStr(const BcProgram * p,const char * restrict code,size_t * restrict bgn)3286 static void bc_program_printStr(const BcProgram *p, const char *restrict code,
3287 size_t *restrict bgn)
3288 {
3289 size_t idx = bc_program_index(code, bgn);
3290 char *s;
3291
3292 s = *((char**) bc_vec_item(p->strs, idx));
3293
3294 bc_vm_printf(" (\"%s\") ", s);
3295 }
3296
bc_program_printInst(const BcProgram * p,const char * restrict code,size_t * restrict bgn)3297 void bc_program_printInst(const BcProgram *p, const char *restrict code,
3298 size_t *restrict bgn)
3299 {
3300 uchar inst = (uchar) code[(*bgn)++];
3301
3302 bc_vm_printf("Inst[%zu]: %s [%lu]; ", *bgn - 1,
3303 bc_inst_names[inst], (unsigned long) inst);
3304
3305 if (inst == BC_INST_VAR || inst == BC_INST_ARRAY_ELEM ||
3306 inst == BC_INST_ARRAY)
3307 {
3308 bc_program_printIndex(code, bgn);
3309 }
3310 else if (inst == BC_INST_STR) bc_program_printStr(p, code, bgn);
3311 else if (inst == BC_INST_NUM) {
3312 size_t idx = bc_program_index(code, bgn);
3313 BcConst *c = bc_vec_item(p->consts, idx);
3314 bc_vm_printf("(%s)", c->val);
3315 }
3316 else if (inst == BC_INST_CALL ||
3317 (inst > BC_INST_STR && inst <= BC_INST_JUMP_ZERO))
3318 {
3319 bc_program_printIndex(code, bgn);
3320 if (inst == BC_INST_CALL) bc_program_printIndex(code, bgn);
3321 }
3322
3323 bc_vm_putchar('\n', bc_flush_err);
3324 }
3325
bc_program_code(const BcProgram * p)3326 void bc_program_code(const BcProgram* p) {
3327
3328 BcFunc *f;
3329 char *code;
3330 BcInstPtr ip;
3331 size_t i;
3332
3333 for (i = 0; i < p->fns.len; ++i) {
3334
3335 ip.idx = ip.len = 0;
3336 ip.func = i;
3337
3338 f = bc_vec_item(&p->fns, ip.func);
3339 code = f->code.v;
3340
3341 bc_vm_printf("func[%zu]:\n", ip.func);
3342 while (ip.idx < f->code.len) bc_program_printInst(p, code, &ip.idx);
3343 bc_file_puts(&vm.fout, bc_flush_err, "\n\n");
3344 }
3345 }
3346 #endif // BC_ENABLED && DC_ENABLED
3347 #endif // BC_DEBUG_CODE
3348