xref: /freebsd-src/contrib/bc/src/program.c (revision 12e0d316644a4f80f5f1f78cf07bd93def43b1ca)
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(&copy.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, &copy);
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(&copy.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