/*1* *****************************************************************************2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2018-2025 Gavin D. Howard and contributors.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions are met:9*10* * Redistributions of source code must retain the above copyright notice, this11* list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright notice,14* this list of conditions and the following disclaimer in the documentation15* and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"18* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE21* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS24* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN25* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)26* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGE.28*29* *****************************************************************************30*31* Definitions for program data.32*33*/3435#ifndef BC_LANG_H36#define BC_LANG_H3738#include <stdbool.h>3940// These have to come first to silence a warning on BC_C11 below.41#include <status.h>42#include <vector.h>43#include <num.h>4445#if BC_C1146#include <assert.h>47#endif // BC_C114849/// The instructions for bytecode.50typedef enum BcInst51{52#if BC_ENABLED53/// Postfix increment and decrement. Prefix are translated into54/// BC_INST_ONE with either BC_INST_ASSIGN_PLUS or BC_INST_ASSIGN_MINUS.55BC_INST_INC = 0,56BC_INST_DEC,57#endif // BC_ENABLED5859/// Unary negation.60BC_INST_NEG,6162/// Boolean not.63BC_INST_BOOL_NOT,6465#if BC_ENABLE_EXTRA_MATH66/// Truncation operator.67BC_INST_TRUNC,68#endif // BC_ENABLE_EXTRA_MATH6970/// These should be self-explanatory.71BC_INST_POWER,72BC_INST_MULTIPLY,73BC_INST_DIVIDE,74BC_INST_MODULUS,75BC_INST_PLUS,76BC_INST_MINUS,7778#if BC_ENABLE_EXTRA_MATH79/// Places operator.80BC_INST_PLACES,8182/// Shift operators.83BC_INST_LSHIFT,84BC_INST_RSHIFT,85#endif // BC_ENABLE_EXTRA_MATH8687/// Comparison operators.88BC_INST_REL_EQ,89BC_INST_REL_LE,90BC_INST_REL_GE,91BC_INST_REL_NE,92BC_INST_REL_LT,93BC_INST_REL_GT,9495/// Boolean or and and.96BC_INST_BOOL_OR,97BC_INST_BOOL_AND,9899#if BC_ENABLED100/// Same as the normal operators, but assigment. So ^=, *=, /=, etc.101BC_INST_ASSIGN_POWER,102BC_INST_ASSIGN_MULTIPLY,103BC_INST_ASSIGN_DIVIDE,104BC_INST_ASSIGN_MODULUS,105BC_INST_ASSIGN_PLUS,106BC_INST_ASSIGN_MINUS,107#if BC_ENABLE_EXTRA_MATH108/// Places and shift assignment operators.109BC_INST_ASSIGN_PLACES,110BC_INST_ASSIGN_LSHIFT,111BC_INST_ASSIGN_RSHIFT,112#endif // BC_ENABLE_EXTRA_MATH113114/// Normal assignment.115BC_INST_ASSIGN,116117/// bc and dc detect when the value from an assignment is not necessary.118/// For example, a plain assignment statement means the value is never used.119/// In those cases, we can get lots of performance back by not even creating120/// a copy at all. In fact, it saves a copy, a push onto the results stack,121/// a pop from the results stack, and a free. Definitely worth it to detect.122BC_INST_ASSIGN_POWER_NO_VAL,123BC_INST_ASSIGN_MULTIPLY_NO_VAL,124BC_INST_ASSIGN_DIVIDE_NO_VAL,125BC_INST_ASSIGN_MODULUS_NO_VAL,126BC_INST_ASSIGN_PLUS_NO_VAL,127BC_INST_ASSIGN_MINUS_NO_VAL,128#if BC_ENABLE_EXTRA_MATH129/// Same as above.130BC_INST_ASSIGN_PLACES_NO_VAL,131BC_INST_ASSIGN_LSHIFT_NO_VAL,132BC_INST_ASSIGN_RSHIFT_NO_VAL,133#endif // BC_ENABLE_EXTRA_MATH134#endif // BC_ENABLED135136/// Normal assignment that pushes no value on the stack.137BC_INST_ASSIGN_NO_VAL,138139/// Push a constant onto the results stack.140BC_INST_NUM,141142/// Push a variable onto the results stack.143BC_INST_VAR,144145/// Push an array element onto the results stack.146BC_INST_ARRAY_ELEM,147148/// Push an array onto the results stack. This is different from pushing an149/// array *element* onto the results stack; it pushes a reference to the150/// whole array. This is needed in bc for function arguments that are151/// arrays. It is also needed for returning the length of an array.152BC_INST_ARRAY,153154/// Push a zero or a one onto the stack. These are special cased because it155/// does help performance, particularly for one since inc/dec operators156/// use it.157BC_INST_ZERO,158BC_INST_ONE,159160#if BC_ENABLED161/// Push the last printed value onto the stack.162BC_INST_LAST,163#endif // BC_ENABLED164165/// Push the value of any of the globals onto the stack.166BC_INST_IBASE,167BC_INST_OBASE,168BC_INST_SCALE,169170#if BC_ENABLE_EXTRA_MATH171/// Push the value of the seed global onto the stack.172BC_INST_SEED,173#endif // BC_ENABLE_EXTRA_MATH174175/// These are builtin functions.176BC_INST_LENGTH,177BC_INST_SCALE_FUNC,178BC_INST_SQRT,179BC_INST_ABS,180BC_INST_IS_NUMBER,181BC_INST_IS_STRING,182183#if BC_ENABLE_EXTRA_MATH184/// Another builtin function.185BC_INST_IRAND,186#endif // BC_ENABLE_EXTRA_MATH187188/// Asciify.189BC_INST_ASCIIFY,190191/// Another builtin function.192BC_INST_READ,193194#if BC_ENABLE_EXTRA_MATH195/// Another builtin function.196BC_INST_RAND,197#endif // BC_ENABLE_EXTRA_MATH198199/// Return the max for the various globals.200BC_INST_MAXIBASE,201BC_INST_MAXOBASE,202BC_INST_MAXSCALE,203#if BC_ENABLE_EXTRA_MATH204/// Return the max value returned by rand().205BC_INST_MAXRAND,206#endif // BC_ENABLE_EXTRA_MATH207208/// bc line_length() builtin function.209BC_INST_LINE_LENGTH,210211#if BC_ENABLED212213/// bc global_stacks() builtin function.214BC_INST_GLOBAL_STACKS,215216#endif // BC_ENABLED217218/// bc leading_zero() builtin function.219BC_INST_LEADING_ZERO,220221/// This is slightly misnamed versus BC_INST_PRINT_POP. Well, it is in bc.222/// dc uses this instruction to print, but not pop. That's valid in dc.223/// However, in bc, it is *never* valid to print without popping. In bc,224/// BC_INST_PRINT_POP is used to indicate when a string should be printed225/// because of a print statement or whether it should be printed raw. The226/// reason for this is because a print statement handles escaped characters.227/// So BC_INST_PRINT_POP is for printing a string from a print statement,228/// BC_INST_PRINT_STR is for printing a string by itself.229///230/// In dc, BC_INST_PRINT_POP prints and pops, and BC_INST_PRINT just prints.231///232/// Oh, and BC_INST_STR pushes a string onto the results stack.233BC_INST_PRINT,234BC_INST_PRINT_POP,235BC_INST_STR,236#if BC_ENABLED237BC_INST_PRINT_STR,238239/// Jumps unconditionally.240BC_INST_JUMP,241242/// Jumps if the top of the results stack is zero (condition failed). It243/// turns out that we only want to jump when conditions fail to "skip" code.244BC_INST_JUMP_ZERO,245246/// Call a function.247BC_INST_CALL,248249/// Return the top of the stack to the caller.250BC_INST_RET,251252/// Return 0 to the caller.253BC_INST_RET0,254255/// Special return instruction for void functions.256BC_INST_RET_VOID,257258/// Special halt instruction.259BC_INST_HALT,260#endif // BC_ENABLED261262/// Pop an item off of the results stack.263BC_INST_POP,264265/// Swaps the top two items on the results stack.266BC_INST_SWAP,267268/// Modular exponentiation.269BC_INST_MODEXP,270271/// Do divide and modulus at the same time.272BC_INST_DIVMOD,273274/// Turns a number into a string and prints it.275BC_INST_PRINT_STREAM,276277#if DC_ENABLED278279/// dc extended registers command.280BC_INST_EXTENDED_REGISTERS,281282/// dc's return; it pops an executing string off of the stack.283BC_INST_POP_EXEC,284285/// Unconditionally execute a string.286BC_INST_EXECUTE,287288/// Conditionally execute a string.289BC_INST_EXEC_COND,290291/// Prints each item on the results stack, separated by newlines.292BC_INST_PRINT_STACK,293294/// Pops everything off of the results stack.295BC_INST_CLEAR_STACK,296297/// Pushes the current length of a register stack onto the results stack.298BC_INST_REG_STACK_LEN,299300/// Pushes the current length of the results stack onto the results stack.301BC_INST_STACK_LEN,302303/// Pushes a copy of the item on the top of the results stack onto the304/// results stack.305BC_INST_DUPLICATE,306307/// Copies the value in a register and pushes the copy onto the results308/// stack.309BC_INST_LOAD,310311/// Pops an item off of a register stack and pushes it onto the results312/// stack.313BC_INST_PUSH_VAR,314315/// Pops an item off of the results stack and pushes it onto a register's316/// stack.317BC_INST_PUSH_TO_VAR,318319/// Quit.320BC_INST_QUIT,321322/// Quit executing some number of strings.323BC_INST_NQUIT,324325/// Push the depth of the execution stack onto the stack.326BC_INST_EXEC_STACK_LEN,327328#endif // DC_ENABLED329330/// Invalid instruction.331BC_INST_INVALID,332333} BcInst;334335#if BC_C11336_Static_assert(BC_INST_INVALID <= UCHAR_MAX,337"Too many instructions to fit into an unsigned char");338#endif // BC_C11339340/// Used by maps to identify where items are in the array.341typedef struct BcId342{343/// The name of the item.344char* name;345346/// The index into the array where the item is.347size_t idx;348349} BcId;350351/// The location of a var, array, or array element.352typedef struct BcLoc353{354/// The index of the var or array.355size_t loc;356357/// The index of the array or variable in the array stack. This is to358/// prevent a bug with getting the wrong array element or variable after a359/// function call. See the tests/bc/scripts/array.bc test for the array360/// case; the variable case is in various variable tests.361size_t stack_idx;362363/// The index of the array element. Only used for array elements.364size_t idx;365366} BcLoc;367368/// An entry for a constant.369typedef struct BcConst370{371/// The original string as parsed from the source code.372char* val;373374/// The last base that the constant was parsed in.375BcBigDig base;376377/// The parsed constant.378BcNum num;379380} BcConst;381382/// A function. This is also used in dc, not just bc. The reason is that strings383/// are executed in dc, and they are converted to functions in order to be384/// executed.385typedef struct BcFunc386{387/// The bytecode instructions.388BcVec code;389390#if BC_ENABLED391392/// The labels. This is a vector of indices. The index is the index into393/// the bytecode vector where the label is.394BcVec labels;395396/// The autos for the function. The first items are the parameters, and the397/// arguments to the parameters must match the types in this vector.398BcVec autos;399400/// The number of parameters the function takes.401size_t nparams;402403#endif // BC_ENABLED404405/// The function's name.406const char* name;407408#if BC_ENABLED409/// True if the function is a void function.410bool voidfn;411#endif // BC_ENABLED412413} BcFunc;414415/// Types of results that can be pushed onto the results stack.416typedef enum BcResultType417{418/// Result is a variable.419BC_RESULT_VAR,420421/// Result is an array element.422BC_RESULT_ARRAY_ELEM,423424/// Result is an array. This is only allowed for function arguments or425/// returning the length of the array.426BC_RESULT_ARRAY,427428/// Result is a string.429BC_RESULT_STR,430431/// Result is a temporary. This is used for the result of almost all432/// expressions.433BC_RESULT_TEMP,434435/// Special casing the two below gave performance improvements.436437/// Result is a 0.438BC_RESULT_ZERO,439440/// Result is a 1. Useful for inc/dec operators.441BC_RESULT_ONE,442443#if BC_ENABLED444445/// Result is the special "last" variable.446BC_RESULT_LAST,447448/// Result is the return value of a void function.449BC_RESULT_VOID,450#endif // BC_ENABLED451452/// Result is the value of ibase.453BC_RESULT_IBASE,454455/// Result is the value of obase.456BC_RESULT_OBASE,457458/// Result is the value of scale.459BC_RESULT_SCALE,460461#if BC_ENABLE_EXTRA_MATH462463/// Result is the value of seed.464BC_RESULT_SEED,465466#endif // BC_ENABLE_EXTRA_MATH467468} BcResultType;469470/// A union to store data for various result types.471typedef union BcResultData472{473/// A number. Strings are stored here too; they are numbers with474/// cap == 0 && num == NULL. The string's index into the strings vector is475/// stored in the scale field. But this is only used for strings stored in476/// variables.477BcNum n;478479/// A vector.480BcVec v;481482/// A variable, array, or array element reference. This could also be a483/// string if a string is not stored in a variable (dc only).484BcLoc loc;485486} BcResultData;487488/// A tagged union for results.489typedef struct BcResult490{491/// The tag. The type of the result.492BcResultType t;493494/// The data. The data for the result.495BcResultData d;496497} BcResult;498499/// An instruction pointer. This is how bc knows where in the bytecode vector,500/// and which function, the current execution is.501typedef struct BcInstPtr502{503/// The index of the currently executing function in the fns vector.504size_t func;505506/// The index into the bytecode vector of the *next* instruction.507size_t idx;508509/// The length of the results vector when this function started executing.510/// This is mostly used for bc where functions should not affect the results511/// of their callers.512size_t len;513514} BcInstPtr;515516/// Types of identifiers.517typedef enum BcType518{519/// Variable.520BC_TYPE_VAR,521522/// Array.523BC_TYPE_ARRAY,524525#if BC_ENABLED526527/// Array reference.528BC_TYPE_REF,529530#endif // BC_ENABLED531532} BcType;533534#if BC_ENABLED535536/// Check if type array or array reference537#define BC_IS_ARRAY(e) (e == BC_TYPE_ARRAY || e == BC_TYPE_REF)538539/// An auto variable in bc.540typedef struct BcAuto541{542/// The index of the variable in the vars or arrs vectors.543size_t idx;544545/// The type of the variable.546BcType type;547548} BcAuto;549#endif // BC_ENABLED550551/// Forward declaration.552struct BcProgram;553554/**555* Initializes a function.556* @param f The function to initialize.557* @param name The name of the function. The string is assumed to be owned by558* some other entity.559*/560void561bc_func_init(BcFunc* f, const char* name);562563/**564* Inserts an auto into the function.565* @param f The function to insert into.566* @param p The program. This is to search for the variable or array name.567* @param name The name of the auto to insert.568* @param type The type of the auto.569* @param line The line in the source code where the insert happened. This is570* solely for error reporting.571*/572void573bc_func_insert(BcFunc* f, struct BcProgram* p, char* name, BcType type,574size_t line);575576/**577* Resets a function in preparation for it to be reused. This can happen in bc578* because it is a dynamic language and functions can be redefined.579* @param f The functio to reset.580*/581void582bc_func_reset(BcFunc* f);583584#if BC_DEBUG585/**586* Frees a function. This is a destructor. This is only used in debug builds587* because all functions are freed at exit. We free them in debug builds to588* check for memory leaks.589* @param func The function to free as a void pointer.590*/591void592bc_func_free(void* func);593#endif // BC_DEBUG594595/**596* Initializes an array, which is the array type in bc and dc source code. Since597* variables and arrays are both arrays (see the development manual,598* manuals/development.md#execution, for more information), the @a nums599* parameter tells bc whether to initialize an array of numbers or an array of600* arrays of numbers. If the latter, it does a recursive call with nums set to601* true.602* @param a The array to initialize.603* @param nums True if the array should be for numbers, false if it should be604* for vectors.605*/606void607bc_array_init(BcVec* a, bool nums);608609/**610* Copies an array to another array. This is used to do pass arrays to functions611* that do not take references to arrays. The arrays are passed entirely by612* value, which means that they need to be copied.613* @param d The destination array.614* @param s The source array.615*/616void617bc_array_copy(BcVec* d, const BcVec* s);618619/**620* Frees a string stored in a function. This is a destructor.621* @param string The string to free as a void pointer.622*/623void624bc_string_free(void* string);625626/**627* Frees a constant stored in a function. This is a destructor.628* @param constant The constant to free as a void pointer.629*/630void631bc_const_free(void* constant);632633/**634* Clears a result. It sets the type to BC_RESULT_TEMP and clears the union by635* clearing the BcNum in the union. This is to ensure that bc does not use636* uninitialized data.637* @param r The result to clear.638*/639void640bc_result_clear(BcResult* r);641642/**643* Copies a result into another. This is done for things like duplicating the644* top of the results stack or copying the result of an assignment to put back645* on the results stack.646* @param d The destination result.647* @param src The source result.648*/649void650bc_result_copy(BcResult* d, BcResult* src);651652/**653* Frees a result. This is a destructor.654* @param result The result to free as a void pointer.655*/656void657bc_result_free(void* result);658659/**660* Expands an array to @a len. This can happen because in bc, you do not have to661* explicitly initialize elements of an array. If you access an element that is662* not initialized, the array is expanded to fit it, and all missing elements663* are initialized to 0 if they are numbers, or arrays with one element of 0.664* This function does that expansion.665* @param a The array to expand.666* @param len The length to expand to.667*/668void669bc_array_expand(BcVec* a, size_t len);670671#if BC_ENABLED672673/**674* Returns non-zero if the bytecode instruction i is an assignment instruction.675* @param i The instruction to test.676* @return Non-zero if i is an assignment instruction, zero otherwise.677*/678#define BC_INST_IS_ASSIGN(i) \679((i) == BC_INST_ASSIGN || (i) == BC_INST_ASSIGN_NO_VAL)680681/**682* Returns true if the bytecode instruction @a i requires the value to be683* returned for use.684* @param i The instruction to test.685* @return True if @a i requires the value to be returned for use, false686* otherwise.687*/688#define BC_INST_USE_VAL(i) ((i) <= BC_INST_ASSIGN)689690#else // BC_ENABLED691692/**693* Returns non-zero if the bytecode instruction i is an assignment instruction.694* @param i The instruction to test.695* @return Non-zero if i is an assignment instruction, zero otherwise.696*/697#define BC_INST_IS_ASSIGN(i) ((i) == BC_INST_ASSIGN_NO_VAL)698699/**700* Returns true if the bytecode instruction @a i requires the value to be701* returned for use.702* @param i The instruction to test.703* @return True if @a i requires the value to be returned for use, false704* otherwise.705*/706#define BC_INST_USE_VAL(i) (false)707708#endif // BC_ENABLED709710#if BC_DEBUG_CODE711/// Reference to string names for all of the instructions. For debugging.712extern const char* bc_inst_names[];713#endif // BC_DEBUG_CODE714715/// References to the names of the main and read functions.716extern const char bc_func_main[];717extern const char bc_func_read[];718719#endif // BC_LANG_H720721722