/*1* *****************************************************************************2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2018-2025 Gavin D. Howard and contributors.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions are met:9*10* * Redistributions of source code must retain the above copyright notice, this11* list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright notice,14* this list of conditions and the following disclaimer in the documentation15* and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"18* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE21* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS24* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN25* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)26* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGE.28*29* *****************************************************************************30*31* Code to execute bc programs.32*33*/3435#include <assert.h>36#include <stdbool.h>37#include <string.h>3839#include <setjmp.h>4041#include <signal.h>4243#include <time.h>4445#include <read.h>46#include <parse.h>47#include <program.h>48#include <vm.h>4950/**51* Does a type check for something that expects a number.52* @param r The result that will be checked.53* @param n The result's number.54*/55static inline void56bc_program_type_num(BcResult* r, BcNum* n)57{58#if BC_ENABLED5960// This should have already been taken care of.61assert(r->t != BC_RESULT_VOID);6263#endif // BC_ENABLED6465if (BC_ERR(!BC_PROG_NUM(r, n))) bc_err(BC_ERR_EXEC_TYPE);66}6768#if BC_ENABLED6970/**71* Does a type check.72* @param r The result to check.73* @param t The type that the result should be.74*/75static void76bc_program_type_match(BcResult* r, BcType t)77{78if (BC_ERR((r->t != BC_RESULT_ARRAY) != (!t))) bc_err(BC_ERR_EXEC_TYPE);79}80#endif // BC_ENABLED8182/**83* Pulls an index out of a bytecode vector and updates the index into the vector84* to point to the spot after the index. For more details on bytecode indices,85* see the development manual (manuals/development.md#bytecode-indices).86* @param code The bytecode vector.87* @param bgn An in/out parameter; the index into the vector that will be88* updated.89* @return The index at @a bgn in the bytecode vector.90*/91static size_t92bc_program_index(const char* restrict code, size_t* restrict bgn)93{94uchar amt = (uchar) code[(*bgn)++], i = 0;95size_t res = 0;9697for (; i < amt; ++i, ++(*bgn))98{99size_t temp = ((size_t) ((int) (uchar) code[*bgn]) & UCHAR_MAX);100res |= (temp << (i * CHAR_BIT));101}102103return res;104}105106/**107* Returns a string from a result and its number.108* @param p The program.109* @param n The number tied to the result.110* @return The string corresponding to the result and number.111*/112static inline char*113bc_program_string(BcProgram* p, const BcNum* n)114{115return *((char**) bc_vec_item(&p->strs, n->scale));116}117118#if BC_ENABLED119120/**121* Prepares the globals for a function call. This is only called when global122* stacks are on because it pushes a copy of the current globals onto each of123* their respective stacks.124* @param p The program.125*/126static void127bc_program_prepGlobals(BcProgram* p)128{129size_t i;130131for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)132{133bc_vec_push(p->globals_v + i, p->globals + i);134}135136#if BC_ENABLE_EXTRA_MATH137bc_rand_push(&p->rng);138#endif // BC_ENABLE_EXTRA_MATH139}140141/**142* Pops globals stacks on returning from a function, or in the case of reset,143* pops all but one item on each global stack.144* @param p The program.145* @param reset True if all but one item on each stack should be popped, false146* otherwise.147*/148static void149bc_program_popGlobals(BcProgram* p, bool reset)150{151size_t i;152153BC_SIG_ASSERT_LOCKED;154155for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)156{157BcVec* v = p->globals_v + i;158bc_vec_npop(v, reset ? v->len - 1 : 1);159p->globals[i] = BC_PROG_GLOBAL(v);160}161162#if BC_ENABLE_EXTRA_MATH163bc_rand_pop(&p->rng, reset);164#endif // BC_ENABLE_EXTRA_MATH165}166167/**168* Derefeneces an array reference and returns a pointer to the real array.169* @param p The program.170* @param vec The reference vector.171* @return A pointer to the desired array.172*/173static BcVec*174bc_program_dereference(const BcProgram* p, BcVec* vec)175{176BcVec* v;177size_t vidx, nidx, i = 0;178179// We want to be sure we have a reference vector.180assert(vec->size == sizeof(uchar));181182// Get the index of the vector in arrs, then the index of the original183// referenced vector.184vidx = bc_program_index(vec->v, &i);185nidx = bc_program_index(vec->v, &i);186187v = bc_vec_item(bc_vec_item(&p->arrs, vidx), nidx);188189// We want to be sure we do *not* have a reference vector.190assert(v->size != sizeof(uchar));191192return v;193}194195#endif // BC_ENABLED196197/**198* Creates a BcNum from a BcBigDig and pushes onto the results stack. This is a199* convenience function.200* @param p The program.201* @param dig The BcBigDig to push onto the results stack.202* @param type The type that the pushed result should be.203*/204static void205bc_program_pushBigdig(BcProgram* p, BcBigDig dig, BcResultType type)206{207BcResult res;208209res.t = type;210211BC_SIG_LOCK;212213bc_num_createFromBigdig(&res.d.n, dig);214bc_vec_push(&p->results, &res);215216BC_SIG_UNLOCK;217}218219size_t220bc_program_addString(BcProgram* p, const char* str)221{222size_t idx;223224BC_SIG_ASSERT_LOCKED;225226if (bc_map_insert(&p->str_map, str, p->strs.len, &idx))227{228char** str_ptr;229BcId* id = bc_vec_item(&p->str_map, idx);230231// Get the index.232idx = id->idx;233234// Push an empty string on the proper vector.235str_ptr = bc_vec_pushEmpty(&p->strs);236237// We reuse the string in the ID (allocated by bc_map_insert()), because238// why not?239*str_ptr = id->name;240}241else242{243BcId* id = bc_vec_item(&p->str_map, idx);244idx = id->idx;245}246247return idx;248}249250size_t251bc_program_search(BcProgram* p, const char* name, bool var)252{253BcVec* v;254BcVec* map;255size_t i;256257BC_SIG_ASSERT_LOCKED;258259// Grab the right vector and map.260v = var ? &p->vars : &p->arrs;261map = var ? &p->var_map : &p->arr_map;262263// We do an insert because the variable might not exist yet. This is because264// the parser calls this function. If the insert succeeds, we create a stack265// for the variable/array. But regardless, bc_map_insert() gives us the266// index of the item in i.267if (bc_map_insert(map, name, v->len, &i))268{269BcVec* temp = bc_vec_pushEmpty(v);270bc_array_init(temp, var);271}272273return ((BcId*) bc_vec_item(map, i))->idx;274}275276/**277* Returns the correct variable or array stack for the type.278* @param p The program.279* @param idx The index of the variable or array in the variable or array280* vector.281* @param type The type of vector to return.282* @return A pointer to the variable or array stack.283*/284static inline BcVec*285bc_program_vec(const BcProgram* p, size_t idx, BcType type)286{287const BcVec* v = (type == BC_TYPE_VAR) ? &p->vars : &p->arrs;288return bc_vec_item(v, idx);289}290291/**292* Returns a pointer to the BcNum corresponding to the result. There is one293* case, however, where this returns a pointer to a BcVec: if the type of the294* result is array. In that case, the pointer is casted to a pointer to BcNum,295* but is never used. The function that calls this expecting an array casts the296* pointer back. This function is called a lot and needs to be as fast as297* possible.298* @param p The program.299* @param r The result whose number will be returned.300* @return The BcNum corresponding to the result.301*/302static BcNum*303bc_program_num(BcProgram* p, BcResult* r)304{305BcNum* n;306307#ifdef _WIN32308// Windows made it an error to not initialize this, so shut it up.309// I don't want to do this on other platforms because this procedure310// is one of the most heavily-used, and eliminating the initialization311// is a performance win.312n = NULL;313#endif // _WIN32314315switch (r->t)316{317case BC_RESULT_STR:318case BC_RESULT_TEMP:319case BC_RESULT_IBASE:320case BC_RESULT_SCALE:321case BC_RESULT_OBASE:322#if BC_ENABLE_EXTRA_MATH323case BC_RESULT_SEED:324#endif // BC_ENABLE_EXTRA_MATH325{326n = &r->d.n;327break;328}329330case BC_RESULT_VAR:331case BC_RESULT_ARRAY:332case BC_RESULT_ARRAY_ELEM:333{334BcVec* v;335BcType type = (r->t == BC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY;336337// Get the correct variable or array vector.338v = bc_program_vec(p, r->d.loc.loc, type);339340// Surprisingly enough, the hard case is *not* returning an array;341// it's returning an array element. This is because we have to dig342// deeper to get *to* the element. That's what the code inside this343// if statement does.344if (r->t == BC_RESULT_ARRAY_ELEM)345{346size_t idx = r->d.loc.idx;347348v = bc_vec_item(v, r->d.loc.stack_idx);349350#if BC_ENABLED351// If this is true, we have a reference vector, so dereference352// it. The reason we don't need to worry about it for returning353// a straight array is because we only care about references354// when we access elements of an array that is a reference. That355// is this code, so in essence, this line takes care of arrays356// as well.357if (v->size == sizeof(uchar)) v = bc_program_dereference(p, v);358#endif // BC_ENABLED359360// We want to be sure we got a valid array of numbers.361assert(v->size == sizeof(BcNum));362363// The bc spec says that if an element is accessed that does not364// exist, it should be preinitialized to 0. Well, if we access365// an element *way* out there, we have to preinitialize all366// elements between the current last element and the actual367// accessed element.368if (v->len <= idx)369{370BC_SIG_LOCK;371bc_array_expand(v, bc_vm_growSize(idx, 1));372BC_SIG_UNLOCK;373}374375n = bc_vec_item(v, idx);376}377// This is either a number (for a var) or an array (for an array).378// Because bc_vec_top() and bc_vec_item() return a void*, we don't379// need to cast.380else381{382#if BC_ENABLED383if (BC_IS_BC)384{385n = bc_vec_item(v, r->d.loc.stack_idx);386}387else388#endif // BC_ENABLED389{390n = bc_vec_top(v);391}392}393394break;395}396397case BC_RESULT_ZERO:398{399n = &vm->zero;400break;401}402403case BC_RESULT_ONE:404{405n = &vm->one;406break;407}408409#if BC_ENABLED410// We should never get here; this is taken care of earlier because a411// result is expected.412case BC_RESULT_VOID:413#if BC_DEBUG414{415abort();416// Fallthrough417}418#endif // BC_DEBUG419case BC_RESULT_LAST:420{421n = &p->last;422break;423}424#endif // BC_ENABLED425426#if BC_GCC427// This is here in GCC to quiet the "maybe-uninitialized" warning.428default:429{430abort();431}432#endif // BC_GCC433}434435return n;436}437438/**439* Prepares an operand for use.440* @param p The program.441* @param r An out parameter; this is set to the pointer to the result that442* we care about.443* @param n An out parameter; this is set to the pointer to the number that444* we care about.445* @param idx The index of the result from the top of the results stack.446*/447static void448bc_program_operand(BcProgram* p, BcResult** r, BcNum** n, size_t idx)449{450*r = bc_vec_item_rev(&p->results, idx);451452#if BC_ENABLED453if (BC_ERR((*r)->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL);454#endif // BC_ENABLED455456*n = bc_program_num(p, *r);457}458459/**460* Prepares the operands of a binary operator.461* @param p The program.462* @param l An out parameter; this is set to the pointer to the result for463* the left operand.464* @param ln An out parameter; this is set to the pointer to the number for465* the left operand.466* @param r An out parameter; this is set to the pointer to the result for467* the right operand.468* @param rn An out parameter; this is set to the pointer to the number for469* the right operand.470* @param idx The starting index where the operands are in the results stack,471* starting from the top.472*/473static void474bc_program_binPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r,475BcNum** rn, size_t idx)476{477BcResultType lt;478479assert(p != NULL && l != NULL && ln != NULL && r != NULL && rn != NULL);480481#ifndef BC_PROG_NO_STACK_CHECK482// Check the stack for dc.483if (BC_IS_DC)484{485if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 2)))486{487bc_err(BC_ERR_EXEC_STACK);488}489}490#endif // BC_PROG_NO_STACK_CHECK491492assert(BC_PROG_STACK(&p->results, idx + 2));493494// Get the operands.495bc_program_operand(p, l, ln, idx + 1);496bc_program_operand(p, r, rn, idx);497498lt = (*l)->t;499500#if BC_ENABLED501// bc_program_operand() checked these for us.502assert(lt != BC_RESULT_VOID && (*r)->t != BC_RESULT_VOID);503#endif // BC_ENABLED504505// We run this again under these conditions in case any vector has been506// reallocated out from under the BcNums or arrays we had. In other words,507// this is to fix pointer invalidation.508if (lt == (*r)->t && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM))509{510*ln = bc_program_num(p, *l);511}512513if (BC_ERR(lt == BC_RESULT_STR)) bc_err(BC_ERR_EXEC_TYPE);514}515516/**517* Prepares the operands of a binary operator and type checks them. This is518* separate from bc_program_binPrep() because some places want this, others want519* bc_program_binPrep().520* @param p The program.521* @param l An out parameter; this is set to the pointer to the result for522* the left operand.523* @param ln An out parameter; this is set to the pointer to the number for524* the left operand.525* @param r An out parameter; this is set to the pointer to the result for526* the right operand.527* @param rn An out parameter; this is set to the pointer to the number for528* the right operand.529* @param idx The starting index where the operands are in the results stack,530* starting from the top.531*/532static void533bc_program_binOpPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r,534BcNum** rn, size_t idx)535{536bc_program_binPrep(p, l, ln, r, rn, idx);537bc_program_type_num(*l, *ln);538bc_program_type_num(*r, *rn);539}540541/**542* Prepares the operands of an assignment operator.543* @param p The program.544* @param l An out parameter; this is set to the pointer to the result for the545* left operand.546* @param ln An out parameter; this is set to the pointer to the number for the547* left operand.548* @param r An out parameter; this is set to the pointer to the result for the549* right operand.550* @param rn An out parameter; this is set to the pointer to the number for the551* right operand.552*/553static void554bc_program_assignPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r,555BcNum** rn)556{557BcResultType lt, min;558bool good;559560// This is the min non-allowable result type. dc allows strings.561min = BC_RESULT_TEMP - ((unsigned int) (BC_IS_BC));562563// Prepare the operands.564bc_program_binPrep(p, l, ln, r, rn, 0);565566lt = (*l)->t;567568// Typecheck the left.569if (BC_ERR(lt >= min && lt <= BC_RESULT_ONE)) bc_err(BC_ERR_EXEC_TYPE);570571// Strings can be assigned to variables. We are already good if we are572// assigning a string.573good = ((*r)->t == BC_RESULT_STR && lt <= BC_RESULT_ARRAY_ELEM);574575assert(BC_PROG_STR(*rn) || (*r)->t != BC_RESULT_STR);576577// If not, type check for a number.578if (!good) bc_program_type_num(*r, *rn);579}580581/**582* Prepares a single operand and type checks it. This is separate from583* bc_program_operand() because different places want one or the other.584* @param p The program.585* @param r An out parameter; this is set to the pointer to the result that586* we care about.587* @param n An out parameter; this is set to the pointer to the number that588* we care about.589* @param idx The index of the result from the top of the results stack.590*/591static void592bc_program_prep(BcProgram* p, BcResult** r, BcNum** n, size_t idx)593{594assert(p != NULL && r != NULL && n != NULL);595596#ifndef BC_PROG_NO_STACK_CHECK597// Check the stack for dc.598if (BC_IS_DC)599{600if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1)))601{602bc_err(BC_ERR_EXEC_STACK);603}604}605#endif // BC_PROG_NO_STACK_CHECK606607assert(BC_PROG_STACK(&p->results, idx + 1));608609bc_program_operand(p, r, n, idx);610611// dc does not allow strings in this case.612bc_program_type_num(*r, *n);613}614615/**616* Prepares and returns a clean result for the result of an operation.617* @param p The program.618* @return A clean result.619*/620static BcResult*621bc_program_prepResult(BcProgram* p)622{623BcResult* res = bc_vec_pushEmpty(&p->results);624625// Mark a result as not retired.626p->nresults += 1;627628bc_result_clear(res);629630return res;631}632633/**634* Prepares a constant for use. This parses the constant into a number and then635* pushes that number onto the results stack.636* @param p The program.637* @param code The bytecode vector that we will pull the index of the constant638* from.639* @param bgn An in/out parameter; marks the start of the index in the640* bytecode vector and will be updated to point to after the index.641*/642static void643bc_program_const(BcProgram* p, const char* code, size_t* bgn)644{645// I lied. I actually push the result first. I can do this because the646// result will be popped on error. I also get the constant itself.647BcResult* r = bc_program_prepResult(p);648BcConst* c = bc_vec_item(&p->consts, bc_program_index(code, bgn));649BcBigDig base = BC_PROG_IBASE(p);650651assert(p->nresults == 1);652653// Only reparse if the base changed.654if (c->base != base)655{656// Allocate if we haven't yet.657if (c->num.num == NULL)658{659// The plus 1 is in case of overflow with lack of clamping.660size_t len = strlen(c->val) + (BC_DIGIT_CLAMP == 0);661662BC_SIG_LOCK;663bc_num_init(&c->num, BC_NUM_RDX(len));664BC_SIG_UNLOCK;665}666// We need to zero an already existing number.667else bc_num_zero(&c->num);668669// bc_num_parse() should only do operations that cannot fail.670bc_num_parse(&c->num, c->val, base);671672c->base = base;673}674675BC_SIG_LOCK;676677bc_num_createCopy(&r->d.n, &c->num);678679BC_SIG_UNLOCK;680681// XXX: Make sure to clear the number of results.682p->nresults -= 1;683}684685/**686* Executes a binary operator operation.687* @param p The program.688* @param inst The instruction corresponding to the binary operator to execute.689*/690static void691bc_program_op(BcProgram* p, uchar inst)692{693BcResult* opd1;694BcResult* opd2;695BcResult* res;696BcNum* n1;697BcNum* n2;698size_t idx = inst - BC_INST_POWER;699700res = bc_program_prepResult(p);701702assert(p->nresults == 1);703704bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);705706BC_SIG_LOCK;707708// Initialize the number with enough space, using the correct709// BcNumBinaryOpReq function. This looks weird because it is executing an710// item of an array. Rest assured that item is a function.711bc_num_init(&res->d.n, bc_program_opReqs[idx](n1, n2, BC_PROG_SCALE(p)));712713BC_SIG_UNLOCK;714715assert(BC_NUM_RDX_VALID(n1));716assert(BC_NUM_RDX_VALID(n2));717718// Run the operation. This also executes an item of an array.719bc_program_ops[idx](n1, n2, &res->d.n, BC_PROG_SCALE(p));720721bc_program_retire(p, 2);722}723724/**725* Executes a read() or ? command.726* @param p The program.727*/728static void729bc_program_read(BcProgram* p)730{731BcStatus s;732BcInstPtr ip;733size_t i;734const char* file;735BcMode mode;736BcFunc* f = bc_vec_item(&p->fns, BC_PROG_READ);737738// If we are already executing a read, that is an error. So look for a read739// and barf.740for (i = 0; i < p->stack.len; ++i)741{742BcInstPtr* ip_ptr = bc_vec_item(&p->stack, i);743if (ip_ptr->func == BC_PROG_READ) bc_err(BC_ERR_EXEC_REC_READ);744}745746BC_SIG_LOCK;747748// Save the filename because we are going to overwrite it.749file = vm->file;750mode = vm->mode;751752// It is a parse error if there needs to be more than one line, so we unset753// this to tell the lexer to not request more. We set it back later.754vm->mode = BC_MODE_FILE;755756if (!BC_PARSE_IS_INITED(&vm->read_prs, p))757{758// We need to parse, but we don't want to use the existing parser759// because it has state it needs to keep. (It could have a partial parse760// state.) So we create a new parser. This parser is in the BcVm struct761// so that it is not local, which means that a longjmp() could change762// it.763bc_parse_init(&vm->read_prs, p, BC_PROG_READ);764765// We need a separate input buffer; that's why it is also in the BcVm766// struct.767bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE);768}769else770{771// This needs to be updated because the parser could have been used772// somewhere else.773bc_parse_updateFunc(&vm->read_prs, BC_PROG_READ);774775// The read buffer also needs to be emptied or else it will still776// contain previous read expressions.777bc_vec_empty(&vm->read_buf);778}779780BC_SETJMP_LOCKED(vm, exec_err);781782BC_SIG_UNLOCK;783784// Set up the lexer and the read function.785bc_lex_file(&vm->read_prs.l, bc_program_stdin_name);786bc_vec_popAll(&f->code);787788// Read a line.789if (!BC_R) s = bc_read_line(&vm->read_buf, "");790else s = bc_read_line(&vm->read_buf, BC_VM_READ_PROMPT);791792// We should *not* have run into EOF.793if (s == BC_STATUS_EOF) bc_err(BC_ERR_EXEC_READ_EXPR);794795// Parse *one* expression, so mode should not be stdin.796bc_parse_text(&vm->read_prs, vm->read_buf.v, BC_MODE_FILE);797BC_SIG_LOCK;798vm->expr(&vm->read_prs, BC_PARSE_NOREAD | BC_PARSE_NEEDVAL);799BC_SIG_UNLOCK;800801// We *must* have a valid expression. A semicolon cannot end an expression,802// although EOF can.803if (BC_ERR(vm->read_prs.l.t != BC_LEX_NLINE &&804vm->read_prs.l.t != BC_LEX_EOF))805{806bc_err(BC_ERR_EXEC_READ_EXPR);807}808809#if BC_ENABLED810// Push on the globals stack if necessary.811if (BC_G) bc_program_prepGlobals(p);812#endif // BC_ENABLED813814// Set up a new BcInstPtr.815ip.func = BC_PROG_READ;816ip.idx = 0;817ip.len = p->results.len;818819// Update this pointer, just in case.820f = bc_vec_item(&p->fns, BC_PROG_READ);821822// We want a return instruction to simplify things.823bc_vec_pushByte(&f->code, vm->read_ret);824825// This lock is here to make sure dc's tail calls are the same length.826BC_SIG_LOCK;827bc_vec_push(&p->stack, &ip);828829#if DC_ENABLED830// We need a new tail call entry for dc.831if (BC_IS_DC)832{833size_t temp = 0;834bc_vec_push(&p->tail_calls, &temp);835}836#endif // DC_ENABLED837838exec_err:839BC_SIG_MAYLOCK;840vm->mode = (uchar) mode;841vm->file = file;842BC_LONGJMP_CONT(vm);843}844845#if BC_ENABLE_EXTRA_MATH846847/**848* Execute a rand().849* @param p The program.850*/851static void852bc_program_rand(BcProgram* p)853{854BcRand rand = bc_rand_int(&p->rng);855856bc_program_pushBigdig(p, (BcBigDig) rand, BC_RESULT_TEMP);857858#if BC_DEBUG859// This is just to ensure that the generated number is correct. I also use860// braces because I declare every local at the top of the scope.861{862BcResult* r = bc_vec_top(&p->results);863assert(BC_NUM_RDX_VALID_NP(r->d.n));864}865#endif // BC_DEBUG866}867#endif // BC_ENABLE_EXTRA_MATH868869/**870* Prints a series of characters, without escapes.871* @param str The string (series of characters).872*/873static void874bc_program_printChars(const char* str)875{876const char* nl;877size_t len = vm->nchars + strlen(str);878sig_atomic_t lock;879880BC_SIG_TRYLOCK(lock);881882bc_file_puts(&vm->fout, bc_flush_save, str);883884// We need to update the number of characters, so we find the last newline885// and set the characters accordingly.886nl = strrchr(str, '\n');887888if (nl != NULL) len = strlen(nl + 1);889890vm->nchars = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len;891892BC_SIG_TRYUNLOCK(lock);893}894895/**896* Prints a string with escapes.897* @param str The string.898*/899static void900bc_program_printString(const char* restrict str)901{902size_t i, len = strlen(str);903904#if DC_ENABLED905// This is to ensure a nul byte is printed for dc's stream operation.906if (!len && BC_IS_DC)907{908bc_vm_putchar('\0', bc_flush_save);909return;910}911#endif // DC_ENABLED912913// Loop over the characters, processing escapes and printing the rest.914for (i = 0; i < len; ++i)915{916int c = str[i];917918// If we have an escape...919if (c == '\\' && i != len - 1)920{921const char* ptr;922923// Get the escape character and its companion.924c = str[++i];925ptr = strchr(bc_program_esc_chars, c);926927// If we have a companion character...928if (ptr != NULL)929{930// We need to specially handle a newline.931if (c == 'n')932{933BC_SIG_LOCK;934vm->nchars = UINT16_MAX;935BC_SIG_UNLOCK;936}937938// Grab the actual character.939c = bc_program_esc_seqs[(size_t) (ptr - bc_program_esc_chars)];940}941else942{943// Just print the backslash if there is no companion character.944// The following character will be printed later after the outer945// if statement.946bc_vm_putchar('\\', bc_flush_save);947}948}949950bc_vm_putchar(c, bc_flush_save);951}952}953954/**955* Executes a print. This function handles all printing except streaming.956* @param p The program.957* @param inst The instruction for the type of print we are doing.958* @param idx The index of the result that we are printing.959*/960static void961bc_program_print(BcProgram* p, uchar inst, size_t idx)962{963BcResult* r;964char* str;965BcNum* n;966bool pop = (inst != BC_INST_PRINT);967968assert(p != NULL);969970#ifndef BC_PROG_NO_STACK_CHECK971if (BC_IS_DC)972{973if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1)))974{975bc_err(BC_ERR_EXEC_STACK);976}977}978#endif // BC_PROG_NO_STACK_CHECK979980assert(BC_PROG_STACK(&p->results, idx + 1));981982r = bc_vec_item_rev(&p->results, idx);983984#if BC_ENABLED985// If we have a void value, that's not necessarily an error. It is if pop is986// true because that means that we are executing a print statement, but987// attempting to do a print on a lone void value is allowed because that's988// exactly how we want void values used.989if (r->t == BC_RESULT_VOID)990{991if (BC_ERR(pop)) bc_err(BC_ERR_EXEC_VOID_VAL);992bc_vec_pop(&p->results);993return;994}995#endif // BC_ENABLED996997n = bc_program_num(p, r);998999// If we have a number...1000if (BC_PROG_NUM(r, n))1001{1002#if BC_ENABLED1003assert(inst != BC_INST_PRINT_STR);1004#endif // BC_ENABLED10051006// Print the number.1007bc_num_print(n, BC_PROG_OBASE(p), !pop);10081009#if BC_ENABLED1010// Need to store the number in last.1011if (BC_IS_BC) bc_num_copy(&p->last, n);1012#endif // BC_ENABLED1013}1014else1015{1016// We want to flush any stuff in the stdout buffer first.1017bc_file_flush(&vm->fout, bc_flush_save);1018str = bc_program_string(p, n);10191020#if BC_ENABLED1021if (inst == BC_INST_PRINT_STR) bc_program_printChars(str);1022else1023#endif // BC_ENABLED1024{1025bc_program_printString(str);10261027// Need to print a newline only in this case.1028if (inst == BC_INST_PRINT) bc_vm_putchar('\n', bc_flush_err);1029}1030}10311032// bc always pops. This macro makes sure that happens.1033if (BC_PROGRAM_POP(pop)) bc_vec_pop(&p->results);1034}10351036void1037bc_program_negate(BcResult* r, BcNum* n)1038{1039bc_num_copy(&r->d.n, n);1040if (BC_NUM_NONZERO(&r->d.n)) BC_NUM_NEG_TGL_NP(r->d.n);1041}10421043void1044bc_program_not(BcResult* r, BcNum* n)1045{1046if (!bc_num_cmpZero(n)) bc_num_one(&r->d.n);1047}10481049#if BC_ENABLE_EXTRA_MATH1050void1051bc_program_trunc(BcResult* r, BcNum* n)1052{1053bc_num_copy(&r->d.n, n);1054bc_num_truncate(&r->d.n, n->scale);1055}1056#endif // BC_ENABLE_EXTRA_MATH10571058/**1059* Runs a unary operation.1060* @param p The program.1061* @param inst The unary operation.1062*/1063static void1064bc_program_unary(BcProgram* p, uchar inst)1065{1066BcResult* res;1067BcResult* ptr;1068BcNum* num;10691070res = bc_program_prepResult(p);10711072assert(p->nresults == 1);10731074bc_program_prep(p, &ptr, &num, 1);10751076BC_SIG_LOCK;10771078bc_num_init(&res->d.n, num->len);10791080BC_SIG_UNLOCK;10811082// This calls a function that is in an array.1083bc_program_unarys[inst - BC_INST_NEG](res, num);1084bc_program_retire(p, 1);1085}10861087/**1088* Executes a logical operator.1089* @param p The program.1090* @param inst The operator.1091*/1092static void1093bc_program_logical(BcProgram* p, uchar inst)1094{1095BcResult* opd1;1096BcResult* opd2;1097BcResult* res;1098BcNum* n1;1099BcNum* n2;1100bool cond = 0;1101ssize_t cmp;11021103res = bc_program_prepResult(p);11041105assert(p->nresults == 1);11061107// All logical operators (except boolean not, which is taken care of by1108// bc_program_unary()), are binary operators.1109bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);11101111// Boolean and and or are not short circuiting. This is why; they can be1112// implemented much easier this way.1113if (inst == BC_INST_BOOL_AND)1114{1115cond = (bc_num_cmpZero(n1) && bc_num_cmpZero(n2));1116}1117else if (inst == BC_INST_BOOL_OR)1118{1119cond = (bc_num_cmpZero(n1) || bc_num_cmpZero(n2));1120}1121else1122{1123// We have a relational operator, so do a comparison.1124cmp = bc_num_cmp(n1, n2);11251126switch (inst)1127{1128case BC_INST_REL_EQ:1129{1130cond = (cmp == 0);1131break;1132}11331134case BC_INST_REL_LE:1135{1136cond = (cmp <= 0);1137break;1138}11391140case BC_INST_REL_GE:1141{1142cond = (cmp >= 0);1143break;1144}11451146case BC_INST_REL_NE:1147{1148cond = (cmp != 0);1149break;1150}11511152case BC_INST_REL_LT:1153{1154cond = (cmp < 0);1155break;1156}11571158case BC_INST_REL_GT:1159{1160cond = (cmp > 0);1161break;1162}1163#if BC_DEBUG1164default:1165{1166// There is a bug if we get here.1167abort();1168}1169#endif // BC_DEBUG1170}1171}11721173BC_SIG_LOCK;11741175bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);11761177BC_SIG_UNLOCK;11781179if (cond) bc_num_one(&res->d.n);11801181bc_program_retire(p, 2);1182}11831184/**1185* Assigns a string to a variable.1186* @param p The program.1187* @param num The location of the string as a BcNum.1188* @param v The stack for the variable.1189* @param push Whether to push the string or not. To push means to move the1190* string from the results stack and push it onto the variable1191* stack.1192*/1193static void1194bc_program_assignStr(BcProgram* p, BcNum* num, BcVec* v, bool push)1195{1196BcNum* n;11971198assert(BC_PROG_STACK(&p->results, 1 + !push));1199assert(num != NULL && num->num == NULL && num->cap == 0);12001201// If we are not pushing onto the variable stack, we need to replace the1202// top of the variable stack.1203if (!push) bc_vec_pop(v);12041205bc_vec_npop(&p->results, 1 + !push);12061207n = bc_vec_pushEmpty(v);12081209// We can just copy because the num should not have allocated anything.1210// NOLINTNEXTLINE1211memcpy(n, num, sizeof(BcNum));1212}12131214/**1215* Copies a value to a variable. This is used for storing in dc as well as to1216* set function parameters to arguments in bc.1217* @param p The program.1218* @param idx The index of the variable or array to copy to.1219* @param t The type to copy to. This could be a variable or an array.1220*/1221static void1222bc_program_copyToVar(BcProgram* p, size_t idx, BcType t)1223{1224BcResult *ptr = NULL, r;1225BcVec* vec;1226BcNum* n = NULL;1227bool var = (t == BC_TYPE_VAR);12281229#if DC_ENABLED1230// Check the stack for dc.1231if (BC_IS_DC)1232{1233if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);1234}1235#endif12361237assert(BC_PROG_STACK(&p->results, 1));12381239bc_program_operand(p, &ptr, &n, 0);12401241#if BC_ENABLED1242// Get the variable for a bc function call.1243if (BC_IS_BC)1244{1245// Type match the result.1246bc_program_type_match(ptr, t);1247}1248#endif // BC_ENABLED12491250vec = bc_program_vec(p, idx, t);12511252// We can shortcut in dc if it's assigning a string by using1253// bc_program_assignStr().1254if (ptr->t == BC_RESULT_STR)1255{1256assert(BC_PROG_STR(n));12571258if (BC_ERR(!var)) bc_err(BC_ERR_EXEC_TYPE);12591260bc_program_assignStr(p, n, vec, true);12611262return;1263}12641265BC_SIG_LOCK;12661267// Just create and copy for a normal variable.1268if (var)1269{1270if (BC_PROG_STR(n))1271{1272// NOLINTNEXTLINE1273memcpy(&r.d.n, n, sizeof(BcNum));1274}1275else bc_num_createCopy(&r.d.n, n);1276}1277else1278{1279// If we get here, we are handling an array. This is one place we need1280// to cast the number from bc_program_num() to a vector.1281BcVec* v = (BcVec*) n;1282BcVec* rv = &r.d.v;12831284#if BC_ENABLED12851286if (BC_IS_BC)1287{1288bool ref, ref_size;12891290// True if we are using a reference.1291ref = (v->size == sizeof(BcNum) && t == BC_TYPE_REF);12921293// True if we already have a reference vector. This is slightly1294// (okay, a lot; it just doesn't look that way) different from1295// above. The above means that we need to construct a reference1296// vector, whereas this means that we have one and we might have to1297// *dereference* it.1298ref_size = (v->size == sizeof(uchar));12991300// If we *should* have a reference.1301if (ref || (ref_size && t == BC_TYPE_REF))1302{1303// Create a new reference vector.1304bc_vec_init(rv, sizeof(uchar), BC_DTOR_NONE);13051306// If this is true, then we need to construct a reference.1307if (ref)1308{1309// Make sure the pointer was not invalidated.1310vec = bc_program_vec(p, idx, t);13111312// Push the indices onto the reference vector. This takes1313// care of last; it ensures the reference goes to the right1314// place.1315bc_vec_pushIndex(rv, ptr->d.loc.loc);1316bc_vec_pushIndex(rv, ptr->d.loc.stack_idx);1317}1318// If we get here, we are copying a ref to a ref. Just push a1319// copy of all of the bytes.1320else bc_vec_npush(rv, v->len * sizeof(uchar), v->v);13211322// Push the reference vector onto the array stack and pop the1323// source.1324bc_vec_push(vec, &r.d);1325bc_vec_pop(&p->results);13261327// We need to return early to avoid executing code that we must1328// not touch.1329BC_SIG_UNLOCK;1330return;1331}1332// If we get here, we have a reference, but we need an array, so1333// dereference the array.1334else if (ref_size && t != BC_TYPE_REF)1335{1336v = bc_program_dereference(p, v);1337}1338}1339#endif // BC_ENABLED13401341// If we get here, we need to copy the array because in bc, all1342// arguments are passed by value. Yes, this is expensive.1343bc_array_init(rv, true);1344bc_array_copy(rv, v);1345}13461347// Push the vector onto the array stack and pop the source.1348bc_vec_push(vec, &r.d);1349bc_vec_pop(&p->results);13501351BC_SIG_UNLOCK;1352}13531354void1355bc_program_assignBuiltin(BcProgram* p, bool scale, bool obase, BcBigDig val)1356{1357BcBigDig* ptr_t;1358BcBigDig max, min;1359#if BC_ENABLED1360BcVec* v;1361BcBigDig* ptr;1362#endif // BC_ENABLED13631364assert(!scale || !obase);13651366// Scale needs handling separate from ibase and obase.1367if (scale)1368{1369// Set the min and max.1370min = 0;1371max = vm->maxes[BC_PROG_GLOBALS_SCALE];13721373#if BC_ENABLED1374// Get a pointer to the stack.1375v = p->globals_v + BC_PROG_GLOBALS_SCALE;1376#endif // BC_ENABLED13771378// Get a pointer to the current value.1379ptr_t = p->globals + BC_PROG_GLOBALS_SCALE;1380}1381else1382{1383// Set the min and max.1384min = BC_NUM_MIN_BASE;1385if (BC_ENABLE_EXTRA_MATH && obase && (BC_IS_DC || !BC_IS_POSIX))1386{1387min = 0;1388}1389max = vm->maxes[obase + BC_PROG_GLOBALS_IBASE];13901391#if BC_ENABLED1392// Get a pointer to the stack.1393v = p->globals_v + BC_PROG_GLOBALS_IBASE + obase;1394#endif // BC_ENABLED13951396// Get a pointer to the current value.1397ptr_t = p->globals + BC_PROG_GLOBALS_IBASE + obase;1398}13991400// Check for error.1401if (BC_ERR(val > max || val < min))1402{1403BcErr e;14041405// This grabs the right error.1406if (scale) e = BC_ERR_EXEC_SCALE;1407else if (obase) e = BC_ERR_EXEC_OBASE;1408else e = BC_ERR_EXEC_IBASE;14091410bc_verr(e, min, max);1411}14121413#if BC_ENABLED1414// Set the top of the stack.1415ptr = bc_vec_top(v);1416*ptr = val;1417#endif // BC_ENABLED14181419// Set the actual global variable.1420*ptr_t = val;1421}14221423#if BC_ENABLE_EXTRA_MATH1424void1425bc_program_assignSeed(BcProgram* p, BcNum* val)1426{1427bc_num_rng(val, &p->rng);1428}1429#endif // BC_ENABLE_EXTRA_MATH14301431/**1432* Executes an assignment operator.1433* @param p The program.1434* @param inst The assignment operator to execute.1435*/1436static void1437bc_program_assign(BcProgram* p, uchar inst)1438{1439// The local use_val is true when the assigned value needs to be copied.1440BcResult* left;1441BcResult* right;1442BcResult res;1443BcNum* l;1444BcNum* r;1445bool ob, sc, use_val = BC_INST_USE_VAL(inst);14461447bc_program_assignPrep(p, &left, &l, &right, &r);14481449// Assigning to a string should be impossible simply because of the parse.1450assert(left->t != BC_RESULT_STR);14511452// If we are assigning a string...1453if (right->t == BC_RESULT_STR)1454{1455assert(BC_PROG_STR(r));14561457#if BC_ENABLED1458if (inst != BC_INST_ASSIGN && inst != BC_INST_ASSIGN_NO_VAL)1459{1460bc_err(BC_ERR_EXEC_TYPE);1461}1462#endif // BC_ENABLED14631464// If we are assigning to an array element...1465if (left->t == BC_RESULT_ARRAY_ELEM)1466{1467BC_SIG_LOCK;14681469// We need to free the number and clear it.1470bc_num_free(l);14711472// NOLINTNEXTLINE1473memcpy(l, r, sizeof(BcNum));14741475// Now we can pop the results.1476bc_vec_npop(&p->results, 2);14771478BC_SIG_UNLOCK;1479}1480else1481{1482// If we get here, we are assigning to a variable, which we can use1483// bc_program_assignStr() for.1484BcVec* v = bc_program_vec(p, left->d.loc.loc, BC_TYPE_VAR);1485bc_program_assignStr(p, r, v, false);1486}14871488#if BC_ENABLED14891490// If this is true, the value is going to be used again, so we want to1491// push a temporary with the string.1492if (inst == BC_INST_ASSIGN)1493{1494res.t = BC_RESULT_STR;1495// NOLINTNEXTLINE1496memcpy(&res.d.n, r, sizeof(BcNum));1497bc_vec_push(&p->results, &res);1498}14991500#endif // BC_ENABLED15011502// By using bc_program_assignStr(), we short-circuited this, so return.1503return;1504}15051506// If we have a normal assignment operator, not a math one...1507if (BC_INST_IS_ASSIGN(inst))1508{1509// Assigning to a variable that has a string here is fine because there1510// is no math done on it.15111512// BC_RESULT_TEMP, BC_RESULT_IBASE, BC_RESULT_OBASE, BC_RESULT_SCALE,1513// and BC_RESULT_SEED all have temporary copies. Because that's the1514// case, we can free the left and just move the value over. We set the1515// type of right to BC_RESULT_ZERO in order to prevent it from being1516// freed. We also don't have to worry about BC_RESULT_STR because it's1517// take care of above.1518if (right->t == BC_RESULT_TEMP || right->t >= BC_RESULT_IBASE)1519{1520BC_SIG_LOCK;15211522bc_num_free(l);1523// NOLINTNEXTLINE1524memcpy(l, r, sizeof(BcNum));1525right->t = BC_RESULT_ZERO;15261527BC_SIG_UNLOCK;1528}1529// Copy over.1530else bc_num_copy(l, r);1531}1532#if BC_ENABLED1533else1534{1535// If we get here, we are doing a math assignment (+=, -=, etc.). So1536// we need to prepare for a binary operator.1537BcBigDig scale = BC_PROG_SCALE(p);15381539// At this point, the left side could still be a string because it could1540// be a variable that has the string. If that's the case, we have a type1541// error.1542if (BC_PROG_STR(l)) bc_err(BC_ERR_EXEC_TYPE);15431544// Get the right type of assignment operator, whether val is used or1545// NO_VAL for performance.1546if (!use_val)1547{1548inst -= (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER);1549}15501551assert(BC_NUM_RDX_VALID(l));1552assert(BC_NUM_RDX_VALID(r));15531554// Run the actual operation. We do not need worry about reallocating l1555// because bc_num_binary() does that behind the scenes for us.1556bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, scale);1557}1558#endif // BC_ENABLED15591560ob = (left->t == BC_RESULT_OBASE);1561sc = (left->t == BC_RESULT_SCALE);15621563// The globals need special handling, especially the non-seed ones. The1564// first part of the if statement handles them.1565if (ob || sc || left->t == BC_RESULT_IBASE)1566{1567// Get the actual value.1568BcBigDig val = bc_num_bigdig(l);15691570bc_program_assignBuiltin(p, sc, ob, val);1571}1572#if BC_ENABLE_EXTRA_MATH1573// To assign to steed, let bc_num_rng() do its magic.1574else if (left->t == BC_RESULT_SEED) bc_program_assignSeed(p, l);1575#endif // BC_ENABLE_EXTRA_MATH15761577BC_SIG_LOCK;15781579// If we needed to use the value, then we need to copy it. Otherwise, we can1580// pop indiscriminately. Oh, and the copy should be a BC_RESULT_TEMP.1581if (use_val)1582{1583bc_num_createCopy(&res.d.n, l);1584res.t = BC_RESULT_TEMP;1585bc_vec_npop(&p->results, 2);1586bc_vec_push(&p->results, &res);1587}1588else bc_vec_npop(&p->results, 2);15891590BC_SIG_UNLOCK;1591}15921593/**1594* Pushes a variable's value onto the results stack.1595* @param p The program.1596* @param code The bytecode vector to pull the variable's index out of.1597* @param bgn An in/out parameter; the start of the index in the bytecode1598* vector, and will be updated to point after the index on return.1599* @param pop True if the variable's value should be popped off its stack.1600* This is only used in dc.1601* @param copy True if the variable's value should be copied to the results1602* stack. This is only used in dc.1603*/1604static void1605bc_program_pushVar(BcProgram* p, const char* restrict code,1606size_t* restrict bgn, bool pop, bool copy)1607{1608BcResult r;1609size_t idx = bc_program_index(code, bgn);1610BcVec* v;16111612// Set the result appropriately.1613r.t = BC_RESULT_VAR;1614r.d.loc.loc = idx;16151616// Get the stack for the variable. This is used in both bc and dc.1617v = bc_program_vec(p, idx, BC_TYPE_VAR);1618r.d.loc.stack_idx = v->len - 1;16191620#if DC_ENABLED1621// If this condition is true, then we have the hard case, where we have to1622// adjust dc registers.1623if (BC_IS_DC && (pop || copy))1624{1625// Get the number at the top at the top of the stack.1626BcNum* num = bc_vec_top(v);16271628// Ensure there are enough elements on the stack.1629if (BC_ERR(!BC_PROG_STACK(v, 2 - copy)))1630{1631const char* name = bc_map_name(&p->var_map, idx);1632bc_verr(BC_ERR_EXEC_STACK_REGISTER, name);1633}16341635assert(BC_PROG_STACK(v, 2 - copy));16361637// If the top of the stack is actually a number...1638if (!BC_PROG_STR(num))1639{1640BC_SIG_LOCK;16411642// Create a copy to go onto the results stack as appropriate.1643r.t = BC_RESULT_TEMP;1644bc_num_createCopy(&r.d.n, num);16451646// If we are not actually copying, we need to do a replace, so pop.1647if (!copy) bc_vec_pop(v);16481649bc_vec_push(&p->results, &r);16501651BC_SIG_UNLOCK;16521653return;1654}1655else1656{1657// Set the string result. We can just memcpy because all of the1658// fields in the num should be cleared.1659// NOLINTNEXTLINE1660memcpy(&r.d.n, num, sizeof(BcNum));1661r.t = BC_RESULT_STR;1662}16631664// If we are not actually copying, we need to do a replace, so pop.1665if (!copy) bc_vec_pop(v);1666}1667#endif // DC_ENABLED16681669bc_vec_push(&p->results, &r);1670}16711672/**1673* Pushes an array or an array element onto the results stack.1674* @param p The program.1675* @param code The bytecode vector to pull the variable's index out of.1676* @param bgn An in/out parameter; the start of the index in the bytecode1677* vector, and will be updated to point after the index on return.1678* @param inst The instruction; whether to push an array or an array element.1679*/1680static void1681bc_program_pushArray(BcProgram* p, const char* restrict code,1682size_t* restrict bgn, uchar inst)1683{1684BcResult r;1685BcResult* operand;1686BcNum* num;1687BcBigDig temp;1688BcVec* v;16891690// Get the index of the array.1691r.d.loc.loc = bc_program_index(code, bgn);16921693// We need the array to get its length.1694v = bc_program_vec(p, r.d.loc.loc, BC_TYPE_ARRAY);1695assert(v != NULL);16961697r.d.loc.stack_idx = v->len - 1;16981699// Doing an array is easy; just set the result type and finish.1700if (inst == BC_INST_ARRAY)1701{1702r.t = BC_RESULT_ARRAY;1703bc_vec_push(&p->results, &r);1704return;1705}17061707// Grab the top element of the results stack for the array index.1708bc_program_prep(p, &operand, &num, 0);1709temp = bc_num_bigdig(num);17101711// Set the result.1712r.t = BC_RESULT_ARRAY_ELEM;1713r.d.loc.idx = (size_t) temp;17141715BC_SIG_LOCK;17161717// Pop the index and push the element.1718bc_vec_pop(&p->results);1719bc_vec_push(&p->results, &r);17201721BC_SIG_UNLOCK;1722}17231724#if BC_ENABLED17251726/**1727* Executes an increment or decrement operator. This only handles postfix1728* inc/dec because the parser translates prefix inc/dec into an assignment where1729* the value is used.1730* @param p The program.1731* @param inst The instruction; whether to do an increment or decrement.1732*/1733static void1734bc_program_incdec(BcProgram* p, uchar inst)1735{1736BcResult *ptr, res, copy;1737BcNum* num;1738uchar inst2;17391740bc_program_prep(p, &ptr, &num, 0);17411742BC_SIG_LOCK;17431744// We need a copy from *before* the operation.1745copy.t = BC_RESULT_TEMP;1746bc_num_createCopy(©.d.n, num);17471748BC_SETJMP_LOCKED(vm, exit);17491750BC_SIG_UNLOCK;17511752// Create the proper assignment.1753res.t = BC_RESULT_ONE;1754inst2 = BC_INST_ASSIGN_PLUS_NO_VAL + (inst & 0x01);17551756bc_vec_push(&p->results, &res);1757bc_program_assign(p, inst2);17581759BC_SIG_LOCK;17601761bc_vec_push(&p->results, ©);17621763BC_UNSETJMP(vm);17641765BC_SIG_UNLOCK;17661767// No need to free the copy here because we pushed it onto the stack.1768return;17691770exit:1771BC_SIG_MAYLOCK;1772bc_num_free(©.d.n);1773BC_LONGJMP_CONT(vm);1774}17751776/**1777* Executes a function call for bc.1778* @param p The program.1779* @param code The bytecode vector to pull the number of arguments and the1780* function index out of.1781* @param bgn An in/out parameter; the start of the indices in the bytecode1782* vector, and will be updated to point after the indices on1783* return.1784*/1785static void1786bc_program_call(BcProgram* p, const char* restrict code, size_t* restrict bgn)1787{1788BcInstPtr ip;1789size_t i, nargs;1790BcFunc* f;1791BcVec* v;1792BcAuto* a;1793BcResult* arg;17941795// Pull the number of arguments out of the bytecode vector.1796nargs = bc_program_index(code, bgn);17971798// Set up instruction pointer.1799ip.idx = 0;1800ip.func = bc_program_index(code, bgn);1801f = bc_vec_item(&p->fns, ip.func);18021803// Error checking.1804if (BC_ERR(!f->code.len)) bc_verr(BC_ERR_EXEC_UNDEF_FUNC, f->name);1805if (BC_ERR(nargs != f->nparams))1806{1807bc_verr(BC_ERR_EXEC_PARAMS, f->nparams, nargs);1808}18091810// Set the length of the results stack. We discount the argument, of course.1811ip.len = p->results.len - nargs;18121813assert(BC_PROG_STACK(&p->results, nargs));18141815// Prepare the globals' stacks.1816if (BC_G) bc_program_prepGlobals(p);18171818// Push the arguments onto the stacks of their respective parameters.1819for (i = 0; i < nargs; ++i)1820{1821arg = bc_vec_top(&p->results);1822if (BC_ERR(arg->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL);18231824// Get the corresponding parameter.1825a = bc_vec_item(&f->autos, nargs - 1 - i);18261827// Actually push the value onto the parameter's stack.1828bc_program_copyToVar(p, a->idx, a->type);1829}18301831BC_SIG_LOCK;18321833// Push zeroes onto the stacks of the auto variables.1834for (; i < f->autos.len; ++i)1835{1836// Get the auto and its stack.1837a = bc_vec_item(&f->autos, i);1838v = bc_program_vec(p, a->idx, a->type);18391840// If a variable, just push a 0; otherwise, push an array.1841if (a->type == BC_TYPE_VAR)1842{1843BcNum* n = bc_vec_pushEmpty(v);1844bc_num_init(n, BC_NUM_DEF_SIZE);1845}1846else1847{1848BcVec* v2;18491850assert(a->type == BC_TYPE_ARRAY);18511852v2 = bc_vec_pushEmpty(v);1853bc_array_init(v2, true);1854}1855}18561857// Push the instruction pointer onto the execution stack.1858bc_vec_push(&p->stack, &ip);18591860BC_SIG_UNLOCK;1861}18621863/**1864* Executes a return instruction.1865* @param p The program.1866* @param inst The return instruction. bc can return void, and we need to know1867* if it is.1868*/1869static void1870bc_program_return(BcProgram* p, uchar inst)1871{1872BcResult* res;1873BcFunc* f;1874BcInstPtr* ip;1875size_t i, nresults;18761877// Get the instruction pointer.1878ip = bc_vec_top(&p->stack);18791880// Get the difference between the actual number of results and the number of1881// results the caller expects.1882nresults = p->results.len - ip->len;18831884// If this isn't true, there was a missing call somewhere.1885assert(BC_PROG_STACK(&p->stack, 2));18861887// If this isn't true, the parser screwed by giving us no value when we1888// expected one, or giving us a value when we expected none.1889assert(BC_PROG_STACK(&p->results, ip->len + (inst == BC_INST_RET)));18901891// Get the function we are returning from.1892f = bc_vec_item(&p->fns, ip->func);18931894res = bc_program_prepResult(p);18951896assert(p->nresults == 1);18971898// If we are returning normally...1899if (inst == BC_INST_RET)1900{1901BcNum* num;1902BcResult* operand;19031904// Prepare and copy the return value.1905bc_program_operand(p, &operand, &num, 1);19061907if (BC_PROG_STR(num))1908{1909// We need to set this because otherwise, it will be a1910// BC_RESULT_TEMP, and BC_RESULT_TEMP needs an actual number to make1911// it easier to do type checking.1912res->t = BC_RESULT_STR;19131914// NOLINTNEXTLINE1915memcpy(&res->d.n, num, sizeof(BcNum));1916}1917else1918{1919BC_SIG_LOCK;19201921bc_num_createCopy(&res->d.n, num);1922}1923}1924// Void is easy; set the result.1925else if (inst == BC_INST_RET_VOID) res->t = BC_RESULT_VOID;1926else1927{1928BC_SIG_LOCK;19291930// If we get here, the instruction is for returning a zero, so do that.1931bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);1932}19331934BC_SIG_MAYUNLOCK;19351936// We need to pop items off of the stacks of arguments and autos as well.1937for (i = 0; i < f->autos.len; ++i)1938{1939BcAuto* a = bc_vec_item(&f->autos, i);1940BcVec* v = bc_program_vec(p, a->idx, a->type);19411942bc_vec_pop(v);1943}19441945BC_SIG_LOCK;19461947// When we retire, pop all of the unused results.1948bc_program_retire(p, nresults);19491950// Pop the globals, if necessary.1951if (BC_G) bc_program_popGlobals(p, false);19521953// Pop the stack. This is what causes the function to actually "return."1954bc_vec_pop(&p->stack);19551956BC_SIG_UNLOCK;1957}1958#endif // BC_ENABLED19591960/**1961* Executes a builtin function.1962* @param p The program.1963* @param inst The builtin to execute.1964*/1965static void1966bc_program_builtin(BcProgram* p, uchar inst)1967{1968BcResult* opd;1969BcResult* res;1970BcNum* num;1971bool len = (inst == BC_INST_LENGTH);19721973// Ensure we have a valid builtin.1974#if BC_ENABLE_EXTRA_MATH1975assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IRAND);1976#else // BC_ENABLE_EXTRA_MATH1977assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IS_STRING);1978#endif // BC_ENABLE_EXTRA_MATH19791980#ifndef BC_PROG_NO_STACK_CHECK1981// Check stack for dc.1982if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 1)))1983{1984bc_err(BC_ERR_EXEC_STACK);1985}1986#endif // BC_PROG_NO_STACK_CHECK19871988assert(BC_PROG_STACK(&p->results, 1));19891990res = bc_program_prepResult(p);19911992assert(p->nresults == 1);19931994bc_program_operand(p, &opd, &num, 1);19951996assert(num != NULL);19971998// We need to ensure that strings and arrays aren't passed to most builtins.1999// The scale function can take strings in dc.2000if (!len && (inst != BC_INST_SCALE_FUNC || BC_IS_BC) &&2001inst != BC_INST_IS_NUMBER && inst != BC_INST_IS_STRING)2002{2003bc_program_type_num(opd, num);2004}20052006// Square root is easy.2007if (inst == BC_INST_SQRT) bc_num_sqrt(num, &res->d.n, BC_PROG_SCALE(p));20082009// Absolute value is easy.2010else if (inst == BC_INST_ABS)2011{2012BC_SIG_LOCK;20132014bc_num_createCopy(&res->d.n, num);20152016BC_SIG_UNLOCK;20172018BC_NUM_NEG_CLR_NP(res->d.n);2019}20202021// Testing for number or string is easy.2022else if (inst == BC_INST_IS_NUMBER || inst == BC_INST_IS_STRING)2023{2024bool cond;2025bool is_str;20262027BC_SIG_LOCK;20282029bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);20302031BC_SIG_UNLOCK;20322033// Test if the number is a string.2034is_str = BC_PROG_STR(num);20352036// This confusing condition simply means that the instruction must be2037// true if is_str is, or it must be false if is_str is. Otherwise, the2038// returned value is false (0).2039cond = ((inst == BC_INST_IS_STRING) == is_str);2040if (cond) bc_num_one(&res->d.n);2041}20422043#if BC_ENABLE_EXTRA_MATH20442045// irand() is easy.2046else if (inst == BC_INST_IRAND)2047{2048BC_SIG_LOCK;20492050bc_num_init(&res->d.n, num->len - BC_NUM_RDX_VAL(num));20512052BC_SIG_UNLOCK;20532054bc_num_irand(num, &res->d.n, &p->rng);2055}20562057#endif // BC_ENABLE_EXTRA_MATH20582059// Everything else is...not easy.2060else2061{2062BcBigDig val = 0;20632064// Well, scale() is easy, but length() is not.2065if (len)2066{2067// If we are bc and we have an array...2068if (opd->t == BC_RESULT_ARRAY)2069{2070// Yes, this is one place where we need to cast the number from2071// bc_program_num() to a vector.2072BcVec* v = (BcVec*) num;20732074// XXX: If this is changed, you should also change the similar2075// code in bc_program_asciify().20762077#if BC_ENABLED2078// Dereference the array, if necessary.2079if (BC_IS_BC && v->size == sizeof(uchar))2080{2081v = bc_program_dereference(p, v);2082}2083#endif // BC_ENABLED20842085assert(v->size == sizeof(BcNum));20862087val = (BcBigDig) v->len;2088}2089else2090{2091// If the item is a string...2092if (!BC_PROG_NUM(opd, num))2093{2094char* str;20952096// Get the string, then get the length.2097str = bc_program_string(p, num);2098val = (BcBigDig) strlen(str);2099}2100else2101{2102// Calculate the length of the number.2103val = (BcBigDig) bc_num_len(num);2104}2105}2106}2107// Like I said; scale() is actually easy. It just also needs the integer2108// conversion that length() does.2109else if (BC_IS_BC || BC_PROG_NUM(opd, num))2110{2111val = (BcBigDig) bc_num_scale(num);2112}21132114BC_SIG_LOCK;21152116// Create the result.2117bc_num_createFromBigdig(&res->d.n, val);21182119BC_SIG_UNLOCK;2120}21212122bc_program_retire(p, 1);2123}21242125/**2126* Executes a divmod.2127* @param p The program.2128*/2129static void2130bc_program_divmod(BcProgram* p)2131{2132BcResult* opd1;2133BcResult* opd2;2134BcResult* res;2135BcResult* res2;2136BcNum* n1;2137BcNum* n2;2138size_t req;21392140// We grow first to avoid pointer invalidation.2141bc_vec_grow(&p->results, 2);21422143// We don't need to update the pointer because2144// the capacity is enough due to the line above.2145res2 = bc_program_prepResult(p);2146res = bc_program_prepResult(p);2147assert(p->nresults == 2);21482149// Prepare the operands.2150bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 2);21512152req = bc_num_mulReq(n1, n2, BC_PROG_SCALE(p));21532154BC_SIG_LOCK;21552156// Initialize the results.2157bc_num_init(&res->d.n, req);2158bc_num_init(&res2->d.n, req);21592160BC_SIG_UNLOCK;21612162// Execute.2163bc_num_divmod(n1, n2, &res2->d.n, &res->d.n, BC_PROG_SCALE(p));21642165bc_program_retire(p, 2);2166}21672168/**2169* Executes modular exponentiation.2170* @param p The program.2171*/2172static void2173bc_program_modexp(BcProgram* p)2174{2175BcResult* r1;2176BcResult* r2;2177BcResult* r3;2178BcResult* res;2179BcNum* n1;2180BcNum* n2;2181BcNum* n3;21822183#if DC_ENABLED21842185// Check the stack.2186if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 3)))2187{2188bc_err(BC_ERR_EXEC_STACK);2189}21902191#endif // DC_ENABLED21922193assert(BC_PROG_STACK(&p->results, 3));21942195res = bc_program_prepResult(p);21962197assert(p->nresults == 1);21982199// Get the first operand and typecheck.2200bc_program_operand(p, &r1, &n1, 3);2201bc_program_type_num(r1, n1);22022203// Get the last two operands.2204bc_program_binOpPrep(p, &r2, &n2, &r3, &n3, 1);22052206// Make sure that the values have their pointers updated, if necessary.2207// Only array elements are possible because this is dc.2208if (r1->t == BC_RESULT_ARRAY_ELEM && (r1->t == r2->t || r1->t == r3->t))2209{2210n1 = bc_program_num(p, r1);2211}22122213BC_SIG_LOCK;22142215bc_num_init(&res->d.n, n3->len);22162217BC_SIG_UNLOCK;22182219bc_num_modexp(n1, n2, n3, &res->d.n);22202221bc_program_retire(p, 3);2222}22232224/**2225* Asciifies a number for dc. This is a helper for bc_program_asciify().2226* @param p The program.2227* @param n The number to asciify.2228*/2229static uchar2230bc_program_asciifyNum(BcProgram* p, BcNum* n)2231{2232bc_num_copy(&p->asciify, n);22332234// We want to clear the scale and sign for easy mod later.2235bc_num_truncate(&p->asciify, p->asciify.scale);2236BC_NUM_NEG_CLR(&p->asciify);22372238// This is guaranteed to not have a divide by 02239// because strmb is equal to 256.2240bc_num_mod(&p->asciify, &p->strmb, &p->asciify, 0);22412242// This is also guaranteed to not error because num is in the range2243// [0, UCHAR_MAX], which is definitely in range for a BcBigDig. And2244// it is not negative.2245return (uchar) bc_num_bigdig2(&p->asciify);2246}22472248/**2249* Executes the "asciify" command in bc and dc.2250* @param p The program.2251*/2252static void2253bc_program_asciify(BcProgram* p)2254{2255BcResult *r, res;2256BcNum* n;2257uchar c;2258size_t idx;2259#if BC_ENABLED2260// This is in the outer scope because it has to be freed after a jump.2261char* temp_str;2262#endif // BC_ENABLED22632264// Check the stack.2265if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);22662267assert(BC_PROG_STACK(&p->results, 1));22682269// Get the top of the results stack.2270bc_program_operand(p, &r, &n, 0);22712272assert(n != NULL);2273assert(BC_IS_BC || r->t != BC_RESULT_ARRAY);22742275#if BC_ENABLED2276// Handle arrays in bc specially.2277if (r->t == BC_RESULT_ARRAY)2278{2279// Yes, this is one place where we need to cast the number from2280// bc_program_num() to a vector.2281BcVec* v = (BcVec*) n;2282size_t i;22832284// XXX: If this is changed, you should also change the similar code in2285// bc_program_builtin().22862287// Dereference the array, if necessary.2288if (v->size == sizeof(uchar))2289{2290v = bc_program_dereference(p, v);2291}22922293assert(v->size == sizeof(BcNum));22942295// Allocate the string and set the jump for it.2296BC_SIG_LOCK;2297temp_str = bc_vm_malloc(v->len + 1);2298BC_SETJMP_LOCKED(vm, exit);2299BC_SIG_UNLOCK;23002301// Convert the array.2302for (i = 0; i < v->len; ++i)2303{2304BcNum* num = (BcNum*) bc_vec_item(v, i);23052306if (BC_PROG_STR(num))2307{2308temp_str[i] = (bc_program_string(p, num))[0];2309}2310else2311{2312temp_str[i] = (char) bc_program_asciifyNum(p, num);2313}2314}23152316temp_str[v->len] = '\0';23172318// Store the string in the slab and map, and free the temp string.2319BC_SIG_LOCK;2320idx = bc_program_addString(p, temp_str);2321free(temp_str);2322BC_UNSETJMP(vm);2323BC_SIG_UNLOCK;2324}2325else2326#endif // BC_ENABLED2327{2328char str[2];2329char* str2;23302331// Asciify.2332if (BC_PROG_NUM(r, n)) c = bc_program_asciifyNum(p, n);2333else2334{2335// Get the string itself, then the first character.2336str2 = bc_program_string(p, n);2337c = (uchar) str2[0];2338}23392340// Fill the resulting string.2341str[0] = (char) c;2342str[1] = '\0';23432344// Add the string to the data structures.2345BC_SIG_LOCK;2346idx = bc_program_addString(p, str);2347BC_SIG_UNLOCK;2348}23492350// Set the result2351res.t = BC_RESULT_STR;2352bc_num_clear(&res.d.n);2353res.d.n.scale = idx;23542355// Pop and push.2356bc_vec_pop(&p->results);2357bc_vec_push(&p->results, &res);23582359return;23602361#if BC_ENABLED2362exit:2363free(temp_str);2364#endif // BC_ENABLED2365}23662367/**2368* Streams a number or a string to stdout.2369* @param p The program.2370*/2371static void2372bc_program_printStream(BcProgram* p)2373{2374BcResult* r;2375BcNum* n;23762377// Check the stack.2378if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);23792380assert(BC_PROG_STACK(&p->results, 1));23812382// Get the top of the results stack.2383bc_program_operand(p, &r, &n, 0);23842385assert(n != NULL);23862387// Stream appropriately.2388if (BC_PROG_NUM(r, n)) bc_num_stream(n);2389else bc_program_printChars(bc_program_string(p, n));23902391// Pop the operand.2392bc_vec_pop(&p->results);2393}23942395#if DC_ENABLED23962397/**2398* Gets the length of a register in dc and pushes it onto the results stack.2399* @param p The program.2400* @param code The bytecode vector to pull the register's index out of.2401* @param bgn An in/out parameter; the start of the index in the bytecode2402* vector, and will be updated to point after the index on return.2403*/2404static void2405bc_program_regStackLen(BcProgram* p, const char* restrict code,2406size_t* restrict bgn)2407{2408size_t idx = bc_program_index(code, bgn);2409BcVec* v = bc_program_vec(p, idx, BC_TYPE_VAR);24102411bc_program_pushBigdig(p, (BcBigDig) v->len, BC_RESULT_TEMP);2412}24132414/**2415* Pushes the length of the results stack onto the results stack.2416* @param p The program.2417*/2418static void2419bc_program_stackLen(BcProgram* p)2420{2421bc_program_pushBigdig(p, (BcBigDig) p->results.len, BC_RESULT_TEMP);2422}24232424/**2425* Pops a certain number of elements off the execution stack.2426* @param p The program.2427* @param inst The instruction to tell us how many. There is one to pop up to2428* 2, and one to pop the amount equal to the number at the top of2429* the results stack.2430*/2431static void2432bc_program_nquit(BcProgram* p, uchar inst)2433{2434BcResult* opnd;2435BcNum* num;2436BcBigDig val;2437size_t i;24382439// Ensure that the tail calls stack is correct.2440assert(p->stack.len == p->tail_calls.len);24412442// Get the number of executions to pop.2443if (inst == BC_INST_QUIT) val = 2;2444else2445{2446bc_program_prep(p, &opnd, &num, 0);2447val = bc_num_bigdig(num);24482449bc_vec_pop(&p->results);2450}24512452// Loop over the tail call stack and adjust the quit value appropriately.2453for (i = 0; val && i < p->tail_calls.len; ++i)2454{2455// Get the number of tail calls for this one.2456size_t calls = *((size_t*) bc_vec_item_rev(&p->tail_calls, i)) + 1;24572458// Adjust the value.2459if (calls >= val) val = 0;2460else val -= (BcBigDig) calls;2461}24622463// If we don't have enough executions, just quit.2464if (i == p->stack.len)2465{2466vm->status = BC_STATUS_QUIT;2467BC_JMP;2468}2469else2470{2471// We can always pop the last item we reached on the tail call stack2472// because these are for tail calls. That means that any executions that2473// we would not have quit in that position on the stack would have quit2474// anyway.2475BC_SIG_LOCK;2476bc_vec_npop(&p->stack, i);2477bc_vec_npop(&p->tail_calls, i);2478BC_SIG_UNLOCK;2479}2480}24812482/**2483* Pushes the depth of the execution stack onto the stack.2484* @param p The program.2485*/2486static void2487bc_program_execStackLen(BcProgram* p)2488{2489size_t i, amt, len = p->tail_calls.len;24902491amt = len;24922493for (i = 0; i < len; ++i)2494{2495amt += *((size_t*) bc_vec_item(&p->tail_calls, i));2496}24972498bc_program_pushBigdig(p, (BcBigDig) amt, BC_RESULT_TEMP);2499}25002501/**2502*2503* @param p The program.2504* @param code The bytecode vector to pull the register's index out of.2505* @param bgn An in/out parameter; the start of the index in the bytecode2506* vector, and will be updated to point after the index on return.2507* @param cond True if the execution is conditional, false otherwise.2508* @param len The number of bytes in the bytecode vector.2509*/2510static void2511bc_program_execStr(BcProgram* p, const char* restrict code,2512size_t* restrict bgn, bool cond, size_t len)2513{2514BcResult* r;2515char* str;2516BcFunc* f;2517BcInstPtr ip;2518size_t fidx;2519BcNum* n;25202521assert(p->stack.len == p->tail_calls.len);25222523// Check the stack.2524if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);25252526assert(BC_PROG_STACK(&p->results, 1));25272528// Get the operand.2529bc_program_operand(p, &r, &n, 0);25302531// If execution is conditional...2532if (cond)2533{2534bool exec;2535size_t then_idx;2536// These are volatile to quiet warnings on GCC about clobbering with2537// longjmp().2538volatile size_t else_idx;2539volatile size_t idx;25402541// Get the index of the "then" var and "else" var.2542then_idx = bc_program_index(code, bgn);2543else_idx = bc_program_index(code, bgn);25442545// Figure out if we should execute.2546exec = (r->d.n.len != 0);25472548idx = exec ? then_idx : else_idx;25492550BC_SIG_LOCK;2551BC_SETJMP_LOCKED(vm, exit);25522553// If we are supposed to execute, execute. If else_idx == SIZE_MAX, that2554// means there was no else clause, so if execute is false and else does2555// not exist, we don't execute. The goto skips all of the setup for the2556// execution.2557if (exec || (else_idx != SIZE_MAX))2558{2559n = bc_vec_top(bc_program_vec(p, idx, BC_TYPE_VAR));2560}2561else goto exit;25622563if (BC_ERR(!BC_PROG_STR(n))) bc_err(BC_ERR_EXEC_TYPE);25642565BC_UNSETJMP(vm);2566BC_SIG_UNLOCK;2567}2568else2569{2570// In non-conditional situations, only the top of stack can be executed,2571// and in those cases, variables are not allowed to be "on the stack";2572// they are only put on the stack to be assigned to.2573assert(r->t != BC_RESULT_VAR);25742575if (r->t != BC_RESULT_STR) return;2576}25772578assert(BC_PROG_STR(n));25792580// Get the string.2581str = bc_program_string(p, n);25822583// Get the function index and function.2584BC_SIG_LOCK;2585fidx = bc_program_insertFunc(p, str);2586BC_SIG_UNLOCK;2587f = bc_vec_item(&p->fns, fidx);25882589// If the function has not been parsed yet...2590if (!f->code.len)2591{2592BC_SIG_LOCK;25932594if (!BC_PARSE_IS_INITED(&vm->read_prs, p))2595{2596bc_parse_init(&vm->read_prs, p, fidx);25972598// Initialize this too because bc_vm_shutdown() expects them to be2599// initialized togther.2600bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE);2601}2602// This needs to be updated because the parser could have been used2603// somewhere else2604else bc_parse_updateFunc(&vm->read_prs, fidx);26052606bc_lex_file(&vm->read_prs.l, vm->file);26072608BC_SETJMP_LOCKED(vm, err);26092610BC_SIG_UNLOCK;26112612// Parse. Only one expression is needed, so stdin isn't used.2613bc_parse_text(&vm->read_prs, str, BC_MODE_FILE);26142615BC_SIG_LOCK;2616vm->expr(&vm->read_prs, BC_PARSE_NOCALL);26172618BC_UNSETJMP(vm);26192620// We can just assert this here because2621// dc should parse everything until EOF.2622assert(vm->read_prs.l.t == BC_LEX_EOF);26232624BC_SIG_UNLOCK;2625}26262627// Set the instruction pointer.2628ip.idx = 0;2629ip.len = p->results.len;2630ip.func = fidx;26312632BC_SIG_LOCK;26332634// Pop the operand.2635bc_vec_pop(&p->results);26362637// Tail call processing. This condition means that there is more on the2638// execution stack, and we are at the end of the bytecode vector, and the2639// last instruction is just a BC_INST_POP_EXEC, which would return.2640if (p->stack.len > 1 && *bgn == len - 1 && code[*bgn] == BC_INST_POP_EXEC)2641{2642size_t* call_ptr = bc_vec_top(&p->tail_calls);26432644// Add one to the tail call.2645*call_ptr += 1;26462647// Pop the execution stack before pushing the new instruction pointer2648// on.2649bc_vec_pop(&p->stack);2650}2651// If not a tail call, just push a new one.2652else bc_vec_push(&p->tail_calls, &ip.idx);26532654// Push the new function onto the execution stack and return.2655bc_vec_push(&p->stack, &ip);26562657BC_SIG_UNLOCK;26582659return;26602661err:2662BC_SIG_MAYLOCK;26632664f = bc_vec_item(&p->fns, fidx);26652666// Make sure to erase the bytecode vector so dc knows it is not parsed.2667bc_vec_popAll(&f->code);26682669exit:2670bc_vec_pop(&p->results);2671BC_LONGJMP_CONT(vm);2672}26732674/**2675* Prints every item on the results stack, one per line.2676* @param p The program.2677*/2678static void2679bc_program_printStack(BcProgram* p)2680{2681size_t idx;26822683for (idx = 0; idx < p->results.len; ++idx)2684{2685bc_program_print(p, BC_INST_PRINT, idx);2686}2687}2688#endif // DC_ENABLED26892690/**2691* Pushes the value of a global onto the results stack.2692* @param p The program.2693* @param inst Which global to push, as an instruction.2694*/2695static void2696bc_program_pushGlobal(BcProgram* p, uchar inst)2697{2698BcResultType t;26992700// Make sure the instruction is valid.2701assert(inst >= BC_INST_IBASE && inst <= BC_INST_SCALE);27022703// Push the global.2704t = inst - BC_INST_IBASE + BC_RESULT_IBASE;2705bc_program_pushBigdig(p, p->globals[inst - BC_INST_IBASE], t);2706}27072708/**2709* Pushes the value of a global setting onto the stack.2710* @param p The program.2711* @param inst Which global setting to push, as an instruction.2712*/2713static void2714bc_program_globalSetting(BcProgram* p, uchar inst)2715{2716BcBigDig val;27172718// Make sure the instruction is valid.2719#if DC_ENABLED2720assert((inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO) ||2721(BC_IS_DC && inst == BC_INST_EXTENDED_REGISTERS));2722#else // DC_ENABLED2723assert(inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO);2724#endif // DC_ENABLED27252726if (inst == BC_INST_LINE_LENGTH)2727{2728val = (BcBigDig) vm->line_len;2729}2730#if BC_ENABLED2731else if (inst == BC_INST_GLOBAL_STACKS)2732{2733val = (BC_G != 0);2734}2735#endif // BC_ENABLED2736#if DC_ENABLED2737else if (inst == BC_INST_EXTENDED_REGISTERS)2738{2739val = (DC_X != 0);2740}2741#endif // DC_ENABLED2742else val = (BC_Z != 0);27432744// Push the global.2745bc_program_pushBigdig(p, val, BC_RESULT_TEMP);2746}27472748#if BC_ENABLE_EXTRA_MATH27492750/**2751* Pushes the value of seed on the stack.2752* @param p The program.2753*/2754static void2755bc_program_pushSeed(BcProgram* p)2756{2757BcResult* res;27582759res = bc_program_prepResult(p);27602761assert(p->nresults == 1);27622763res->t = BC_RESULT_SEED;27642765BC_SIG_LOCK;27662767// We need 2*BC_RAND_NUM_SIZE because of the size of the state.2768bc_num_init(&res->d.n, 2 * BC_RAND_NUM_SIZE);27692770BC_SIG_UNLOCK;27712772bc_num_createFromRNG(&res->d.n, &p->rng);27732774// XXX: Clear the number of results.2775p->nresults = 0;2776}27772778#endif // BC_ENABLE_EXTRA_MATH27792780/**2781* Adds a function to the fns array. The function's ID must have already been2782* inserted into the map.2783* @param p The program.2784* @param id_ptr The ID of the function as inserted into the map.2785*/2786static void2787bc_program_addFunc(BcProgram* p, BcId* id_ptr)2788{2789BcFunc* f;27902791BC_SIG_ASSERT_LOCKED;27922793// Push and init.2794f = bc_vec_pushEmpty(&p->fns);2795bc_func_init(f, id_ptr->name);2796}27972798size_t2799bc_program_insertFunc(BcProgram* p, const char* name)2800{2801BcId* id_ptr;2802bool new;2803size_t idx;28042805BC_SIG_ASSERT_LOCKED;28062807assert(p != NULL && name != NULL);28082809// Insert into the map and get the resulting ID.2810new = bc_map_insert(&p->fn_map, name, p->fns.len, &idx);2811id_ptr = (BcId*) bc_vec_item(&p->fn_map, idx);2812idx = id_ptr->idx;28132814// If the function is new...2815if (new)2816{2817// Add the function to the fns array.2818bc_program_addFunc(p, id_ptr);2819}2820#if BC_ENABLED2821// bc has to reset the function because it's about to be redefined.2822else if (BC_IS_BC)2823{2824BcFunc* func = bc_vec_item(&p->fns, idx);2825bc_func_reset(func);2826}2827#endif // BC_ENABLED28282829return idx;2830}28312832#if BC_DEBUG || BC_ENABLE_MEMCHECK2833void2834bc_program_free(BcProgram* p)2835{2836#if BC_ENABLED2837size_t i;2838#endif // BC_ENABLED28392840BC_SIG_ASSERT_LOCKED;28412842assert(p != NULL);28432844#if BC_ENABLED2845// Free the globals stacks.2846for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)2847{2848bc_vec_free(p->globals_v + i);2849}2850#endif // BC_ENABLED28512852bc_vec_free(&p->fns);2853bc_vec_free(&p->fn_map);2854bc_vec_free(&p->vars);2855bc_vec_free(&p->var_map);2856bc_vec_free(&p->arrs);2857bc_vec_free(&p->arr_map);2858bc_vec_free(&p->results);2859bc_vec_free(&p->stack);2860bc_vec_free(&p->consts);2861bc_vec_free(&p->const_map);2862bc_vec_free(&p->strs);2863bc_vec_free(&p->str_map);28642865bc_num_free(&p->asciify);28662867#if BC_ENABLED2868if (BC_IS_BC) bc_num_free(&p->last);2869#endif // BC_ENABLED28702871#if BC_ENABLE_EXTRA_MATH2872bc_rand_free(&p->rng);2873#endif // BC_ENABLE_EXTRA_MATH28742875#if DC_ENABLED2876if (BC_IS_DC) bc_vec_free(&p->tail_calls);2877#endif // DC_ENABLED2878}2879#endif // BC_DEBUG || BC_ENABLE_MEMCHECK28802881void2882bc_program_init(BcProgram* p)2883{2884BcInstPtr ip;2885size_t i;28862887BC_SIG_ASSERT_LOCKED;28882889assert(p != NULL);28902891// We want this clear.2892// NOLINTNEXTLINE2893memset(&ip, 0, sizeof(BcInstPtr));28942895// Setup the globals stacks and the current values.2896for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)2897{2898BcBigDig val = i == BC_PROG_GLOBALS_SCALE ? 0 : BC_BASE;28992900#if BC_ENABLED2901bc_vec_init(p->globals_v + i, sizeof(BcBigDig), BC_DTOR_NONE);2902bc_vec_push(p->globals_v + i, &val);2903#endif // BC_ENABLED29042905p->globals[i] = val;2906}29072908#if DC_ENABLED2909// dc-only setup.2910if (BC_IS_DC)2911{2912bc_vec_init(&p->tail_calls, sizeof(size_t), BC_DTOR_NONE);29132914// We want an item for the main function on the tail call stack.2915i = 0;2916bc_vec_push(&p->tail_calls, &i);2917}2918#endif // DC_ENABLED29192920bc_num_setup(&p->strmb, p->strmb_num, BC_NUM_BIGDIG_LOG10);2921bc_num_bigdig2num(&p->strmb, BC_NUM_STREAM_BASE);29222923bc_num_init(&p->asciify, BC_NUM_DEF_SIZE);29242925#if BC_ENABLE_EXTRA_MATH2926// We need to initialize srand() just in case /dev/urandom and /dev/random2927// are not available.2928srand((unsigned int) time(NULL));2929bc_rand_init(&p->rng);2930#endif // BC_ENABLE_EXTRA_MATH29312932#if BC_ENABLED2933if (BC_IS_BC) bc_num_init(&p->last, BC_NUM_DEF_SIZE);2934#endif // BC_ENABLED29352936#if BC_DEBUG2937bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_FUNC);2938#else // BC_DEBUG2939bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_NONE);2940#endif // BC_DEBUG2941bc_map_init(&p->fn_map);2942bc_program_insertFunc(p, bc_func_main);2943bc_program_insertFunc(p, bc_func_read);29442945bc_vec_init(&p->vars, sizeof(BcVec), BC_DTOR_VEC);2946bc_map_init(&p->var_map);29472948bc_vec_init(&p->arrs, sizeof(BcVec), BC_DTOR_VEC);2949bc_map_init(&p->arr_map);29502951bc_vec_init(&p->results, sizeof(BcResult), BC_DTOR_RESULT);29522953// Push the first instruction pointer onto the execution stack.2954bc_vec_init(&p->stack, sizeof(BcInstPtr), BC_DTOR_NONE);2955bc_vec_push(&p->stack, &ip);29562957bc_vec_init(&p->consts, sizeof(BcConst), BC_DTOR_CONST);2958bc_map_init(&p->const_map);2959bc_vec_init(&p->strs, sizeof(char*), BC_DTOR_NONE);2960bc_map_init(&p->str_map);29612962// XXX: Clear the number of results.2963p->nresults = 0;2964}29652966void2967bc_program_printStackTrace(BcProgram* p)2968{2969size_t i, max_digits;29702971max_digits = bc_vm_numDigits(p->stack.len - 1);29722973for (i = 0; i < p->stack.len; ++i)2974{2975BcInstPtr* ip = bc_vec_item_rev(&p->stack, i);2976BcFunc* f = bc_vec_item(&p->fns, ip->func);2977size_t j, digits;29782979digits = bc_vm_numDigits(i);29802981bc_file_puts(&vm->ferr, bc_flush_none, " ");29822983for (j = 0; j < max_digits - digits; ++j)2984{2985bc_file_putchar(&vm->ferr, bc_flush_none, ' ');2986}29872988bc_file_printf(&vm->ferr, "%zu: %s", i, f->name);29892990#if BC_ENABLED2991if (BC_IS_BC && ip->func != BC_PROG_MAIN && ip->func != BC_PROG_READ)2992{2993bc_file_puts(&vm->ferr, bc_flush_none, "()");2994}2995#endif // BC_ENABLED29962997bc_file_putchar(&vm->ferr, bc_flush_none, '\n');2998}2999}30003001void3002bc_program_reset(BcProgram* p)3003{3004BcFunc* f;3005BcInstPtr* ip;30063007BC_SIG_ASSERT_LOCKED;30083009// Pop all but the last execution.3010bc_vec_npop(&p->stack, p->stack.len - 1);30113012#if DC_ENABLED3013// We need to pop tail calls too.3014if (BC_IS_DC) bc_vec_npop(&p->tail_calls, p->tail_calls.len - 1);3015#endif // DC_ENABLED30163017// Clear the stack if we are in bc. We have to do this in bc because bc's3018// stack is implicit.3019//3020// XXX: We don't do this in dc because other dc implementations don't.3021// However, we *MUST* pop the items for results that are not retired yet.3022if (BC_IS_DC && BC_I) bc_vec_npop(&p->results, p->nresults);3023else bc_vec_popAll(&p->results);30243025// Now clear how many results there are.3026p->nresults = 0;30273028#if BC_ENABLED3029// Clear the globals' stacks.3030if (BC_G) bc_program_popGlobals(p, true);3031#endif // BC_ENABLED30323033// Clear the bytecode vector of the main function.3034f = bc_vec_item(&p->fns, BC_PROG_MAIN);3035bc_vec_npop(&f->code, f->code.len);30363037// Reset the instruction pointer.3038ip = bc_vec_top(&p->stack);3039// NOLINTNEXTLINE3040memset(ip, 0, sizeof(BcInstPtr));30413042if (BC_SIG_INTERRUPT(vm))3043{3044// Write the ready message for a signal.3045bc_file_printf(&vm->fout, "%s", bc_program_ready_msg);3046bc_file_flush(&vm->fout, bc_flush_err);3047}30483049// Clear the signal.3050vm->sig = 0;3051}30523053void3054bc_program_exec(BcProgram* p)3055{3056size_t idx;3057BcResult r;3058BcResult* ptr;3059BcInstPtr* ip;3060BcFunc* func;3061char* code;3062bool cond = false;3063uchar inst;3064#if BC_ENABLED3065BcNum* num;3066#endif // BC_ENABLED3067#if !BC_HAS_COMPUTED_GOTO3068#if BC_DEBUG3069size_t jmp_bufs_len;3070#endif // BC_DEBUG3071#endif // !BC_HAS_COMPUTED_GOTO30723073#if BC_HAS_COMPUTED_GOTO30743075#if BC_GCC3076#pragma GCC diagnostic push3077#pragma GCC diagnostic ignored "-Wpedantic"3078#endif // BC_GCC30793080#if BC_CLANG3081#pragma clang diagnostic push3082#pragma clang diagnostic ignored "-Wgnu-label-as-value"3083#endif // BC_CLANG30843085BC_PROG_LBLS;3086BC_PROG_LBLS_ASSERT;30873088#if BC_CLANG3089#pragma clang diagnostic pop3090#endif // BC_CLANG30913092#if BC_GCC3093#pragma GCC diagnostic pop3094#endif // BC_GCC30953096// BC_INST_INVALID is a marker for the end so that we don't have to have an3097// execution loop.3098func = (BcFunc*) bc_vec_item(&p->fns, BC_PROG_MAIN);3099bc_vec_pushByte(&func->code, BC_INST_INVALID);3100#endif // BC_HAS_COMPUTED_GOTO31013102BC_SETJMP(vm, end);31033104ip = bc_vec_top(&p->stack);3105func = (BcFunc*) bc_vec_item(&p->fns, ip->func);3106code = func->code.v;31073108#if !BC_HAS_COMPUTED_GOTO31093110#if BC_DEBUG3111jmp_bufs_len = vm->jmp_bufs.len;3112#endif // BC_DEBUG31133114// This loop is the heart of the execution engine. It *is* the engine. For3115// computed goto, it is ignored.3116while (ip->idx < func->code.len)3117#endif // !BC_HAS_COMPUTED_GOTO3118{3119BC_SIG_ASSERT_NOT_LOCKED;31203121#if BC_HAS_COMPUTED_GOTO31223123#if BC_GCC3124#pragma GCC diagnostic push3125#pragma GCC diagnostic ignored "-Wpedantic"3126#endif // BC_GCC31273128#if BC_CLANG3129#pragma clang diagnostic push3130#pragma clang diagnostic ignored "-Wgnu-label-as-value"3131#endif // BC_CLANG31323133BC_PROG_JUMP(inst, code, ip);31343135#else // BC_HAS_COMPUTED_GOTO31363137// Get the next instruction and increment the index.3138inst = (uchar) code[(ip->idx)++];31393140#endif // BC_HAS_COMPUTED_GOTO31413142#if BC_DEBUG_CODE3143bc_file_printf(&vm->ferr, "inst: %s\n", bc_inst_names[inst]);3144bc_file_flush(&vm->ferr, bc_flush_none);3145#endif // BC_DEBUG_CODE31463147#if !BC_HAS_COMPUTED_GOTO3148switch (inst)3149#endif // !BC_HAS_COMPUTED_GOTO3150{3151#if BC_ENABLED3152// This just sets up the condition for the unconditional jump below,3153// which checks the condition, if necessary.3154// clang-format off3155BC_PROG_LBL(BC_INST_JUMP_ZERO):3156// clang-format on3157{3158bc_program_prep(p, &ptr, &num, 0);31593160cond = !bc_num_cmpZero(num);3161bc_vec_pop(&p->results);31623163BC_PROG_DIRECT_JUMP(BC_INST_JUMP)3164}3165// Fallthrough.3166BC_PROG_FALLTHROUGH31673168// clang-format off3169BC_PROG_LBL(BC_INST_JUMP):3170// clang-format on3171{3172idx = bc_program_index(code, &ip->idx);31733174// If a jump is required...3175if (inst == BC_INST_JUMP || cond)3176{3177// Get the address to jump to.3178size_t* addr = bc_vec_item(&func->labels, idx);31793180// If this fails, then the parser failed to set up the3181// labels correctly.3182assert(*addr != SIZE_MAX);31833184// Set the new address.3185ip->idx = *addr;3186}31873188BC_PROG_JUMP(inst, code, ip);3189}31903191// clang-format off3192BC_PROG_LBL(BC_INST_CALL):3193// clang-format on3194{3195assert(BC_IS_BC);31963197bc_program_call(p, code, &ip->idx);31983199// Because we changed the execution stack and where we are3200// executing, we have to update all of this.3201BC_SIG_LOCK;3202ip = bc_vec_top(&p->stack);3203func = bc_vec_item(&p->fns, ip->func);3204code = func->code.v;3205BC_SIG_UNLOCK;32063207BC_PROG_JUMP(inst, code, ip);3208}32093210// clang-format off3211BC_PROG_LBL(BC_INST_INC):3212BC_PROG_LBL(BC_INST_DEC):3213// clang-format on3214{3215bc_program_incdec(p, inst);3216BC_PROG_JUMP(inst, code, ip);3217}32183219// clang-format off3220BC_PROG_LBL(BC_INST_HALT):3221// clang-format on3222{3223vm->status = BC_STATUS_QUIT;32243225// Just jump out. The jump series will take care of everything.3226BC_JMP;32273228BC_PROG_JUMP(inst, code, ip);3229}32303231// clang-format off3232BC_PROG_LBL(BC_INST_RET):3233BC_PROG_LBL(BC_INST_RET0):3234BC_PROG_LBL(BC_INST_RET_VOID):3235// clang-format on3236{3237bc_program_return(p, inst);32383239// Because we changed the execution stack and where we are3240// executing, we have to update all of this.3241BC_SIG_LOCK;3242ip = bc_vec_top(&p->stack);3243func = bc_vec_item(&p->fns, ip->func);3244code = func->code.v;3245BC_SIG_UNLOCK;32463247BC_PROG_JUMP(inst, code, ip);3248}3249#endif // BC_ENABLED32503251// clang-format off3252BC_PROG_LBL(BC_INST_BOOL_OR):3253BC_PROG_LBL(BC_INST_BOOL_AND):3254BC_PROG_LBL(BC_INST_REL_EQ):3255BC_PROG_LBL(BC_INST_REL_LE):3256BC_PROG_LBL(BC_INST_REL_GE):3257BC_PROG_LBL(BC_INST_REL_NE):3258BC_PROG_LBL(BC_INST_REL_LT):3259BC_PROG_LBL(BC_INST_REL_GT):3260// clang-format on3261{3262bc_program_logical(p, inst);3263BC_PROG_JUMP(inst, code, ip);3264}32653266// clang-format off3267BC_PROG_LBL(BC_INST_READ):3268// clang-format on3269{3270// We want to flush output before3271// this in case there is a prompt.3272bc_file_flush(&vm->fout, bc_flush_save);32733274bc_program_read(p);32753276// Because we changed the execution stack and where we are3277// executing, we have to update all of this.3278BC_SIG_LOCK;3279ip = bc_vec_top(&p->stack);3280func = bc_vec_item(&p->fns, ip->func);3281code = func->code.v;3282BC_SIG_UNLOCK;32833284BC_PROG_JUMP(inst, code, ip);3285}32863287#if BC_ENABLE_EXTRA_MATH3288// clang-format off3289BC_PROG_LBL(BC_INST_RAND):3290// clang-format on3291{3292bc_program_rand(p);3293BC_PROG_JUMP(inst, code, ip);3294}3295#endif // BC_ENABLE_EXTRA_MATH32963297// clang-format off3298BC_PROG_LBL(BC_INST_MAXIBASE):3299BC_PROG_LBL(BC_INST_MAXOBASE):3300BC_PROG_LBL(BC_INST_MAXSCALE):3301#if BC_ENABLE_EXTRA_MATH3302BC_PROG_LBL(BC_INST_MAXRAND):3303#endif // BC_ENABLE_EXTRA_MATH3304// clang-format on3305{3306BcBigDig dig = vm->maxes[inst - BC_INST_MAXIBASE];3307bc_program_pushBigdig(p, dig, BC_RESULT_TEMP);3308BC_PROG_JUMP(inst, code, ip);3309}33103311// clang-format off3312BC_PROG_LBL(BC_INST_LINE_LENGTH):3313#if BC_ENABLED3314BC_PROG_LBL(BC_INST_GLOBAL_STACKS):3315#endif // BC_ENABLED3316#if DC_ENABLED3317BC_PROG_LBL(BC_INST_EXTENDED_REGISTERS):3318#endif // DC_ENABLE3319BC_PROG_LBL(BC_INST_LEADING_ZERO):3320// clang-format on3321{3322bc_program_globalSetting(p, inst);3323BC_PROG_JUMP(inst, code, ip);3324}33253326// clang-format off3327BC_PROG_LBL(BC_INST_VAR):3328// clang-format on3329{3330bc_program_pushVar(p, code, &ip->idx, false, false);3331BC_PROG_JUMP(inst, code, ip);3332}33333334// clang-format off3335BC_PROG_LBL(BC_INST_ARRAY_ELEM):3336BC_PROG_LBL(BC_INST_ARRAY):3337// clang-format on3338{3339bc_program_pushArray(p, code, &ip->idx, inst);3340BC_PROG_JUMP(inst, code, ip);3341}33423343// clang-format off3344BC_PROG_LBL(BC_INST_IBASE):3345BC_PROG_LBL(BC_INST_SCALE):3346BC_PROG_LBL(BC_INST_OBASE):3347// clang-format on3348{3349bc_program_pushGlobal(p, inst);3350BC_PROG_JUMP(inst, code, ip);3351}33523353#if BC_ENABLE_EXTRA_MATH3354// clang-format off3355BC_PROG_LBL(BC_INST_SEED):3356// clang-format on3357{3358bc_program_pushSeed(p);3359BC_PROG_JUMP(inst, code, ip);3360}3361#endif // BC_ENABLE_EXTRA_MATH33623363// clang-format off3364BC_PROG_LBL(BC_INST_LENGTH):3365BC_PROG_LBL(BC_INST_SCALE_FUNC):3366BC_PROG_LBL(BC_INST_SQRT):3367BC_PROG_LBL(BC_INST_ABS):3368BC_PROG_LBL(BC_INST_IS_NUMBER):3369BC_PROG_LBL(BC_INST_IS_STRING):3370#if BC_ENABLE_EXTRA_MATH3371BC_PROG_LBL(BC_INST_IRAND):3372#endif // BC_ENABLE_EXTRA_MATH3373// clang-format on3374{3375bc_program_builtin(p, inst);3376BC_PROG_JUMP(inst, code, ip);3377}33783379// clang-format off3380BC_PROG_LBL(BC_INST_ASCIIFY):3381// clang-format on3382{3383bc_program_asciify(p);33843385// Because we changed the execution stack and where we are3386// executing, we have to update all of this.3387BC_SIG_LOCK;3388ip = bc_vec_top(&p->stack);3389func = bc_vec_item(&p->fns, ip->func);3390code = func->code.v;3391BC_SIG_UNLOCK;33923393BC_PROG_JUMP(inst, code, ip);3394}33953396// clang-format off3397BC_PROG_LBL(BC_INST_NUM):3398// clang-format on3399{3400bc_program_const(p, code, &ip->idx);3401BC_PROG_JUMP(inst, code, ip);3402}34033404// clang-format off3405BC_PROG_LBL(BC_INST_ZERO):3406BC_PROG_LBL(BC_INST_ONE):3407#if BC_ENABLED3408BC_PROG_LBL(BC_INST_LAST):3409#endif // BC_ENABLED3410// clang-format on3411{3412r.t = BC_RESULT_ZERO + (inst - BC_INST_ZERO);3413bc_vec_push(&p->results, &r);3414BC_PROG_JUMP(inst, code, ip);3415}34163417// clang-format off3418BC_PROG_LBL(BC_INST_PRINT):3419BC_PROG_LBL(BC_INST_PRINT_POP):3420#if BC_ENABLED3421BC_PROG_LBL(BC_INST_PRINT_STR):3422#endif // BC_ENABLED3423// clang-format on3424{3425bc_program_print(p, inst, 0);34263427// We want to flush right away to save the output for history,3428// if history must preserve it when taking input.3429bc_file_flush(&vm->fout, bc_flush_save);34303431BC_PROG_JUMP(inst, code, ip);3432}34333434// clang-format off3435BC_PROG_LBL(BC_INST_STR):3436// clang-format on3437{3438// Set up the result and push.3439r.t = BC_RESULT_STR;3440bc_num_clear(&r.d.n);3441r.d.n.scale = bc_program_index(code, &ip->idx);3442bc_vec_push(&p->results, &r);3443BC_PROG_JUMP(inst, code, ip);3444}34453446// clang-format off3447BC_PROG_LBL(BC_INST_POWER):3448BC_PROG_LBL(BC_INST_MULTIPLY):3449BC_PROG_LBL(BC_INST_DIVIDE):3450BC_PROG_LBL(BC_INST_MODULUS):3451BC_PROG_LBL(BC_INST_PLUS):3452BC_PROG_LBL(BC_INST_MINUS):3453#if BC_ENABLE_EXTRA_MATH3454BC_PROG_LBL(BC_INST_PLACES):3455BC_PROG_LBL(BC_INST_LSHIFT):3456BC_PROG_LBL(BC_INST_RSHIFT):3457#endif // BC_ENABLE_EXTRA_MATH3458// clang-format on3459{3460bc_program_op(p, inst);3461BC_PROG_JUMP(inst, code, ip);3462}34633464// clang-format off3465BC_PROG_LBL(BC_INST_NEG):3466BC_PROG_LBL(BC_INST_BOOL_NOT):3467#if BC_ENABLE_EXTRA_MATH3468BC_PROG_LBL(BC_INST_TRUNC):3469#endif // BC_ENABLE_EXTRA_MATH3470// clang-format on3471{3472bc_program_unary(p, inst);3473BC_PROG_JUMP(inst, code, ip);3474}34753476// clang-format off3477#if BC_ENABLED3478BC_PROG_LBL(BC_INST_ASSIGN_POWER):3479BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY):3480BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE):3481BC_PROG_LBL(BC_INST_ASSIGN_MODULUS):3482BC_PROG_LBL(BC_INST_ASSIGN_PLUS):3483BC_PROG_LBL(BC_INST_ASSIGN_MINUS):3484#if BC_ENABLE_EXTRA_MATH3485BC_PROG_LBL(BC_INST_ASSIGN_PLACES):3486BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT):3487BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT):3488#endif // BC_ENABLE_EXTRA_MATH3489BC_PROG_LBL(BC_INST_ASSIGN):3490BC_PROG_LBL(BC_INST_ASSIGN_POWER_NO_VAL):3491BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY_NO_VAL):3492BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE_NO_VAL):3493BC_PROG_LBL(BC_INST_ASSIGN_MODULUS_NO_VAL):3494BC_PROG_LBL(BC_INST_ASSIGN_PLUS_NO_VAL):3495BC_PROG_LBL(BC_INST_ASSIGN_MINUS_NO_VAL):3496#if BC_ENABLE_EXTRA_MATH3497BC_PROG_LBL(BC_INST_ASSIGN_PLACES_NO_VAL):3498BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT_NO_VAL):3499BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT_NO_VAL):3500#endif // BC_ENABLE_EXTRA_MATH3501#endif // BC_ENABLED3502BC_PROG_LBL(BC_INST_ASSIGN_NO_VAL):3503// clang-format on3504{3505bc_program_assign(p, inst);3506BC_PROG_JUMP(inst, code, ip);3507}35083509// clang-format off3510BC_PROG_LBL(BC_INST_POP):3511// clang-format on3512{3513#ifndef BC_PROG_NO_STACK_CHECK3514// dc must do a stack check, but bc does not.3515if (BC_IS_DC)3516{3517if (BC_ERR(!BC_PROG_STACK(&p->results, 1)))3518{3519bc_err(BC_ERR_EXEC_STACK);3520}3521}3522#endif // BC_PROG_NO_STACK_CHECK35233524assert(BC_PROG_STACK(&p->results, 1));35253526bc_vec_pop(&p->results);35273528BC_PROG_JUMP(inst, code, ip);3529}35303531// clang-format off3532BC_PROG_LBL(BC_INST_SWAP):3533// clang-format on3534{3535BcResult* ptr2;35363537// Check the stack.3538if (BC_ERR(!BC_PROG_STACK(&p->results, 2)))3539{3540bc_err(BC_ERR_EXEC_STACK);3541}35423543assert(BC_PROG_STACK(&p->results, 2));35443545// Get the two items.3546ptr = bc_vec_item_rev(&p->results, 0);3547ptr2 = bc_vec_item_rev(&p->results, 1);35483549// Swap. It's just easiest to do it this way.3550// NOLINTNEXTLINE3551memcpy(&r, ptr, sizeof(BcResult));3552// NOLINTNEXTLINE3553memcpy(ptr, ptr2, sizeof(BcResult));3554// NOLINTNEXTLINE3555memcpy(ptr2, &r, sizeof(BcResult));35563557BC_PROG_JUMP(inst, code, ip);3558}35593560// clang-format off3561BC_PROG_LBL(BC_INST_MODEXP):3562// clang-format on3563{3564bc_program_modexp(p);3565BC_PROG_JUMP(inst, code, ip);3566}35673568// clang-format off3569BC_PROG_LBL(BC_INST_DIVMOD):3570// clang-format on3571{3572bc_program_divmod(p);3573BC_PROG_JUMP(inst, code, ip);3574}35753576// clang-format off3577BC_PROG_LBL(BC_INST_PRINT_STREAM):3578// clang-format on3579{3580bc_program_printStream(p);3581BC_PROG_JUMP(inst, code, ip);3582}35833584#if DC_ENABLED3585// clang-format off3586BC_PROG_LBL(BC_INST_POP_EXEC):3587// clang-format on3588{3589// If this fails, the dc parser got something wrong.3590assert(BC_PROG_STACK(&p->stack, 2));35913592// Pop the execution stack and tail call stack.3593bc_vec_pop(&p->stack);3594bc_vec_pop(&p->tail_calls);35953596// Because we changed the execution stack and where we are3597// executing, we have to update all of this.3598BC_SIG_LOCK;3599ip = bc_vec_top(&p->stack);3600func = bc_vec_item(&p->fns, ip->func);3601code = func->code.v;3602BC_SIG_UNLOCK;36033604BC_PROG_JUMP(inst, code, ip);3605}36063607// clang-format off3608BC_PROG_LBL(BC_INST_EXECUTE):3609BC_PROG_LBL(BC_INST_EXEC_COND):3610// clang-format on3611{3612cond = (inst == BC_INST_EXEC_COND);36133614bc_program_execStr(p, code, &ip->idx, cond, func->code.len);36153616// Because we changed the execution stack and where we are3617// executing, we have to update all of this.3618BC_SIG_LOCK;3619ip = bc_vec_top(&p->stack);3620func = bc_vec_item(&p->fns, ip->func);3621code = func->code.v;3622BC_SIG_UNLOCK;36233624BC_PROG_JUMP(inst, code, ip);3625}36263627// clang-format off3628BC_PROG_LBL(BC_INST_PRINT_STACK):3629// clang-format on3630{3631bc_program_printStack(p);3632BC_PROG_JUMP(inst, code, ip);3633}36343635// clang-format off3636BC_PROG_LBL(BC_INST_CLEAR_STACK):3637// clang-format on3638{3639bc_vec_popAll(&p->results);3640BC_PROG_JUMP(inst, code, ip);3641}36423643// clang-format off3644BC_PROG_LBL(BC_INST_REG_STACK_LEN):3645// clang-format on3646{3647bc_program_regStackLen(p, code, &ip->idx);3648BC_PROG_JUMP(inst, code, ip);3649}36503651// clang-format off3652BC_PROG_LBL(BC_INST_STACK_LEN):3653// clang-format on3654{3655bc_program_stackLen(p);3656BC_PROG_JUMP(inst, code, ip);3657}36583659// clang-format off3660BC_PROG_LBL(BC_INST_DUPLICATE):3661// clang-format on3662{3663// Check the stack.3664if (BC_ERR(!BC_PROG_STACK(&p->results, 1)))3665{3666bc_err(BC_ERR_EXEC_STACK);3667}36683669assert(BC_PROG_STACK(&p->results, 1));36703671// Get the top of the stack.3672ptr = bc_vec_top(&p->results);36733674BC_SIG_LOCK;36753676// Copy and push.3677bc_result_copy(&r, ptr);3678bc_vec_push(&p->results, &r);36793680BC_SIG_UNLOCK;36813682BC_PROG_JUMP(inst, code, ip);3683}36843685// clang-format off3686BC_PROG_LBL(BC_INST_LOAD):3687BC_PROG_LBL(BC_INST_PUSH_VAR):3688// clang-format on3689{3690bool copy = (inst == BC_INST_LOAD);3691bc_program_pushVar(p, code, &ip->idx, true, copy);3692BC_PROG_JUMP(inst, code, ip);3693}36943695// clang-format off3696BC_PROG_LBL(BC_INST_PUSH_TO_VAR):3697// clang-format on3698{3699idx = bc_program_index(code, &ip->idx);3700bc_program_copyToVar(p, idx, BC_TYPE_VAR);3701BC_PROG_JUMP(inst, code, ip);3702}37033704// clang-format off3705BC_PROG_LBL(BC_INST_QUIT):3706BC_PROG_LBL(BC_INST_NQUIT):3707// clang-format on3708{3709bc_program_nquit(p, inst);37103711// Because we changed the execution stack and where we are3712// executing, we have to update all of this.3713BC_SIG_LOCK;3714ip = bc_vec_top(&p->stack);3715func = bc_vec_item(&p->fns, ip->func);3716code = func->code.v;3717BC_SIG_UNLOCK;37183719BC_PROG_JUMP(inst, code, ip);3720}37213722// clang-format off3723BC_PROG_LBL(BC_INST_EXEC_STACK_LEN):3724// clang-format on3725{3726bc_program_execStackLen(p);3727BC_PROG_JUMP(inst, code, ip);3728}3729#endif // DC_ENABLED37303731#if BC_HAS_COMPUTED_GOTO3732// clang-format off3733BC_PROG_LBL(BC_INST_INVALID):3734// clang-format on3735{3736goto end;3737}3738#else // BC_HAS_COMPUTED_GOTO3739default:3740{3741BC_UNREACHABLE3742#if BC_DEBUG && !BC_CLANG3743abort();3744#endif // BC_DEBUG && !BC_CLANG3745}3746#endif // BC_HAS_COMPUTED_GOTO3747}37483749#if BC_HAS_COMPUTED_GOTO37503751#if BC_CLANG3752#pragma clang diagnostic pop3753#endif // BC_CLANG37543755#if BC_GCC3756#pragma GCC diagnostic pop3757#endif // BC_GCC37583759#else // BC_HAS_COMPUTED_GOTO37603761#if BC_DEBUG3762// This is to allow me to use a debugger to see the last instruction,3763// which will point to which function was the problem. But it's also a3764// good smoke test for error handling changes.3765assert(jmp_bufs_len == vm->jmp_bufs.len);3766#endif // BC_DEBUG37673768#endif // BC_HAS_COMPUTED_GOTO3769}37703771end:3772BC_SIG_MAYLOCK;37733774// This is here just to print a stack trace on interrupts. This is for3775// finding infinite loops.3776if (BC_SIG_INTERRUPT(vm))3777{3778BcStatus s;37793780bc_file_putchar(&vm->ferr, bc_flush_none, '\n');37813782bc_program_printStackTrace(p);37833784s = bc_file_flushErr(&vm->ferr, bc_flush_err);3785if (BC_ERR(s != BC_STATUS_SUCCESS && vm->status == BC_STATUS_SUCCESS))3786{3787vm->status = (sig_atomic_t) s;3788}3789}37903791BC_LONGJMP_CONT(vm);3792}37933794#if BC_DEBUG_CODE3795#if BC_ENABLED && DC_ENABLED3796void3797bc_program_printStackDebug(BcProgram* p)3798{3799bc_file_puts(&vm->fout, bc_flush_err, "-------------- Stack ----------\n");3800bc_program_printStack(p);3801bc_file_puts(&vm->fout, bc_flush_err, "-------------- Stack End ------\n");3802}38033804static void3805bc_program_printIndex(const char* restrict code, size_t* restrict bgn)3806{3807uchar byte, i, bytes = (uchar) code[(*bgn)++];3808ulong val = 0;38093810for (byte = 1, i = 0; byte && i < bytes; ++i)3811{3812byte = (uchar) code[(*bgn)++];3813if (byte) val |= ((ulong) byte) << (CHAR_BIT * i);3814}38153816bc_vm_printf(" (%lu) ", val);3817}38183819static void3820bc_program_printStr(const BcProgram* p, const char* restrict code,3821size_t* restrict bgn)3822{3823size_t idx = bc_program_index(code, bgn);3824char* s;38253826s = *((char**) bc_vec_item(&p->strs, idx));38273828bc_vm_printf(" (\"%s\") ", s);3829}38303831void3832bc_program_printInst(const BcProgram* p, const char* restrict code,3833size_t* restrict bgn)3834{3835uchar inst = (uchar) code[(*bgn)++];38363837bc_vm_printf("Inst[%zu]: %s [%lu]; ", *bgn - 1, bc_inst_names[inst],3838(unsigned long) inst);38393840if (inst == BC_INST_VAR || inst == BC_INST_ARRAY_ELEM ||3841inst == BC_INST_ARRAY)3842{3843bc_program_printIndex(code, bgn);3844}3845else if (inst == BC_INST_STR) bc_program_printStr(p, code, bgn);3846else if (inst == BC_INST_NUM)3847{3848size_t idx = bc_program_index(code, bgn);3849BcConst* c = bc_vec_item(&p->consts, idx);3850bc_vm_printf("(%s)", c->val);3851}3852else if (inst == BC_INST_CALL ||3853(inst > BC_INST_STR && inst <= BC_INST_JUMP_ZERO))3854{3855bc_program_printIndex(code, bgn);3856if (inst == BC_INST_CALL) bc_program_printIndex(code, bgn);3857}38583859bc_vm_putchar('\n', bc_flush_err);3860}38613862void3863bc_program_code(const BcProgram* p)3864{3865BcFunc* f;3866char* code;3867BcInstPtr ip;3868size_t i;38693870for (i = 0; i < p->fns.len; ++i)3871{3872ip.idx = ip.len = 0;3873ip.func = i;38743875f = bc_vec_item(&p->fns, ip.func);3876code = f->code.v;38773878bc_vm_printf("func[%zu]:\n", ip.func);3879while (ip.idx < f->code.len)3880{3881bc_program_printInst(p, code, &ip.idx);3882}3883bc_file_puts(&vm->fout, bc_flush_err, "\n\n");3884}3885}3886#endif // BC_ENABLED && DC_ENABLED3887#endif // BC_DEBUG_CODE388838893890