/*1* *****************************************************************************2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2018-2025 Gavin D. Howard and contributors.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions are met:9*10* * Redistributions of source code must retain the above copyright notice, this11* list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright notice,14* this list of conditions and the following disclaimer in the documentation15* and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"18* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE21* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS24* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN25* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)26* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGE.28*29* *****************************************************************************30*31* Code common to the parsers.32*33*/3435#include <assert.h>36#include <stddef.h>37#include <stdlib.h>38#include <string.h>3940#include <limits.h>4142#include <parse.h>43#include <program.h>44#include <vm.h>4546void47bc_parse_updateFunc(BcParse* p, size_t fidx)48{49p->fidx = fidx;50p->func = bc_vec_item(&p->prog->fns, fidx);51}5253inline void54bc_parse_pushName(const BcParse* p, char* name, bool var)55{56bc_parse_pushIndex(p, bc_program_search(p->prog, name, var));57}5859/**60* Updates the function, then pushes the instruction and the index. This is a61* convenience function.62* @param p The parser.63* @param inst The instruction to push.64* @param idx The index to push.65*/66static inline void67bc_parse_pushInstIdx(BcParse* p, uchar inst, size_t idx)68{69bc_parse_push(p, inst);70bc_parse_pushIndex(p, idx);71}7273void74bc_parse_addString(BcParse* p)75{76size_t idx;7778idx = bc_program_addString(p->prog, p->l.str.v);7980// Push the string info.81bc_parse_pushInstIdx(p, BC_INST_STR, idx);82}8384static void85bc_parse_addNum(BcParse* p, const char* string)86{87BcProgram* prog = p->prog;88size_t idx;8990// XXX: This function has an implicit assumption: that string is a valid C91// string with a nul terminator. This is because of the unchecked array92// accesses below. I can't check this with an assert() because that could93// lead to out-of-bounds access.94//95// XXX: In fact, just for safety's sake, assume that this function needs a96// non-empty string with a nul terminator, just in case bc_parse_zero or97// bc_parse_one change in the future, which I doubt.9899BC_SIG_ASSERT_LOCKED;100101// Special case 0.102if (bc_parse_zero[0] == string[0] && bc_parse_zero[1] == string[1])103{104bc_parse_push(p, BC_INST_ZERO);105return;106}107108// Special case 1.109if (bc_parse_one[0] == string[0] && bc_parse_one[1] == string[1])110{111bc_parse_push(p, BC_INST_ONE);112return;113}114115if (bc_map_insert(&prog->const_map, string, prog->consts.len, &idx))116{117BcConst* c;118BcId* id = bc_vec_item(&prog->const_map, idx);119120// Get the index.121idx = id->idx;122123// Push an empty constant.124c = bc_vec_pushEmpty(&prog->consts);125126// Set the fields. We reuse the string in the ID (allocated by127// bc_map_insert()), because why not?128c->val = id->name;129c->base = BC_NUM_BIGDIG_MAX;130131// We need this to be able to tell that the number has not been132// allocated.133bc_num_clear(&c->num);134}135else136{137BcId* id = bc_vec_item(&prog->const_map, idx);138idx = id->idx;139}140141bc_parse_pushInstIdx(p, BC_INST_NUM, idx);142}143144void145bc_parse_number(BcParse* p)146{147#if BC_ENABLE_EXTRA_MATH148char* exp = strchr(p->l.str.v, 'e');149size_t idx = SIZE_MAX;150151// Do we have a number in scientific notation? If so, add a nul byte where152// the e is.153if (exp != NULL)154{155idx = ((size_t) (exp - p->l.str.v));156*exp = 0;157}158#endif // BC_ENABLE_EXTRA_MATH159160bc_parse_addNum(p, p->l.str.v);161162#if BC_ENABLE_EXTRA_MATH163// If we have a number in scientific notation...164if (exp != NULL)165{166bool neg;167168// Figure out if the exponent is negative.169neg = (*((char*) bc_vec_item(&p->l.str, idx + 1)) == BC_LEX_NEG_CHAR);170171// Add the number and instruction.172bc_parse_addNum(p, bc_vec_item(&p->l.str, idx + 1 + neg));173bc_parse_push(p, BC_INST_LSHIFT + neg);174}175#endif // BC_ENABLE_EXTRA_MATH176}177178void179bc_parse_text(BcParse* p, const char* text, BcMode mode)180{181BC_SIG_LOCK;182183// Make sure the pointer isn't invalidated.184p->func = bc_vec_item(&p->prog->fns, p->fidx);185bc_lex_text(&p->l, text, mode);186187BC_SIG_UNLOCK;188}189190void191bc_parse_reset(BcParse* p)192{193BC_SIG_ASSERT_LOCKED;194195// Reset the function if it isn't main and switch to main.196if (p->fidx != BC_PROG_MAIN)197{198bc_func_reset(p->func);199bc_parse_updateFunc(p, BC_PROG_MAIN);200}201202// Reset the lexer.203p->l.i = p->l.len;204p->l.t = BC_LEX_EOF;205206#if BC_ENABLED207if (BC_IS_BC)208{209// Get rid of the bc parser state.210p->auto_part = false;211bc_vec_npop(&p->flags, p->flags.len - 1);212bc_vec_popAll(&p->exits);213bc_vec_popAll(&p->conds);214bc_vec_popAll(&p->ops);215}216#endif // BC_ENABLED217218// Reset the program. This might clear the error.219bc_program_reset(p->prog);220221// Jump if there is an error.222if (BC_ERR(vm->status)) BC_JMP;223}224225#if BC_DEBUG226void227bc_parse_free(BcParse* p)228{229BC_SIG_ASSERT_LOCKED;230231assert(p != NULL);232233#if BC_ENABLED234if (BC_IS_BC)235{236bc_vec_free(&p->flags);237bc_vec_free(&p->exits);238bc_vec_free(&p->conds);239bc_vec_free(&p->ops);240bc_vec_free(&p->buf);241}242#endif // BC_ENABLED243244bc_lex_free(&p->l);245}246#endif // BC_DEBUG247248void249bc_parse_init(BcParse* p, BcProgram* prog, size_t func)250{251#if BC_ENABLED252uint16_t flag = 0;253#endif // BC_ENABLED254255BC_SIG_ASSERT_LOCKED;256257assert(p != NULL && prog != NULL);258259#if BC_ENABLED260if (BC_IS_BC)261{262// We always want at least one flag set on the flags stack.263bc_vec_init(&p->flags, sizeof(uint16_t), BC_DTOR_NONE);264bc_vec_push(&p->flags, &flag);265266bc_vec_init(&p->exits, sizeof(BcInstPtr), BC_DTOR_NONE);267bc_vec_init(&p->conds, sizeof(size_t), BC_DTOR_NONE);268bc_vec_init(&p->ops, sizeof(BcLexType), BC_DTOR_NONE);269bc_vec_init(&p->buf, sizeof(char), BC_DTOR_NONE);270271p->auto_part = false;272}273#endif // BC_ENABLED274275bc_lex_init(&p->l);276277// Set up the function.278p->prog = prog;279bc_parse_updateFunc(p, func);280}281282283