xref: /freebsd-src/contrib/bc/src/parse.c (revision a970610a3af63b3f4df5b69d91c6b4093a00ed8f)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
6*a970610aSStefan 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 common to the parsers.
33252884aeSStefan Eßer  *
34252884aeSStefan Eßer  */
35252884aeSStefan Eßer 
36252884aeSStefan Eßer #include <assert.h>
37252884aeSStefan Eßer #include <stddef.h>
38252884aeSStefan Eßer #include <stdlib.h>
39252884aeSStefan Eßer #include <string.h>
40252884aeSStefan Eßer 
41252884aeSStefan Eßer #include <limits.h>
42252884aeSStefan Eßer 
43252884aeSStefan Eßer #include <parse.h>
44252884aeSStefan Eßer #include <program.h>
45252884aeSStefan Eßer #include <vm.h>
46252884aeSStefan Eßer 
4778bc019dSStefan Eßer void
4878bc019dSStefan Eßer bc_parse_updateFunc(BcParse* p, size_t fidx)
4978bc019dSStefan Eßer {
50252884aeSStefan Eßer 	p->fidx = fidx;
51252884aeSStefan Eßer 	p->func = bc_vec_item(&p->prog->fns, fidx);
52252884aeSStefan Eßer }
53252884aeSStefan Eßer 
5478bc019dSStefan Eßer inline void
5578bc019dSStefan Eßer bc_parse_pushName(const BcParse* p, char* name, bool var)
5678bc019dSStefan Eßer {
57252884aeSStefan Eßer 	bc_parse_pushIndex(p, bc_program_search(p->prog, name, var));
58252884aeSStefan Eßer }
59252884aeSStefan Eßer 
6044d4804dSStefan Eßer /**
6144d4804dSStefan Eßer  * Updates the function, then pushes the instruction and the index. This is a
6244d4804dSStefan Eßer  * convenience function.
6344d4804dSStefan Eßer  * @param p     The parser.
6444d4804dSStefan Eßer  * @param inst  The instruction to push.
6544d4804dSStefan Eßer  * @param idx   The index to push.
6644d4804dSStefan Eßer  */
67d101cdd6SStefan Eßer static inline void
68d101cdd6SStefan Eßer bc_parse_pushInstIdx(BcParse* p, uchar inst, size_t idx)
6978bc019dSStefan Eßer {
70252884aeSStefan Eßer 	bc_parse_push(p, inst);
71252884aeSStefan Eßer 	bc_parse_pushIndex(p, idx);
72252884aeSStefan Eßer }
73252884aeSStefan Eßer 
7478bc019dSStefan Eßer void
7578bc019dSStefan Eßer bc_parse_addString(BcParse* p)
7678bc019dSStefan Eßer {
77252884aeSStefan Eßer 	size_t idx;
78252884aeSStefan Eßer 
79d101cdd6SStefan Eßer 	idx = bc_program_addString(p->prog, p->l.str.v);
80252884aeSStefan Eßer 
8144d4804dSStefan Eßer 	// Push the string info.
82d101cdd6SStefan Eßer 	bc_parse_pushInstIdx(p, BC_INST_STR, idx);
83252884aeSStefan Eßer }
84252884aeSStefan Eßer 
8578bc019dSStefan Eßer static void
8678bc019dSStefan Eßer bc_parse_addNum(BcParse* p, const char* string)
8778bc019dSStefan Eßer {
88d101cdd6SStefan Eßer 	BcProgram* prog = p->prog;
89252884aeSStefan Eßer 	size_t idx;
90d101cdd6SStefan Eßer 
91d101cdd6SStefan Eßer 	// XXX: This function has an implicit assumption: that string is a valid C
92d101cdd6SStefan Eßer 	// string with a nul terminator. This is because of the unchecked array
93d101cdd6SStefan Eßer 	// accesses below. I can't check this with an assert() because that could
94d101cdd6SStefan Eßer 	// lead to out-of-bounds access.
95d101cdd6SStefan Eßer 	//
96d101cdd6SStefan Eßer 	// XXX: In fact, just for safety's sake, assume that this function needs a
97d101cdd6SStefan Eßer 	// non-empty string with a nul terminator, just in case bc_parse_zero or
98d101cdd6SStefan Eßer 	// bc_parse_one change in the future, which I doubt.
99252884aeSStefan Eßer 
10010041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
10110041e99SStefan Eßer 
10244d4804dSStefan Eßer 	// Special case 0.
10378bc019dSStefan Eßer 	if (bc_parse_zero[0] == string[0] && bc_parse_zero[1] == string[1])
10478bc019dSStefan Eßer 	{
1053aa99676SStefan Eßer 		bc_parse_push(p, BC_INST_ZERO);
1063aa99676SStefan Eßer 		return;
1073aa99676SStefan Eßer 	}
10844d4804dSStefan Eßer 
10944d4804dSStefan Eßer 	// Special case 1.
11078bc019dSStefan Eßer 	if (bc_parse_one[0] == string[0] && bc_parse_one[1] == string[1])
11178bc019dSStefan Eßer 	{
112252884aeSStefan Eßer 		bc_parse_push(p, BC_INST_ONE);
113252884aeSStefan Eßer 		return;
114252884aeSStefan Eßer 	}
115252884aeSStefan Eßer 
116d101cdd6SStefan Eßer 	if (bc_map_insert(&prog->const_map, string, prog->consts.len, &idx))
117d101cdd6SStefan Eßer 	{
118d101cdd6SStefan Eßer 		BcConst* c;
119d101cdd6SStefan Eßer 		BcId* id = bc_vec_item(&prog->const_map, idx);
120252884aeSStefan Eßer 
121d101cdd6SStefan Eßer 		// Get the index.
122d101cdd6SStefan Eßer 		idx = id->idx;
123252884aeSStefan Eßer 
12444d4804dSStefan Eßer 		// Push an empty constant.
125d101cdd6SStefan Eßer 		c = bc_vec_pushEmpty(&prog->consts);
12644d4804dSStefan Eßer 
127d101cdd6SStefan Eßer 		// Set the fields. We reuse the string in the ID (allocated by
128d101cdd6SStefan Eßer 		// bc_map_insert()), because why not?
129d101cdd6SStefan Eßer 		c->val = id->name;
13044d4804dSStefan Eßer 		c->base = BC_NUM_BIGDIG_MAX;
13144d4804dSStefan Eßer 
132d101cdd6SStefan Eßer 		// We need this to be able to tell that the number has not been
133d101cdd6SStefan Eßer 		// allocated.
13444d4804dSStefan Eßer 		bc_num_clear(&c->num);
135d101cdd6SStefan Eßer 	}
136d101cdd6SStefan Eßer 	else
137d101cdd6SStefan Eßer 	{
138d101cdd6SStefan Eßer 		BcId* id = bc_vec_item(&prog->const_map, idx);
139d101cdd6SStefan Eßer 		idx = id->idx;
140d101cdd6SStefan Eßer 	}
141252884aeSStefan Eßer 
142d101cdd6SStefan Eßer 	bc_parse_pushInstIdx(p, BC_INST_NUM, idx);
143252884aeSStefan Eßer }
144252884aeSStefan Eßer 
14578bc019dSStefan Eßer void
14678bc019dSStefan Eßer bc_parse_number(BcParse* p)
14778bc019dSStefan Eßer {
148252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
149252884aeSStefan Eßer 	char* exp = strchr(p->l.str.v, 'e');
150252884aeSStefan Eßer 	size_t idx = SIZE_MAX;
151252884aeSStefan Eßer 
15244d4804dSStefan Eßer 	// Do we have a number in scientific notation? If so, add a nul byte where
15344d4804dSStefan Eßer 	// the e is.
15478bc019dSStefan Eßer 	if (exp != NULL)
15578bc019dSStefan Eßer 	{
156252884aeSStefan Eßer 		idx = ((size_t) (exp - p->l.str.v));
157252884aeSStefan Eßer 		*exp = 0;
158252884aeSStefan Eßer 	}
159252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
160252884aeSStefan Eßer 
161252884aeSStefan Eßer 	bc_parse_addNum(p, p->l.str.v);
162252884aeSStefan Eßer 
163252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
16444d4804dSStefan Eßer 	// If we have a number in scientific notation...
16578bc019dSStefan Eßer 	if (exp != NULL)
16678bc019dSStefan Eßer 	{
167252884aeSStefan Eßer 		bool neg;
168252884aeSStefan Eßer 
16944d4804dSStefan Eßer 		// Figure out if the exponent is negative.
170252884aeSStefan Eßer 		neg = (*((char*) bc_vec_item(&p->l.str, idx + 1)) == BC_LEX_NEG_CHAR);
171252884aeSStefan Eßer 
17244d4804dSStefan Eßer 		// Add the number and instruction.
173252884aeSStefan Eßer 		bc_parse_addNum(p, bc_vec_item(&p->l.str, idx + 1 + neg));
174252884aeSStefan Eßer 		bc_parse_push(p, BC_INST_LSHIFT + neg);
175252884aeSStefan Eßer 	}
176252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
177252884aeSStefan Eßer }
178252884aeSStefan Eßer 
17978bc019dSStefan Eßer void
180d101cdd6SStefan Eßer bc_parse_text(BcParse* p, const char* text, BcMode mode)
18178bc019dSStefan Eßer {
18210041e99SStefan Eßer 	BC_SIG_LOCK;
18310041e99SStefan Eßer 
184252884aeSStefan Eßer 	// Make sure the pointer isn't invalidated.
185252884aeSStefan Eßer 	p->func = bc_vec_item(&p->prog->fns, p->fidx);
186d101cdd6SStefan Eßer 	bc_lex_text(&p->l, text, mode);
18710041e99SStefan Eßer 
18810041e99SStefan Eßer 	BC_SIG_UNLOCK;
189252884aeSStefan Eßer }
190252884aeSStefan Eßer 
19178bc019dSStefan Eßer void
19278bc019dSStefan Eßer bc_parse_reset(BcParse* p)
19378bc019dSStefan Eßer {
194252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
195252884aeSStefan Eßer 
19644d4804dSStefan Eßer 	// Reset the function if it isn't main and switch to main.
19778bc019dSStefan Eßer 	if (p->fidx != BC_PROG_MAIN)
19878bc019dSStefan Eßer 	{
199252884aeSStefan Eßer 		bc_func_reset(p->func);
200252884aeSStefan Eßer 		bc_parse_updateFunc(p, BC_PROG_MAIN);
201252884aeSStefan Eßer 	}
202252884aeSStefan Eßer 
20344d4804dSStefan Eßer 	// Reset the lexer.
204252884aeSStefan Eßer 	p->l.i = p->l.len;
205252884aeSStefan Eßer 	p->l.t = BC_LEX_EOF;
206252884aeSStefan Eßer 
207252884aeSStefan Eßer #if BC_ENABLED
20878bc019dSStefan Eßer 	if (BC_IS_BC)
20978bc019dSStefan Eßer 	{
21044d4804dSStefan Eßer 		// Get rid of the bc parser state.
21144d4804dSStefan Eßer 		p->auto_part = false;
212252884aeSStefan Eßer 		bc_vec_npop(&p->flags, p->flags.len - 1);
21310328f8bSStefan Eßer 		bc_vec_popAll(&p->exits);
21410328f8bSStefan Eßer 		bc_vec_popAll(&p->conds);
21510328f8bSStefan Eßer 		bc_vec_popAll(&p->ops);
216252884aeSStefan Eßer 	}
217252884aeSStefan Eßer #endif // BC_ENABLED
218252884aeSStefan Eßer 
21944d4804dSStefan Eßer 	// Reset the program. This might clear the error.
220252884aeSStefan Eßer 	bc_program_reset(p->prog);
221252884aeSStefan Eßer 
22244d4804dSStefan Eßer 	// Jump if there is an error.
223d101cdd6SStefan Eßer 	if (BC_ERR(vm->status)) BC_JMP;
224252884aeSStefan Eßer }
225252884aeSStefan Eßer 
226103d7cdfSStefan Eßer #if BC_DEBUG
22778bc019dSStefan Eßer void
22878bc019dSStefan Eßer bc_parse_free(BcParse* p)
22978bc019dSStefan Eßer {
230252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
231252884aeSStefan Eßer 
232252884aeSStefan Eßer 	assert(p != NULL);
233252884aeSStefan Eßer 
234252884aeSStefan Eßer #if BC_ENABLED
23578bc019dSStefan Eßer 	if (BC_IS_BC)
23678bc019dSStefan Eßer 	{
237252884aeSStefan Eßer 		bc_vec_free(&p->flags);
238252884aeSStefan Eßer 		bc_vec_free(&p->exits);
239252884aeSStefan Eßer 		bc_vec_free(&p->conds);
240252884aeSStefan Eßer 		bc_vec_free(&p->ops);
241252884aeSStefan Eßer 		bc_vec_free(&p->buf);
242252884aeSStefan Eßer 	}
243252884aeSStefan Eßer #endif // BC_ENABLED
244252884aeSStefan Eßer 
245252884aeSStefan Eßer 	bc_lex_free(&p->l);
246252884aeSStefan Eßer }
247103d7cdfSStefan Eßer #endif // BC_DEBUG
248252884aeSStefan Eßer 
24978bc019dSStefan Eßer void
25078bc019dSStefan Eßer bc_parse_init(BcParse* p, BcProgram* prog, size_t func)
25178bc019dSStefan Eßer {
252252884aeSStefan Eßer #if BC_ENABLED
253252884aeSStefan Eßer 	uint16_t flag = 0;
254252884aeSStefan Eßer #endif // BC_ENABLED
255252884aeSStefan Eßer 
256252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
257252884aeSStefan Eßer 
258252884aeSStefan Eßer 	assert(p != NULL && prog != NULL);
259252884aeSStefan Eßer 
260252884aeSStefan Eßer #if BC_ENABLED
26178bc019dSStefan Eßer 	if (BC_IS_BC)
26278bc019dSStefan Eßer 	{
26344d4804dSStefan Eßer 		// We always want at least one flag set on the flags stack.
26444d4804dSStefan Eßer 		bc_vec_init(&p->flags, sizeof(uint16_t), BC_DTOR_NONE);
265252884aeSStefan Eßer 		bc_vec_push(&p->flags, &flag);
26644d4804dSStefan Eßer 
26744d4804dSStefan Eßer 		bc_vec_init(&p->exits, sizeof(BcInstPtr), BC_DTOR_NONE);
26844d4804dSStefan Eßer 		bc_vec_init(&p->conds, sizeof(size_t), BC_DTOR_NONE);
26944d4804dSStefan Eßer 		bc_vec_init(&p->ops, sizeof(BcLexType), BC_DTOR_NONE);
27044d4804dSStefan Eßer 		bc_vec_init(&p->buf, sizeof(char), BC_DTOR_NONE);
27144d4804dSStefan Eßer 
27244d4804dSStefan Eßer 		p->auto_part = false;
273252884aeSStefan Eßer 	}
274252884aeSStefan Eßer #endif // BC_ENABLED
275252884aeSStefan Eßer 
276252884aeSStefan Eßer 	bc_lex_init(&p->l);
277252884aeSStefan Eßer 
27844d4804dSStefan Eßer 	// Set up the function.
279252884aeSStefan Eßer 	p->prog = prog;
280252884aeSStefan Eßer 	bc_parse_updateFunc(p, func);
281252884aeSStefan Eßer }
282