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