1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 6a970610aSStefan Eßer * Copyright (c) 2018-2024 Gavin D. Howard and contributors. 7252884aeSStefan Eßer * 8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met: 10252884aeSStefan Eßer * 11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 12252884aeSStefan Eßer * list of conditions and the following disclaimer. 13252884aeSStefan Eßer * 14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation 16252884aeSStefan Eßer * and/or other materials provided with the distribution. 17252884aeSStefan Eßer * 18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 29252884aeSStefan Eßer * 30252884aeSStefan Eßer * ***************************************************************************** 31252884aeSStefan Eßer * 32252884aeSStefan Eßer * Code to execute bc programs. 33252884aeSStefan Eßer * 34252884aeSStefan Eßer */ 35252884aeSStefan Eßer 36252884aeSStefan Eßer #include <assert.h> 37252884aeSStefan Eßer #include <stdbool.h> 38252884aeSStefan Eßer #include <string.h> 39252884aeSStefan Eßer 40252884aeSStefan Eßer #include <setjmp.h> 41252884aeSStefan Eßer 42252884aeSStefan Eßer #include <signal.h> 43252884aeSStefan Eßer 44252884aeSStefan Eßer #include <time.h> 45252884aeSStefan Eßer 46252884aeSStefan Eßer #include <read.h> 47252884aeSStefan Eßer #include <parse.h> 48252884aeSStefan Eßer #include <program.h> 49252884aeSStefan Eßer #include <vm.h> 50252884aeSStefan Eßer 5144d4804dSStefan Eßer /** 5244d4804dSStefan Eßer * Does a type check for something that expects a number. 5344d4804dSStefan Eßer * @param r The result that will be checked. 5444d4804dSStefan Eßer * @param n The result's number. 5544d4804dSStefan Eßer */ 5678bc019dSStefan Eßer static inline void 5778bc019dSStefan Eßer bc_program_type_num(BcResult* r, BcNum* n) 5878bc019dSStefan Eßer { 59252884aeSStefan Eßer #if BC_ENABLED 6044d4804dSStefan Eßer 6144d4804dSStefan Eßer // This should have already been taken care of. 62252884aeSStefan Eßer assert(r->t != BC_RESULT_VOID); 6344d4804dSStefan Eßer 64252884aeSStefan Eßer #endif // BC_ENABLED 65252884aeSStefan Eßer 6644d4804dSStefan Eßer if (BC_ERR(!BC_PROG_NUM(r, n))) bc_err(BC_ERR_EXEC_TYPE); 67252884aeSStefan Eßer } 68252884aeSStefan Eßer 69252884aeSStefan Eßer #if BC_ENABLED 7044d4804dSStefan Eßer 7144d4804dSStefan Eßer /** 7244d4804dSStefan Eßer * Does a type check. 7344d4804dSStefan Eßer * @param r The result to check. 7444d4804dSStefan Eßer * @param t The type that the result should be. 7544d4804dSStefan Eßer */ 7678bc019dSStefan Eßer static void 7778bc019dSStefan Eßer bc_program_type_match(BcResult* r, BcType t) 7878bc019dSStefan Eßer { 7944d4804dSStefan Eßer if (BC_ERR((r->t != BC_RESULT_ARRAY) != (!t))) bc_err(BC_ERR_EXEC_TYPE); 80252884aeSStefan Eßer } 81252884aeSStefan Eßer #endif // BC_ENABLED 82252884aeSStefan Eßer 8344d4804dSStefan Eßer /** 8444d4804dSStefan Eßer * Pulls an index out of a bytecode vector and updates the index into the vector 8544d4804dSStefan Eßer * to point to the spot after the index. For more details on bytecode indices, 8644d4804dSStefan Eßer * see the development manual (manuals/development.md#bytecode-indices). 8744d4804dSStefan Eßer * @param code The bytecode vector. 8844d4804dSStefan Eßer * @param bgn An in/out parameter; the index into the vector that will be 8944d4804dSStefan Eßer * updated. 9044d4804dSStefan Eßer * @return The index at @a bgn in the bytecode vector. 9144d4804dSStefan Eßer */ 9278bc019dSStefan Eßer static size_t 9378bc019dSStefan Eßer bc_program_index(const char* restrict code, size_t* restrict bgn) 94252884aeSStefan Eßer { 95252884aeSStefan Eßer uchar amt = (uchar) code[(*bgn)++], i = 0; 96252884aeSStefan Eßer size_t res = 0; 97252884aeSStefan Eßer 9878bc019dSStefan Eßer for (; i < amt; ++i, ++(*bgn)) 9978bc019dSStefan Eßer { 100252884aeSStefan Eßer size_t temp = ((size_t) ((int) (uchar) code[*bgn]) & UCHAR_MAX); 101252884aeSStefan Eßer res |= (temp << (i * CHAR_BIT)); 102252884aeSStefan Eßer } 103252884aeSStefan Eßer 104252884aeSStefan Eßer return res; 105252884aeSStefan Eßer } 106252884aeSStefan Eßer 10744d4804dSStefan Eßer /** 10844d4804dSStefan Eßer * Returns a string from a result and its number. 10944d4804dSStefan Eßer * @param p The program. 11044d4804dSStefan Eßer * @param n The number tied to the result. 11144d4804dSStefan Eßer * @return The string corresponding to the result and number. 11244d4804dSStefan Eßer */ 113d101cdd6SStefan Eßer static inline char* 11478bc019dSStefan Eßer bc_program_string(BcProgram* p, const BcNum* n) 11578bc019dSStefan Eßer { 116d101cdd6SStefan Eßer return *((char**) bc_vec_item(&p->strs, n->scale)); 11744d4804dSStefan Eßer } 11844d4804dSStefan Eßer 1193aa99676SStefan Eßer #if BC_ENABLED 12044d4804dSStefan Eßer 12144d4804dSStefan Eßer /** 12244d4804dSStefan Eßer * Prepares the globals for a function call. This is only called when global 12344d4804dSStefan Eßer * stacks are on because it pushes a copy of the current globals onto each of 12444d4804dSStefan Eßer * their respective stacks. 12544d4804dSStefan Eßer * @param p The program. 12644d4804dSStefan Eßer */ 12778bc019dSStefan Eßer static void 12878bc019dSStefan Eßer bc_program_prepGlobals(BcProgram* p) 12978bc019dSStefan Eßer { 130252884aeSStefan Eßer size_t i; 131252884aeSStefan Eßer 132252884aeSStefan Eßer for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) 13378bc019dSStefan Eßer { 134252884aeSStefan Eßer bc_vec_push(p->globals_v + i, p->globals + i); 13578bc019dSStefan Eßer } 136252884aeSStefan Eßer 13744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 138252884aeSStefan Eßer bc_rand_push(&p->rng); 13944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 140252884aeSStefan Eßer } 141252884aeSStefan Eßer 14244d4804dSStefan Eßer /** 14344d4804dSStefan Eßer * Pops globals stacks on returning from a function, or in the case of reset, 14444d4804dSStefan Eßer * pops all but one item on each global stack. 14544d4804dSStefan Eßer * @param p The program. 14644d4804dSStefan Eßer * @param reset True if all but one item on each stack should be popped, false 14744d4804dSStefan Eßer * otherwise. 14844d4804dSStefan Eßer */ 14978bc019dSStefan Eßer static void 15078bc019dSStefan Eßer bc_program_popGlobals(BcProgram* p, bool reset) 15178bc019dSStefan Eßer { 152252884aeSStefan Eßer size_t i; 153252884aeSStefan Eßer 15410041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 15510041e99SStefan Eßer 15678bc019dSStefan Eßer for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) 15778bc019dSStefan Eßer { 158252884aeSStefan Eßer BcVec* v = p->globals_v + i; 159252884aeSStefan Eßer bc_vec_npop(v, reset ? v->len - 1 : 1); 160252884aeSStefan Eßer p->globals[i] = BC_PROG_GLOBAL(v); 161252884aeSStefan Eßer } 162252884aeSStefan Eßer 16344d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 164252884aeSStefan Eßer bc_rand_pop(&p->rng, reset); 16544d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 16644d4804dSStefan Eßer } 16744d4804dSStefan Eßer 16844d4804dSStefan Eßer /** 16944d4804dSStefan Eßer * Derefeneces an array reference and returns a pointer to the real array. 17044d4804dSStefan Eßer * @param p The program. 17144d4804dSStefan Eßer * @param vec The reference vector. 17244d4804dSStefan Eßer * @return A pointer to the desired array. 17344d4804dSStefan Eßer */ 17478bc019dSStefan Eßer static BcVec* 17578bc019dSStefan Eßer bc_program_dereference(const BcProgram* p, BcVec* vec) 17678bc019dSStefan Eßer { 17744d4804dSStefan Eßer BcVec* v; 17844d4804dSStefan Eßer size_t vidx, nidx, i = 0; 17944d4804dSStefan Eßer 18044d4804dSStefan Eßer // We want to be sure we have a reference vector. 18144d4804dSStefan Eßer assert(vec->size == sizeof(uchar)); 18244d4804dSStefan Eßer 18344d4804dSStefan Eßer // Get the index of the vector in arrs, then the index of the original 18444d4804dSStefan Eßer // referenced vector. 18544d4804dSStefan Eßer vidx = bc_program_index(vec->v, &i); 18644d4804dSStefan Eßer nidx = bc_program_index(vec->v, &i); 18744d4804dSStefan Eßer 18844d4804dSStefan Eßer v = bc_vec_item(bc_vec_item(&p->arrs, vidx), nidx); 18944d4804dSStefan Eßer 19044d4804dSStefan Eßer // We want to be sure we do *not* have a reference vector. 19144d4804dSStefan Eßer assert(v->size != sizeof(uchar)); 19244d4804dSStefan Eßer 19344d4804dSStefan Eßer return v; 194252884aeSStefan Eßer } 195d101cdd6SStefan Eßer 1963aa99676SStefan Eßer #endif // BC_ENABLED 197252884aeSStefan Eßer 19844d4804dSStefan Eßer /** 19944d4804dSStefan Eßer * Creates a BcNum from a BcBigDig and pushes onto the results stack. This is a 20044d4804dSStefan Eßer * convenience function. 20144d4804dSStefan Eßer * @param p The program. 20244d4804dSStefan Eßer * @param dig The BcBigDig to push onto the results stack. 20344d4804dSStefan Eßer * @param type The type that the pushed result should be. 20444d4804dSStefan Eßer */ 20578bc019dSStefan Eßer static void 20678bc019dSStefan Eßer bc_program_pushBigdig(BcProgram* p, BcBigDig dig, BcResultType type) 207252884aeSStefan Eßer { 208252884aeSStefan Eßer BcResult res; 209252884aeSStefan Eßer 210252884aeSStefan Eßer res.t = type; 211252884aeSStefan Eßer 212252884aeSStefan Eßer BC_SIG_LOCK; 213252884aeSStefan Eßer 214252884aeSStefan Eßer bc_num_createFromBigdig(&res.d.n, dig); 215252884aeSStefan Eßer bc_vec_push(&p->results, &res); 216252884aeSStefan Eßer 217252884aeSStefan Eßer BC_SIG_UNLOCK; 218252884aeSStefan Eßer } 219252884aeSStefan Eßer 22078bc019dSStefan Eßer size_t 221d101cdd6SStefan Eßer bc_program_addString(BcProgram* p, const char* str) 22278bc019dSStefan Eßer { 223d101cdd6SStefan Eßer size_t idx; 224252884aeSStefan Eßer 22544d4804dSStefan Eßer BC_SIG_ASSERT_LOCKED; 226252884aeSStefan Eßer 227d101cdd6SStefan Eßer if (bc_map_insert(&p->str_map, str, p->strs.len, &idx)) 228d101cdd6SStefan Eßer { 229d101cdd6SStefan Eßer char** str_ptr; 230d101cdd6SStefan Eßer BcId* id = bc_vec_item(&p->str_map, idx); 231d101cdd6SStefan Eßer 232d101cdd6SStefan Eßer // Get the index. 233d101cdd6SStefan Eßer idx = id->idx; 234d101cdd6SStefan Eßer 23544d4804dSStefan Eßer // Push an empty string on the proper vector. 236d101cdd6SStefan Eßer str_ptr = bc_vec_pushEmpty(&p->strs); 237252884aeSStefan Eßer 238d101cdd6SStefan Eßer // We reuse the string in the ID (allocated by bc_map_insert()), because 239d101cdd6SStefan Eßer // why not? 240d101cdd6SStefan Eßer *str_ptr = id->name; 241d101cdd6SStefan Eßer } 242d101cdd6SStefan Eßer else 243d101cdd6SStefan Eßer { 244d101cdd6SStefan Eßer BcId* id = bc_vec_item(&p->str_map, idx); 245d101cdd6SStefan Eßer idx = id->idx; 246d101cdd6SStefan Eßer } 247252884aeSStefan Eßer 248d101cdd6SStefan Eßer return idx; 249252884aeSStefan Eßer } 250252884aeSStefan Eßer 25178bc019dSStefan Eßer size_t 252d101cdd6SStefan Eßer bc_program_search(BcProgram* p, const char* name, bool var) 25378bc019dSStefan Eßer { 25478bc019dSStefan Eßer BcVec* v; 25578bc019dSStefan Eßer BcVec* map; 256252884aeSStefan Eßer size_t i; 257252884aeSStefan Eßer 25810041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 25910041e99SStefan Eßer 26044d4804dSStefan Eßer // Grab the right vector and map. 261252884aeSStefan Eßer v = var ? &p->vars : &p->arrs; 262252884aeSStefan Eßer map = var ? &p->var_map : &p->arr_map; 263252884aeSStefan Eßer 26444d4804dSStefan Eßer // We do an insert because the variable might not exist yet. This is because 26544d4804dSStefan Eßer // the parser calls this function. If the insert succeeds, we create a stack 26644d4804dSStefan Eßer // for the variable/array. But regardless, bc_map_insert() gives us the 26744d4804dSStefan Eßer // index of the item in i. 268d101cdd6SStefan Eßer if (bc_map_insert(map, name, v->len, &i)) 26978bc019dSStefan Eßer { 27044d4804dSStefan Eßer BcVec* temp = bc_vec_pushEmpty(v); 27144d4804dSStefan Eßer bc_array_init(temp, var); 272252884aeSStefan Eßer } 273252884aeSStefan Eßer 274252884aeSStefan Eßer return ((BcId*) bc_vec_item(map, i))->idx; 275252884aeSStefan Eßer } 276252884aeSStefan Eßer 27744d4804dSStefan Eßer /** 27844d4804dSStefan Eßer * Returns the correct variable or array stack for the type. 27944d4804dSStefan Eßer * @param p The program. 28044d4804dSStefan Eßer * @param idx The index of the variable or array in the variable or array 28144d4804dSStefan Eßer * vector. 28244d4804dSStefan Eßer * @param type The type of vector to return. 28344d4804dSStefan Eßer * @return A pointer to the variable or array stack. 28444d4804dSStefan Eßer */ 28578bc019dSStefan Eßer static inline BcVec* 28678bc019dSStefan Eßer bc_program_vec(const BcProgram* p, size_t idx, BcType type) 287252884aeSStefan Eßer { 288252884aeSStefan Eßer const BcVec* v = (type == BC_TYPE_VAR) ? &p->vars : &p->arrs; 289252884aeSStefan Eßer return bc_vec_item(v, idx); 290252884aeSStefan Eßer } 291252884aeSStefan Eßer 29244d4804dSStefan Eßer /** 29344d4804dSStefan Eßer * Returns a pointer to the BcNum corresponding to the result. There is one 29444d4804dSStefan Eßer * case, however, where this returns a pointer to a BcVec: if the type of the 29544d4804dSStefan Eßer * result is array. In that case, the pointer is casted to a pointer to BcNum, 29644d4804dSStefan Eßer * but is never used. The function that calls this expecting an array casts the 29744d4804dSStefan Eßer * pointer back. This function is called a lot and needs to be as fast as 29844d4804dSStefan Eßer * possible. 29944d4804dSStefan Eßer * @param p The program. 30044d4804dSStefan Eßer * @param r The result whose number will be returned. 30144d4804dSStefan Eßer * @return The BcNum corresponding to the result. 30244d4804dSStefan Eßer */ 30378bc019dSStefan Eßer static BcNum* 30478bc019dSStefan Eßer bc_program_num(BcProgram* p, BcResult* r) 30578bc019dSStefan Eßer { 306d213476dSStefan Eßer BcNum* n; 307252884aeSStefan Eßer 308d43fa8efSStefan Eßer #ifdef _WIN32 309d43fa8efSStefan Eßer // Windows made it an error to not initialize this, so shut it up. 310d43fa8efSStefan Eßer // I don't want to do this on other platforms because this procedure 311d43fa8efSStefan Eßer // is one of the most heavily-used, and eliminating the initialization 312d43fa8efSStefan Eßer // is a performance win. 313d43fa8efSStefan Eßer n = NULL; 314d43fa8efSStefan Eßer #endif // _WIN32 315d43fa8efSStefan Eßer 31678bc019dSStefan Eßer switch (r->t) 31778bc019dSStefan Eßer { 318252884aeSStefan Eßer case BC_RESULT_STR: 319252884aeSStefan Eßer case BC_RESULT_TEMP: 320252884aeSStefan Eßer case BC_RESULT_IBASE: 321252884aeSStefan Eßer case BC_RESULT_SCALE: 322252884aeSStefan Eßer case BC_RESULT_OBASE: 323252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 324252884aeSStefan Eßer case BC_RESULT_SEED: 325252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 326252884aeSStefan Eßer { 327252884aeSStefan Eßer n = &r->d.n; 328252884aeSStefan Eßer break; 329252884aeSStefan Eßer } 330252884aeSStefan Eßer 331252884aeSStefan Eßer case BC_RESULT_VAR: 332252884aeSStefan Eßer case BC_RESULT_ARRAY: 333252884aeSStefan Eßer case BC_RESULT_ARRAY_ELEM: 334252884aeSStefan Eßer { 335252884aeSStefan Eßer BcVec* v; 336252884aeSStefan Eßer BcType type = (r->t == BC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY; 337252884aeSStefan Eßer 33844d4804dSStefan Eßer // Get the correct variable or array vector. 339252884aeSStefan Eßer v = bc_program_vec(p, r->d.loc.loc, type); 340252884aeSStefan Eßer 34144d4804dSStefan Eßer // Surprisingly enough, the hard case is *not* returning an array; 34244d4804dSStefan Eßer // it's returning an array element. This is because we have to dig 34344d4804dSStefan Eßer // deeper to get *to* the element. That's what the code inside this 34444d4804dSStefan Eßer // if statement does. 34578bc019dSStefan Eßer if (r->t == BC_RESULT_ARRAY_ELEM) 34678bc019dSStefan Eßer { 347252884aeSStefan Eßer size_t idx = r->d.loc.idx; 348252884aeSStefan Eßer 349d101cdd6SStefan Eßer v = bc_vec_item(v, r->d.loc.stack_idx); 350252884aeSStefan Eßer 351252884aeSStefan Eßer #if BC_ENABLED 35244d4804dSStefan Eßer // If this is true, we have a reference vector, so dereference 35344d4804dSStefan Eßer // it. The reason we don't need to worry about it for returning 35444d4804dSStefan Eßer // a straight array is because we only care about references 35544d4804dSStefan Eßer // when we access elements of an array that is a reference. That 35644d4804dSStefan Eßer // is this code, so in essence, this line takes care of arrays 35744d4804dSStefan Eßer // as well. 358252884aeSStefan Eßer if (v->size == sizeof(uchar)) v = bc_program_dereference(p, v); 359252884aeSStefan Eßer #endif // BC_ENABLED 360252884aeSStefan Eßer 36144d4804dSStefan Eßer // We want to be sure we got a valid array of numbers. 362252884aeSStefan Eßer assert(v->size == sizeof(BcNum)); 363252884aeSStefan Eßer 36444d4804dSStefan Eßer // The bc spec says that if an element is accessed that does not 36544d4804dSStefan Eßer // exist, it should be preinitialized to 0. Well, if we access 36644d4804dSStefan Eßer // an element *way* out there, we have to preinitialize all 36744d4804dSStefan Eßer // elements between the current last element and the actual 36844d4804dSStefan Eßer // accessed element. 36978bc019dSStefan Eßer if (v->len <= idx) 37078bc019dSStefan Eßer { 371252884aeSStefan Eßer BC_SIG_LOCK; 372252884aeSStefan Eßer bc_array_expand(v, bc_vm_growSize(idx, 1)); 373252884aeSStefan Eßer BC_SIG_UNLOCK; 374252884aeSStefan Eßer } 375252884aeSStefan Eßer 376252884aeSStefan Eßer n = bc_vec_item(v, idx); 377252884aeSStefan Eßer } 37844d4804dSStefan Eßer // This is either a number (for a var) or an array (for an array). 379d101cdd6SStefan Eßer // Because bc_vec_top() and bc_vec_item() return a void*, we don't 380d101cdd6SStefan Eßer // need to cast. 381d101cdd6SStefan Eßer else 382d101cdd6SStefan Eßer { 383d101cdd6SStefan Eßer #if BC_ENABLED 384d101cdd6SStefan Eßer if (BC_IS_BC) 385d101cdd6SStefan Eßer { 386d101cdd6SStefan Eßer n = bc_vec_item(v, r->d.loc.stack_idx); 387d101cdd6SStefan Eßer } 388d101cdd6SStefan Eßer else 389d101cdd6SStefan Eßer #endif // BC_ENABLED 390d101cdd6SStefan Eßer { 391d101cdd6SStefan Eßer n = bc_vec_top(v); 392d101cdd6SStefan Eßer } 393d101cdd6SStefan Eßer } 394252884aeSStefan Eßer 395252884aeSStefan Eßer break; 396252884aeSStefan Eßer } 397252884aeSStefan Eßer 3983aa99676SStefan Eßer case BC_RESULT_ZERO: 3993aa99676SStefan Eßer { 400d101cdd6SStefan Eßer n = &vm->zero; 4013aa99676SStefan Eßer break; 4023aa99676SStefan Eßer } 4033aa99676SStefan Eßer 404252884aeSStefan Eßer case BC_RESULT_ONE: 405252884aeSStefan Eßer { 406d101cdd6SStefan Eßer n = &vm->one; 407252884aeSStefan Eßer break; 408252884aeSStefan Eßer } 409252884aeSStefan Eßer 410252884aeSStefan Eßer #if BC_ENABLED 41144d4804dSStefan Eßer // We should never get here; this is taken care of earlier because a 41244d4804dSStefan Eßer // result is expected. 413252884aeSStefan Eßer case BC_RESULT_VOID: 414103d7cdfSStefan Eßer #if BC_DEBUG 415252884aeSStefan Eßer { 416252884aeSStefan Eßer abort(); 417d101cdd6SStefan Eßer // Fallthrough 418252884aeSStefan Eßer } 419103d7cdfSStefan Eßer #endif // BC_DEBUG 420252884aeSStefan Eßer case BC_RESULT_LAST: 421252884aeSStefan Eßer { 422252884aeSStefan Eßer n = &p->last; 423252884aeSStefan Eßer break; 424252884aeSStefan Eßer } 425252884aeSStefan Eßer #endif // BC_ENABLED 426d101cdd6SStefan Eßer 427d101cdd6SStefan Eßer #if BC_GCC 428d101cdd6SStefan Eßer // This is here in GCC to quiet the "maybe-uninitialized" warning. 429d101cdd6SStefan Eßer default: 430d101cdd6SStefan Eßer { 431d101cdd6SStefan Eßer abort(); 432d101cdd6SStefan Eßer } 433d101cdd6SStefan Eßer #endif // BC_GCC 434252884aeSStefan Eßer } 435252884aeSStefan Eßer 436252884aeSStefan Eßer return n; 437252884aeSStefan Eßer } 438252884aeSStefan Eßer 43944d4804dSStefan Eßer /** 44044d4804dSStefan Eßer * Prepares an operand for use. 44144d4804dSStefan Eßer * @param p The program. 44244d4804dSStefan Eßer * @param r An out parameter; this is set to the pointer to the result that 44344d4804dSStefan Eßer * we care about. 44444d4804dSStefan Eßer * @param n An out parameter; this is set to the pointer to the number that 44544d4804dSStefan Eßer * we care about. 44644d4804dSStefan Eßer * @param idx The index of the result from the top of the results stack. 44744d4804dSStefan Eßer */ 44878bc019dSStefan Eßer static void 44978bc019dSStefan Eßer bc_program_operand(BcProgram* p, BcResult** r, BcNum** n, size_t idx) 450252884aeSStefan Eßer { 451252884aeSStefan Eßer *r = bc_vec_item_rev(&p->results, idx); 452252884aeSStefan Eßer 453252884aeSStefan Eßer #if BC_ENABLED 45444d4804dSStefan Eßer if (BC_ERR((*r)->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL); 455252884aeSStefan Eßer #endif // BC_ENABLED 456252884aeSStefan Eßer 457252884aeSStefan Eßer *n = bc_program_num(p, *r); 458252884aeSStefan Eßer } 459252884aeSStefan Eßer 46044d4804dSStefan Eßer /** 46144d4804dSStefan Eßer * Prepares the operands of a binary operator. 46244d4804dSStefan Eßer * @param p The program. 46344d4804dSStefan Eßer * @param l An out parameter; this is set to the pointer to the result for 46444d4804dSStefan Eßer * the left operand. 46544d4804dSStefan Eßer * @param ln An out parameter; this is set to the pointer to the number for 46644d4804dSStefan Eßer * the left operand. 46744d4804dSStefan Eßer * @param r An out parameter; this is set to the pointer to the result for 46844d4804dSStefan Eßer * the right operand. 46944d4804dSStefan Eßer * @param rn An out parameter; this is set to the pointer to the number for 47044d4804dSStefan Eßer * the right operand. 47144d4804dSStefan Eßer * @param idx The starting index where the operands are in the results stack, 47244d4804dSStefan Eßer * starting from the top. 47344d4804dSStefan Eßer */ 47478bc019dSStefan Eßer static void 47578bc019dSStefan Eßer bc_program_binPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r, 47678bc019dSStefan Eßer BcNum** rn, size_t idx) 477252884aeSStefan Eßer { 478252884aeSStefan Eßer BcResultType lt; 479252884aeSStefan Eßer 480252884aeSStefan Eßer assert(p != NULL && l != NULL && ln != NULL && r != NULL && rn != NULL); 481252884aeSStefan Eßer 482252884aeSStefan Eßer #ifndef BC_PROG_NO_STACK_CHECK 48344d4804dSStefan Eßer // Check the stack for dc. 48478bc019dSStefan Eßer if (BC_IS_DC) 48578bc019dSStefan Eßer { 486252884aeSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 2))) 48778bc019dSStefan Eßer { 48844d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 489252884aeSStefan Eßer } 49078bc019dSStefan Eßer } 491252884aeSStefan Eßer #endif // BC_PROG_NO_STACK_CHECK 492252884aeSStefan Eßer 493252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, idx + 2)); 494252884aeSStefan Eßer 49544d4804dSStefan Eßer // Get the operands. 496252884aeSStefan Eßer bc_program_operand(p, l, ln, idx + 1); 497252884aeSStefan Eßer bc_program_operand(p, r, rn, idx); 498252884aeSStefan Eßer 499252884aeSStefan Eßer lt = (*l)->t; 500252884aeSStefan Eßer 501252884aeSStefan Eßer #if BC_ENABLED 50244d4804dSStefan Eßer // bc_program_operand() checked these for us. 503252884aeSStefan Eßer assert(lt != BC_RESULT_VOID && (*r)->t != BC_RESULT_VOID); 504252884aeSStefan Eßer #endif // BC_ENABLED 505252884aeSStefan Eßer 506252884aeSStefan Eßer // We run this again under these conditions in case any vector has been 50744d4804dSStefan Eßer // reallocated out from under the BcNums or arrays we had. In other words, 50844d4804dSStefan Eßer // this is to fix pointer invalidation. 509252884aeSStefan Eßer if (lt == (*r)->t && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM)) 51078bc019dSStefan Eßer { 511252884aeSStefan Eßer *ln = bc_program_num(p, *l); 51278bc019dSStefan Eßer } 513252884aeSStefan Eßer 51444d4804dSStefan Eßer if (BC_ERR(lt == BC_RESULT_STR)) bc_err(BC_ERR_EXEC_TYPE); 515252884aeSStefan Eßer } 516252884aeSStefan Eßer 51744d4804dSStefan Eßer /** 51844d4804dSStefan Eßer * Prepares the operands of a binary operator and type checks them. This is 51944d4804dSStefan Eßer * separate from bc_program_binPrep() because some places want this, others want 52044d4804dSStefan Eßer * bc_program_binPrep(). 52144d4804dSStefan Eßer * @param p The program. 52244d4804dSStefan Eßer * @param l An out parameter; this is set to the pointer to the result for 52344d4804dSStefan Eßer * the left operand. 52444d4804dSStefan Eßer * @param ln An out parameter; this is set to the pointer to the number for 52544d4804dSStefan Eßer * the left operand. 52644d4804dSStefan Eßer * @param r An out parameter; this is set to the pointer to the result for 52744d4804dSStefan Eßer * the right operand. 52844d4804dSStefan Eßer * @param rn An out parameter; this is set to the pointer to the number for 52944d4804dSStefan Eßer * the right operand. 53044d4804dSStefan Eßer * @param idx The starting index where the operands are in the results stack, 53144d4804dSStefan Eßer * starting from the top. 53244d4804dSStefan Eßer */ 53378bc019dSStefan Eßer static void 53478bc019dSStefan Eßer bc_program_binOpPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r, 53578bc019dSStefan Eßer BcNum** rn, size_t idx) 536252884aeSStefan Eßer { 537252884aeSStefan Eßer bc_program_binPrep(p, l, ln, r, rn, idx); 538252884aeSStefan Eßer bc_program_type_num(*l, *ln); 539252884aeSStefan Eßer bc_program_type_num(*r, *rn); 540252884aeSStefan Eßer } 541252884aeSStefan Eßer 54244d4804dSStefan Eßer /** 54344d4804dSStefan Eßer * Prepares the operands of an assignment operator. 54444d4804dSStefan Eßer * @param p The program. 54544d4804dSStefan Eßer * @param l An out parameter; this is set to the pointer to the result for the 54644d4804dSStefan Eßer * left operand. 54744d4804dSStefan Eßer * @param ln An out parameter; this is set to the pointer to the number for the 54844d4804dSStefan Eßer * left operand. 54944d4804dSStefan Eßer * @param r An out parameter; this is set to the pointer to the result for the 55044d4804dSStefan Eßer * right operand. 55144d4804dSStefan Eßer * @param rn An out parameter; this is set to the pointer to the number for the 55244d4804dSStefan Eßer * right operand. 55344d4804dSStefan Eßer */ 55478bc019dSStefan Eßer static void 55578bc019dSStefan Eßer bc_program_assignPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r, 55678bc019dSStefan Eßer BcNum** rn) 557252884aeSStefan Eßer { 558252884aeSStefan Eßer BcResultType lt, min; 559d101cdd6SStefan Eßer bool good; 560252884aeSStefan Eßer 56144d4804dSStefan Eßer // This is the min non-allowable result type. dc allows strings. 5623aa99676SStefan Eßer min = BC_RESULT_TEMP - ((unsigned int) (BC_IS_BC)); 563252884aeSStefan Eßer 56444d4804dSStefan Eßer // Prepare the operands. 565252884aeSStefan Eßer bc_program_binPrep(p, l, ln, r, rn, 0); 566252884aeSStefan Eßer 567252884aeSStefan Eßer lt = (*l)->t; 568252884aeSStefan Eßer 56944d4804dSStefan Eßer // Typecheck the left. 57044d4804dSStefan Eßer if (BC_ERR(lt >= min && lt <= BC_RESULT_ONE)) bc_err(BC_ERR_EXEC_TYPE); 571252884aeSStefan Eßer 57244d4804dSStefan Eßer // Strings can be assigned to variables. We are already good if we are 57344d4804dSStefan Eßer // assigning a string. 574d101cdd6SStefan Eßer good = ((*r)->t == BC_RESULT_STR && lt <= BC_RESULT_ARRAY_ELEM); 575252884aeSStefan Eßer 57644d4804dSStefan Eßer assert(BC_PROG_STR(*rn) || (*r)->t != BC_RESULT_STR); 577252884aeSStefan Eßer 57844d4804dSStefan Eßer // If not, type check for a number. 579252884aeSStefan Eßer if (!good) bc_program_type_num(*r, *rn); 580252884aeSStefan Eßer } 581252884aeSStefan Eßer 58244d4804dSStefan Eßer /** 58344d4804dSStefan Eßer * Prepares a single operand and type checks it. This is separate from 58444d4804dSStefan Eßer * bc_program_operand() because different places want one or the other. 58544d4804dSStefan Eßer * @param p The program. 58644d4804dSStefan Eßer * @param r An out parameter; this is set to the pointer to the result that 58744d4804dSStefan Eßer * we care about. 58844d4804dSStefan Eßer * @param n An out parameter; this is set to the pointer to the number that 58944d4804dSStefan Eßer * we care about. 59044d4804dSStefan Eßer * @param idx The index of the result from the top of the results stack. 59144d4804dSStefan Eßer */ 59278bc019dSStefan Eßer static void 59378bc019dSStefan Eßer bc_program_prep(BcProgram* p, BcResult** r, BcNum** n, size_t idx) 59478bc019dSStefan Eßer { 595252884aeSStefan Eßer assert(p != NULL && r != NULL && n != NULL); 596252884aeSStefan Eßer 597252884aeSStefan Eßer #ifndef BC_PROG_NO_STACK_CHECK 59844d4804dSStefan Eßer // Check the stack for dc. 59978bc019dSStefan Eßer if (BC_IS_DC) 60078bc019dSStefan Eßer { 601252884aeSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1))) 60278bc019dSStefan Eßer { 60344d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 604252884aeSStefan Eßer } 60578bc019dSStefan Eßer } 606252884aeSStefan Eßer #endif // BC_PROG_NO_STACK_CHECK 607252884aeSStefan Eßer 608252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, idx + 1)); 609252884aeSStefan Eßer 610252884aeSStefan Eßer bc_program_operand(p, r, n, idx); 611252884aeSStefan Eßer 61244d4804dSStefan Eßer // dc does not allow strings in this case. 613252884aeSStefan Eßer bc_program_type_num(*r, *n); 614252884aeSStefan Eßer } 615252884aeSStefan Eßer 61644d4804dSStefan Eßer /** 61744d4804dSStefan Eßer * Prepares and returns a clean result for the result of an operation. 61844d4804dSStefan Eßer * @param p The program. 61944d4804dSStefan Eßer * @return A clean result. 62044d4804dSStefan Eßer */ 62178bc019dSStefan Eßer static BcResult* 62278bc019dSStefan Eßer bc_program_prepResult(BcProgram* p) 62378bc019dSStefan Eßer { 62444d4804dSStefan Eßer BcResult* res = bc_vec_pushEmpty(&p->results); 625252884aeSStefan Eßer 62644d4804dSStefan Eßer bc_result_clear(res); 627252884aeSStefan Eßer 62844d4804dSStefan Eßer return res; 629252884aeSStefan Eßer } 630252884aeSStefan Eßer 63144d4804dSStefan Eßer /** 63244d4804dSStefan Eßer * Prepares a constant for use. This parses the constant into a number and then 63344d4804dSStefan Eßer * pushes that number onto the results stack. 63444d4804dSStefan Eßer * @param p The program. 63544d4804dSStefan Eßer * @param code The bytecode vector that we will pull the index of the constant 63644d4804dSStefan Eßer * from. 63744d4804dSStefan Eßer * @param bgn An in/out parameter; marks the start of the index in the 63844d4804dSStefan Eßer * bytecode vector and will be updated to point to after the index. 63944d4804dSStefan Eßer */ 64078bc019dSStefan Eßer static void 64178bc019dSStefan Eßer bc_program_const(BcProgram* p, const char* code, size_t* bgn) 64278bc019dSStefan Eßer { 64344d4804dSStefan Eßer // I lied. I actually push the result first. I can do this because the 64444d4804dSStefan Eßer // result will be popped on error. I also get the constant itself. 6453aa99676SStefan Eßer BcResult* r = bc_program_prepResult(p); 646d101cdd6SStefan Eßer BcConst* c = bc_vec_item(&p->consts, bc_program_index(code, bgn)); 6473aa99676SStefan Eßer BcBigDig base = BC_PROG_IBASE(p); 6483aa99676SStefan Eßer 64944d4804dSStefan Eßer // Only reparse if the base changed. 65078bc019dSStefan Eßer if (c->base != base) 65178bc019dSStefan Eßer { 65244d4804dSStefan Eßer // Allocate if we haven't yet. 65378bc019dSStefan Eßer if (c->num.num == NULL) 65478bc019dSStefan Eßer { 655d101cdd6SStefan Eßer // The plus 1 is in case of overflow with lack of clamping. 656d101cdd6SStefan Eßer size_t len = strlen(c->val) + (BC_DIGIT_CLAMP == 0); 657d101cdd6SStefan Eßer 6583aa99676SStefan Eßer BC_SIG_LOCK; 659d101cdd6SStefan Eßer bc_num_init(&c->num, BC_NUM_RDX(len)); 6603aa99676SStefan Eßer BC_SIG_UNLOCK; 6613aa99676SStefan Eßer } 662d101cdd6SStefan Eßer // We need to zero an already existing number. 663d101cdd6SStefan Eßer else bc_num_zero(&c->num); 6643aa99676SStefan Eßer 6653aa99676SStefan Eßer // bc_num_parse() should only do operations that cannot fail. 66650696a6eSStefan Eßer bc_num_parse(&c->num, c->val, base); 6673aa99676SStefan Eßer 6683aa99676SStefan Eßer c->base = base; 6693aa99676SStefan Eßer } 6703aa99676SStefan Eßer 6713aa99676SStefan Eßer BC_SIG_LOCK; 6723aa99676SStefan Eßer 6733aa99676SStefan Eßer bc_num_createCopy(&r->d.n, &c->num); 6743aa99676SStefan Eßer 6753aa99676SStefan Eßer BC_SIG_UNLOCK; 6763aa99676SStefan Eßer } 6773aa99676SStefan Eßer 67844d4804dSStefan Eßer /** 67944d4804dSStefan Eßer * Executes a binary operator operation. 68044d4804dSStefan Eßer * @param p The program. 68144d4804dSStefan Eßer * @param inst The instruction corresponding to the binary operator to execute. 68244d4804dSStefan Eßer */ 68378bc019dSStefan Eßer static void 68478bc019dSStefan Eßer bc_program_op(BcProgram* p, uchar inst) 68578bc019dSStefan Eßer { 68678bc019dSStefan Eßer BcResult* opd1; 68778bc019dSStefan Eßer BcResult* opd2; 68878bc019dSStefan Eßer BcResult* res; 68978bc019dSStefan Eßer BcNum* n1; 69078bc019dSStefan Eßer BcNum* n2; 691252884aeSStefan Eßer size_t idx = inst - BC_INST_POWER; 692252884aeSStefan Eßer 693252884aeSStefan Eßer res = bc_program_prepResult(p); 694252884aeSStefan Eßer 695252884aeSStefan Eßer bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1); 696252884aeSStefan Eßer 697252884aeSStefan Eßer BC_SIG_LOCK; 698252884aeSStefan Eßer 69944d4804dSStefan Eßer // Initialize the number with enough space, using the correct 70044d4804dSStefan Eßer // BcNumBinaryOpReq function. This looks weird because it is executing an 70144d4804dSStefan Eßer // item of an array. Rest assured that item is a function. 702252884aeSStefan Eßer bc_num_init(&res->d.n, bc_program_opReqs[idx](n1, n2, BC_PROG_SCALE(p))); 703252884aeSStefan Eßer 704252884aeSStefan Eßer BC_SIG_UNLOCK; 705252884aeSStefan Eßer 70650696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(n1)); 70750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(n2)); 70850696a6eSStefan Eßer 70944d4804dSStefan Eßer // Run the operation. This also executes an item of an array. 710252884aeSStefan Eßer bc_program_ops[idx](n1, n2, &res->d.n, BC_PROG_SCALE(p)); 711252884aeSStefan Eßer 712252884aeSStefan Eßer bc_program_retire(p, 1, 2); 713252884aeSStefan Eßer } 714252884aeSStefan Eßer 71544d4804dSStefan Eßer /** 71644d4804dSStefan Eßer * Executes a read() or ? command. 71744d4804dSStefan Eßer * @param p The program. 71844d4804dSStefan Eßer */ 71978bc019dSStefan Eßer static void 72078bc019dSStefan Eßer bc_program_read(BcProgram* p) 72178bc019dSStefan Eßer { 722252884aeSStefan Eßer BcStatus s; 723252884aeSStefan Eßer BcInstPtr ip; 724252884aeSStefan Eßer size_t i; 725252884aeSStefan Eßer const char* file; 726d101cdd6SStefan Eßer BcMode mode; 727252884aeSStefan Eßer BcFunc* f = bc_vec_item(&p->fns, BC_PROG_READ); 728252884aeSStefan Eßer 72944d4804dSStefan Eßer // If we are already executing a read, that is an error. So look for a read 73044d4804dSStefan Eßer // and barf. 73178bc019dSStefan Eßer for (i = 0; i < p->stack.len; ++i) 73278bc019dSStefan Eßer { 733252884aeSStefan Eßer BcInstPtr* ip_ptr = bc_vec_item(&p->stack, i); 73444d4804dSStefan Eßer if (ip_ptr->func == BC_PROG_READ) bc_err(BC_ERR_EXEC_REC_READ); 735252884aeSStefan Eßer } 736252884aeSStefan Eßer 737252884aeSStefan Eßer BC_SIG_LOCK; 738252884aeSStefan Eßer 73944d4804dSStefan Eßer // Save the filename because we are going to overwrite it. 740d101cdd6SStefan Eßer file = vm->file; 741d101cdd6SStefan Eßer mode = vm->mode; 74244d4804dSStefan Eßer 74344d4804dSStefan Eßer // It is a parse error if there needs to be more than one line, so we unset 74444d4804dSStefan Eßer // this to tell the lexer to not request more. We set it back later. 745d101cdd6SStefan Eßer vm->mode = BC_MODE_FILE; 74644d4804dSStefan Eßer 747d101cdd6SStefan Eßer if (!BC_PARSE_IS_INITED(&vm->read_prs, p)) 74878bc019dSStefan Eßer { 74944d4804dSStefan Eßer // We need to parse, but we don't want to use the existing parser 75044d4804dSStefan Eßer // because it has state it needs to keep. (It could have a partial parse 75144d4804dSStefan Eßer // state.) So we create a new parser. This parser is in the BcVm struct 75244d4804dSStefan Eßer // so that it is not local, which means that a longjmp() could change 75344d4804dSStefan Eßer // it. 754d101cdd6SStefan Eßer bc_parse_init(&vm->read_prs, p, BC_PROG_READ); 75544d4804dSStefan Eßer 75644d4804dSStefan Eßer // We need a separate input buffer; that's why it is also in the BcVm 75744d4804dSStefan Eßer // struct. 758d101cdd6SStefan Eßer bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE); 75944d4804dSStefan Eßer } 760175a4d10SStefan Eßer else 761175a4d10SStefan Eßer { 76244d4804dSStefan Eßer // This needs to be updated because the parser could have been used 763175a4d10SStefan Eßer // somewhere else. 764175a4d10SStefan Eßer bc_parse_updateFunc(&vm->read_prs, BC_PROG_READ); 765175a4d10SStefan Eßer 766175a4d10SStefan Eßer // The read buffer also needs to be emptied or else it will still 767175a4d10SStefan Eßer // contain previous read expressions. 768175a4d10SStefan Eßer bc_vec_empty(&vm->read_buf); 769175a4d10SStefan Eßer } 770252884aeSStefan Eßer 771d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, exec_err); 772252884aeSStefan Eßer 773252884aeSStefan Eßer BC_SIG_UNLOCK; 774252884aeSStefan Eßer 77544d4804dSStefan Eßer // Set up the lexer and the read function. 776d101cdd6SStefan Eßer bc_lex_file(&vm->read_prs.l, bc_program_stdin_name); 77710328f8bSStefan Eßer bc_vec_popAll(&f->code); 778252884aeSStefan Eßer 77944d4804dSStefan Eßer // Read a line. 780d101cdd6SStefan Eßer if (!BC_R) s = bc_read_line(&vm->read_buf, ""); 781d101cdd6SStefan Eßer else s = bc_read_line(&vm->read_buf, BC_VM_READ_PROMPT); 7827e5c51e5SStefan Eßer 78344d4804dSStefan Eßer // We should *not* have run into EOF. 78444d4804dSStefan Eßer if (s == BC_STATUS_EOF) bc_err(BC_ERR_EXEC_READ_EXPR); 785252884aeSStefan Eßer 786d101cdd6SStefan Eßer // Parse *one* expression, so mode should not be stdin. 787d101cdd6SStefan Eßer bc_parse_text(&vm->read_prs, vm->read_buf.v, BC_MODE_FILE); 78810041e99SStefan Eßer BC_SIG_LOCK; 789d101cdd6SStefan Eßer vm->expr(&vm->read_prs, BC_PARSE_NOREAD | BC_PARSE_NEEDVAL); 79010041e99SStefan Eßer BC_SIG_UNLOCK; 791252884aeSStefan Eßer 79244d4804dSStefan Eßer // We *must* have a valid expression. A semicolon cannot end an expression, 79344d4804dSStefan Eßer // although EOF can. 794d101cdd6SStefan Eßer if (BC_ERR(vm->read_prs.l.t != BC_LEX_NLINE && 795d101cdd6SStefan Eßer vm->read_prs.l.t != BC_LEX_EOF)) 79644d4804dSStefan Eßer { 79744d4804dSStefan Eßer bc_err(BC_ERR_EXEC_READ_EXPR); 79844d4804dSStefan Eßer } 799252884aeSStefan Eßer 8003aa99676SStefan Eßer #if BC_ENABLED 80144d4804dSStefan Eßer // Push on the globals stack if necessary. 802252884aeSStefan Eßer if (BC_G) bc_program_prepGlobals(p); 8033aa99676SStefan Eßer #endif // BC_ENABLED 804252884aeSStefan Eßer 80544d4804dSStefan Eßer // Set up a new BcInstPtr. 806252884aeSStefan Eßer ip.func = BC_PROG_READ; 807252884aeSStefan Eßer ip.idx = 0; 808252884aeSStefan Eßer ip.len = p->results.len; 809252884aeSStefan Eßer 810252884aeSStefan Eßer // Update this pointer, just in case. 811252884aeSStefan Eßer f = bc_vec_item(&p->fns, BC_PROG_READ); 812252884aeSStefan Eßer 81344d4804dSStefan Eßer // We want a return instruction to simplify things. 814d101cdd6SStefan Eßer bc_vec_pushByte(&f->code, vm->read_ret); 81510041e99SStefan Eßer 81610041e99SStefan Eßer // This lock is here to make sure dc's tail calls are the same length. 81710041e99SStefan Eßer BC_SIG_LOCK; 818252884aeSStefan Eßer bc_vec_push(&p->stack, &ip); 8193aa99676SStefan Eßer 820252884aeSStefan Eßer #if DC_ENABLED 82144d4804dSStefan Eßer // We need a new tail call entry for dc. 82278bc019dSStefan Eßer if (BC_IS_DC) 82378bc019dSStefan Eßer { 824252884aeSStefan Eßer size_t temp = 0; 825252884aeSStefan Eßer bc_vec_push(&p->tail_calls, &temp); 826252884aeSStefan Eßer } 827252884aeSStefan Eßer #endif // DC_ENABLED 828252884aeSStefan Eßer 829252884aeSStefan Eßer exec_err: 830252884aeSStefan Eßer BC_SIG_MAYLOCK; 831d101cdd6SStefan Eßer vm->mode = (uchar) mode; 832d101cdd6SStefan Eßer vm->file = file; 833d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 834252884aeSStefan Eßer } 835252884aeSStefan Eßer 83644d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 83744d4804dSStefan Eßer 83844d4804dSStefan Eßer /** 83944d4804dSStefan Eßer * Execute a rand(). 84044d4804dSStefan Eßer * @param p The program. 84144d4804dSStefan Eßer */ 84278bc019dSStefan Eßer static void 84378bc019dSStefan Eßer bc_program_rand(BcProgram* p) 84478bc019dSStefan Eßer { 845252884aeSStefan Eßer BcRand rand = bc_rand_int(&p->rng); 84644d4804dSStefan Eßer 847252884aeSStefan Eßer bc_program_pushBigdig(p, (BcBigDig) rand, BC_RESULT_TEMP); 84844d4804dSStefan Eßer 849103d7cdfSStefan Eßer #if BC_DEBUG 85044d4804dSStefan Eßer // This is just to ensure that the generated number is correct. I also use 85144d4804dSStefan Eßer // braces because I declare every local at the top of the scope. 85250696a6eSStefan Eßer { 85350696a6eSStefan Eßer BcResult* r = bc_vec_top(&p->results); 85450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(r->d.n)); 85550696a6eSStefan Eßer } 856103d7cdfSStefan Eßer #endif // BC_DEBUG 857252884aeSStefan Eßer } 85844d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 859252884aeSStefan Eßer 86044d4804dSStefan Eßer /** 86144d4804dSStefan Eßer * Prints a series of characters, without escapes. 86244d4804dSStefan Eßer * @param str The string (series of characters). 86344d4804dSStefan Eßer */ 86478bc019dSStefan Eßer static void 86578bc019dSStefan Eßer bc_program_printChars(const char* str) 86678bc019dSStefan Eßer { 867252884aeSStefan Eßer const char* nl; 868d101cdd6SStefan Eßer size_t len = vm->nchars + strlen(str); 86910041e99SStefan Eßer sig_atomic_t lock; 87010041e99SStefan Eßer 87110041e99SStefan Eßer BC_SIG_TRYLOCK(lock); 872252884aeSStefan Eßer 873d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_save, str); 87444d4804dSStefan Eßer 87544d4804dSStefan Eßer // We need to update the number of characters, so we find the last newline 87644d4804dSStefan Eßer // and set the characters accordingly. 877252884aeSStefan Eßer nl = strrchr(str, '\n'); 878252884aeSStefan Eßer 879252884aeSStefan Eßer if (nl != NULL) len = strlen(nl + 1); 880252884aeSStefan Eßer 881d101cdd6SStefan Eßer vm->nchars = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len; 88210041e99SStefan Eßer 88310041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 884252884aeSStefan Eßer } 885252884aeSStefan Eßer 88644d4804dSStefan Eßer /** 88744d4804dSStefan Eßer * Prints a string with escapes. 88844d4804dSStefan Eßer * @param str The string. 88944d4804dSStefan Eßer */ 89078bc019dSStefan Eßer static void 89178bc019dSStefan Eßer bc_program_printString(const char* restrict str) 89278bc019dSStefan Eßer { 893252884aeSStefan Eßer size_t i, len = strlen(str); 894252884aeSStefan Eßer 895252884aeSStefan Eßer #if DC_ENABLED 89644d4804dSStefan Eßer // This is to ensure a nul byte is printed for dc's stream operation. 89778bc019dSStefan Eßer if (!len && BC_IS_DC) 89878bc019dSStefan Eßer { 8997e5c51e5SStefan Eßer bc_vm_putchar('\0', bc_flush_save); 900252884aeSStefan Eßer return; 901252884aeSStefan Eßer } 902252884aeSStefan Eßer #endif // DC_ENABLED 903252884aeSStefan Eßer 90444d4804dSStefan Eßer // Loop over the characters, processing escapes and printing the rest. 90578bc019dSStefan Eßer for (i = 0; i < len; ++i) 90678bc019dSStefan Eßer { 907252884aeSStefan Eßer int c = str[i]; 908252884aeSStefan Eßer 90944d4804dSStefan Eßer // If we have an escape... 91078bc019dSStefan Eßer if (c == '\\' && i != len - 1) 91178bc019dSStefan Eßer { 912252884aeSStefan Eßer const char* ptr; 913252884aeSStefan Eßer 91444d4804dSStefan Eßer // Get the escape character and its companion. 915252884aeSStefan Eßer c = str[++i]; 916252884aeSStefan Eßer ptr = strchr(bc_program_esc_chars, c); 917252884aeSStefan Eßer 91844d4804dSStefan Eßer // If we have a companion character... 91978bc019dSStefan Eßer if (ptr != NULL) 92078bc019dSStefan Eßer { 92144d4804dSStefan Eßer // We need to specially handle a newline. 92278bc019dSStefan Eßer if (c == 'n') 92378bc019dSStefan Eßer { 92410041e99SStefan Eßer BC_SIG_LOCK; 925d101cdd6SStefan Eßer vm->nchars = UINT16_MAX; 92610041e99SStefan Eßer BC_SIG_UNLOCK; 92710041e99SStefan Eßer } 92844d4804dSStefan Eßer 92944d4804dSStefan Eßer // Grab the actual character. 930252884aeSStefan Eßer c = bc_program_esc_seqs[(size_t) (ptr - bc_program_esc_chars)]; 931252884aeSStefan Eßer } 93278bc019dSStefan Eßer else 93378bc019dSStefan Eßer { 93444d4804dSStefan Eßer // Just print the backslash if there is no companion character. 93544d4804dSStefan Eßer // The following character will be printed later after the outer 93644d4804dSStefan Eßer // if statement. 9377e5c51e5SStefan Eßer bc_vm_putchar('\\', bc_flush_save); 938252884aeSStefan Eßer } 939252884aeSStefan Eßer } 940252884aeSStefan Eßer 9417e5c51e5SStefan Eßer bc_vm_putchar(c, bc_flush_save); 942252884aeSStefan Eßer } 943252884aeSStefan Eßer } 944252884aeSStefan Eßer 94544d4804dSStefan Eßer /** 94644d4804dSStefan Eßer * Executes a print. This function handles all printing except streaming. 94744d4804dSStefan Eßer * @param p The program. 94844d4804dSStefan Eßer * @param inst The instruction for the type of print we are doing. 94944d4804dSStefan Eßer * @param idx The index of the result that we are printing. 95044d4804dSStefan Eßer */ 95178bc019dSStefan Eßer static void 95278bc019dSStefan Eßer bc_program_print(BcProgram* p, uchar inst, size_t idx) 95378bc019dSStefan Eßer { 954252884aeSStefan Eßer BcResult* r; 955252884aeSStefan Eßer char* str; 956252884aeSStefan Eßer BcNum* n; 957252884aeSStefan Eßer bool pop = (inst != BC_INST_PRINT); 958252884aeSStefan Eßer 959252884aeSStefan Eßer assert(p != NULL); 960252884aeSStefan Eßer 961252884aeSStefan Eßer #ifndef BC_PROG_NO_STACK_CHECK 96278bc019dSStefan Eßer if (BC_IS_DC) 96378bc019dSStefan Eßer { 964252884aeSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1))) 96578bc019dSStefan Eßer { 96644d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 967252884aeSStefan Eßer } 96878bc019dSStefan Eßer } 969252884aeSStefan Eßer #endif // BC_PROG_NO_STACK_CHECK 970252884aeSStefan Eßer 971252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, idx + 1)); 972252884aeSStefan Eßer 973252884aeSStefan Eßer r = bc_vec_item_rev(&p->results, idx); 974252884aeSStefan Eßer 975252884aeSStefan Eßer #if BC_ENABLED 97644d4804dSStefan Eßer // If we have a void value, that's not necessarily an error. It is if pop is 97744d4804dSStefan Eßer // true because that means that we are executing a print statement, but 97844d4804dSStefan Eßer // attempting to do a print on a lone void value is allowed because that's 97944d4804dSStefan Eßer // exactly how we want void values used. 98078bc019dSStefan Eßer if (r->t == BC_RESULT_VOID) 98178bc019dSStefan Eßer { 98244d4804dSStefan Eßer if (BC_ERR(pop)) bc_err(BC_ERR_EXEC_VOID_VAL); 983252884aeSStefan Eßer bc_vec_pop(&p->results); 984252884aeSStefan Eßer return; 985252884aeSStefan Eßer } 986252884aeSStefan Eßer #endif // BC_ENABLED 987252884aeSStefan Eßer 988252884aeSStefan Eßer n = bc_program_num(p, r); 989252884aeSStefan Eßer 99044d4804dSStefan Eßer // If we have a number... 99178bc019dSStefan Eßer if (BC_PROG_NUM(r, n)) 99278bc019dSStefan Eßer { 993252884aeSStefan Eßer #if BC_ENABLED 99444d4804dSStefan Eßer assert(inst != BC_INST_PRINT_STR); 99544d4804dSStefan Eßer #endif // BC_ENABLED 99644d4804dSStefan Eßer 99744d4804dSStefan Eßer // Print the number. 99844d4804dSStefan Eßer bc_num_print(n, BC_PROG_OBASE(p), !pop); 99944d4804dSStefan Eßer 100044d4804dSStefan Eßer #if BC_ENABLED 100144d4804dSStefan Eßer // Need to store the number in last. 1002252884aeSStefan Eßer if (BC_IS_BC) bc_num_copy(&p->last, n); 1003252884aeSStefan Eßer #endif // BC_ENABLED 1004252884aeSStefan Eßer } 100578bc019dSStefan Eßer else 100678bc019dSStefan Eßer { 100744d4804dSStefan Eßer // We want to flush any stuff in the stdout buffer first. 1008d101cdd6SStefan Eßer bc_file_flush(&vm->fout, bc_flush_save); 100944d4804dSStefan Eßer str = bc_program_string(p, n); 1010252884aeSStefan Eßer 101144d4804dSStefan Eßer #if BC_ENABLED 1012252884aeSStefan Eßer if (inst == BC_INST_PRINT_STR) bc_program_printChars(str); 101344d4804dSStefan Eßer else 101444d4804dSStefan Eßer #endif // BC_ENABLED 101544d4804dSStefan Eßer { 1016252884aeSStefan Eßer bc_program_printString(str); 101744d4804dSStefan Eßer 101844d4804dSStefan Eßer // Need to print a newline only in this case. 101978bc019dSStefan Eßer if (inst == BC_INST_PRINT) bc_vm_putchar('\n', bc_flush_err); 1020252884aeSStefan Eßer } 1021252884aeSStefan Eßer } 1022252884aeSStefan Eßer 1023d101cdd6SStefan Eßer // bc always pops. This macro makes sure that happens. 1024d101cdd6SStefan Eßer if (BC_PROGRAM_POP(pop)) bc_vec_pop(&p->results); 1025252884aeSStefan Eßer } 1026252884aeSStefan Eßer 102778bc019dSStefan Eßer void 102878bc019dSStefan Eßer bc_program_negate(BcResult* r, BcNum* n) 102978bc019dSStefan Eßer { 1030252884aeSStefan Eßer bc_num_copy(&r->d.n, n); 103150696a6eSStefan Eßer if (BC_NUM_NONZERO(&r->d.n)) BC_NUM_NEG_TGL_NP(r->d.n); 1032252884aeSStefan Eßer } 1033252884aeSStefan Eßer 103478bc019dSStefan Eßer void 103578bc019dSStefan Eßer bc_program_not(BcResult* r, BcNum* n) 103678bc019dSStefan Eßer { 1037252884aeSStefan Eßer if (!bc_num_cmpZero(n)) bc_num_one(&r->d.n); 1038252884aeSStefan Eßer } 1039252884aeSStefan Eßer 1040252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 104178bc019dSStefan Eßer void 104278bc019dSStefan Eßer bc_program_trunc(BcResult* r, BcNum* n) 104378bc019dSStefan Eßer { 1044252884aeSStefan Eßer bc_num_copy(&r->d.n, n); 1045252884aeSStefan Eßer bc_num_truncate(&r->d.n, n->scale); 1046252884aeSStefan Eßer } 1047252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1048252884aeSStefan Eßer 104944d4804dSStefan Eßer /** 105044d4804dSStefan Eßer * Runs a unary operation. 105144d4804dSStefan Eßer * @param p The program. 105244d4804dSStefan Eßer * @param inst The unary operation. 105344d4804dSStefan Eßer */ 105478bc019dSStefan Eßer static void 105578bc019dSStefan Eßer bc_program_unary(BcProgram* p, uchar inst) 105678bc019dSStefan Eßer { 105778bc019dSStefan Eßer BcResult* res; 105878bc019dSStefan Eßer BcResult* ptr; 1059252884aeSStefan Eßer BcNum* num; 1060252884aeSStefan Eßer 1061252884aeSStefan Eßer res = bc_program_prepResult(p); 1062252884aeSStefan Eßer 1063252884aeSStefan Eßer bc_program_prep(p, &ptr, &num, 1); 1064252884aeSStefan Eßer 1065252884aeSStefan Eßer BC_SIG_LOCK; 1066252884aeSStefan Eßer 1067252884aeSStefan Eßer bc_num_init(&res->d.n, num->len); 1068252884aeSStefan Eßer 1069252884aeSStefan Eßer BC_SIG_UNLOCK; 1070252884aeSStefan Eßer 107144d4804dSStefan Eßer // This calls a function that is in an array. 1072252884aeSStefan Eßer bc_program_unarys[inst - BC_INST_NEG](res, num); 1073252884aeSStefan Eßer bc_program_retire(p, 1, 1); 1074252884aeSStefan Eßer } 1075252884aeSStefan Eßer 107644d4804dSStefan Eßer /** 107744d4804dSStefan Eßer * Executes a logical operator. 107844d4804dSStefan Eßer * @param p The program. 107944d4804dSStefan Eßer * @param inst The operator. 108044d4804dSStefan Eßer */ 108178bc019dSStefan Eßer static void 108278bc019dSStefan Eßer bc_program_logical(BcProgram* p, uchar inst) 108378bc019dSStefan Eßer { 108478bc019dSStefan Eßer BcResult* opd1; 108578bc019dSStefan Eßer BcResult* opd2; 108678bc019dSStefan Eßer BcResult* res; 108778bc019dSStefan Eßer BcNum* n1; 108878bc019dSStefan Eßer BcNum* n2; 1089252884aeSStefan Eßer bool cond = 0; 1090252884aeSStefan Eßer ssize_t cmp; 1091252884aeSStefan Eßer 1092252884aeSStefan Eßer res = bc_program_prepResult(p); 1093252884aeSStefan Eßer 109444d4804dSStefan Eßer // All logical operators (except boolean not, which is taken care of by 109544d4804dSStefan Eßer // bc_program_unary()), are binary operators. 1096252884aeSStefan Eßer bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1); 1097252884aeSStefan Eßer 109844d4804dSStefan Eßer // Boolean and and or are not short circuiting. This is why; they can be 109944d4804dSStefan Eßer // implemented much easier this way. 1100252884aeSStefan Eßer if (inst == BC_INST_BOOL_AND) 110178bc019dSStefan Eßer { 1102252884aeSStefan Eßer cond = (bc_num_cmpZero(n1) && bc_num_cmpZero(n2)); 110378bc019dSStefan Eßer } 1104252884aeSStefan Eßer else if (inst == BC_INST_BOOL_OR) 110578bc019dSStefan Eßer { 1106252884aeSStefan Eßer cond = (bc_num_cmpZero(n1) || bc_num_cmpZero(n2)); 110778bc019dSStefan Eßer } 110878bc019dSStefan Eßer else 110978bc019dSStefan Eßer { 111044d4804dSStefan Eßer // We have a relational operator, so do a comparison. 1111252884aeSStefan Eßer cmp = bc_num_cmp(n1, n2); 1112252884aeSStefan Eßer 111378bc019dSStefan Eßer switch (inst) 111478bc019dSStefan Eßer { 1115252884aeSStefan Eßer case BC_INST_REL_EQ: 1116252884aeSStefan Eßer { 1117252884aeSStefan Eßer cond = (cmp == 0); 1118252884aeSStefan Eßer break; 1119252884aeSStefan Eßer } 1120252884aeSStefan Eßer 1121252884aeSStefan Eßer case BC_INST_REL_LE: 1122252884aeSStefan Eßer { 1123252884aeSStefan Eßer cond = (cmp <= 0); 1124252884aeSStefan Eßer break; 1125252884aeSStefan Eßer } 1126252884aeSStefan Eßer 1127252884aeSStefan Eßer case BC_INST_REL_GE: 1128252884aeSStefan Eßer { 1129252884aeSStefan Eßer cond = (cmp >= 0); 1130252884aeSStefan Eßer break; 1131252884aeSStefan Eßer } 1132252884aeSStefan Eßer 1133252884aeSStefan Eßer case BC_INST_REL_NE: 1134252884aeSStefan Eßer { 1135252884aeSStefan Eßer cond = (cmp != 0); 1136252884aeSStefan Eßer break; 1137252884aeSStefan Eßer } 1138252884aeSStefan Eßer 1139252884aeSStefan Eßer case BC_INST_REL_LT: 1140252884aeSStefan Eßer { 1141252884aeSStefan Eßer cond = (cmp < 0); 1142252884aeSStefan Eßer break; 1143252884aeSStefan Eßer } 1144252884aeSStefan Eßer 1145252884aeSStefan Eßer case BC_INST_REL_GT: 1146252884aeSStefan Eßer { 1147252884aeSStefan Eßer cond = (cmp > 0); 1148252884aeSStefan Eßer break; 1149252884aeSStefan Eßer } 1150103d7cdfSStefan Eßer #if BC_DEBUG 1151252884aeSStefan Eßer default: 1152252884aeSStefan Eßer { 115344d4804dSStefan Eßer // There is a bug if we get here. 1154252884aeSStefan Eßer abort(); 1155252884aeSStefan Eßer } 1156103d7cdfSStefan Eßer #endif // BC_DEBUG 1157252884aeSStefan Eßer } 1158252884aeSStefan Eßer } 1159252884aeSStefan Eßer 1160252884aeSStefan Eßer BC_SIG_LOCK; 1161252884aeSStefan Eßer 1162252884aeSStefan Eßer bc_num_init(&res->d.n, BC_NUM_DEF_SIZE); 1163252884aeSStefan Eßer 1164252884aeSStefan Eßer BC_SIG_UNLOCK; 1165252884aeSStefan Eßer 1166252884aeSStefan Eßer if (cond) bc_num_one(&res->d.n); 1167252884aeSStefan Eßer 1168252884aeSStefan Eßer bc_program_retire(p, 1, 2); 1169252884aeSStefan Eßer } 1170252884aeSStefan Eßer 117144d4804dSStefan Eßer /** 117244d4804dSStefan Eßer * Assigns a string to a variable. 117344d4804dSStefan Eßer * @param p The program. 117444d4804dSStefan Eßer * @param num The location of the string as a BcNum. 117544d4804dSStefan Eßer * @param v The stack for the variable. 117644d4804dSStefan Eßer * @param push Whether to push the string or not. To push means to move the 117744d4804dSStefan Eßer * string from the results stack and push it onto the variable 117844d4804dSStefan Eßer * stack. 117944d4804dSStefan Eßer */ 118078bc019dSStefan Eßer static void 118178bc019dSStefan Eßer bc_program_assignStr(BcProgram* p, BcNum* num, BcVec* v, bool push) 1182252884aeSStefan Eßer { 118344d4804dSStefan Eßer BcNum* n; 1184252884aeSStefan Eßer 1185252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1 + !push)); 118644d4804dSStefan Eßer assert(num != NULL && num->num == NULL && num->cap == 0); 1187252884aeSStefan Eßer 118844d4804dSStefan Eßer // If we are not pushing onto the variable stack, we need to replace the 118944d4804dSStefan Eßer // top of the variable stack. 1190252884aeSStefan Eßer if (!push) bc_vec_pop(v); 1191252884aeSStefan Eßer 1192252884aeSStefan Eßer bc_vec_npop(&p->results, 1 + !push); 1193252884aeSStefan Eßer 119444d4804dSStefan Eßer n = bc_vec_pushEmpty(v); 119544d4804dSStefan Eßer 119644d4804dSStefan Eßer // We can just copy because the num should not have allocated anything. 119778bc019dSStefan Eßer // NOLINTNEXTLINE 119844d4804dSStefan Eßer memcpy(n, num, sizeof(BcNum)); 119944d4804dSStefan Eßer } 120044d4804dSStefan Eßer 120144d4804dSStefan Eßer /** 120244d4804dSStefan Eßer * Copies a value to a variable. This is used for storing in dc as well as to 120344d4804dSStefan Eßer * set function parameters to arguments in bc. 120444d4804dSStefan Eßer * @param p The program. 120544d4804dSStefan Eßer * @param idx The index of the variable or array to copy to. 120644d4804dSStefan Eßer * @param t The type to copy to. This could be a variable or an array. 120744d4804dSStefan Eßer */ 120878bc019dSStefan Eßer static void 1209d101cdd6SStefan Eßer bc_program_copyToVar(BcProgram* p, size_t idx, BcType t) 1210252884aeSStefan Eßer { 1211252884aeSStefan Eßer BcResult *ptr = NULL, r; 1212252884aeSStefan Eßer BcVec* vec; 1213252884aeSStefan Eßer BcNum* n = NULL; 1214252884aeSStefan Eßer bool var = (t == BC_TYPE_VAR); 1215252884aeSStefan Eßer 1216252884aeSStefan Eßer #if DC_ENABLED 121744d4804dSStefan Eßer // Check the stack for dc. 121878bc019dSStefan Eßer if (BC_IS_DC) 121978bc019dSStefan Eßer { 122044d4804dSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK); 122144d4804dSStefan Eßer } 122244d4804dSStefan Eßer #endif 1223252884aeSStefan Eßer 1224252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 1225252884aeSStefan Eßer 1226252884aeSStefan Eßer bc_program_operand(p, &ptr, &n, 0); 1227252884aeSStefan Eßer 1228252884aeSStefan Eßer #if BC_ENABLED 122944d4804dSStefan Eßer // Get the variable for a bc function call. 1230252884aeSStefan Eßer if (BC_IS_BC) 1231252884aeSStefan Eßer { 123244d4804dSStefan Eßer // Type match the result. 1233252884aeSStefan Eßer bc_program_type_match(ptr, t); 123478bc019dSStefan Eßer } 1235252884aeSStefan Eßer #endif // BC_ENABLED 1236252884aeSStefan Eßer 1237252884aeSStefan Eßer vec = bc_program_vec(p, idx, t); 1238252884aeSStefan Eßer 123944d4804dSStefan Eßer // We can shortcut in dc if it's assigning a string by using 124044d4804dSStefan Eßer // bc_program_assignStr(). 124178bc019dSStefan Eßer if (ptr->t == BC_RESULT_STR) 124278bc019dSStefan Eßer { 124344d4804dSStefan Eßer assert(BC_PROG_STR(n)); 124410328f8bSStefan Eßer 124544d4804dSStefan Eßer if (BC_ERR(!var)) bc_err(BC_ERR_EXEC_TYPE); 124610328f8bSStefan Eßer 124744d4804dSStefan Eßer bc_program_assignStr(p, n, vec, true); 124810328f8bSStefan Eßer 1249252884aeSStefan Eßer return; 1250252884aeSStefan Eßer } 1251252884aeSStefan Eßer 1252252884aeSStefan Eßer BC_SIG_LOCK; 1253252884aeSStefan Eßer 125444d4804dSStefan Eßer // Just create and copy for a normal variable. 125578bc019dSStefan Eßer if (var) 125678bc019dSStefan Eßer { 125778bc019dSStefan Eßer if (BC_PROG_STR(n)) 125878bc019dSStefan Eßer { 125978bc019dSStefan Eßer // NOLINTNEXTLINE 126078bc019dSStefan Eßer memcpy(&r.d.n, n, sizeof(BcNum)); 126178bc019dSStefan Eßer } 126244d4804dSStefan Eßer else bc_num_createCopy(&r.d.n, n); 126344d4804dSStefan Eßer } 126478bc019dSStefan Eßer else 126578bc019dSStefan Eßer { 126644d4804dSStefan Eßer // If we get here, we are handling an array. This is one place we need 126744d4804dSStefan Eßer // to cast the number from bc_program_num() to a vector. 126878bc019dSStefan Eßer BcVec* v = (BcVec*) n; 126978bc019dSStefan Eßer BcVec* rv = &r.d.v; 127044d4804dSStefan Eßer 1271252884aeSStefan Eßer #if BC_ENABLED 127244d4804dSStefan Eßer 127378bc019dSStefan Eßer if (BC_IS_BC) 127478bc019dSStefan Eßer { 1275252884aeSStefan Eßer bool ref, ref_size; 1276252884aeSStefan Eßer 127744d4804dSStefan Eßer // True if we are using a reference. 1278252884aeSStefan Eßer ref = (v->size == sizeof(BcNum) && t == BC_TYPE_REF); 127944d4804dSStefan Eßer 128044d4804dSStefan Eßer // True if we already have a reference vector. This is slightly 128144d4804dSStefan Eßer // (okay, a lot; it just doesn't look that way) different from 128244d4804dSStefan Eßer // above. The above means that we need to construct a reference 128344d4804dSStefan Eßer // vector, whereas this means that we have one and we might have to 128444d4804dSStefan Eßer // *dereference* it. 1285252884aeSStefan Eßer ref_size = (v->size == sizeof(uchar)); 1286252884aeSStefan Eßer 128744d4804dSStefan Eßer // If we *should* have a reference. 128878bc019dSStefan Eßer if (ref || (ref_size && t == BC_TYPE_REF)) 128978bc019dSStefan Eßer { 129044d4804dSStefan Eßer // Create a new reference vector. 129144d4804dSStefan Eßer bc_vec_init(rv, sizeof(uchar), BC_DTOR_NONE); 1292252884aeSStefan Eßer 129344d4804dSStefan Eßer // If this is true, then we need to construct a reference. 129478bc019dSStefan Eßer if (ref) 129578bc019dSStefan Eßer { 1296252884aeSStefan Eßer // Make sure the pointer was not invalidated. 1297252884aeSStefan Eßer vec = bc_program_vec(p, idx, t); 1298252884aeSStefan Eßer 129944d4804dSStefan Eßer // Push the indices onto the reference vector. This takes 130044d4804dSStefan Eßer // care of last; it ensures the reference goes to the right 130144d4804dSStefan Eßer // place. 1302252884aeSStefan Eßer bc_vec_pushIndex(rv, ptr->d.loc.loc); 1303d101cdd6SStefan Eßer bc_vec_pushIndex(rv, ptr->d.loc.stack_idx); 1304252884aeSStefan Eßer } 130544d4804dSStefan Eßer // If we get here, we are copying a ref to a ref. Just push a 130644d4804dSStefan Eßer // copy of all of the bytes. 1307252884aeSStefan Eßer else bc_vec_npush(rv, v->len * sizeof(uchar), v->v); 1308252884aeSStefan Eßer 130944d4804dSStefan Eßer // Push the reference vector onto the array stack and pop the 131044d4804dSStefan Eßer // source. 1311252884aeSStefan Eßer bc_vec_push(vec, &r.d); 1312252884aeSStefan Eßer bc_vec_pop(&p->results); 1313252884aeSStefan Eßer 131444d4804dSStefan Eßer // We need to return early to avoid executing code that we must 131544d4804dSStefan Eßer // not touch. 1316252884aeSStefan Eßer BC_SIG_UNLOCK; 1317252884aeSStefan Eßer return; 1318252884aeSStefan Eßer } 131944d4804dSStefan Eßer // If we get here, we have a reference, but we need an array, so 132044d4804dSStefan Eßer // dereference the array. 132144d4804dSStefan Eßer else if (ref_size && t != BC_TYPE_REF) 132278bc019dSStefan Eßer { 132344d4804dSStefan Eßer v = bc_program_dereference(p, v); 132444d4804dSStefan Eßer } 132578bc019dSStefan Eßer } 1326252884aeSStefan Eßer #endif // BC_ENABLED 1327252884aeSStefan Eßer 132844d4804dSStefan Eßer // If we get here, we need to copy the array because in bc, all 132944d4804dSStefan Eßer // arguments are passed by value. Yes, this is expensive. 1330252884aeSStefan Eßer bc_array_init(rv, true); 1331252884aeSStefan Eßer bc_array_copy(rv, v); 1332252884aeSStefan Eßer } 1333252884aeSStefan Eßer 133444d4804dSStefan Eßer // Push the vector onto the array stack and pop the source. 1335252884aeSStefan Eßer bc_vec_push(vec, &r.d); 1336252884aeSStefan Eßer bc_vec_pop(&p->results); 1337252884aeSStefan Eßer 1338252884aeSStefan Eßer BC_SIG_UNLOCK; 1339252884aeSStefan Eßer } 1340252884aeSStefan Eßer 134178bc019dSStefan Eßer void 134278bc019dSStefan Eßer bc_program_assignBuiltin(BcProgram* p, bool scale, bool obase, BcBigDig val) 134378bc019dSStefan Eßer { 134478bc019dSStefan Eßer BcBigDig* ptr_t; 134578bc019dSStefan Eßer BcBigDig max, min; 1346d101cdd6SStefan Eßer #if BC_ENABLED 1347d101cdd6SStefan Eßer BcVec* v; 1348d101cdd6SStefan Eßer BcBigDig* ptr; 1349d101cdd6SStefan Eßer #endif // BC_ENABLED 135078bc019dSStefan Eßer 135178bc019dSStefan Eßer assert(!scale || !obase); 135278bc019dSStefan Eßer 135378bc019dSStefan Eßer // Scale needs handling separate from ibase and obase. 135478bc019dSStefan Eßer if (scale) 135578bc019dSStefan Eßer { 135678bc019dSStefan Eßer // Set the min and max. 135778bc019dSStefan Eßer min = 0; 1358d101cdd6SStefan Eßer max = vm->maxes[BC_PROG_GLOBALS_SCALE]; 135978bc019dSStefan Eßer 1360d101cdd6SStefan Eßer #if BC_ENABLED 1361d101cdd6SStefan Eßer // Get a pointer to the stack. 136278bc019dSStefan Eßer v = p->globals_v + BC_PROG_GLOBALS_SCALE; 1363d101cdd6SStefan Eßer #endif // BC_ENABLED 1364d101cdd6SStefan Eßer 1365d101cdd6SStefan Eßer // Get a pointer to the current value. 136678bc019dSStefan Eßer ptr_t = p->globals + BC_PROG_GLOBALS_SCALE; 136778bc019dSStefan Eßer } 136878bc019dSStefan Eßer else 136978bc019dSStefan Eßer { 137078bc019dSStefan Eßer // Set the min and max. 137178bc019dSStefan Eßer min = BC_NUM_MIN_BASE; 137278bc019dSStefan Eßer if (BC_ENABLE_EXTRA_MATH && obase && (BC_IS_DC || !BC_IS_POSIX)) 137378bc019dSStefan Eßer { 137478bc019dSStefan Eßer min = 0; 137578bc019dSStefan Eßer } 1376d101cdd6SStefan Eßer max = vm->maxes[obase + BC_PROG_GLOBALS_IBASE]; 137778bc019dSStefan Eßer 1378d101cdd6SStefan Eßer #if BC_ENABLED 1379d101cdd6SStefan Eßer // Get a pointer to the stack. 138078bc019dSStefan Eßer v = p->globals_v + BC_PROG_GLOBALS_IBASE + obase; 1381d101cdd6SStefan Eßer #endif // BC_ENABLED 1382d101cdd6SStefan Eßer 1383d101cdd6SStefan Eßer // Get a pointer to the current value. 138478bc019dSStefan Eßer ptr_t = p->globals + BC_PROG_GLOBALS_IBASE + obase; 138578bc019dSStefan Eßer } 138678bc019dSStefan Eßer 138778bc019dSStefan Eßer // Check for error. 138878bc019dSStefan Eßer if (BC_ERR(val > max || val < min)) 138978bc019dSStefan Eßer { 139078bc019dSStefan Eßer BcErr e; 139178bc019dSStefan Eßer 139278bc019dSStefan Eßer // This grabs the right error. 139378bc019dSStefan Eßer if (scale) e = BC_ERR_EXEC_SCALE; 139478bc019dSStefan Eßer else if (obase) e = BC_ERR_EXEC_OBASE; 139578bc019dSStefan Eßer else e = BC_ERR_EXEC_IBASE; 139678bc019dSStefan Eßer 139778bc019dSStefan Eßer bc_verr(e, min, max); 139878bc019dSStefan Eßer } 139978bc019dSStefan Eßer 1400d101cdd6SStefan Eßer #if BC_ENABLED 1401d101cdd6SStefan Eßer // Set the top of the stack. 140278bc019dSStefan Eßer ptr = bc_vec_top(v); 140378bc019dSStefan Eßer *ptr = val; 1404d101cdd6SStefan Eßer #endif // BC_ENABLED 1405d101cdd6SStefan Eßer 1406d101cdd6SStefan Eßer // Set the actual global variable. 140778bc019dSStefan Eßer *ptr_t = val; 140878bc019dSStefan Eßer } 140978bc019dSStefan Eßer 141078bc019dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 141178bc019dSStefan Eßer void 141278bc019dSStefan Eßer bc_program_assignSeed(BcProgram* p, BcNum* val) 141378bc019dSStefan Eßer { 141478bc019dSStefan Eßer bc_num_rng(val, &p->rng); 141578bc019dSStefan Eßer } 141678bc019dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 141778bc019dSStefan Eßer 141844d4804dSStefan Eßer /** 141944d4804dSStefan Eßer * Executes an assignment operator. 142044d4804dSStefan Eßer * @param p The program. 142144d4804dSStefan Eßer * @param inst The assignment operator to execute. 142244d4804dSStefan Eßer */ 142378bc019dSStefan Eßer static void 142478bc019dSStefan Eßer bc_program_assign(BcProgram* p, uchar inst) 142578bc019dSStefan Eßer { 142644d4804dSStefan Eßer // The local use_val is true when the assigned value needs to be copied. 142778bc019dSStefan Eßer BcResult* left; 142878bc019dSStefan Eßer BcResult* right; 142978bc019dSStefan Eßer BcResult res; 143078bc019dSStefan Eßer BcNum* l; 143178bc019dSStefan Eßer BcNum* r; 1432252884aeSStefan Eßer bool ob, sc, use_val = BC_INST_USE_VAL(inst); 1433252884aeSStefan Eßer 1434252884aeSStefan Eßer bc_program_assignPrep(p, &left, &l, &right, &r); 1435252884aeSStefan Eßer 143644d4804dSStefan Eßer // Assigning to a string should be impossible simply because of the parse. 1437252884aeSStefan Eßer assert(left->t != BC_RESULT_STR); 1438252884aeSStefan Eßer 143944d4804dSStefan Eßer // If we are assigning a string... 144078bc019dSStefan Eßer if (right->t == BC_RESULT_STR) 144178bc019dSStefan Eßer { 144244d4804dSStefan Eßer assert(BC_PROG_STR(r)); 1443252884aeSStefan Eßer 144444d4804dSStefan Eßer #if BC_ENABLED 144544d4804dSStefan Eßer if (inst != BC_INST_ASSIGN && inst != BC_INST_ASSIGN_NO_VAL) 144678bc019dSStefan Eßer { 144744d4804dSStefan Eßer bc_err(BC_ERR_EXEC_TYPE); 144878bc019dSStefan Eßer } 144944d4804dSStefan Eßer #endif // BC_ENABLED 145044d4804dSStefan Eßer 145144d4804dSStefan Eßer // If we are assigning to an array element... 145278bc019dSStefan Eßer if (left->t == BC_RESULT_ARRAY_ELEM) 145378bc019dSStefan Eßer { 1454252884aeSStefan Eßer BC_SIG_LOCK; 145544d4804dSStefan Eßer 145644d4804dSStefan Eßer // We need to free the number and clear it. 1457252884aeSStefan Eßer bc_num_free(l); 145844d4804dSStefan Eßer 145978bc019dSStefan Eßer // NOLINTNEXTLINE 146044d4804dSStefan Eßer memcpy(l, r, sizeof(BcNum)); 146144d4804dSStefan Eßer 146244d4804dSStefan Eßer // Now we can pop the results. 1463252884aeSStefan Eßer bc_vec_npop(&p->results, 2); 146444d4804dSStefan Eßer 1465252884aeSStefan Eßer BC_SIG_UNLOCK; 1466252884aeSStefan Eßer } 146778bc019dSStefan Eßer else 146878bc019dSStefan Eßer { 146944d4804dSStefan Eßer // If we get here, we are assigning to a variable, which we can use 147044d4804dSStefan Eßer // bc_program_assignStr() for. 1471252884aeSStefan Eßer BcVec* v = bc_program_vec(p, left->d.loc.loc, BC_TYPE_VAR); 147244d4804dSStefan Eßer bc_program_assignStr(p, r, v, false); 1473252884aeSStefan Eßer } 1474252884aeSStefan Eßer 147544d4804dSStefan Eßer #if BC_ENABLED 147644d4804dSStefan Eßer 147744d4804dSStefan Eßer // If this is true, the value is going to be used again, so we want to 147844d4804dSStefan Eßer // push a temporary with the string. 147978bc019dSStefan Eßer if (inst == BC_INST_ASSIGN) 148078bc019dSStefan Eßer { 148144d4804dSStefan Eßer res.t = BC_RESULT_STR; 148278bc019dSStefan Eßer // NOLINTNEXTLINE 148344d4804dSStefan Eßer memcpy(&res.d.n, r, sizeof(BcNum)); 148444d4804dSStefan Eßer bc_vec_push(&p->results, &res); 148544d4804dSStefan Eßer } 148644d4804dSStefan Eßer 148744d4804dSStefan Eßer #endif // BC_ENABLED 148844d4804dSStefan Eßer 148944d4804dSStefan Eßer // By using bc_program_assignStr(), we short-circuited this, so return. 1490252884aeSStefan Eßer return; 1491252884aeSStefan Eßer } 1492252884aeSStefan Eßer 149344d4804dSStefan Eßer // If we have a normal assignment operator, not a math one... 149478bc019dSStefan Eßer if (BC_INST_IS_ASSIGN(inst)) 149578bc019dSStefan Eßer { 149644d4804dSStefan Eßer // Assigning to a variable that has a string here is fine because there 149744d4804dSStefan Eßer // is no math done on it. 149844d4804dSStefan Eßer 149944d4804dSStefan Eßer // BC_RESULT_TEMP, BC_RESULT_IBASE, BC_RESULT_OBASE, BC_RESULT_SCALE, 150044d4804dSStefan Eßer // and BC_RESULT_SEED all have temporary copies. Because that's the 150144d4804dSStefan Eßer // case, we can free the left and just move the value over. We set the 150244d4804dSStefan Eßer // type of right to BC_RESULT_ZERO in order to prevent it from being 150344d4804dSStefan Eßer // freed. We also don't have to worry about BC_RESULT_STR because it's 150444d4804dSStefan Eßer // take care of above. 150578bc019dSStefan Eßer if (right->t == BC_RESULT_TEMP || right->t >= BC_RESULT_IBASE) 150678bc019dSStefan Eßer { 150744d4804dSStefan Eßer BC_SIG_LOCK; 150844d4804dSStefan Eßer 150944d4804dSStefan Eßer bc_num_free(l); 151078bc019dSStefan Eßer // NOLINTNEXTLINE 151144d4804dSStefan Eßer memcpy(l, r, sizeof(BcNum)); 151244d4804dSStefan Eßer right->t = BC_RESULT_ZERO; 151344d4804dSStefan Eßer 151444d4804dSStefan Eßer BC_SIG_UNLOCK; 151544d4804dSStefan Eßer } 151644d4804dSStefan Eßer // Copy over. 151744d4804dSStefan Eßer else bc_num_copy(l, r); 151844d4804dSStefan Eßer } 1519252884aeSStefan Eßer #if BC_ENABLED 152078bc019dSStefan Eßer else 152178bc019dSStefan Eßer { 152244d4804dSStefan Eßer // If we get here, we are doing a math assignment (+=, -=, etc.). So 152344d4804dSStefan Eßer // we need to prepare for a binary operator. 1524252884aeSStefan Eßer BcBigDig scale = BC_PROG_SCALE(p); 1525252884aeSStefan Eßer 152644d4804dSStefan Eßer // At this point, the left side could still be a string because it could 152744d4804dSStefan Eßer // be a variable that has the string. If that's the case, we have a type 152844d4804dSStefan Eßer // error. 152944d4804dSStefan Eßer if (BC_PROG_STR(l)) bc_err(BC_ERR_EXEC_TYPE); 153044d4804dSStefan Eßer 153144d4804dSStefan Eßer // Get the right type of assignment operator, whether val is used or 153244d4804dSStefan Eßer // NO_VAL for performance. 1533252884aeSStefan Eßer if (!use_val) 153478bc019dSStefan Eßer { 1535252884aeSStefan Eßer inst -= (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER); 153678bc019dSStefan Eßer } 1537252884aeSStefan Eßer 153850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(l)); 153950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(r)); 154050696a6eSStefan Eßer 154144d4804dSStefan Eßer // Run the actual operation. We do not need worry about reallocating l 154244d4804dSStefan Eßer // because bc_num_binary() does that behind the scenes for us. 1543252884aeSStefan Eßer bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, scale); 1544252884aeSStefan Eßer } 1545252884aeSStefan Eßer #endif // BC_ENABLED 1546252884aeSStefan Eßer 1547252884aeSStefan Eßer ob = (left->t == BC_RESULT_OBASE); 1548252884aeSStefan Eßer sc = (left->t == BC_RESULT_SCALE); 1549252884aeSStefan Eßer 155044d4804dSStefan Eßer // The globals need special handling, especially the non-seed ones. The 155144d4804dSStefan Eßer // first part of the if statement handles them. 155278bc019dSStefan Eßer if (ob || sc || left->t == BC_RESULT_IBASE) 155378bc019dSStefan Eßer { 155444d4804dSStefan Eßer // Get the actual value. 155578bc019dSStefan Eßer BcBigDig val = bc_num_bigdig(l); 1556252884aeSStefan Eßer 155778bc019dSStefan Eßer bc_program_assignBuiltin(p, sc, ob, val); 1558252884aeSStefan Eßer } 155944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 156044d4804dSStefan Eßer // To assign to steed, let bc_num_rng() do its magic. 156178bc019dSStefan Eßer else if (left->t == BC_RESULT_SEED) bc_program_assignSeed(p, l); 156244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1563252884aeSStefan Eßer 1564252884aeSStefan Eßer BC_SIG_LOCK; 1565252884aeSStefan Eßer 156644d4804dSStefan Eßer // If we needed to use the value, then we need to copy it. Otherwise, we can 156744d4804dSStefan Eßer // pop indiscriminately. Oh, and the copy should be a BC_RESULT_TEMP. 156878bc019dSStefan Eßer if (use_val) 156978bc019dSStefan Eßer { 1570252884aeSStefan Eßer bc_num_createCopy(&res.d.n, l); 1571252884aeSStefan Eßer res.t = BC_RESULT_TEMP; 1572252884aeSStefan Eßer bc_vec_npop(&p->results, 2); 1573252884aeSStefan Eßer bc_vec_push(&p->results, &res); 1574252884aeSStefan Eßer } 1575252884aeSStefan Eßer else bc_vec_npop(&p->results, 2); 1576252884aeSStefan Eßer 1577252884aeSStefan Eßer BC_SIG_UNLOCK; 1578252884aeSStefan Eßer } 1579252884aeSStefan Eßer 158044d4804dSStefan Eßer /** 158144d4804dSStefan Eßer * Pushes a variable's value onto the results stack. 158244d4804dSStefan Eßer * @param p The program. 158344d4804dSStefan Eßer * @param code The bytecode vector to pull the variable's index out of. 158444d4804dSStefan Eßer * @param bgn An in/out parameter; the start of the index in the bytecode 158544d4804dSStefan Eßer * vector, and will be updated to point after the index on return. 158644d4804dSStefan Eßer * @param pop True if the variable's value should be popped off its stack. 158744d4804dSStefan Eßer * This is only used in dc. 158844d4804dSStefan Eßer * @param copy True if the variable's value should be copied to the results 158944d4804dSStefan Eßer * stack. This is only used in dc. 159044d4804dSStefan Eßer */ 159178bc019dSStefan Eßer static void 159278bc019dSStefan Eßer bc_program_pushVar(BcProgram* p, const char* restrict code, 1593252884aeSStefan Eßer size_t* restrict bgn, bool pop, bool copy) 1594252884aeSStefan Eßer { 1595252884aeSStefan Eßer BcResult r; 1596252884aeSStefan Eßer size_t idx = bc_program_index(code, bgn); 1597d101cdd6SStefan Eßer BcVec* v; 1598252884aeSStefan Eßer 159944d4804dSStefan Eßer // Set the result appropriately. 1600252884aeSStefan Eßer r.t = BC_RESULT_VAR; 1601252884aeSStefan Eßer r.d.loc.loc = idx; 1602252884aeSStefan Eßer 1603d101cdd6SStefan Eßer // Get the stack for the variable. This is used in both bc and dc. 1604d101cdd6SStefan Eßer v = bc_program_vec(p, idx, BC_TYPE_VAR); 1605d101cdd6SStefan Eßer r.d.loc.stack_idx = v->len - 1; 1606d101cdd6SStefan Eßer 1607252884aeSStefan Eßer #if DC_ENABLED 160844d4804dSStefan Eßer // If this condition is true, then we have the hard case, where we have to 160944d4804dSStefan Eßer // adjust dc registers. 161078bc019dSStefan Eßer if (BC_IS_DC && (pop || copy)) 161178bc019dSStefan Eßer { 1612d101cdd6SStefan Eßer // Get the number at the top at the top of the stack. 1613252884aeSStefan Eßer BcNum* num = bc_vec_top(v); 1614252884aeSStefan Eßer 161544d4804dSStefan Eßer // Ensure there are enough elements on the stack. 161678bc019dSStefan Eßer if (BC_ERR(!BC_PROG_STACK(v, 2 - copy))) 161778bc019dSStefan Eßer { 161844d4804dSStefan Eßer const char* name = bc_map_name(&p->var_map, idx); 161944d4804dSStefan Eßer bc_verr(BC_ERR_EXEC_STACK_REGISTER, name); 162044d4804dSStefan Eßer } 1621252884aeSStefan Eßer 1622252884aeSStefan Eßer assert(BC_PROG_STACK(v, 2 - copy)); 1623252884aeSStefan Eßer 162444d4804dSStefan Eßer // If the top of the stack is actually a number... 162578bc019dSStefan Eßer if (!BC_PROG_STR(num)) 162678bc019dSStefan Eßer { 1627252884aeSStefan Eßer BC_SIG_LOCK; 1628252884aeSStefan Eßer 162944d4804dSStefan Eßer // Create a copy to go onto the results stack as appropriate. 1630252884aeSStefan Eßer r.t = BC_RESULT_TEMP; 1631252884aeSStefan Eßer bc_num_createCopy(&r.d.n, num); 1632252884aeSStefan Eßer 163344d4804dSStefan Eßer // If we are not actually copying, we need to do a replace, so pop. 1634252884aeSStefan Eßer if (!copy) bc_vec_pop(v); 1635252884aeSStefan Eßer 1636252884aeSStefan Eßer bc_vec_push(&p->results, &r); 1637252884aeSStefan Eßer 1638252884aeSStefan Eßer BC_SIG_UNLOCK; 1639252884aeSStefan Eßer 1640252884aeSStefan Eßer return; 1641252884aeSStefan Eßer } 164278bc019dSStefan Eßer else 164378bc019dSStefan Eßer { 164444d4804dSStefan Eßer // Set the string result. We can just memcpy because all of the 164544d4804dSStefan Eßer // fields in the num should be cleared. 164678bc019dSStefan Eßer // NOLINTNEXTLINE 164744d4804dSStefan Eßer memcpy(&r.d.n, num, sizeof(BcNum)); 1648252884aeSStefan Eßer r.t = BC_RESULT_STR; 1649252884aeSStefan Eßer } 1650252884aeSStefan Eßer 165144d4804dSStefan Eßer // If we are not actually copying, we need to do a replace, so pop. 1652252884aeSStefan Eßer if (!copy) bc_vec_pop(v); 1653252884aeSStefan Eßer } 1654252884aeSStefan Eßer #endif // DC_ENABLED 1655252884aeSStefan Eßer 1656252884aeSStefan Eßer bc_vec_push(&p->results, &r); 1657252884aeSStefan Eßer } 1658252884aeSStefan Eßer 165944d4804dSStefan Eßer /** 166044d4804dSStefan Eßer * Pushes an array or an array element onto the results stack. 166144d4804dSStefan Eßer * @param p The program. 166244d4804dSStefan Eßer * @param code The bytecode vector to pull the variable's index out of. 166344d4804dSStefan Eßer * @param bgn An in/out parameter; the start of the index in the bytecode 166444d4804dSStefan Eßer * vector, and will be updated to point after the index on return. 166544d4804dSStefan Eßer * @param inst The instruction; whether to push an array or an array element. 166644d4804dSStefan Eßer */ 166778bc019dSStefan Eßer static void 166878bc019dSStefan Eßer bc_program_pushArray(BcProgram* p, const char* restrict code, 1669252884aeSStefan Eßer size_t* restrict bgn, uchar inst) 1670252884aeSStefan Eßer { 167178bc019dSStefan Eßer BcResult r; 167278bc019dSStefan Eßer BcResult* operand; 1673252884aeSStefan Eßer BcNum* num; 1674252884aeSStefan Eßer BcBigDig temp; 1675d101cdd6SStefan Eßer BcVec* v; 1676252884aeSStefan Eßer 167744d4804dSStefan Eßer // Get the index of the array. 1678252884aeSStefan Eßer r.d.loc.loc = bc_program_index(code, bgn); 1679252884aeSStefan Eßer 1680d101cdd6SStefan Eßer // We need the array to get its length. 1681d101cdd6SStefan Eßer v = bc_program_vec(p, r.d.loc.loc, BC_TYPE_ARRAY); 1682d101cdd6SStefan Eßer assert(v != NULL); 1683d101cdd6SStefan Eßer 1684d101cdd6SStefan Eßer r.d.loc.stack_idx = v->len - 1; 1685d101cdd6SStefan Eßer 168644d4804dSStefan Eßer // Doing an array is easy; just set the result type and finish. 168778bc019dSStefan Eßer if (inst == BC_INST_ARRAY) 168878bc019dSStefan Eßer { 1689252884aeSStefan Eßer r.t = BC_RESULT_ARRAY; 1690252884aeSStefan Eßer bc_vec_push(&p->results, &r); 1691252884aeSStefan Eßer return; 1692252884aeSStefan Eßer } 1693252884aeSStefan Eßer 169444d4804dSStefan Eßer // Grab the top element of the results stack for the array index. 1695252884aeSStefan Eßer bc_program_prep(p, &operand, &num, 0); 169644d4804dSStefan Eßer temp = bc_num_bigdig(num); 1697252884aeSStefan Eßer 169844d4804dSStefan Eßer // Set the result. 1699252884aeSStefan Eßer r.t = BC_RESULT_ARRAY_ELEM; 1700252884aeSStefan Eßer r.d.loc.idx = (size_t) temp; 17013aa99676SStefan Eßer 17023aa99676SStefan Eßer BC_SIG_LOCK; 17033aa99676SStefan Eßer 170444d4804dSStefan Eßer // Pop the index and push the element. 1705252884aeSStefan Eßer bc_vec_pop(&p->results); 1706252884aeSStefan Eßer bc_vec_push(&p->results, &r); 17073aa99676SStefan Eßer 17083aa99676SStefan Eßer BC_SIG_UNLOCK; 1709252884aeSStefan Eßer } 1710252884aeSStefan Eßer 1711252884aeSStefan Eßer #if BC_ENABLED 171244d4804dSStefan Eßer 171344d4804dSStefan Eßer /** 171444d4804dSStefan Eßer * Executes an increment or decrement operator. This only handles postfix 171544d4804dSStefan Eßer * inc/dec because the parser translates prefix inc/dec into an assignment where 171644d4804dSStefan Eßer * the value is used. 171744d4804dSStefan Eßer * @param p The program. 171844d4804dSStefan Eßer * @param inst The instruction; whether to do an increment or decrement. 171944d4804dSStefan Eßer */ 172078bc019dSStefan Eßer static void 172178bc019dSStefan Eßer bc_program_incdec(BcProgram* p, uchar inst) 172278bc019dSStefan Eßer { 1723252884aeSStefan Eßer BcResult *ptr, res, copy; 1724252884aeSStefan Eßer BcNum* num; 1725252884aeSStefan Eßer uchar inst2; 1726252884aeSStefan Eßer 1727252884aeSStefan Eßer bc_program_prep(p, &ptr, &num, 0); 1728252884aeSStefan Eßer 1729252884aeSStefan Eßer BC_SIG_LOCK; 1730252884aeSStefan Eßer 173144d4804dSStefan Eßer // We need a copy from *before* the operation. 1732252884aeSStefan Eßer copy.t = BC_RESULT_TEMP; 1733252884aeSStefan Eßer bc_num_createCopy(©.d.n, num); 1734252884aeSStefan Eßer 1735d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, exit); 1736252884aeSStefan Eßer 1737252884aeSStefan Eßer BC_SIG_UNLOCK; 1738252884aeSStefan Eßer 173944d4804dSStefan Eßer // Create the proper assignment. 1740252884aeSStefan Eßer res.t = BC_RESULT_ONE; 174144d4804dSStefan Eßer inst2 = BC_INST_ASSIGN_PLUS_NO_VAL + (inst & 0x01); 1742252884aeSStefan Eßer 1743252884aeSStefan Eßer bc_vec_push(&p->results, &res); 1744252884aeSStefan Eßer bc_program_assign(p, inst2); 1745252884aeSStefan Eßer 1746252884aeSStefan Eßer BC_SIG_LOCK; 1747252884aeSStefan Eßer 1748252884aeSStefan Eßer bc_vec_push(&p->results, ©); 1749252884aeSStefan Eßer 1750d101cdd6SStefan Eßer BC_UNSETJMP(vm); 1751252884aeSStefan Eßer 1752252884aeSStefan Eßer BC_SIG_UNLOCK; 1753252884aeSStefan Eßer 175444d4804dSStefan Eßer // No need to free the copy here because we pushed it onto the stack. 1755252884aeSStefan Eßer return; 1756252884aeSStefan Eßer 1757252884aeSStefan Eßer exit: 1758252884aeSStefan Eßer BC_SIG_MAYLOCK; 1759252884aeSStefan Eßer bc_num_free(©.d.n); 1760d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 1761252884aeSStefan Eßer } 1762252884aeSStefan Eßer 176344d4804dSStefan Eßer /** 176444d4804dSStefan Eßer * Executes a function call for bc. 176544d4804dSStefan Eßer * @param p The program. 176644d4804dSStefan Eßer * @param code The bytecode vector to pull the number of arguments and the 176744d4804dSStefan Eßer * function index out of. 176844d4804dSStefan Eßer * @param bgn An in/out parameter; the start of the indices in the bytecode 176944d4804dSStefan Eßer * vector, and will be updated to point after the indices on 177044d4804dSStefan Eßer * return. 177144d4804dSStefan Eßer */ 177278bc019dSStefan Eßer static void 177378bc019dSStefan Eßer bc_program_call(BcProgram* p, const char* restrict code, size_t* restrict bgn) 1774252884aeSStefan Eßer { 1775252884aeSStefan Eßer BcInstPtr ip; 177644d4804dSStefan Eßer size_t i, nargs; 1777252884aeSStefan Eßer BcFunc* f; 1778252884aeSStefan Eßer BcVec* v; 177944d4804dSStefan Eßer BcAuto* a; 1780252884aeSStefan Eßer BcResult* arg; 1781252884aeSStefan Eßer 178244d4804dSStefan Eßer // Pull the number of arguments out of the bytecode vector. 178344d4804dSStefan Eßer nargs = bc_program_index(code, bgn); 178444d4804dSStefan Eßer 178544d4804dSStefan Eßer // Set up instruction pointer. 1786252884aeSStefan Eßer ip.idx = 0; 178744d4804dSStefan Eßer ip.func = bc_program_index(code, bgn); 1788252884aeSStefan Eßer f = bc_vec_item(&p->fns, ip.func); 1789252884aeSStefan Eßer 179044d4804dSStefan Eßer // Error checking. 179144d4804dSStefan Eßer if (BC_ERR(!f->code.len)) bc_verr(BC_ERR_EXEC_UNDEF_FUNC, f->name); 179244d4804dSStefan Eßer if (BC_ERR(nargs != f->nparams)) 179378bc019dSStefan Eßer { 179444d4804dSStefan Eßer bc_verr(BC_ERR_EXEC_PARAMS, f->nparams, nargs); 179578bc019dSStefan Eßer } 1796252884aeSStefan Eßer 179744d4804dSStefan Eßer // Set the length of the results stack. We discount the argument, of course. 179844d4804dSStefan Eßer ip.len = p->results.len - nargs; 1799252884aeSStefan Eßer 180044d4804dSStefan Eßer assert(BC_PROG_STACK(&p->results, nargs)); 180144d4804dSStefan Eßer 180244d4804dSStefan Eßer // Prepare the globals' stacks. 1803252884aeSStefan Eßer if (BC_G) bc_program_prepGlobals(p); 1804252884aeSStefan Eßer 180544d4804dSStefan Eßer // Push the arguments onto the stacks of their respective parameters. 180678bc019dSStefan Eßer for (i = 0; i < nargs; ++i) 180778bc019dSStefan Eßer { 1808252884aeSStefan Eßer arg = bc_vec_top(&p->results); 180944d4804dSStefan Eßer if (BC_ERR(arg->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL); 1810252884aeSStefan Eßer 181144d4804dSStefan Eßer // Get the corresponding parameter. 181244d4804dSStefan Eßer a = bc_vec_item(&f->autos, nargs - 1 - i); 1813252884aeSStefan Eßer 181444d4804dSStefan Eßer // Actually push the value onto the parameter's stack. 1815d101cdd6SStefan Eßer bc_program_copyToVar(p, a->idx, a->type); 1816252884aeSStefan Eßer } 1817252884aeSStefan Eßer 1818252884aeSStefan Eßer BC_SIG_LOCK; 1819252884aeSStefan Eßer 182044d4804dSStefan Eßer // Push zeroes onto the stacks of the auto variables. 182178bc019dSStefan Eßer for (; i < f->autos.len; ++i) 182278bc019dSStefan Eßer { 182344d4804dSStefan Eßer // Get the auto and its stack. 1824252884aeSStefan Eßer a = bc_vec_item(&f->autos, i); 182544d4804dSStefan Eßer v = bc_program_vec(p, a->idx, a->type); 1826252884aeSStefan Eßer 182744d4804dSStefan Eßer // If a variable, just push a 0; otherwise, push an array. 182878bc019dSStefan Eßer if (a->type == BC_TYPE_VAR) 182978bc019dSStefan Eßer { 183044d4804dSStefan Eßer BcNum* n = bc_vec_pushEmpty(v); 183144d4804dSStefan Eßer bc_num_init(n, BC_NUM_DEF_SIZE); 1832252884aeSStefan Eßer } 183378bc019dSStefan Eßer else 183478bc019dSStefan Eßer { 183544d4804dSStefan Eßer BcVec* v2; 183644d4804dSStefan Eßer 183744d4804dSStefan Eßer assert(a->type == BC_TYPE_ARRAY); 183844d4804dSStefan Eßer 183944d4804dSStefan Eßer v2 = bc_vec_pushEmpty(v); 184044d4804dSStefan Eßer bc_array_init(v2, true); 1841252884aeSStefan Eßer } 1842252884aeSStefan Eßer } 1843252884aeSStefan Eßer 184444d4804dSStefan Eßer // Push the instruction pointer onto the execution stack. 1845252884aeSStefan Eßer bc_vec_push(&p->stack, &ip); 1846252884aeSStefan Eßer 1847252884aeSStefan Eßer BC_SIG_UNLOCK; 1848252884aeSStefan Eßer } 1849252884aeSStefan Eßer 185044d4804dSStefan Eßer /** 185144d4804dSStefan Eßer * Executes a return instruction. 185244d4804dSStefan Eßer * @param p The program. 185344d4804dSStefan Eßer * @param inst The return instruction. bc can return void, and we need to know 185444d4804dSStefan Eßer * if it is. 185544d4804dSStefan Eßer */ 185678bc019dSStefan Eßer static void 185778bc019dSStefan Eßer bc_program_return(BcProgram* p, uchar inst) 185878bc019dSStefan Eßer { 1859252884aeSStefan Eßer BcResult* res; 1860252884aeSStefan Eßer BcFunc* f; 186144d4804dSStefan Eßer BcInstPtr* ip; 186244d4804dSStefan Eßer size_t i, nresults; 1863252884aeSStefan Eßer 186444d4804dSStefan Eßer // Get the instruction pointer. 186544d4804dSStefan Eßer ip = bc_vec_top(&p->stack); 186644d4804dSStefan Eßer 186744d4804dSStefan Eßer // Get the difference between the actual number of results and the number of 186844d4804dSStefan Eßer // results the caller expects. 186944d4804dSStefan Eßer nresults = p->results.len - ip->len; 187044d4804dSStefan Eßer 187144d4804dSStefan Eßer // If this isn't true, there was a missing call somewhere. 1872252884aeSStefan Eßer assert(BC_PROG_STACK(&p->stack, 2)); 187344d4804dSStefan Eßer 187444d4804dSStefan Eßer // If this isn't true, the parser screwed by giving us no value when we 187544d4804dSStefan Eßer // expected one, or giving us a value when we expected none. 1876252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, ip->len + (inst == BC_INST_RET))); 1877252884aeSStefan Eßer 187844d4804dSStefan Eßer // Get the function we are returning from. 1879252884aeSStefan Eßer f = bc_vec_item(&p->fns, ip->func); 188044d4804dSStefan Eßer 1881252884aeSStefan Eßer res = bc_program_prepResult(p); 1882252884aeSStefan Eßer 188344d4804dSStefan Eßer // If we are returning normally... 188478bc019dSStefan Eßer if (inst == BC_INST_RET) 188578bc019dSStefan Eßer { 1886252884aeSStefan Eßer BcNum* num; 1887252884aeSStefan Eßer BcResult* operand; 1888252884aeSStefan Eßer 188944d4804dSStefan Eßer // Prepare and copy the return value. 1890252884aeSStefan Eßer bc_program_operand(p, &operand, &num, 1); 1891252884aeSStefan Eßer 189278bc019dSStefan Eßer if (BC_PROG_STR(num)) 189378bc019dSStefan Eßer { 189444d4804dSStefan Eßer // We need to set this because otherwise, it will be a 189544d4804dSStefan Eßer // BC_RESULT_TEMP, and BC_RESULT_TEMP needs an actual number to make 189644d4804dSStefan Eßer // it easier to do type checking. 189744d4804dSStefan Eßer res->t = BC_RESULT_STR; 189844d4804dSStefan Eßer 189978bc019dSStefan Eßer // NOLINTNEXTLINE 190044d4804dSStefan Eßer memcpy(&res->d.n, num, sizeof(BcNum)); 190144d4804dSStefan Eßer } 190278bc019dSStefan Eßer else 190378bc019dSStefan Eßer { 1904252884aeSStefan Eßer BC_SIG_LOCK; 1905252884aeSStefan Eßer 1906252884aeSStefan Eßer bc_num_createCopy(&res->d.n, num); 1907252884aeSStefan Eßer } 190844d4804dSStefan Eßer } 190944d4804dSStefan Eßer // Void is easy; set the result. 1910252884aeSStefan Eßer else if (inst == BC_INST_RET_VOID) res->t = BC_RESULT_VOID; 191178bc019dSStefan Eßer else 191278bc019dSStefan Eßer { 1913252884aeSStefan Eßer BC_SIG_LOCK; 191444d4804dSStefan Eßer 191544d4804dSStefan Eßer // If we get here, the instruction is for returning a zero, so do that. 1916252884aeSStefan Eßer bc_num_init(&res->d.n, BC_NUM_DEF_SIZE); 1917252884aeSStefan Eßer } 1918252884aeSStefan Eßer 1919252884aeSStefan Eßer BC_SIG_MAYUNLOCK; 1920252884aeSStefan Eßer 192144d4804dSStefan Eßer // We need to pop items off of the stacks of arguments and autos as well. 192278bc019dSStefan Eßer for (i = 0; i < f->autos.len; ++i) 192378bc019dSStefan Eßer { 192444d4804dSStefan Eßer BcAuto* a = bc_vec_item(&f->autos, i); 192544d4804dSStefan Eßer BcVec* v = bc_program_vec(p, a->idx, a->type); 1926252884aeSStefan Eßer 1927252884aeSStefan Eßer bc_vec_pop(v); 1928252884aeSStefan Eßer } 1929252884aeSStefan Eßer 193010041e99SStefan Eßer BC_SIG_LOCK; 193110041e99SStefan Eßer 193244d4804dSStefan Eßer // When we retire, pop all of the unused results. 193344d4804dSStefan Eßer bc_program_retire(p, 1, nresults); 1934252884aeSStefan Eßer 193544d4804dSStefan Eßer // Pop the globals, if necessary. 1936252884aeSStefan Eßer if (BC_G) bc_program_popGlobals(p, false); 1937252884aeSStefan Eßer 193844d4804dSStefan Eßer // Pop the stack. This is what causes the function to actually "return." 1939252884aeSStefan Eßer bc_vec_pop(&p->stack); 194010041e99SStefan Eßer 194110041e99SStefan Eßer BC_SIG_UNLOCK; 1942252884aeSStefan Eßer } 1943252884aeSStefan Eßer #endif // BC_ENABLED 1944252884aeSStefan Eßer 194544d4804dSStefan Eßer /** 194644d4804dSStefan Eßer * Executes a builtin function. 194744d4804dSStefan Eßer * @param p The program. 194844d4804dSStefan Eßer * @param inst The builtin to execute. 194944d4804dSStefan Eßer */ 195078bc019dSStefan Eßer static void 195178bc019dSStefan Eßer bc_program_builtin(BcProgram* p, uchar inst) 195278bc019dSStefan Eßer { 195378bc019dSStefan Eßer BcResult* opd; 195478bc019dSStefan Eßer BcResult* res; 1955252884aeSStefan Eßer BcNum* num; 1956252884aeSStefan Eßer bool len = (inst == BC_INST_LENGTH); 1957252884aeSStefan Eßer 195844d4804dSStefan Eßer // Ensure we have a valid builtin. 195944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 1960252884aeSStefan Eßer assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IRAND); 196144d4804dSStefan Eßer #else // BC_ENABLE_EXTRA_MATH 1962d101cdd6SStefan Eßer assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IS_STRING); 196344d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1964252884aeSStefan Eßer 1965252884aeSStefan Eßer #ifndef BC_PROG_NO_STACK_CHECK 196644d4804dSStefan Eßer // Check stack for dc. 196744d4804dSStefan Eßer if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 1))) 196878bc019dSStefan Eßer { 196944d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 197078bc019dSStefan Eßer } 1971252884aeSStefan Eßer #endif // BC_PROG_NO_STACK_CHECK 1972252884aeSStefan Eßer 1973252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 1974252884aeSStefan Eßer 1975252884aeSStefan Eßer res = bc_program_prepResult(p); 1976252884aeSStefan Eßer 1977252884aeSStefan Eßer bc_program_operand(p, &opd, &num, 1); 1978252884aeSStefan Eßer 1979252884aeSStefan Eßer assert(num != NULL); 1980252884aeSStefan Eßer 198144d4804dSStefan Eßer // We need to ensure that strings and arrays aren't passed to most builtins. 198244d4804dSStefan Eßer // The scale function can take strings in dc. 1983d101cdd6SStefan Eßer if (!len && (inst != BC_INST_SCALE_FUNC || BC_IS_BC) && 1984d101cdd6SStefan Eßer inst != BC_INST_IS_NUMBER && inst != BC_INST_IS_STRING) 198578bc019dSStefan Eßer { 198644d4804dSStefan Eßer bc_program_type_num(opd, num); 198778bc019dSStefan Eßer } 1988252884aeSStefan Eßer 198944d4804dSStefan Eßer // Square root is easy. 1990252884aeSStefan Eßer if (inst == BC_INST_SQRT) bc_num_sqrt(num, &res->d.n, BC_PROG_SCALE(p)); 199144d4804dSStefan Eßer 199244d4804dSStefan Eßer // Absolute value is easy. 199378bc019dSStefan Eßer else if (inst == BC_INST_ABS) 199478bc019dSStefan Eßer { 1995252884aeSStefan Eßer BC_SIG_LOCK; 1996252884aeSStefan Eßer 1997252884aeSStefan Eßer bc_num_createCopy(&res->d.n, num); 1998252884aeSStefan Eßer 1999252884aeSStefan Eßer BC_SIG_UNLOCK; 2000252884aeSStefan Eßer 200150696a6eSStefan Eßer BC_NUM_NEG_CLR_NP(res->d.n); 2002252884aeSStefan Eßer } 2003d101cdd6SStefan Eßer 2004d101cdd6SStefan Eßer // Testing for number or string is easy. 2005d101cdd6SStefan Eßer else if (inst == BC_INST_IS_NUMBER || inst == BC_INST_IS_STRING) 2006d101cdd6SStefan Eßer { 2007d101cdd6SStefan Eßer bool cond; 2008d101cdd6SStefan Eßer bool is_str; 2009d101cdd6SStefan Eßer 2010d101cdd6SStefan Eßer BC_SIG_LOCK; 2011d101cdd6SStefan Eßer 2012d101cdd6SStefan Eßer bc_num_init(&res->d.n, BC_NUM_DEF_SIZE); 2013d101cdd6SStefan Eßer 2014d101cdd6SStefan Eßer BC_SIG_UNLOCK; 2015d101cdd6SStefan Eßer 2016d101cdd6SStefan Eßer // Test if the number is a string. 2017d101cdd6SStefan Eßer is_str = BC_PROG_STR(num); 2018d101cdd6SStefan Eßer 2019d101cdd6SStefan Eßer // This confusing condition simply means that the instruction must be 2020d101cdd6SStefan Eßer // true if is_str is, or it must be false if is_str is. Otherwise, the 2021d101cdd6SStefan Eßer // returned value is false (0). 2022d101cdd6SStefan Eßer cond = ((inst == BC_INST_IS_STRING) == is_str); 2023d101cdd6SStefan Eßer if (cond) bc_num_one(&res->d.n); 2024d101cdd6SStefan Eßer } 2025d101cdd6SStefan Eßer 202644d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2027d101cdd6SStefan Eßer 202844d4804dSStefan Eßer // irand() is easy. 202978bc019dSStefan Eßer else if (inst == BC_INST_IRAND) 203078bc019dSStefan Eßer { 2031252884aeSStefan Eßer BC_SIG_LOCK; 2032252884aeSStefan Eßer 203344d4804dSStefan Eßer bc_num_init(&res->d.n, num->len - BC_NUM_RDX_VAL(num)); 2034252884aeSStefan Eßer 2035252884aeSStefan Eßer BC_SIG_UNLOCK; 2036252884aeSStefan Eßer 2037252884aeSStefan Eßer bc_num_irand(num, &res->d.n, &p->rng); 2038252884aeSStefan Eßer } 2039d101cdd6SStefan Eßer 204044d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 204144d4804dSStefan Eßer 204244d4804dSStefan Eßer // Everything else is...not easy. 204378bc019dSStefan Eßer else 204478bc019dSStefan Eßer { 2045252884aeSStefan Eßer BcBigDig val = 0; 2046252884aeSStefan Eßer 204744d4804dSStefan Eßer // Well, scale() is easy, but length() is not. 204878bc019dSStefan Eßer if (len) 204978bc019dSStefan Eßer { 205044d4804dSStefan Eßer // If we are bc and we have an array... 205178bc019dSStefan Eßer if (opd->t == BC_RESULT_ARRAY) 205278bc019dSStefan Eßer { 205344d4804dSStefan Eßer // Yes, this is one place where we need to cast the number from 205444d4804dSStefan Eßer // bc_program_num() to a vector. 2055252884aeSStefan Eßer BcVec* v = (BcVec*) num; 2056252884aeSStefan Eßer 2057d101cdd6SStefan Eßer // XXX: If this is changed, you should also change the similar 2058d101cdd6SStefan Eßer // code in bc_program_asciify(). 2059d101cdd6SStefan Eßer 206044d4804dSStefan Eßer #if BC_ENABLED 206144d4804dSStefan Eßer // Dereference the array, if necessary. 206244d4804dSStefan Eßer if (BC_IS_BC && v->size == sizeof(uchar)) 206378bc019dSStefan Eßer { 206444d4804dSStefan Eßer v = bc_program_dereference(p, v); 206578bc019dSStefan Eßer } 206644d4804dSStefan Eßer #endif // BC_ENABLED 2067252884aeSStefan Eßer 2068252884aeSStefan Eßer assert(v->size == sizeof(BcNum)); 2069252884aeSStefan Eßer 2070252884aeSStefan Eßer val = (BcBigDig) v->len; 2071252884aeSStefan Eßer } 207278bc019dSStefan Eßer else 207378bc019dSStefan Eßer { 207444d4804dSStefan Eßer // If the item is a string... 207578bc019dSStefan Eßer if (!BC_PROG_NUM(opd, num)) 207678bc019dSStefan Eßer { 2077252884aeSStefan Eßer char* str; 207810328f8bSStefan Eßer 207944d4804dSStefan Eßer // Get the string, then get the length. 208044d4804dSStefan Eßer str = bc_program_string(p, num); 2081252884aeSStefan Eßer val = (BcBigDig) strlen(str); 2082252884aeSStefan Eßer } 2083252884aeSStefan Eßer else 2084252884aeSStefan Eßer { 208544d4804dSStefan Eßer // Calculate the length of the number. 2086252884aeSStefan Eßer val = (BcBigDig) bc_num_len(num); 2087252884aeSStefan Eßer } 2088252884aeSStefan Eßer } 2089252884aeSStefan Eßer } 209044d4804dSStefan Eßer // Like I said; scale() is actually easy. It just also needs the integer 209144d4804dSStefan Eßer // conversion that length() does. 2092252884aeSStefan Eßer else if (BC_IS_BC || BC_PROG_NUM(opd, num)) 209378bc019dSStefan Eßer { 2094252884aeSStefan Eßer val = (BcBigDig) bc_num_scale(num); 209578bc019dSStefan Eßer } 2096252884aeSStefan Eßer 2097252884aeSStefan Eßer BC_SIG_LOCK; 2098252884aeSStefan Eßer 209944d4804dSStefan Eßer // Create the result. 2100252884aeSStefan Eßer bc_num_createFromBigdig(&res->d.n, val); 2101252884aeSStefan Eßer 2102252884aeSStefan Eßer BC_SIG_UNLOCK; 2103252884aeSStefan Eßer } 2104252884aeSStefan Eßer 2105252884aeSStefan Eßer bc_program_retire(p, 1, 1); 2106252884aeSStefan Eßer } 2107252884aeSStefan Eßer 210844d4804dSStefan Eßer /** 210944d4804dSStefan Eßer * Executes a divmod. 211044d4804dSStefan Eßer * @param p The program. 211144d4804dSStefan Eßer */ 211278bc019dSStefan Eßer static void 211378bc019dSStefan Eßer bc_program_divmod(BcProgram* p) 211478bc019dSStefan Eßer { 211578bc019dSStefan Eßer BcResult* opd1; 211678bc019dSStefan Eßer BcResult* opd2; 211778bc019dSStefan Eßer BcResult* res; 211878bc019dSStefan Eßer BcResult* res2; 211978bc019dSStefan Eßer BcNum* n1; 212078bc019dSStefan Eßer BcNum* n2; 2121252884aeSStefan Eßer size_t req; 2122252884aeSStefan Eßer 212344d4804dSStefan Eßer // We grow first to avoid pointer invalidation. 212450696a6eSStefan Eßer bc_vec_grow(&p->results, 2); 2125119656bcSStefan Eßer 2126119656bcSStefan Eßer // We don't need to update the pointer because 2127119656bcSStefan Eßer // the capacity is enough due to the line above. 2128252884aeSStefan Eßer res2 = bc_program_prepResult(p); 2129252884aeSStefan Eßer res = bc_program_prepResult(p); 2130252884aeSStefan Eßer 213144d4804dSStefan Eßer // Prepare the operands. 2132252884aeSStefan Eßer bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 2); 2133252884aeSStefan Eßer 2134252884aeSStefan Eßer req = bc_num_mulReq(n1, n2, BC_PROG_SCALE(p)); 2135252884aeSStefan Eßer 2136252884aeSStefan Eßer BC_SIG_LOCK; 2137252884aeSStefan Eßer 213844d4804dSStefan Eßer // Initialize the results. 2139252884aeSStefan Eßer bc_num_init(&res->d.n, req); 2140252884aeSStefan Eßer bc_num_init(&res2->d.n, req); 2141252884aeSStefan Eßer 2142252884aeSStefan Eßer BC_SIG_UNLOCK; 2143252884aeSStefan Eßer 214444d4804dSStefan Eßer // Execute. 2145252884aeSStefan Eßer bc_num_divmod(n1, n2, &res2->d.n, &res->d.n, BC_PROG_SCALE(p)); 2146252884aeSStefan Eßer 2147252884aeSStefan Eßer bc_program_retire(p, 2, 2); 2148252884aeSStefan Eßer } 2149252884aeSStefan Eßer 215044d4804dSStefan Eßer /** 215144d4804dSStefan Eßer * Executes modular exponentiation. 215244d4804dSStefan Eßer * @param p The program. 215344d4804dSStefan Eßer */ 215478bc019dSStefan Eßer static void 215578bc019dSStefan Eßer bc_program_modexp(BcProgram* p) 215678bc019dSStefan Eßer { 215778bc019dSStefan Eßer BcResult* r1; 215878bc019dSStefan Eßer BcResult* r2; 215978bc019dSStefan Eßer BcResult* r3; 216078bc019dSStefan Eßer BcResult* res; 216178bc019dSStefan Eßer BcNum* n1; 216278bc019dSStefan Eßer BcNum* n2; 216378bc019dSStefan Eßer BcNum* n3; 2164252884aeSStefan Eßer 216544d4804dSStefan Eßer #if DC_ENABLED 216644d4804dSStefan Eßer 216744d4804dSStefan Eßer // Check the stack. 216844d4804dSStefan Eßer if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 3))) 216978bc019dSStefan Eßer { 217044d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 217178bc019dSStefan Eßer } 217244d4804dSStefan Eßer 217344d4804dSStefan Eßer #endif // DC_ENABLED 2174252884aeSStefan Eßer 2175252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 3)); 2176252884aeSStefan Eßer 2177252884aeSStefan Eßer res = bc_program_prepResult(p); 2178252884aeSStefan Eßer 217944d4804dSStefan Eßer // Get the first operand and typecheck. 2180252884aeSStefan Eßer bc_program_operand(p, &r1, &n1, 3); 2181252884aeSStefan Eßer bc_program_type_num(r1, n1); 2182252884aeSStefan Eßer 218344d4804dSStefan Eßer // Get the last two operands. 2184252884aeSStefan Eßer bc_program_binOpPrep(p, &r2, &n2, &r3, &n3, 1); 2185252884aeSStefan Eßer 2186252884aeSStefan Eßer // Make sure that the values have their pointers updated, if necessary. 218744d4804dSStefan Eßer // Only array elements are possible because this is dc. 2188252884aeSStefan Eßer if (r1->t == BC_RESULT_ARRAY_ELEM && (r1->t == r2->t || r1->t == r3->t)) 218978bc019dSStefan Eßer { 2190252884aeSStefan Eßer n1 = bc_program_num(p, r1); 219178bc019dSStefan Eßer } 2192252884aeSStefan Eßer 2193252884aeSStefan Eßer BC_SIG_LOCK; 2194252884aeSStefan Eßer 2195252884aeSStefan Eßer bc_num_init(&res->d.n, n3->len); 2196252884aeSStefan Eßer 2197252884aeSStefan Eßer BC_SIG_UNLOCK; 2198252884aeSStefan Eßer 2199252884aeSStefan Eßer bc_num_modexp(n1, n2, n3, &res->d.n); 2200252884aeSStefan Eßer 2201252884aeSStefan Eßer bc_program_retire(p, 1, 3); 2202252884aeSStefan Eßer } 2203252884aeSStefan Eßer 220444d4804dSStefan Eßer /** 220544d4804dSStefan Eßer * Asciifies a number for dc. This is a helper for bc_program_asciify(). 220644d4804dSStefan Eßer * @param p The program. 220744d4804dSStefan Eßer * @param n The number to asciify. 220844d4804dSStefan Eßer */ 220978bc019dSStefan Eßer static uchar 221078bc019dSStefan Eßer bc_program_asciifyNum(BcProgram* p, BcNum* n) 221178bc019dSStefan Eßer { 2212d101cdd6SStefan Eßer bc_num_copy(&p->asciify, n); 2213252884aeSStefan Eßer 221444d4804dSStefan Eßer // We want to clear the scale and sign for easy mod later. 2215d101cdd6SStefan Eßer bc_num_truncate(&p->asciify, p->asciify.scale); 2216d101cdd6SStefan Eßer BC_NUM_NEG_CLR(&p->asciify); 2217252884aeSStefan Eßer 2218252884aeSStefan Eßer // This is guaranteed to not have a divide by 0 221944d4804dSStefan Eßer // because strmb is equal to 256. 2220d101cdd6SStefan Eßer bc_num_mod(&p->asciify, &p->strmb, &p->asciify, 0); 2221252884aeSStefan Eßer 2222252884aeSStefan Eßer // This is also guaranteed to not error because num is in the range 2223252884aeSStefan Eßer // [0, UCHAR_MAX], which is definitely in range for a BcBigDig. And 2224252884aeSStefan Eßer // it is not negative. 2225d101cdd6SStefan Eßer return (uchar) bc_num_bigdig2(&p->asciify); 2226252884aeSStefan Eßer } 2227252884aeSStefan Eßer 222844d4804dSStefan Eßer /** 2229d101cdd6SStefan Eßer * Executes the "asciify" command in bc and dc. 223044d4804dSStefan Eßer * @param p The program. 223144d4804dSStefan Eßer */ 223278bc019dSStefan Eßer static void 2233d101cdd6SStefan Eßer bc_program_asciify(BcProgram* p) 223478bc019dSStefan Eßer { 2235252884aeSStefan Eßer BcResult *r, res; 2236252884aeSStefan Eßer BcNum* n; 2237252884aeSStefan Eßer uchar c; 2238252884aeSStefan Eßer size_t idx; 2239d101cdd6SStefan Eßer #if BC_ENABLED 2240d101cdd6SStefan Eßer // This is in the outer scope because it has to be freed after a jump. 2241d101cdd6SStefan Eßer char* temp_str; 2242d101cdd6SStefan Eßer #endif // BC_ENABLED 2243252884aeSStefan Eßer 224444d4804dSStefan Eßer // Check the stack. 224544d4804dSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK); 2246252884aeSStefan Eßer 2247252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 2248252884aeSStefan Eßer 224944d4804dSStefan Eßer // Get the top of the results stack. 2250252884aeSStefan Eßer bc_program_operand(p, &r, &n, 0); 2251252884aeSStefan Eßer 2252252884aeSStefan Eßer assert(n != NULL); 2253d101cdd6SStefan Eßer assert(BC_IS_BC || r->t != BC_RESULT_ARRAY); 2254d101cdd6SStefan Eßer 2255d101cdd6SStefan Eßer #if BC_ENABLED 2256d101cdd6SStefan Eßer // Handle arrays in bc specially. 2257d101cdd6SStefan Eßer if (r->t == BC_RESULT_ARRAY) 2258d101cdd6SStefan Eßer { 2259d101cdd6SStefan Eßer // Yes, this is one place where we need to cast the number from 2260d101cdd6SStefan Eßer // bc_program_num() to a vector. 2261d101cdd6SStefan Eßer BcVec* v = (BcVec*) n; 2262d101cdd6SStefan Eßer size_t i; 2263d101cdd6SStefan Eßer 2264d101cdd6SStefan Eßer // XXX: If this is changed, you should also change the similar code in 2265d101cdd6SStefan Eßer // bc_program_builtin(). 2266d101cdd6SStefan Eßer 2267d101cdd6SStefan Eßer // Dereference the array, if necessary. 2268d101cdd6SStefan Eßer if (v->size == sizeof(uchar)) 2269d101cdd6SStefan Eßer { 2270d101cdd6SStefan Eßer v = bc_program_dereference(p, v); 2271d101cdd6SStefan Eßer } 2272d101cdd6SStefan Eßer 2273d101cdd6SStefan Eßer assert(v->size == sizeof(BcNum)); 2274d101cdd6SStefan Eßer 2275d101cdd6SStefan Eßer // Allocate the string and set the jump for it. 2276d101cdd6SStefan Eßer BC_SIG_LOCK; 2277d101cdd6SStefan Eßer temp_str = bc_vm_malloc(v->len + 1); 2278d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, exit); 2279d101cdd6SStefan Eßer BC_SIG_UNLOCK; 2280d101cdd6SStefan Eßer 2281d101cdd6SStefan Eßer // Convert the array. 2282d101cdd6SStefan Eßer for (i = 0; i < v->len; ++i) 2283d101cdd6SStefan Eßer { 2284d101cdd6SStefan Eßer BcNum* num = (BcNum*) bc_vec_item(v, i); 2285d101cdd6SStefan Eßer 2286d101cdd6SStefan Eßer if (BC_PROG_STR(num)) 2287d101cdd6SStefan Eßer { 2288d101cdd6SStefan Eßer temp_str[i] = (bc_program_string(p, num))[0]; 2289d101cdd6SStefan Eßer } 2290d101cdd6SStefan Eßer else 2291d101cdd6SStefan Eßer { 2292d101cdd6SStefan Eßer temp_str[i] = (char) bc_program_asciifyNum(p, num); 2293d101cdd6SStefan Eßer } 2294d101cdd6SStefan Eßer } 2295d101cdd6SStefan Eßer 2296d101cdd6SStefan Eßer temp_str[v->len] = '\0'; 2297d101cdd6SStefan Eßer 2298d101cdd6SStefan Eßer // Store the string in the slab and map, and free the temp string. 2299d101cdd6SStefan Eßer BC_SIG_LOCK; 2300d101cdd6SStefan Eßer idx = bc_program_addString(p, temp_str); 2301d101cdd6SStefan Eßer free(temp_str); 2302d101cdd6SStefan Eßer BC_UNSETJMP(vm); 2303d101cdd6SStefan Eßer BC_SIG_UNLOCK; 2304d101cdd6SStefan Eßer } 2305d101cdd6SStefan Eßer else 2306d101cdd6SStefan Eßer #endif // BC_ENABLED 2307d101cdd6SStefan Eßer { 2308d101cdd6SStefan Eßer char str[2]; 2309d101cdd6SStefan Eßer char* str2; 2310252884aeSStefan Eßer 231144d4804dSStefan Eßer // Asciify. 2312252884aeSStefan Eßer if (BC_PROG_NUM(r, n)) c = bc_program_asciifyNum(p, n); 231378bc019dSStefan Eßer else 231478bc019dSStefan Eßer { 231544d4804dSStefan Eßer // Get the string itself, then the first character. 231644d4804dSStefan Eßer str2 = bc_program_string(p, n); 2317252884aeSStefan Eßer c = (uchar) str2[0]; 2318252884aeSStefan Eßer } 2319252884aeSStefan Eßer 232044d4804dSStefan Eßer // Fill the resulting string. 2321252884aeSStefan Eßer str[0] = (char) c; 2322252884aeSStefan Eßer str[1] = '\0'; 2323252884aeSStefan Eßer 232444d4804dSStefan Eßer // Add the string to the data structures. 2325252884aeSStefan Eßer BC_SIG_LOCK; 2326d101cdd6SStefan Eßer idx = bc_program_addString(p, str); 2327252884aeSStefan Eßer BC_SIG_UNLOCK; 2328d101cdd6SStefan Eßer } 2329252884aeSStefan Eßer 233044d4804dSStefan Eßer // Set the result 2331252884aeSStefan Eßer res.t = BC_RESULT_STR; 233244d4804dSStefan Eßer bc_num_clear(&res.d.n); 233344d4804dSStefan Eßer res.d.n.scale = idx; 233444d4804dSStefan Eßer 233544d4804dSStefan Eßer // Pop and push. 2336252884aeSStefan Eßer bc_vec_pop(&p->results); 2337252884aeSStefan Eßer bc_vec_push(&p->results, &res); 2338d101cdd6SStefan Eßer 2339d101cdd6SStefan Eßer return; 2340d101cdd6SStefan Eßer 2341d101cdd6SStefan Eßer #if BC_ENABLED 2342d101cdd6SStefan Eßer exit: 2343d101cdd6SStefan Eßer free(temp_str); 2344d101cdd6SStefan Eßer #endif // BC_ENABLED 2345252884aeSStefan Eßer } 2346252884aeSStefan Eßer 234744d4804dSStefan Eßer /** 234844d4804dSStefan Eßer * Streams a number or a string to stdout. 234944d4804dSStefan Eßer * @param p The program. 235044d4804dSStefan Eßer */ 235178bc019dSStefan Eßer static void 235278bc019dSStefan Eßer bc_program_printStream(BcProgram* p) 235378bc019dSStefan Eßer { 2354252884aeSStefan Eßer BcResult* r; 2355252884aeSStefan Eßer BcNum* n; 2356252884aeSStefan Eßer 235744d4804dSStefan Eßer // Check the stack. 235844d4804dSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK); 2359252884aeSStefan Eßer 2360252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 2361252884aeSStefan Eßer 236244d4804dSStefan Eßer // Get the top of the results stack. 2363252884aeSStefan Eßer bc_program_operand(p, &r, &n, 0); 2364252884aeSStefan Eßer 2365252884aeSStefan Eßer assert(n != NULL); 2366252884aeSStefan Eßer 236744d4804dSStefan Eßer // Stream appropriately. 236844d4804dSStefan Eßer if (BC_PROG_NUM(r, n)) bc_num_stream(n); 236944d4804dSStefan Eßer else bc_program_printChars(bc_program_string(p, n)); 23707e5c51e5SStefan Eßer 237144d4804dSStefan Eßer // Pop the operand. 23727e5c51e5SStefan Eßer bc_vec_pop(&p->results); 2373252884aeSStefan Eßer } 2374252884aeSStefan Eßer 237544d4804dSStefan Eßer #if DC_ENABLED 237644d4804dSStefan Eßer 237744d4804dSStefan Eßer /** 237844d4804dSStefan Eßer * Gets the length of a register in dc and pushes it onto the results stack. 237944d4804dSStefan Eßer * @param p The program. 238044d4804dSStefan Eßer * @param code The bytecode vector to pull the register's index out of. 238144d4804dSStefan Eßer * @param bgn An in/out parameter; the start of the index in the bytecode 238244d4804dSStefan Eßer * vector, and will be updated to point after the index on return. 238344d4804dSStefan Eßer */ 238478bc019dSStefan Eßer static void 238578bc019dSStefan Eßer bc_program_regStackLen(BcProgram* p, const char* restrict code, 238644d4804dSStefan Eßer size_t* restrict bgn) 238744d4804dSStefan Eßer { 238844d4804dSStefan Eßer size_t idx = bc_program_index(code, bgn); 238944d4804dSStefan Eßer BcVec* v = bc_program_vec(p, idx, BC_TYPE_VAR); 239044d4804dSStefan Eßer 239144d4804dSStefan Eßer bc_program_pushBigdig(p, (BcBigDig) v->len, BC_RESULT_TEMP); 239244d4804dSStefan Eßer } 239344d4804dSStefan Eßer 239444d4804dSStefan Eßer /** 239544d4804dSStefan Eßer * Pushes the length of the results stack onto the results stack. 239644d4804dSStefan Eßer * @param p The program. 239744d4804dSStefan Eßer */ 239878bc019dSStefan Eßer static void 239978bc019dSStefan Eßer bc_program_stackLen(BcProgram* p) 240078bc019dSStefan Eßer { 240144d4804dSStefan Eßer bc_program_pushBigdig(p, (BcBigDig) p->results.len, BC_RESULT_TEMP); 240244d4804dSStefan Eßer } 240344d4804dSStefan Eßer 240444d4804dSStefan Eßer /** 240544d4804dSStefan Eßer * Pops a certain number of elements off the execution stack. 240644d4804dSStefan Eßer * @param p The program. 240744d4804dSStefan Eßer * @param inst The instruction to tell us how many. There is one to pop up to 240844d4804dSStefan Eßer * 2, and one to pop the amount equal to the number at the top of 240944d4804dSStefan Eßer * the results stack. 241044d4804dSStefan Eßer */ 241178bc019dSStefan Eßer static void 241278bc019dSStefan Eßer bc_program_nquit(BcProgram* p, uchar inst) 241378bc019dSStefan Eßer { 2414252884aeSStefan Eßer BcResult* opnd; 2415252884aeSStefan Eßer BcNum* num; 2416252884aeSStefan Eßer BcBigDig val; 2417252884aeSStefan Eßer size_t i; 2418252884aeSStefan Eßer 241944d4804dSStefan Eßer // Ensure that the tail calls stack is correct. 2420252884aeSStefan Eßer assert(p->stack.len == p->tail_calls.len); 2421252884aeSStefan Eßer 242244d4804dSStefan Eßer // Get the number of executions to pop. 2423252884aeSStefan Eßer if (inst == BC_INST_QUIT) val = 2; 242478bc019dSStefan Eßer else 242578bc019dSStefan Eßer { 2426252884aeSStefan Eßer bc_program_prep(p, &opnd, &num, 0); 242744d4804dSStefan Eßer val = bc_num_bigdig(num); 2428252884aeSStefan Eßer 2429252884aeSStefan Eßer bc_vec_pop(&p->results); 2430252884aeSStefan Eßer } 2431252884aeSStefan Eßer 243244d4804dSStefan Eßer // Loop over the tail call stack and adjust the quit value appropriately. 243378bc019dSStefan Eßer for (i = 0; val && i < p->tail_calls.len; ++i) 243478bc019dSStefan Eßer { 243544d4804dSStefan Eßer // Get the number of tail calls for this one. 2436252884aeSStefan Eßer size_t calls = *((size_t*) bc_vec_item_rev(&p->tail_calls, i)) + 1; 243744d4804dSStefan Eßer 243844d4804dSStefan Eßer // Adjust the value. 2439252884aeSStefan Eßer if (calls >= val) val = 0; 24407e5c51e5SStefan Eßer else val -= (BcBigDig) calls; 2441252884aeSStefan Eßer } 2442252884aeSStefan Eßer 244344d4804dSStefan Eßer // If we don't have enough executions, just quit. 244478bc019dSStefan Eßer if (i == p->stack.len) 244578bc019dSStefan Eßer { 2446d101cdd6SStefan Eßer vm->status = BC_STATUS_QUIT; 244744d4804dSStefan Eßer BC_JMP; 2448252884aeSStefan Eßer } 244978bc019dSStefan Eßer else 245078bc019dSStefan Eßer { 245144d4804dSStefan Eßer // We can always pop the last item we reached on the tail call stack 245244d4804dSStefan Eßer // because these are for tail calls. That means that any executions that 245344d4804dSStefan Eßer // we would not have quit in that position on the stack would have quit 245444d4804dSStefan Eßer // anyway. 245510041e99SStefan Eßer BC_SIG_LOCK; 2456252884aeSStefan Eßer bc_vec_npop(&p->stack, i); 2457252884aeSStefan Eßer bc_vec_npop(&p->tail_calls, i); 245810041e99SStefan Eßer BC_SIG_UNLOCK; 2459252884aeSStefan Eßer } 2460252884aeSStefan Eßer } 2461252884aeSStefan Eßer 246244d4804dSStefan Eßer /** 246344d4804dSStefan Eßer * Pushes the depth of the execution stack onto the stack. 246444d4804dSStefan Eßer * @param p The program. 246544d4804dSStefan Eßer */ 246678bc019dSStefan Eßer static void 246778bc019dSStefan Eßer bc_program_execStackLen(BcProgram* p) 246878bc019dSStefan Eßer { 246944d4804dSStefan Eßer size_t i, amt, len = p->tail_calls.len; 247044d4804dSStefan Eßer 247144d4804dSStefan Eßer amt = len; 247244d4804dSStefan Eßer 247344d4804dSStefan Eßer for (i = 0; i < len; ++i) 247478bc019dSStefan Eßer { 247544d4804dSStefan Eßer amt += *((size_t*) bc_vec_item(&p->tail_calls, i)); 247678bc019dSStefan Eßer } 247744d4804dSStefan Eßer 247844d4804dSStefan Eßer bc_program_pushBigdig(p, (BcBigDig) amt, BC_RESULT_TEMP); 247944d4804dSStefan Eßer } 248044d4804dSStefan Eßer 248144d4804dSStefan Eßer /** 248244d4804dSStefan Eßer * 248344d4804dSStefan Eßer * @param p The program. 248444d4804dSStefan Eßer * @param code The bytecode vector to pull the register's index out of. 248544d4804dSStefan Eßer * @param bgn An in/out parameter; the start of the index in the bytecode 248644d4804dSStefan Eßer * vector, and will be updated to point after the index on return. 248744d4804dSStefan Eßer * @param cond True if the execution is conditional, false otherwise. 248844d4804dSStefan Eßer * @param len The number of bytes in the bytecode vector. 248944d4804dSStefan Eßer */ 249078bc019dSStefan Eßer static void 249178bc019dSStefan Eßer bc_program_execStr(BcProgram* p, const char* restrict code, 2492252884aeSStefan Eßer size_t* restrict bgn, bool cond, size_t len) 2493252884aeSStefan Eßer { 2494252884aeSStefan Eßer BcResult* r; 2495252884aeSStefan Eßer char* str; 2496252884aeSStefan Eßer BcFunc* f; 2497252884aeSStefan Eßer BcInstPtr ip; 249844d4804dSStefan Eßer size_t fidx; 2499252884aeSStefan Eßer BcNum* n; 2500252884aeSStefan Eßer 2501252884aeSStefan Eßer assert(p->stack.len == p->tail_calls.len); 2502252884aeSStefan Eßer 250344d4804dSStefan Eßer // Check the stack. 250444d4804dSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK); 2505252884aeSStefan Eßer 2506252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 2507252884aeSStefan Eßer 250844d4804dSStefan Eßer // Get the operand. 2509252884aeSStefan Eßer bc_program_operand(p, &r, &n, 0); 2510252884aeSStefan Eßer 251144d4804dSStefan Eßer // If execution is conditional... 251278bc019dSStefan Eßer if (cond) 251378bc019dSStefan Eßer { 2514252884aeSStefan Eßer bool exec; 2515d101cdd6SStefan Eßer size_t then_idx; 2516d101cdd6SStefan Eßer // These are volatile to quiet warnings on GCC about clobbering with 2517d101cdd6SStefan Eßer // longjmp(). 2518d101cdd6SStefan Eßer volatile size_t else_idx; 2519d101cdd6SStefan Eßer volatile size_t idx; 2520252884aeSStefan Eßer 252144d4804dSStefan Eßer // Get the index of the "then" var and "else" var. 2522252884aeSStefan Eßer then_idx = bc_program_index(code, bgn); 2523252884aeSStefan Eßer else_idx = bc_program_index(code, bgn); 2524252884aeSStefan Eßer 252544d4804dSStefan Eßer // Figure out if we should execute. 2526252884aeSStefan Eßer exec = (r->d.n.len != 0); 2527252884aeSStefan Eßer 2528252884aeSStefan Eßer idx = exec ? then_idx : else_idx; 2529252884aeSStefan Eßer 2530252884aeSStefan Eßer BC_SIG_LOCK; 2531d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, exit); 2532252884aeSStefan Eßer 253344d4804dSStefan Eßer // If we are supposed to execute, execute. If else_idx == SIZE_MAX, that 253444d4804dSStefan Eßer // means there was no else clause, so if execute is false and else does 253544d4804dSStefan Eßer // not exist, we don't execute. The goto skips all of the setup for the 253644d4804dSStefan Eßer // execution. 2537252884aeSStefan Eßer if (exec || (else_idx != SIZE_MAX)) 253878bc019dSStefan Eßer { 2539252884aeSStefan Eßer n = bc_vec_top(bc_program_vec(p, idx, BC_TYPE_VAR)); 254078bc019dSStefan Eßer } 2541252884aeSStefan Eßer else goto exit; 2542252884aeSStefan Eßer 254344d4804dSStefan Eßer if (BC_ERR(!BC_PROG_STR(n))) bc_err(BC_ERR_EXEC_TYPE); 2544252884aeSStefan Eßer 2545d101cdd6SStefan Eßer BC_UNSETJMP(vm); 2546252884aeSStefan Eßer BC_SIG_UNLOCK; 2547252884aeSStefan Eßer } 254878bc019dSStefan Eßer else 254978bc019dSStefan Eßer { 2550252884aeSStefan Eßer // In non-conditional situations, only the top of stack can be executed, 2551252884aeSStefan Eßer // and in those cases, variables are not allowed to be "on the stack"; 2552252884aeSStefan Eßer // they are only put on the stack to be assigned to. 2553252884aeSStefan Eßer assert(r->t != BC_RESULT_VAR); 2554252884aeSStefan Eßer 255544d4804dSStefan Eßer if (r->t != BC_RESULT_STR) return; 2556252884aeSStefan Eßer } 2557252884aeSStefan Eßer 255844d4804dSStefan Eßer assert(BC_PROG_STR(n)); 255944d4804dSStefan Eßer 256044d4804dSStefan Eßer // Get the string. 256144d4804dSStefan Eßer str = bc_program_string(p, n); 256244d4804dSStefan Eßer 256344d4804dSStefan Eßer // Get the function index and function. 256444d4804dSStefan Eßer BC_SIG_LOCK; 256544d4804dSStefan Eßer fidx = bc_program_insertFunc(p, str); 256644d4804dSStefan Eßer BC_SIG_UNLOCK; 2567252884aeSStefan Eßer f = bc_vec_item(&p->fns, fidx); 2568252884aeSStefan Eßer 256944d4804dSStefan Eßer // If the function has not been parsed yet... 257078bc019dSStefan Eßer if (!f->code.len) 257178bc019dSStefan Eßer { 2572252884aeSStefan Eßer BC_SIG_LOCK; 2573252884aeSStefan Eßer 2574d101cdd6SStefan Eßer if (!BC_PARSE_IS_INITED(&vm->read_prs, p)) 257578bc019dSStefan Eßer { 2576d101cdd6SStefan Eßer bc_parse_init(&vm->read_prs, p, fidx); 257744d4804dSStefan Eßer 257844d4804dSStefan Eßer // Initialize this too because bc_vm_shutdown() expects them to be 257944d4804dSStefan Eßer // initialized togther. 2580d101cdd6SStefan Eßer bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE); 258144d4804dSStefan Eßer } 258244d4804dSStefan Eßer // This needs to be updated because the parser could have been used 258344d4804dSStefan Eßer // somewhere else 2584d101cdd6SStefan Eßer else bc_parse_updateFunc(&vm->read_prs, fidx); 258544d4804dSStefan Eßer 2586d101cdd6SStefan Eßer bc_lex_file(&vm->read_prs.l, vm->file); 2587252884aeSStefan Eßer 2588d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 2589252884aeSStefan Eßer 2590252884aeSStefan Eßer BC_SIG_UNLOCK; 2591252884aeSStefan Eßer 2592d101cdd6SStefan Eßer // Parse. Only one expression is needed, so stdin isn't used. 2593d101cdd6SStefan Eßer bc_parse_text(&vm->read_prs, str, BC_MODE_FILE); 2594252884aeSStefan Eßer 2595252884aeSStefan Eßer BC_SIG_LOCK; 2596d101cdd6SStefan Eßer vm->expr(&vm->read_prs, BC_PARSE_NOCALL); 2597252884aeSStefan Eßer 2598d101cdd6SStefan Eßer BC_UNSETJMP(vm); 2599252884aeSStefan Eßer 2600252884aeSStefan Eßer // We can just assert this here because 2601252884aeSStefan Eßer // dc should parse everything until EOF. 2602d101cdd6SStefan Eßer assert(vm->read_prs.l.t == BC_LEX_EOF); 2603252884aeSStefan Eßer 2604252884aeSStefan Eßer BC_SIG_UNLOCK; 2605252884aeSStefan Eßer } 2606252884aeSStefan Eßer 260744d4804dSStefan Eßer // Set the instruction pointer. 2608252884aeSStefan Eßer ip.idx = 0; 2609252884aeSStefan Eßer ip.len = p->results.len; 2610252884aeSStefan Eßer ip.func = fidx; 2611252884aeSStefan Eßer 261210041e99SStefan Eßer BC_SIG_LOCK; 261310041e99SStefan Eßer 261444d4804dSStefan Eßer // Pop the operand. 2615252884aeSStefan Eßer bc_vec_pop(&p->results); 2616252884aeSStefan Eßer 261744d4804dSStefan Eßer // Tail call processing. This condition means that there is more on the 261844d4804dSStefan Eßer // execution stack, and we are at the end of the bytecode vector, and the 261944d4804dSStefan Eßer // last instruction is just a BC_INST_POP_EXEC, which would return. 262078bc019dSStefan Eßer if (p->stack.len > 1 && *bgn == len - 1 && code[*bgn] == BC_INST_POP_EXEC) 262178bc019dSStefan Eßer { 2622252884aeSStefan Eßer size_t* call_ptr = bc_vec_top(&p->tail_calls); 262344d4804dSStefan Eßer 262444d4804dSStefan Eßer // Add one to the tail call. 2625252884aeSStefan Eßer *call_ptr += 1; 262644d4804dSStefan Eßer 262744d4804dSStefan Eßer // Pop the execution stack before pushing the new instruction pointer 262844d4804dSStefan Eßer // on. 2629252884aeSStefan Eßer bc_vec_pop(&p->stack); 2630252884aeSStefan Eßer } 263144d4804dSStefan Eßer // If not a tail call, just push a new one. 2632252884aeSStefan Eßer else bc_vec_push(&p->tail_calls, &ip.idx); 2633252884aeSStefan Eßer 263444d4804dSStefan Eßer // Push the new function onto the execution stack and return. 2635252884aeSStefan Eßer bc_vec_push(&p->stack, &ip); 2636252884aeSStefan Eßer 263710041e99SStefan Eßer BC_SIG_UNLOCK; 263810041e99SStefan Eßer 2639252884aeSStefan Eßer return; 2640252884aeSStefan Eßer 2641252884aeSStefan Eßer err: 2642252884aeSStefan Eßer BC_SIG_MAYLOCK; 264344d4804dSStefan Eßer 2644252884aeSStefan Eßer f = bc_vec_item(&p->fns, fidx); 264544d4804dSStefan Eßer 264644d4804dSStefan Eßer // Make sure to erase the bytecode vector so dc knows it is not parsed. 264710328f8bSStefan Eßer bc_vec_popAll(&f->code); 264844d4804dSStefan Eßer 2649252884aeSStefan Eßer exit: 2650252884aeSStefan Eßer bc_vec_pop(&p->results); 2651d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 2652252884aeSStefan Eßer } 2653252884aeSStefan Eßer 265444d4804dSStefan Eßer /** 265544d4804dSStefan Eßer * Prints every item on the results stack, one per line. 265644d4804dSStefan Eßer * @param p The program. 265744d4804dSStefan Eßer */ 265878bc019dSStefan Eßer static void 265978bc019dSStefan Eßer bc_program_printStack(BcProgram* p) 266078bc019dSStefan Eßer { 2661252884aeSStefan Eßer size_t idx; 2662252884aeSStefan Eßer 2663252884aeSStefan Eßer for (idx = 0; idx < p->results.len; ++idx) 266478bc019dSStefan Eßer { 2665252884aeSStefan Eßer bc_program_print(p, BC_INST_PRINT, idx); 2666252884aeSStefan Eßer } 266778bc019dSStefan Eßer } 2668252884aeSStefan Eßer #endif // DC_ENABLED 2669252884aeSStefan Eßer 267044d4804dSStefan Eßer /** 267144d4804dSStefan Eßer * Pushes the value of a global onto the results stack. 267244d4804dSStefan Eßer * @param p The program. 267344d4804dSStefan Eßer * @param inst Which global to push, as an instruction. 267444d4804dSStefan Eßer */ 267578bc019dSStefan Eßer static void 267678bc019dSStefan Eßer bc_program_pushGlobal(BcProgram* p, uchar inst) 267778bc019dSStefan Eßer { 2678252884aeSStefan Eßer BcResultType t; 2679252884aeSStefan Eßer 268044d4804dSStefan Eßer // Make sure the instruction is valid. 2681252884aeSStefan Eßer assert(inst >= BC_INST_IBASE && inst <= BC_INST_SCALE); 2682252884aeSStefan Eßer 268344d4804dSStefan Eßer // Push the global. 2684252884aeSStefan Eßer t = inst - BC_INST_IBASE + BC_RESULT_IBASE; 2685252884aeSStefan Eßer bc_program_pushBigdig(p, p->globals[inst - BC_INST_IBASE], t); 2686252884aeSStefan Eßer } 2687252884aeSStefan Eßer 2688d43fa8efSStefan Eßer /** 2689d43fa8efSStefan Eßer * Pushes the value of a global setting onto the stack. 2690d43fa8efSStefan Eßer * @param p The program. 2691d43fa8efSStefan Eßer * @param inst Which global setting to push, as an instruction. 2692d43fa8efSStefan Eßer */ 269378bc019dSStefan Eßer static void 269478bc019dSStefan Eßer bc_program_globalSetting(BcProgram* p, uchar inst) 269578bc019dSStefan Eßer { 2696d43fa8efSStefan Eßer BcBigDig val; 2697d43fa8efSStefan Eßer 2698d43fa8efSStefan Eßer // Make sure the instruction is valid. 2699103d7cdfSStefan Eßer #if DC_ENABLED 2700103d7cdfSStefan Eßer assert((inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO) || 2701103d7cdfSStefan Eßer (BC_IS_DC && inst == BC_INST_EXTENDED_REGISTERS)); 2702103d7cdfSStefan Eßer #else // DC_ENABLED 2703d43fa8efSStefan Eßer assert(inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO); 2704103d7cdfSStefan Eßer #endif // DC_ENABLED 2705d43fa8efSStefan Eßer 270676238846SStefan Eßer if (inst == BC_INST_LINE_LENGTH) 270776238846SStefan Eßer { 270876238846SStefan Eßer val = (BcBigDig) vm->line_len; 270976238846SStefan Eßer } 2710d43fa8efSStefan Eßer #if BC_ENABLED 271176238846SStefan Eßer else if (inst == BC_INST_GLOBAL_STACKS) 271276238846SStefan Eßer { 271376238846SStefan Eßer val = (BC_G != 0); 271476238846SStefan Eßer } 2715d43fa8efSStefan Eßer #endif // BC_ENABLED 2716103d7cdfSStefan Eßer #if DC_ENABLED 271776238846SStefan Eßer else if (inst == BC_INST_EXTENDED_REGISTERS) 271876238846SStefan Eßer { 271976238846SStefan Eßer val = (DC_X != 0); 272076238846SStefan Eßer } 2721103d7cdfSStefan Eßer #endif // DC_ENABLED 2722d43fa8efSStefan Eßer else val = (BC_Z != 0); 2723d43fa8efSStefan Eßer 2724d43fa8efSStefan Eßer // Push the global. 2725d43fa8efSStefan Eßer bc_program_pushBigdig(p, val, BC_RESULT_TEMP); 2726d43fa8efSStefan Eßer } 2727d43fa8efSStefan Eßer 272844d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 272944d4804dSStefan Eßer 273044d4804dSStefan Eßer /** 273144d4804dSStefan Eßer * Pushes the value of seed on the stack. 273244d4804dSStefan Eßer * @param p The program. 273344d4804dSStefan Eßer */ 273478bc019dSStefan Eßer static void 273578bc019dSStefan Eßer bc_program_pushSeed(BcProgram* p) 273678bc019dSStefan Eßer { 2737252884aeSStefan Eßer BcResult* res; 2738252884aeSStefan Eßer 2739252884aeSStefan Eßer res = bc_program_prepResult(p); 2740252884aeSStefan Eßer res->t = BC_RESULT_SEED; 2741252884aeSStefan Eßer 2742252884aeSStefan Eßer BC_SIG_LOCK; 2743252884aeSStefan Eßer 274444d4804dSStefan Eßer // We need 2*BC_RAND_NUM_SIZE because of the size of the state. 2745252884aeSStefan Eßer bc_num_init(&res->d.n, 2 * BC_RAND_NUM_SIZE); 2746252884aeSStefan Eßer 2747252884aeSStefan Eßer BC_SIG_UNLOCK; 2748252884aeSStefan Eßer 2749252884aeSStefan Eßer bc_num_createFromRNG(&res->d.n, &p->rng); 2750252884aeSStefan Eßer } 2751252884aeSStefan Eßer 275244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 275344d4804dSStefan Eßer 275444d4804dSStefan Eßer /** 275544d4804dSStefan Eßer * Adds a function to the fns array. The function's ID must have already been 275644d4804dSStefan Eßer * inserted into the map. 275744d4804dSStefan Eßer * @param p The program. 275844d4804dSStefan Eßer * @param id_ptr The ID of the function as inserted into the map. 275944d4804dSStefan Eßer */ 276078bc019dSStefan Eßer static void 276178bc019dSStefan Eßer bc_program_addFunc(BcProgram* p, BcId* id_ptr) 276278bc019dSStefan Eßer { 276344d4804dSStefan Eßer BcFunc* f; 2764252884aeSStefan Eßer 2765252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2766252884aeSStefan Eßer 276744d4804dSStefan Eßer // Push and init. 276844d4804dSStefan Eßer f = bc_vec_pushEmpty(&p->fns); 2769252884aeSStefan Eßer bc_func_init(f, id_ptr->name); 2770252884aeSStefan Eßer } 2771252884aeSStefan Eßer 277278bc019dSStefan Eßer size_t 277378bc019dSStefan Eßer bc_program_insertFunc(BcProgram* p, const char* name) 277478bc019dSStefan Eßer { 2775252884aeSStefan Eßer BcId* id_ptr; 2776252884aeSStefan Eßer bool new; 2777252884aeSStefan Eßer size_t idx; 2778252884aeSStefan Eßer 2779252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2780252884aeSStefan Eßer 2781252884aeSStefan Eßer assert(p != NULL && name != NULL); 2782252884aeSStefan Eßer 278344d4804dSStefan Eßer // Insert into the map and get the resulting ID. 2784252884aeSStefan Eßer new = bc_map_insert(&p->fn_map, name, p->fns.len, &idx); 2785252884aeSStefan Eßer id_ptr = (BcId*) bc_vec_item(&p->fn_map, idx); 2786252884aeSStefan Eßer idx = id_ptr->idx; 2787252884aeSStefan Eßer 278844d4804dSStefan Eßer // If the function is new... 278978bc019dSStefan Eßer if (new) 279078bc019dSStefan Eßer { 279144d4804dSStefan Eßer // Add the function to the fns array. 279244d4804dSStefan Eßer bc_program_addFunc(p, id_ptr); 279344d4804dSStefan Eßer } 279444d4804dSStefan Eßer #if BC_ENABLED 279544d4804dSStefan Eßer // bc has to reset the function because it's about to be redefined. 279678bc019dSStefan Eßer else if (BC_IS_BC) 279778bc019dSStefan Eßer { 2798252884aeSStefan Eßer BcFunc* func = bc_vec_item(&p->fns, idx); 2799252884aeSStefan Eßer bc_func_reset(func); 2800252884aeSStefan Eßer } 280144d4804dSStefan Eßer #endif // BC_ENABLED 2802252884aeSStefan Eßer 2803252884aeSStefan Eßer return idx; 2804252884aeSStefan Eßer } 2805252884aeSStefan Eßer 2806*12e0d316SStefan Eßer #if BC_DEBUG || BC_ENABLE_MEMCHECK 280778bc019dSStefan Eßer void 280878bc019dSStefan Eßer bc_program_free(BcProgram* p) 280978bc019dSStefan Eßer { 2810d101cdd6SStefan Eßer #if BC_ENABLED 2811252884aeSStefan Eßer size_t i; 2812d101cdd6SStefan Eßer #endif // BC_ENABLED 2813252884aeSStefan Eßer 2814252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2815252884aeSStefan Eßer 2816252884aeSStefan Eßer assert(p != NULL); 2817252884aeSStefan Eßer 2818d101cdd6SStefan Eßer #if BC_ENABLED 281944d4804dSStefan Eßer // Free the globals stacks. 282078bc019dSStefan Eßer for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) 282178bc019dSStefan Eßer { 282278bc019dSStefan Eßer bc_vec_free(p->globals_v + i); 282378bc019dSStefan Eßer } 2824d101cdd6SStefan Eßer #endif // BC_ENABLED 2825252884aeSStefan Eßer 2826252884aeSStefan Eßer bc_vec_free(&p->fns); 2827252884aeSStefan Eßer bc_vec_free(&p->fn_map); 2828252884aeSStefan Eßer bc_vec_free(&p->vars); 2829252884aeSStefan Eßer bc_vec_free(&p->var_map); 2830252884aeSStefan Eßer bc_vec_free(&p->arrs); 2831252884aeSStefan Eßer bc_vec_free(&p->arr_map); 2832252884aeSStefan Eßer bc_vec_free(&p->results); 2833252884aeSStefan Eßer bc_vec_free(&p->stack); 2834d101cdd6SStefan Eßer bc_vec_free(&p->consts); 2835d101cdd6SStefan Eßer bc_vec_free(&p->const_map); 2836d101cdd6SStefan Eßer bc_vec_free(&p->strs); 2837d101cdd6SStefan Eßer bc_vec_free(&p->str_map); 2838d101cdd6SStefan Eßer 2839d101cdd6SStefan Eßer bc_num_free(&p->asciify); 2840252884aeSStefan Eßer 2841252884aeSStefan Eßer #if BC_ENABLED 2842252884aeSStefan Eßer if (BC_IS_BC) bc_num_free(&p->last); 2843252884aeSStefan Eßer #endif // BC_ENABLED 2844252884aeSStefan Eßer 284544d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2846252884aeSStefan Eßer bc_rand_free(&p->rng); 284744d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2848252884aeSStefan Eßer 2849252884aeSStefan Eßer #if DC_ENABLED 285044d4804dSStefan Eßer if (BC_IS_DC) bc_vec_free(&p->tail_calls); 2851252884aeSStefan Eßer #endif // DC_ENABLED 2852252884aeSStefan Eßer } 2853*12e0d316SStefan Eßer #endif // BC_DEBUG || BC_ENABLE_MEMCHECK 2854252884aeSStefan Eßer 285578bc019dSStefan Eßer void 285678bc019dSStefan Eßer bc_program_init(BcProgram* p) 285778bc019dSStefan Eßer { 2858252884aeSStefan Eßer BcInstPtr ip; 2859252884aeSStefan Eßer size_t i; 2860252884aeSStefan Eßer 2861252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2862252884aeSStefan Eßer 2863252884aeSStefan Eßer assert(p != NULL); 2864252884aeSStefan Eßer 286544d4804dSStefan Eßer // We want this clear. 286678bc019dSStefan Eßer // NOLINTNEXTLINE 2867252884aeSStefan Eßer memset(&ip, 0, sizeof(BcInstPtr)); 2868252884aeSStefan Eßer 286944d4804dSStefan Eßer // Setup the globals stacks and the current values. 287078bc019dSStefan Eßer for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) 287178bc019dSStefan Eßer { 287210328f8bSStefan Eßer BcBigDig val = i == BC_PROG_GLOBALS_SCALE ? 0 : BC_BASE; 287344d4804dSStefan Eßer 2874d101cdd6SStefan Eßer #if BC_ENABLED 287544d4804dSStefan Eßer bc_vec_init(p->globals_v + i, sizeof(BcBigDig), BC_DTOR_NONE); 2876252884aeSStefan Eßer bc_vec_push(p->globals_v + i, &val); 2877d101cdd6SStefan Eßer #endif // BC_ENABLED 287844d4804dSStefan Eßer 2879252884aeSStefan Eßer p->globals[i] = val; 2880252884aeSStefan Eßer } 2881252884aeSStefan Eßer 2882252884aeSStefan Eßer #if DC_ENABLED 288344d4804dSStefan Eßer // dc-only setup. 288478bc019dSStefan Eßer if (BC_IS_DC) 288578bc019dSStefan Eßer { 288644d4804dSStefan Eßer bc_vec_init(&p->tail_calls, sizeof(size_t), BC_DTOR_NONE); 2887252884aeSStefan Eßer 288844d4804dSStefan Eßer // We want an item for the main function on the tail call stack. 2889252884aeSStefan Eßer i = 0; 2890252884aeSStefan Eßer bc_vec_push(&p->tail_calls, &i); 2891252884aeSStefan Eßer } 2892252884aeSStefan Eßer #endif // DC_ENABLED 2893252884aeSStefan Eßer 289444d4804dSStefan Eßer bc_num_setup(&p->strmb, p->strmb_num, BC_NUM_BIGDIG_LOG10); 289544d4804dSStefan Eßer bc_num_bigdig2num(&p->strmb, BC_NUM_STREAM_BASE); 289644d4804dSStefan Eßer 2897d101cdd6SStefan Eßer bc_num_init(&p->asciify, BC_NUM_DEF_SIZE); 2898d101cdd6SStefan Eßer 289944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 290044d4804dSStefan Eßer // We need to initialize srand() just in case /dev/urandom and /dev/random 290144d4804dSStefan Eßer // are not available. 2902252884aeSStefan Eßer srand((unsigned int) time(NULL)); 2903252884aeSStefan Eßer bc_rand_init(&p->rng); 290444d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2905252884aeSStefan Eßer 2906252884aeSStefan Eßer #if BC_ENABLED 2907252884aeSStefan Eßer if (BC_IS_BC) bc_num_init(&p->last, BC_NUM_DEF_SIZE); 2908252884aeSStefan Eßer #endif // BC_ENABLED 2909252884aeSStefan Eßer 2910103d7cdfSStefan Eßer #if BC_DEBUG 291144d4804dSStefan Eßer bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_FUNC); 2912103d7cdfSStefan Eßer #else // BC_DEBUG 291344d4804dSStefan Eßer bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_NONE); 2914103d7cdfSStefan Eßer #endif // BC_DEBUG 2915252884aeSStefan Eßer bc_map_init(&p->fn_map); 2916252884aeSStefan Eßer bc_program_insertFunc(p, bc_func_main); 2917252884aeSStefan Eßer bc_program_insertFunc(p, bc_func_read); 2918252884aeSStefan Eßer 291944d4804dSStefan Eßer bc_vec_init(&p->vars, sizeof(BcVec), BC_DTOR_VEC); 2920252884aeSStefan Eßer bc_map_init(&p->var_map); 2921252884aeSStefan Eßer 292244d4804dSStefan Eßer bc_vec_init(&p->arrs, sizeof(BcVec), BC_DTOR_VEC); 2923252884aeSStefan Eßer bc_map_init(&p->arr_map); 2924252884aeSStefan Eßer 292544d4804dSStefan Eßer bc_vec_init(&p->results, sizeof(BcResult), BC_DTOR_RESULT); 292644d4804dSStefan Eßer 292744d4804dSStefan Eßer // Push the first instruction pointer onto the execution stack. 292844d4804dSStefan Eßer bc_vec_init(&p->stack, sizeof(BcInstPtr), BC_DTOR_NONE); 2929252884aeSStefan Eßer bc_vec_push(&p->stack, &ip); 29303aa99676SStefan Eßer 2931d101cdd6SStefan Eßer bc_vec_init(&p->consts, sizeof(BcConst), BC_DTOR_CONST); 2932d101cdd6SStefan Eßer bc_map_init(&p->const_map); 2933d101cdd6SStefan Eßer bc_vec_init(&p->strs, sizeof(char*), BC_DTOR_NONE); 2934d101cdd6SStefan Eßer bc_map_init(&p->str_map); 2935d101cdd6SStefan Eßer } 29363aa99676SStefan Eßer 2937d101cdd6SStefan Eßer void 2938d101cdd6SStefan Eßer bc_program_printStackTrace(BcProgram* p) 2939d101cdd6SStefan Eßer { 2940d101cdd6SStefan Eßer size_t i, max_digits; 2941d101cdd6SStefan Eßer 2942d101cdd6SStefan Eßer max_digits = bc_vm_numDigits(p->stack.len - 1); 2943d101cdd6SStefan Eßer 2944d101cdd6SStefan Eßer for (i = 0; i < p->stack.len; ++i) 2945d101cdd6SStefan Eßer { 2946d101cdd6SStefan Eßer BcInstPtr* ip = bc_vec_item_rev(&p->stack, i); 2947d101cdd6SStefan Eßer BcFunc* f = bc_vec_item(&p->fns, ip->func); 2948d101cdd6SStefan Eßer size_t j, digits; 2949d101cdd6SStefan Eßer 2950d101cdd6SStefan Eßer digits = bc_vm_numDigits(i); 2951d101cdd6SStefan Eßer 2952d101cdd6SStefan Eßer bc_file_puts(&vm->ferr, bc_flush_none, " "); 2953d101cdd6SStefan Eßer 2954d101cdd6SStefan Eßer for (j = 0; j < max_digits - digits; ++j) 2955d101cdd6SStefan Eßer { 2956d101cdd6SStefan Eßer bc_file_putchar(&vm->ferr, bc_flush_none, ' '); 2957d101cdd6SStefan Eßer } 2958d101cdd6SStefan Eßer 2959d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, "%zu: %s", i, f->name); 2960d101cdd6SStefan Eßer 2961d101cdd6SStefan Eßer #if BC_ENABLED 2962d101cdd6SStefan Eßer if (BC_IS_BC && ip->func != BC_PROG_MAIN && ip->func != BC_PROG_READ) 2963d101cdd6SStefan Eßer { 2964d101cdd6SStefan Eßer bc_file_puts(&vm->ferr, bc_flush_none, "()"); 2965d101cdd6SStefan Eßer } 2966d101cdd6SStefan Eßer #endif // BC_ENABLED 2967d101cdd6SStefan Eßer 2968d101cdd6SStefan Eßer bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 2969d101cdd6SStefan Eßer } 2970252884aeSStefan Eßer } 2971252884aeSStefan Eßer 297278bc019dSStefan Eßer void 297378bc019dSStefan Eßer bc_program_reset(BcProgram* p) 297478bc019dSStefan Eßer { 2975252884aeSStefan Eßer BcFunc* f; 2976252884aeSStefan Eßer BcInstPtr* ip; 2977252884aeSStefan Eßer 2978252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2979252884aeSStefan Eßer 2980*12e0d316SStefan Eßer // Pop all but the last execution. 2981252884aeSStefan Eßer bc_vec_npop(&p->stack, p->stack.len - 1); 2982252884aeSStefan Eßer 2983d101cdd6SStefan Eßer #if DC_ENABLED 2984d101cdd6SStefan Eßer // We need to pop tail calls too. 2985d101cdd6SStefan Eßer if (BC_IS_DC) bc_vec_npop(&p->tail_calls, p->tail_calls.len - 1); 2986d101cdd6SStefan Eßer #endif // DC_ENABLED 2987d101cdd6SStefan Eßer 29883aa99676SStefan Eßer #if BC_ENABLED 2989*12e0d316SStefan Eßer // Clear the stack if we are in bc. We have to do this in bc because bc's 2990*12e0d316SStefan Eßer // stack is implicit. 2991*12e0d316SStefan Eßer // 2992*12e0d316SStefan Eßer // XXX: We don't do this in dc because other dc implementations don't. 2993*12e0d316SStefan Eßer if (BC_IS_BC || !BC_I) bc_vec_popAll(&p->results); 2994*12e0d316SStefan Eßer 299544d4804dSStefan Eßer // Clear the globals' stacks. 2996252884aeSStefan Eßer if (BC_G) bc_program_popGlobals(p, true); 29973aa99676SStefan Eßer #endif // BC_ENABLED 2998252884aeSStefan Eßer 299944d4804dSStefan Eßer // Clear the bytecode vector of the main function. 3000252884aeSStefan Eßer f = bc_vec_item(&p->fns, BC_PROG_MAIN); 300110328f8bSStefan Eßer bc_vec_npop(&f->code, f->code.len); 300244d4804dSStefan Eßer 300344d4804dSStefan Eßer // Reset the instruction pointer. 3004252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 300578bc019dSStefan Eßer // NOLINTNEXTLINE 300610328f8bSStefan Eßer memset(ip, 0, sizeof(BcInstPtr)); 3007252884aeSStefan Eßer 3008d101cdd6SStefan Eßer if (BC_SIG_INTERRUPT(vm)) 300978bc019dSStefan Eßer { 3010d101cdd6SStefan Eßer // Write the ready message for a signal. 3011d101cdd6SStefan Eßer bc_file_printf(&vm->fout, "%s", bc_program_ready_msg); 3012d101cdd6SStefan Eßer bc_file_flush(&vm->fout, bc_flush_err); 3013252884aeSStefan Eßer } 3014d101cdd6SStefan Eßer 3015d101cdd6SStefan Eßer // Clear the signal. 3016d101cdd6SStefan Eßer vm->sig = 0; 3017252884aeSStefan Eßer } 3018252884aeSStefan Eßer 301978bc019dSStefan Eßer void 302078bc019dSStefan Eßer bc_program_exec(BcProgram* p) 302178bc019dSStefan Eßer { 3022252884aeSStefan Eßer size_t idx; 302378bc019dSStefan Eßer BcResult r; 302478bc019dSStefan Eßer BcResult* ptr; 302544d4804dSStefan Eßer BcInstPtr* ip; 302644d4804dSStefan Eßer BcFunc* func; 302744d4804dSStefan Eßer char* code; 3028252884aeSStefan Eßer bool cond = false; 302944d4804dSStefan Eßer uchar inst; 3030252884aeSStefan Eßer #if BC_ENABLED 3031252884aeSStefan Eßer BcNum* num; 3032252884aeSStefan Eßer #endif // BC_ENABLED 303344d4804dSStefan Eßer #if !BC_HAS_COMPUTED_GOTO 3034103d7cdfSStefan Eßer #if BC_DEBUG 3035252884aeSStefan Eßer size_t jmp_bufs_len; 3036103d7cdfSStefan Eßer #endif // BC_DEBUG 303744d4804dSStefan Eßer #endif // !BC_HAS_COMPUTED_GOTO 303844d4804dSStefan Eßer 303944d4804dSStefan Eßer #if BC_HAS_COMPUTED_GOTO 3040d101cdd6SStefan Eßer 3041d101cdd6SStefan Eßer #if BC_GCC 3042d101cdd6SStefan Eßer #pragma GCC diagnostic ignored "-Wpedantic" 3043d101cdd6SStefan Eßer #endif // BC_GCC 3044d101cdd6SStefan Eßer 3045d101cdd6SStefan Eßer #if BC_CLANG 3046d101cdd6SStefan Eßer #pragma clang diagnostic ignored "-Wgnu-label-as-value" 3047d101cdd6SStefan Eßer #endif // BC_CLANG 3048d101cdd6SStefan Eßer 304944d4804dSStefan Eßer BC_PROG_LBLS; 305044d4804dSStefan Eßer BC_PROG_LBLS_ASSERT; 305144d4804dSStefan Eßer 3052d101cdd6SStefan Eßer #if BC_CLANG 3053d101cdd6SStefan Eßer #pragma clang diagnostic warning "-Wgnu-label-as-value" 3054d101cdd6SStefan Eßer #endif // BC_CLANG 3055d101cdd6SStefan Eßer 3056d101cdd6SStefan Eßer #if BC_GCC 3057d101cdd6SStefan Eßer #pragma GCC diagnostic warning "-Wpedantic" 3058d101cdd6SStefan Eßer #endif // BC_GCC 3059d101cdd6SStefan Eßer 306044d4804dSStefan Eßer // BC_INST_INVALID is a marker for the end so that we don't have to have an 306144d4804dSStefan Eßer // execution loop. 306244d4804dSStefan Eßer func = (BcFunc*) bc_vec_item(&p->fns, BC_PROG_MAIN); 306344d4804dSStefan Eßer bc_vec_pushByte(&func->code, BC_INST_INVALID); 306444d4804dSStefan Eßer #endif // BC_HAS_COMPUTED_GOTO 306544d4804dSStefan Eßer 3066d101cdd6SStefan Eßer BC_SETJMP(vm, end); 3067d101cdd6SStefan Eßer 306844d4804dSStefan Eßer ip = bc_vec_top(&p->stack); 306944d4804dSStefan Eßer func = (BcFunc*) bc_vec_item(&p->fns, ip->func); 307044d4804dSStefan Eßer code = func->code.v; 307144d4804dSStefan Eßer 307244d4804dSStefan Eßer #if !BC_HAS_COMPUTED_GOTO 3073252884aeSStefan Eßer 3074103d7cdfSStefan Eßer #if BC_DEBUG 3075d101cdd6SStefan Eßer jmp_bufs_len = vm->jmp_bufs.len; 3076103d7cdfSStefan Eßer #endif // BC_DEBUG 3077252884aeSStefan Eßer 307844d4804dSStefan Eßer // This loop is the heart of the execution engine. It *is* the engine. For 307944d4804dSStefan Eßer // computed goto, it is ignored. 308044d4804dSStefan Eßer while (ip->idx < func->code.len) 308144d4804dSStefan Eßer #endif // !BC_HAS_COMPUTED_GOTO 308244d4804dSStefan Eßer { 3083252884aeSStefan Eßer BC_SIG_ASSERT_NOT_LOCKED; 3084252884aeSStefan Eßer 308544d4804dSStefan Eßer #if BC_HAS_COMPUTED_GOTO 3086252884aeSStefan Eßer 3087d101cdd6SStefan Eßer #if BC_GCC 3088d101cdd6SStefan Eßer #pragma GCC diagnostic ignored "-Wpedantic" 3089d101cdd6SStefan Eßer #endif // BC_GCC 3090d101cdd6SStefan Eßer 3091d101cdd6SStefan Eßer #if BC_CLANG 3092d101cdd6SStefan Eßer #pragma clang diagnostic ignored "-Wgnu-label-as-value" 3093d101cdd6SStefan Eßer #endif // BC_CLANG 3094d101cdd6SStefan Eßer 309544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 309644d4804dSStefan Eßer 309744d4804dSStefan Eßer #else // BC_HAS_COMPUTED_GOTO 309844d4804dSStefan Eßer 309944d4804dSStefan Eßer // Get the next instruction and increment the index. 310044d4804dSStefan Eßer inst = (uchar) code[(ip->idx)++]; 310144d4804dSStefan Eßer 310244d4804dSStefan Eßer #endif // BC_HAS_COMPUTED_GOTO 310344d4804dSStefan Eßer 310444d4804dSStefan Eßer #if BC_DEBUG_CODE 3105d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, "inst: %s\n", bc_inst_names[inst]); 3106d101cdd6SStefan Eßer bc_file_flush(&vm->ferr, bc_flush_none); 310744d4804dSStefan Eßer #endif // BC_DEBUG_CODE 310844d4804dSStefan Eßer 310944d4804dSStefan Eßer #if !BC_HAS_COMPUTED_GOTO 311044d4804dSStefan Eßer switch (inst) 311144d4804dSStefan Eßer #endif // !BC_HAS_COMPUTED_GOTO 311244d4804dSStefan Eßer { 3113252884aeSStefan Eßer #if BC_ENABLED 311444d4804dSStefan Eßer // This just sets up the condition for the unconditional jump below, 311544d4804dSStefan Eßer // which checks the condition, if necessary. 311678bc019dSStefan Eßer // clang-format off 311744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_JUMP_ZERO): 311878bc019dSStefan Eßer // clang-format on 3119252884aeSStefan Eßer { 3120252884aeSStefan Eßer bc_program_prep(p, &ptr, &num, 0); 312144d4804dSStefan Eßer 3122252884aeSStefan Eßer cond = !bc_num_cmpZero(num); 3123252884aeSStefan Eßer bc_vec_pop(&p->results); 312444d4804dSStefan Eßer 312544d4804dSStefan Eßer BC_PROG_DIRECT_JUMP(BC_INST_JUMP) 3126252884aeSStefan Eßer } 3127252884aeSStefan Eßer // Fallthrough. 312844d4804dSStefan Eßer BC_PROG_FALLTHROUGH 312950696a6eSStefan Eßer 313078bc019dSStefan Eßer // clang-format off 313144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_JUMP): 313278bc019dSStefan Eßer // clang-format on 3133252884aeSStefan Eßer { 3134252884aeSStefan Eßer idx = bc_program_index(code, &ip->idx); 3135252884aeSStefan Eßer 313644d4804dSStefan Eßer // If a jump is required... 313778bc019dSStefan Eßer if (inst == BC_INST_JUMP || cond) 313878bc019dSStefan Eßer { 313944d4804dSStefan Eßer // Get the address to jump to. 3140252884aeSStefan Eßer size_t* addr = bc_vec_item(&func->labels, idx); 3141252884aeSStefan Eßer 314244d4804dSStefan Eßer // If this fails, then the parser failed to set up the 314344d4804dSStefan Eßer // labels correctly. 3144252884aeSStefan Eßer assert(*addr != SIZE_MAX); 3145252884aeSStefan Eßer 314644d4804dSStefan Eßer // Set the new address. 3147252884aeSStefan Eßer ip->idx = *addr; 3148252884aeSStefan Eßer } 3149252884aeSStefan Eßer 315044d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3151252884aeSStefan Eßer } 3152252884aeSStefan Eßer 315378bc019dSStefan Eßer // clang-format off 315444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_CALL): 315578bc019dSStefan Eßer // clang-format on 3156252884aeSStefan Eßer { 3157252884aeSStefan Eßer assert(BC_IS_BC); 3158252884aeSStefan Eßer 3159252884aeSStefan Eßer bc_program_call(p, code, &ip->idx); 3160252884aeSStefan Eßer 316144d4804dSStefan Eßer // Because we changed the execution stack and where we are 316244d4804dSStefan Eßer // executing, we have to update all of this. 316310041e99SStefan Eßer BC_SIG_LOCK; 3164252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 3165252884aeSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 3166252884aeSStefan Eßer code = func->code.v; 316710041e99SStefan Eßer BC_SIG_UNLOCK; 3168252884aeSStefan Eßer 316944d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3170252884aeSStefan Eßer } 3171252884aeSStefan Eßer 317278bc019dSStefan Eßer // clang-format off 317344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_INC): 317444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_DEC): 317578bc019dSStefan Eßer // clang-format on 3176252884aeSStefan Eßer { 3177252884aeSStefan Eßer bc_program_incdec(p, inst); 317844d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3179252884aeSStefan Eßer } 3180252884aeSStefan Eßer 318178bc019dSStefan Eßer // clang-format off 318244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_HALT): 318378bc019dSStefan Eßer // clang-format on 3184252884aeSStefan Eßer { 3185d101cdd6SStefan Eßer vm->status = BC_STATUS_QUIT; 318644d4804dSStefan Eßer 318744d4804dSStefan Eßer // Just jump out. The jump series will take care of everything. 318844d4804dSStefan Eßer BC_JMP; 318944d4804dSStefan Eßer 319044d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3191252884aeSStefan Eßer } 3192252884aeSStefan Eßer 319378bc019dSStefan Eßer // clang-format off 319444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_RET): 319544d4804dSStefan Eßer BC_PROG_LBL(BC_INST_RET0): 319644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_RET_VOID): 319778bc019dSStefan Eßer // clang-format on 3198252884aeSStefan Eßer { 3199252884aeSStefan Eßer bc_program_return(p, inst); 3200252884aeSStefan Eßer 320144d4804dSStefan Eßer // Because we changed the execution stack and where we are 320244d4804dSStefan Eßer // executing, we have to update all of this. 320310041e99SStefan Eßer BC_SIG_LOCK; 3204252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 3205252884aeSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 3206252884aeSStefan Eßer code = func->code.v; 320710041e99SStefan Eßer BC_SIG_UNLOCK; 3208252884aeSStefan Eßer 320944d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3210252884aeSStefan Eßer } 3211252884aeSStefan Eßer #endif // BC_ENABLED 3212252884aeSStefan Eßer 321378bc019dSStefan Eßer // clang-format off 321444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_BOOL_OR): 321544d4804dSStefan Eßer BC_PROG_LBL(BC_INST_BOOL_AND): 321644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REL_EQ): 321744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REL_LE): 321844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REL_GE): 321944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REL_NE): 322044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REL_LT): 322144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REL_GT): 322278bc019dSStefan Eßer // clang-format on 3223252884aeSStefan Eßer { 3224252884aeSStefan Eßer bc_program_logical(p, inst); 322544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3226252884aeSStefan Eßer } 3227252884aeSStefan Eßer 322878bc019dSStefan Eßer // clang-format off 322944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_READ): 323078bc019dSStefan Eßer // clang-format on 3231252884aeSStefan Eßer { 32329300e880SStefan Eßer // We want to flush output before 32339300e880SStefan Eßer // this in case there is a prompt. 3234d101cdd6SStefan Eßer bc_file_flush(&vm->fout, bc_flush_save); 32359300e880SStefan Eßer 3236252884aeSStefan Eßer bc_program_read(p); 3237252884aeSStefan Eßer 323844d4804dSStefan Eßer // Because we changed the execution stack and where we are 323944d4804dSStefan Eßer // executing, we have to update all of this. 324010041e99SStefan Eßer BC_SIG_LOCK; 3241252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 3242252884aeSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 3243252884aeSStefan Eßer code = func->code.v; 324410041e99SStefan Eßer BC_SIG_UNLOCK; 3245252884aeSStefan Eßer 324644d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3247252884aeSStefan Eßer } 3248252884aeSStefan Eßer 324944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 325078bc019dSStefan Eßer // clang-format off 325144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_RAND): 325278bc019dSStefan Eßer // clang-format on 3253252884aeSStefan Eßer { 3254252884aeSStefan Eßer bc_program_rand(p); 325544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3256252884aeSStefan Eßer } 325744d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 3258252884aeSStefan Eßer 325978bc019dSStefan Eßer // clang-format off 326044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MAXIBASE): 326144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MAXOBASE): 326244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MAXSCALE): 326344d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 326444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MAXRAND): 326544d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 326678bc019dSStefan Eßer // clang-format on 3267252884aeSStefan Eßer { 3268d101cdd6SStefan Eßer BcBigDig dig = vm->maxes[inst - BC_INST_MAXIBASE]; 3269252884aeSStefan Eßer bc_program_pushBigdig(p, dig, BC_RESULT_TEMP); 327044d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3271252884aeSStefan Eßer } 3272252884aeSStefan Eßer 327378bc019dSStefan Eßer // clang-format off 3274d43fa8efSStefan Eßer BC_PROG_LBL(BC_INST_LINE_LENGTH): 3275d43fa8efSStefan Eßer #if BC_ENABLED 3276d43fa8efSStefan Eßer BC_PROG_LBL(BC_INST_GLOBAL_STACKS): 3277d43fa8efSStefan Eßer #endif // BC_ENABLED 3278103d7cdfSStefan Eßer #if DC_ENABLED 3279103d7cdfSStefan Eßer BC_PROG_LBL(BC_INST_EXTENDED_REGISTERS): 3280103d7cdfSStefan Eßer #endif // DC_ENABLE 3281d43fa8efSStefan Eßer BC_PROG_LBL(BC_INST_LEADING_ZERO): 328278bc019dSStefan Eßer // clang-format on 3283d43fa8efSStefan Eßer { 3284d43fa8efSStefan Eßer bc_program_globalSetting(p, inst); 3285d43fa8efSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3286d43fa8efSStefan Eßer } 3287d43fa8efSStefan Eßer 328878bc019dSStefan Eßer // clang-format off 328944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_VAR): 329078bc019dSStefan Eßer // clang-format on 3291252884aeSStefan Eßer { 3292252884aeSStefan Eßer bc_program_pushVar(p, code, &ip->idx, false, false); 329344d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3294252884aeSStefan Eßer } 3295252884aeSStefan Eßer 329678bc019dSStefan Eßer // clang-format off 329744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ARRAY_ELEM): 329844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ARRAY): 329978bc019dSStefan Eßer // clang-format on 3300252884aeSStefan Eßer { 3301252884aeSStefan Eßer bc_program_pushArray(p, code, &ip->idx, inst); 330244d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3303252884aeSStefan Eßer } 3304252884aeSStefan Eßer 330578bc019dSStefan Eßer // clang-format off 330644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_IBASE): 330744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_SCALE): 330844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_OBASE): 330978bc019dSStefan Eßer // clang-format on 3310252884aeSStefan Eßer { 3311252884aeSStefan Eßer bc_program_pushGlobal(p, inst); 331244d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3313252884aeSStefan Eßer } 3314252884aeSStefan Eßer 331544d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 331678bc019dSStefan Eßer // clang-format off 331744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_SEED): 331878bc019dSStefan Eßer // clang-format on 3319252884aeSStefan Eßer { 3320252884aeSStefan Eßer bc_program_pushSeed(p); 332144d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3322252884aeSStefan Eßer } 332344d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 3324252884aeSStefan Eßer 332578bc019dSStefan Eßer // clang-format off 332644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_LENGTH): 332744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_SCALE_FUNC): 332844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_SQRT): 332944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ABS): 3330d101cdd6SStefan Eßer BC_PROG_LBL(BC_INST_IS_NUMBER): 3331d101cdd6SStefan Eßer BC_PROG_LBL(BC_INST_IS_STRING): 333244d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 333344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_IRAND): 333444d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 333578bc019dSStefan Eßer // clang-format on 3336252884aeSStefan Eßer { 3337252884aeSStefan Eßer bc_program_builtin(p, inst); 333844d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3339252884aeSStefan Eßer } 3340252884aeSStefan Eßer 334178bc019dSStefan Eßer // clang-format off 334244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASCIIFY): 334378bc019dSStefan Eßer // clang-format on 334444d4804dSStefan Eßer { 3345d101cdd6SStefan Eßer bc_program_asciify(p); 334644d4804dSStefan Eßer 334744d4804dSStefan Eßer // Because we changed the execution stack and where we are 334844d4804dSStefan Eßer // executing, we have to update all of this. 334910041e99SStefan Eßer BC_SIG_LOCK; 335044d4804dSStefan Eßer ip = bc_vec_top(&p->stack); 335144d4804dSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 335244d4804dSStefan Eßer code = func->code.v; 335310041e99SStefan Eßer BC_SIG_UNLOCK; 335444d4804dSStefan Eßer 335544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 335644d4804dSStefan Eßer } 335744d4804dSStefan Eßer 335878bc019dSStefan Eßer // clang-format off 335944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_NUM): 336078bc019dSStefan Eßer // clang-format on 3361252884aeSStefan Eßer { 33623aa99676SStefan Eßer bc_program_const(p, code, &ip->idx); 336344d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3364252884aeSStefan Eßer } 3365252884aeSStefan Eßer 336678bc019dSStefan Eßer // clang-format off 336744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ZERO): 336844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ONE): 3369252884aeSStefan Eßer #if BC_ENABLED 337044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_LAST): 3371252884aeSStefan Eßer #endif // BC_ENABLED 337278bc019dSStefan Eßer // clang-format on 3373252884aeSStefan Eßer { 33743aa99676SStefan Eßer r.t = BC_RESULT_ZERO + (inst - BC_INST_ZERO); 3375252884aeSStefan Eßer bc_vec_push(&p->results, &r); 337644d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3377252884aeSStefan Eßer } 3378252884aeSStefan Eßer 337978bc019dSStefan Eßer // clang-format off 338044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PRINT): 338144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PRINT_POP): 338244d4804dSStefan Eßer #if BC_ENABLED 338344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PRINT_STR): 338444d4804dSStefan Eßer #endif // BC_ENABLED 338578bc019dSStefan Eßer // clang-format on 3386252884aeSStefan Eßer { 3387252884aeSStefan Eßer bc_program_print(p, inst, 0); 338844d4804dSStefan Eßer 338944d4804dSStefan Eßer // We want to flush right away to save the output for history, 339044d4804dSStefan Eßer // if history must preserve it when taking input. 3391d101cdd6SStefan Eßer bc_file_flush(&vm->fout, bc_flush_save); 339244d4804dSStefan Eßer 339344d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3394252884aeSStefan Eßer } 3395252884aeSStefan Eßer 339678bc019dSStefan Eßer // clang-format off 339744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_STR): 339878bc019dSStefan Eßer // clang-format on 3399252884aeSStefan Eßer { 340044d4804dSStefan Eßer // Set up the result and push. 3401252884aeSStefan Eßer r.t = BC_RESULT_STR; 340244d4804dSStefan Eßer bc_num_clear(&r.d.n); 340344d4804dSStefan Eßer r.d.n.scale = bc_program_index(code, &ip->idx); 3404252884aeSStefan Eßer bc_vec_push(&p->results, &r); 340544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3406252884aeSStefan Eßer } 3407252884aeSStefan Eßer 340878bc019dSStefan Eßer // clang-format off 340944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_POWER): 341044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MULTIPLY): 341144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_DIVIDE): 341244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MODULUS): 341344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PLUS): 341444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MINUS): 3415252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 341644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PLACES): 341744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_LSHIFT): 341844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_RSHIFT): 3419252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 342078bc019dSStefan Eßer // clang-format on 3421252884aeSStefan Eßer { 3422252884aeSStefan Eßer bc_program_op(p, inst); 342344d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3424252884aeSStefan Eßer } 3425252884aeSStefan Eßer 342678bc019dSStefan Eßer // clang-format off 342744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_NEG): 342844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_BOOL_NOT): 3429252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 343044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_TRUNC): 3431252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 343278bc019dSStefan Eßer // clang-format on 3433252884aeSStefan Eßer { 3434252884aeSStefan Eßer bc_program_unary(p, inst); 343544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3436252884aeSStefan Eßer } 3437252884aeSStefan Eßer 343878bc019dSStefan Eßer // clang-format off 3439252884aeSStefan Eßer #if BC_ENABLED 344044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_POWER): 344144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY): 344244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE): 344344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_MODULUS): 344444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_PLUS): 344544d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_MINUS): 3446252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 344744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_PLACES): 344844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT): 344944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT): 3450252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 345144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN): 345244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_POWER_NO_VAL): 345344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY_NO_VAL): 345444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE_NO_VAL): 345544d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_MODULUS_NO_VAL): 345644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_PLUS_NO_VAL): 345744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_MINUS_NO_VAL): 3458252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 345944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_PLACES_NO_VAL): 346044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT_NO_VAL): 346144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT_NO_VAL): 3462252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 3463252884aeSStefan Eßer #endif // BC_ENABLED 346444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_ASSIGN_NO_VAL): 346578bc019dSStefan Eßer // clang-format on 3466252884aeSStefan Eßer { 3467252884aeSStefan Eßer bc_program_assign(p, inst); 346844d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3469252884aeSStefan Eßer } 3470252884aeSStefan Eßer 347178bc019dSStefan Eßer // clang-format off 347244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_POP): 347378bc019dSStefan Eßer // clang-format on 3474252884aeSStefan Eßer { 3475252884aeSStefan Eßer #ifndef BC_PROG_NO_STACK_CHECK 347644d4804dSStefan Eßer // dc must do a stack check, but bc does not. 347778bc019dSStefan Eßer if (BC_IS_DC) 347878bc019dSStefan Eßer { 3479252884aeSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) 348078bc019dSStefan Eßer { 348144d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 3482252884aeSStefan Eßer } 348378bc019dSStefan Eßer } 3484252884aeSStefan Eßer #endif // BC_PROG_NO_STACK_CHECK 3485252884aeSStefan Eßer 3486252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 3487252884aeSStefan Eßer 3488252884aeSStefan Eßer bc_vec_pop(&p->results); 348944d4804dSStefan Eßer 349044d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 349144d4804dSStefan Eßer } 349244d4804dSStefan Eßer 349378bc019dSStefan Eßer // clang-format off 349444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_SWAP): 349578bc019dSStefan Eßer // clang-format on 349644d4804dSStefan Eßer { 349744d4804dSStefan Eßer BcResult* ptr2; 349844d4804dSStefan Eßer 349944d4804dSStefan Eßer // Check the stack. 350044d4804dSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 2))) 350178bc019dSStefan Eßer { 350244d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 350378bc019dSStefan Eßer } 350444d4804dSStefan Eßer 350544d4804dSStefan Eßer assert(BC_PROG_STACK(&p->results, 2)); 350644d4804dSStefan Eßer 350744d4804dSStefan Eßer // Get the two items. 350844d4804dSStefan Eßer ptr = bc_vec_item_rev(&p->results, 0); 350944d4804dSStefan Eßer ptr2 = bc_vec_item_rev(&p->results, 1); 351044d4804dSStefan Eßer 351144d4804dSStefan Eßer // Swap. It's just easiest to do it this way. 351278bc019dSStefan Eßer // NOLINTNEXTLINE 351344d4804dSStefan Eßer memcpy(&r, ptr, sizeof(BcResult)); 351478bc019dSStefan Eßer // NOLINTNEXTLINE 351544d4804dSStefan Eßer memcpy(ptr, ptr2, sizeof(BcResult)); 351678bc019dSStefan Eßer // NOLINTNEXTLINE 351744d4804dSStefan Eßer memcpy(ptr2, &r, sizeof(BcResult)); 351844d4804dSStefan Eßer 351944d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 352044d4804dSStefan Eßer } 352144d4804dSStefan Eßer 352278bc019dSStefan Eßer // clang-format off 352344d4804dSStefan Eßer BC_PROG_LBL(BC_INST_MODEXP): 352478bc019dSStefan Eßer // clang-format on 352544d4804dSStefan Eßer { 352644d4804dSStefan Eßer bc_program_modexp(p); 352744d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 352844d4804dSStefan Eßer } 352944d4804dSStefan Eßer 353078bc019dSStefan Eßer // clang-format off 353144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_DIVMOD): 353278bc019dSStefan Eßer // clang-format on 353344d4804dSStefan Eßer { 353444d4804dSStefan Eßer bc_program_divmod(p); 353544d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 353644d4804dSStefan Eßer } 353744d4804dSStefan Eßer 353878bc019dSStefan Eßer // clang-format off 353944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PRINT_STREAM): 354078bc019dSStefan Eßer // clang-format on 354144d4804dSStefan Eßer { 354244d4804dSStefan Eßer bc_program_printStream(p); 354344d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3544252884aeSStefan Eßer } 3545252884aeSStefan Eßer 3546252884aeSStefan Eßer #if DC_ENABLED 354778bc019dSStefan Eßer // clang-format off 354844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_POP_EXEC): 354978bc019dSStefan Eßer // clang-format on 3550252884aeSStefan Eßer { 355144d4804dSStefan Eßer // If this fails, the dc parser got something wrong. 3552252884aeSStefan Eßer assert(BC_PROG_STACK(&p->stack, 2)); 355344d4804dSStefan Eßer 355444d4804dSStefan Eßer // Pop the execution stack and tail call stack. 3555252884aeSStefan Eßer bc_vec_pop(&p->stack); 3556252884aeSStefan Eßer bc_vec_pop(&p->tail_calls); 355744d4804dSStefan Eßer 355844d4804dSStefan Eßer // Because we changed the execution stack and where we are 355944d4804dSStefan Eßer // executing, we have to update all of this. 356010041e99SStefan Eßer BC_SIG_LOCK; 3561252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 3562252884aeSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 3563252884aeSStefan Eßer code = func->code.v; 356410041e99SStefan Eßer BC_SIG_UNLOCK; 356544d4804dSStefan Eßer 356644d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3567252884aeSStefan Eßer } 3568252884aeSStefan Eßer 356978bc019dSStefan Eßer // clang-format off 357044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_EXECUTE): 357144d4804dSStefan Eßer BC_PROG_LBL(BC_INST_EXEC_COND): 357278bc019dSStefan Eßer // clang-format on 3573252884aeSStefan Eßer { 3574252884aeSStefan Eßer cond = (inst == BC_INST_EXEC_COND); 357544d4804dSStefan Eßer 3576252884aeSStefan Eßer bc_program_execStr(p, code, &ip->idx, cond, func->code.len); 357744d4804dSStefan Eßer 357844d4804dSStefan Eßer // Because we changed the execution stack and where we are 357944d4804dSStefan Eßer // executing, we have to update all of this. 358010041e99SStefan Eßer BC_SIG_LOCK; 3581252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 3582252884aeSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 3583252884aeSStefan Eßer code = func->code.v; 358410041e99SStefan Eßer BC_SIG_UNLOCK; 358544d4804dSStefan Eßer 358644d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3587252884aeSStefan Eßer } 3588252884aeSStefan Eßer 358978bc019dSStefan Eßer // clang-format off 359044d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PRINT_STACK): 359178bc019dSStefan Eßer // clang-format on 3592252884aeSStefan Eßer { 3593252884aeSStefan Eßer bc_program_printStack(p); 359444d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3595252884aeSStefan Eßer } 3596252884aeSStefan Eßer 359778bc019dSStefan Eßer // clang-format off 359844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_CLEAR_STACK): 359978bc019dSStefan Eßer // clang-format on 3600252884aeSStefan Eßer { 360110328f8bSStefan Eßer bc_vec_popAll(&p->results); 360244d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3603252884aeSStefan Eßer } 3604252884aeSStefan Eßer 360578bc019dSStefan Eßer // clang-format off 360644d4804dSStefan Eßer BC_PROG_LBL(BC_INST_REG_STACK_LEN): 360778bc019dSStefan Eßer // clang-format on 360844d4804dSStefan Eßer { 360944d4804dSStefan Eßer bc_program_regStackLen(p, code, &ip->idx); 361044d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 361144d4804dSStefan Eßer } 361244d4804dSStefan Eßer 361378bc019dSStefan Eßer // clang-format off 361444d4804dSStefan Eßer BC_PROG_LBL(BC_INST_STACK_LEN): 361578bc019dSStefan Eßer // clang-format on 3616252884aeSStefan Eßer { 3617252884aeSStefan Eßer bc_program_stackLen(p); 361844d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3619252884aeSStefan Eßer } 3620252884aeSStefan Eßer 362178bc019dSStefan Eßer // clang-format off 362244d4804dSStefan Eßer BC_PROG_LBL(BC_INST_DUPLICATE): 362378bc019dSStefan Eßer // clang-format on 3624252884aeSStefan Eßer { 362544d4804dSStefan Eßer // Check the stack. 3626252884aeSStefan Eßer if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) 362778bc019dSStefan Eßer { 362844d4804dSStefan Eßer bc_err(BC_ERR_EXEC_STACK); 362978bc019dSStefan Eßer } 3630252884aeSStefan Eßer 3631252884aeSStefan Eßer assert(BC_PROG_STACK(&p->results, 1)); 3632252884aeSStefan Eßer 363344d4804dSStefan Eßer // Get the top of the stack. 3634252884aeSStefan Eßer ptr = bc_vec_top(&p->results); 3635252884aeSStefan Eßer 3636252884aeSStefan Eßer BC_SIG_LOCK; 3637252884aeSStefan Eßer 363844d4804dSStefan Eßer // Copy and push. 3639252884aeSStefan Eßer bc_result_copy(&r, ptr); 3640252884aeSStefan Eßer bc_vec_push(&p->results, &r); 3641252884aeSStefan Eßer 3642252884aeSStefan Eßer BC_SIG_UNLOCK; 3643252884aeSStefan Eßer 364444d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3645252884aeSStefan Eßer } 3646252884aeSStefan Eßer 364778bc019dSStefan Eßer // clang-format off 364844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_LOAD): 364944d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PUSH_VAR): 365078bc019dSStefan Eßer // clang-format on 3651252884aeSStefan Eßer { 3652252884aeSStefan Eßer bool copy = (inst == BC_INST_LOAD); 3653252884aeSStefan Eßer bc_program_pushVar(p, code, &ip->idx, true, copy); 365444d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3655252884aeSStefan Eßer } 3656252884aeSStefan Eßer 365778bc019dSStefan Eßer // clang-format off 365844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_PUSH_TO_VAR): 365978bc019dSStefan Eßer // clang-format on 3660252884aeSStefan Eßer { 3661252884aeSStefan Eßer idx = bc_program_index(code, &ip->idx); 3662d101cdd6SStefan Eßer bc_program_copyToVar(p, idx, BC_TYPE_VAR); 366344d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3664252884aeSStefan Eßer } 3665252884aeSStefan Eßer 366678bc019dSStefan Eßer // clang-format off 366744d4804dSStefan Eßer BC_PROG_LBL(BC_INST_QUIT): 366844d4804dSStefan Eßer BC_PROG_LBL(BC_INST_NQUIT): 366978bc019dSStefan Eßer // clang-format on 3670252884aeSStefan Eßer { 3671252884aeSStefan Eßer bc_program_nquit(p, inst); 367244d4804dSStefan Eßer 367344d4804dSStefan Eßer // Because we changed the execution stack and where we are 367444d4804dSStefan Eßer // executing, we have to update all of this. 367510041e99SStefan Eßer BC_SIG_LOCK; 3676252884aeSStefan Eßer ip = bc_vec_top(&p->stack); 3677252884aeSStefan Eßer func = bc_vec_item(&p->fns, ip->func); 3678252884aeSStefan Eßer code = func->code.v; 367910041e99SStefan Eßer BC_SIG_UNLOCK; 368044d4804dSStefan Eßer 368144d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 3682252884aeSStefan Eßer } 3683252884aeSStefan Eßer 368478bc019dSStefan Eßer // clang-format off 368544d4804dSStefan Eßer BC_PROG_LBL(BC_INST_EXEC_STACK_LEN): 368678bc019dSStefan Eßer // clang-format on 368744d4804dSStefan Eßer { 368844d4804dSStefan Eßer bc_program_execStackLen(p); 368944d4804dSStefan Eßer BC_PROG_JUMP(inst, code, ip); 369044d4804dSStefan Eßer } 369144d4804dSStefan Eßer #endif // DC_ENABLED 369244d4804dSStefan Eßer 369344d4804dSStefan Eßer #if BC_HAS_COMPUTED_GOTO 369478bc019dSStefan Eßer // clang-format off 369544d4804dSStefan Eßer BC_PROG_LBL(BC_INST_INVALID): 369678bc019dSStefan Eßer // clang-format on 369744d4804dSStefan Eßer { 3698d101cdd6SStefan Eßer goto end; 369944d4804dSStefan Eßer } 370044d4804dSStefan Eßer #else // BC_HAS_COMPUTED_GOTO 370144d4804dSStefan Eßer default: 370244d4804dSStefan Eßer { 370344d4804dSStefan Eßer BC_UNREACHABLE 3704103d7cdfSStefan Eßer #if BC_DEBUG && !BC_CLANG 370544d4804dSStefan Eßer abort(); 3706103d7cdfSStefan Eßer #endif // BC_DEBUG && !BC_CLANG 370744d4804dSStefan Eßer } 370844d4804dSStefan Eßer #endif // BC_HAS_COMPUTED_GOTO 370944d4804dSStefan Eßer } 371044d4804dSStefan Eßer 3711d101cdd6SStefan Eßer #if BC_HAS_COMPUTED_GOTO 3712d101cdd6SStefan Eßer 3713d101cdd6SStefan Eßer #if BC_CLANG 3714d101cdd6SStefan Eßer #pragma clang diagnostic warning "-Wgnu-label-as-value" 3715d101cdd6SStefan Eßer #endif // BC_CLANG 3716d101cdd6SStefan Eßer 3717d101cdd6SStefan Eßer #if BC_GCC 3718d101cdd6SStefan Eßer #pragma GCC diagnostic warning "-Wpedantic" 3719d101cdd6SStefan Eßer #endif // BC_GCC 3720d101cdd6SStefan Eßer 3721d101cdd6SStefan Eßer #else // BC_HAS_COMPUTED_GOTO 3722d101cdd6SStefan Eßer 3723103d7cdfSStefan Eßer #if BC_DEBUG 3724252884aeSStefan Eßer // This is to allow me to use a debugger to see the last instruction, 372544d4804dSStefan Eßer // which will point to which function was the problem. But it's also a 372644d4804dSStefan Eßer // good smoke test for error handling changes. 3727d101cdd6SStefan Eßer assert(jmp_bufs_len == vm->jmp_bufs.len); 3728103d7cdfSStefan Eßer #endif // BC_DEBUG 3729d101cdd6SStefan Eßer 3730d101cdd6SStefan Eßer #endif // BC_HAS_COMPUTED_GOTO 3731252884aeSStefan Eßer } 3732d101cdd6SStefan Eßer 3733d101cdd6SStefan Eßer end: 3734d101cdd6SStefan Eßer BC_SIG_MAYLOCK; 3735d101cdd6SStefan Eßer 3736d101cdd6SStefan Eßer // This is here just to print a stack trace on interrupts. This is for 3737d101cdd6SStefan Eßer // finding infinite loops. 3738d101cdd6SStefan Eßer if (BC_SIG_INTERRUPT(vm)) 3739d101cdd6SStefan Eßer { 3740d101cdd6SStefan Eßer BcStatus s; 3741d101cdd6SStefan Eßer 3742d101cdd6SStefan Eßer bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 3743d101cdd6SStefan Eßer 3744d101cdd6SStefan Eßer bc_program_printStackTrace(p); 3745d101cdd6SStefan Eßer 3746d101cdd6SStefan Eßer s = bc_file_flushErr(&vm->ferr, bc_flush_err); 3747d101cdd6SStefan Eßer if (BC_ERR(s != BC_STATUS_SUCCESS && vm->status == BC_STATUS_SUCCESS)) 3748d101cdd6SStefan Eßer { 3749d101cdd6SStefan Eßer vm->status = (sig_atomic_t) s; 3750d101cdd6SStefan Eßer } 3751d101cdd6SStefan Eßer } 3752d101cdd6SStefan Eßer 3753d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 3754252884aeSStefan Eßer } 3755252884aeSStefan Eßer 3756252884aeSStefan Eßer #if BC_DEBUG_CODE 3757252884aeSStefan Eßer #if BC_ENABLED && DC_ENABLED 375878bc019dSStefan Eßer void 375978bc019dSStefan Eßer bc_program_printStackDebug(BcProgram* p) 376078bc019dSStefan Eßer { 3761d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_err, "-------------- Stack ----------\n"); 3762252884aeSStefan Eßer bc_program_printStack(p); 3763d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_err, "-------------- Stack End ------\n"); 3764252884aeSStefan Eßer } 3765252884aeSStefan Eßer 376678bc019dSStefan Eßer static void 376778bc019dSStefan Eßer bc_program_printIndex(const char* restrict code, size_t* restrict bgn) 3768252884aeSStefan Eßer { 3769252884aeSStefan Eßer uchar byte, i, bytes = (uchar) code[(*bgn)++]; 3770252884aeSStefan Eßer ulong val = 0; 3771252884aeSStefan Eßer 377278bc019dSStefan Eßer for (byte = 1, i = 0; byte && i < bytes; ++i) 377378bc019dSStefan Eßer { 3774252884aeSStefan Eßer byte = (uchar) code[(*bgn)++]; 3775252884aeSStefan Eßer if (byte) val |= ((ulong) byte) << (CHAR_BIT * i); 3776252884aeSStefan Eßer } 3777252884aeSStefan Eßer 3778252884aeSStefan Eßer bc_vm_printf(" (%lu) ", val); 3779252884aeSStefan Eßer } 3780252884aeSStefan Eßer 378178bc019dSStefan Eßer static void 378278bc019dSStefan Eßer bc_program_printStr(const BcProgram* p, const char* restrict code, 3783252884aeSStefan Eßer size_t* restrict bgn) 3784252884aeSStefan Eßer { 3785252884aeSStefan Eßer size_t idx = bc_program_index(code, bgn); 3786252884aeSStefan Eßer char* s; 3787252884aeSStefan Eßer 3788f4fbc49dSStefan Eßer s = *((char**) bc_vec_item(&p->strs, idx)); 3789252884aeSStefan Eßer 3790252884aeSStefan Eßer bc_vm_printf(" (\"%s\") ", s); 3791252884aeSStefan Eßer } 3792252884aeSStefan Eßer 379378bc019dSStefan Eßer void 379478bc019dSStefan Eßer bc_program_printInst(const BcProgram* p, const char* restrict code, 3795252884aeSStefan Eßer size_t* restrict bgn) 3796252884aeSStefan Eßer { 3797252884aeSStefan Eßer uchar inst = (uchar) code[(*bgn)++]; 3798252884aeSStefan Eßer 379978bc019dSStefan Eßer bc_vm_printf("Inst[%zu]: %s [%lu]; ", *bgn - 1, bc_inst_names[inst], 380078bc019dSStefan Eßer (unsigned long) inst); 3801252884aeSStefan Eßer 3802252884aeSStefan Eßer if (inst == BC_INST_VAR || inst == BC_INST_ARRAY_ELEM || 3803252884aeSStefan Eßer inst == BC_INST_ARRAY) 3804252884aeSStefan Eßer { 3805252884aeSStefan Eßer bc_program_printIndex(code, bgn); 3806252884aeSStefan Eßer } 3807252884aeSStefan Eßer else if (inst == BC_INST_STR) bc_program_printStr(p, code, bgn); 380878bc019dSStefan Eßer else if (inst == BC_INST_NUM) 380978bc019dSStefan Eßer { 3810252884aeSStefan Eßer size_t idx = bc_program_index(code, bgn); 3811f4fbc49dSStefan Eßer BcConst* c = bc_vec_item(&p->consts, idx); 3812252884aeSStefan Eßer bc_vm_printf("(%s)", c->val); 3813252884aeSStefan Eßer } 3814252884aeSStefan Eßer else if (inst == BC_INST_CALL || 3815252884aeSStefan Eßer (inst > BC_INST_STR && inst <= BC_INST_JUMP_ZERO)) 3816252884aeSStefan Eßer { 3817252884aeSStefan Eßer bc_program_printIndex(code, bgn); 3818252884aeSStefan Eßer if (inst == BC_INST_CALL) bc_program_printIndex(code, bgn); 3819252884aeSStefan Eßer } 3820252884aeSStefan Eßer 38217e5c51e5SStefan Eßer bc_vm_putchar('\n', bc_flush_err); 3822252884aeSStefan Eßer } 3823252884aeSStefan Eßer 382478bc019dSStefan Eßer void 382578bc019dSStefan Eßer bc_program_code(const BcProgram* p) 382678bc019dSStefan Eßer { 3827252884aeSStefan Eßer BcFunc* f; 3828252884aeSStefan Eßer char* code; 3829252884aeSStefan Eßer BcInstPtr ip; 3830252884aeSStefan Eßer size_t i; 3831252884aeSStefan Eßer 383278bc019dSStefan Eßer for (i = 0; i < p->fns.len; ++i) 383378bc019dSStefan Eßer { 3834252884aeSStefan Eßer ip.idx = ip.len = 0; 3835252884aeSStefan Eßer ip.func = i; 3836252884aeSStefan Eßer 3837252884aeSStefan Eßer f = bc_vec_item(&p->fns, ip.func); 3838252884aeSStefan Eßer code = f->code.v; 3839252884aeSStefan Eßer 3840252884aeSStefan Eßer bc_vm_printf("func[%zu]:\n", ip.func); 384178bc019dSStefan Eßer while (ip.idx < f->code.len) 384278bc019dSStefan Eßer { 384378bc019dSStefan Eßer bc_program_printInst(p, code, &ip.idx); 384478bc019dSStefan Eßer } 3845d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_err, "\n\n"); 3846252884aeSStefan Eßer } 3847252884aeSStefan Eßer } 3848252884aeSStefan Eßer #endif // BC_ENABLED && DC_ENABLED 3849252884aeSStefan Eßer #endif // BC_DEBUG_CODE 3850