1/*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 *   list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 *   this list of conditions and the following disclaimer in the documentation
16 *   and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *****************************************************************************
31 *
32 * Code common to the parsers.
33 *
34 */
35
36#include <assert.h>
37#include <stddef.h>
38#include <stdlib.h>
39#include <string.h>
40
41#include <limits.h>
42
43#include <parse.h>
44#include <program.h>
45#include <vm.h>
46
47void
48bc_parse_updateFunc(BcParse* p, size_t fidx)
49{
50	p->fidx = fidx;
51	p->func = bc_vec_item(&p->prog->fns, fidx);
52}
53
54inline void
55bc_parse_pushName(const BcParse* p, char* name, bool var)
56{
57	bc_parse_pushIndex(p, bc_program_search(p->prog, name, var));
58}
59
60/**
61 * Updates the function, then pushes the instruction and the index. This is a
62 * convenience function.
63 * @param p     The parser.
64 * @param inst  The instruction to push.
65 * @param idx   The index to push.
66 */
67static inline void
68bc_parse_pushInstIdx(BcParse* p, uchar inst, size_t idx)
69{
70	bc_parse_push(p, inst);
71	bc_parse_pushIndex(p, idx);
72}
73
74void
75bc_parse_addString(BcParse* p)
76{
77	size_t idx;
78
79	idx = bc_program_addString(p->prog, p->l.str.v);
80
81	// Push the string info.
82	bc_parse_pushInstIdx(p, BC_INST_STR, idx);
83}
84
85static void
86bc_parse_addNum(BcParse* p, const char* string)
87{
88	BcProgram* prog = p->prog;
89	size_t idx;
90
91	// XXX: This function has an implicit assumption: that string is a valid C
92	// string with a nul terminator. This is because of the unchecked array
93	// accesses below. I can't check this with an assert() because that could
94	// lead to out-of-bounds access.
95	//
96	// XXX: In fact, just for safety's sake, assume that this function needs a
97	// non-empty string with a nul terminator, just in case bc_parse_zero or
98	// bc_parse_one change in the future, which I doubt.
99
100	BC_SIG_ASSERT_LOCKED;
101
102	// Special case 0.
103	if (bc_parse_zero[0] == string[0] && bc_parse_zero[1] == string[1])
104	{
105		bc_parse_push(p, BC_INST_ZERO);
106		return;
107	}
108
109	// Special case 1.
110	if (bc_parse_one[0] == string[0] && bc_parse_one[1] == string[1])
111	{
112		bc_parse_push(p, BC_INST_ONE);
113		return;
114	}
115
116	if (bc_map_insert(&prog->const_map, string, prog->consts.len, &idx))
117	{
118		BcConst* c;
119		BcId* id = bc_vec_item(&prog->const_map, idx);
120
121		// Get the index.
122		idx = id->idx;
123
124		// Push an empty constant.
125		c = bc_vec_pushEmpty(&prog->consts);
126
127		// Set the fields. We reuse the string in the ID (allocated by
128		// bc_map_insert()), because why not?
129		c->val = id->name;
130		c->base = BC_NUM_BIGDIG_MAX;
131
132		// We need this to be able to tell that the number has not been
133		// allocated.
134		bc_num_clear(&c->num);
135	}
136	else
137	{
138		BcId* id = bc_vec_item(&prog->const_map, idx);
139		idx = id->idx;
140	}
141
142	bc_parse_pushInstIdx(p, BC_INST_NUM, idx);
143}
144
145void
146bc_parse_number(BcParse* p)
147{
148#if BC_ENABLE_EXTRA_MATH
149	char* exp = strchr(p->l.str.v, 'e');
150	size_t idx = SIZE_MAX;
151
152	// Do we have a number in scientific notation? If so, add a nul byte where
153	// the e is.
154	if (exp != NULL)
155	{
156		idx = ((size_t) (exp - p->l.str.v));
157		*exp = 0;
158	}
159#endif // BC_ENABLE_EXTRA_MATH
160
161	bc_parse_addNum(p, p->l.str.v);
162
163#if BC_ENABLE_EXTRA_MATH
164	// If we have a number in scientific notation...
165	if (exp != NULL)
166	{
167		bool neg;
168
169		// Figure out if the exponent is negative.
170		neg = (*((char*) bc_vec_item(&p->l.str, idx + 1)) == BC_LEX_NEG_CHAR);
171
172		// Add the number and instruction.
173		bc_parse_addNum(p, bc_vec_item(&p->l.str, idx + 1 + neg));
174		bc_parse_push(p, BC_INST_LSHIFT + neg);
175	}
176#endif // BC_ENABLE_EXTRA_MATH
177}
178
179void
180bc_parse_text(BcParse* p, const char* text, BcMode mode)
181{
182	BC_SIG_LOCK;
183
184	// Make sure the pointer isn't invalidated.
185	p->func = bc_vec_item(&p->prog->fns, p->fidx);
186	bc_lex_text(&p->l, text, mode);
187
188	BC_SIG_UNLOCK;
189}
190
191void
192bc_parse_reset(BcParse* p)
193{
194	BC_SIG_ASSERT_LOCKED;
195
196	// Reset the function if it isn't main and switch to main.
197	if (p->fidx != BC_PROG_MAIN)
198	{
199		bc_func_reset(p->func);
200		bc_parse_updateFunc(p, BC_PROG_MAIN);
201	}
202
203	// Reset the lexer.
204	p->l.i = p->l.len;
205	p->l.t = BC_LEX_EOF;
206
207#if BC_ENABLED
208	if (BC_IS_BC)
209	{
210		// Get rid of the bc parser state.
211		p->auto_part = false;
212		bc_vec_npop(&p->flags, p->flags.len - 1);
213		bc_vec_popAll(&p->exits);
214		bc_vec_popAll(&p->conds);
215		bc_vec_popAll(&p->ops);
216	}
217#endif // BC_ENABLED
218
219	// Reset the program. This might clear the error.
220	bc_program_reset(p->prog);
221
222	// Jump if there is an error.
223	if (BC_ERR(vm->status)) BC_JMP;
224}
225
226#if BC_DEBUG
227void
228bc_parse_free(BcParse* p)
229{
230	BC_SIG_ASSERT_LOCKED;
231
232	assert(p != NULL);
233
234#if BC_ENABLED
235	if (BC_IS_BC)
236	{
237		bc_vec_free(&p->flags);
238		bc_vec_free(&p->exits);
239		bc_vec_free(&p->conds);
240		bc_vec_free(&p->ops);
241		bc_vec_free(&p->buf);
242	}
243#endif // BC_ENABLED
244
245	bc_lex_free(&p->l);
246}
247#endif // BC_DEBUG
248
249void
250bc_parse_init(BcParse* p, BcProgram* prog, size_t func)
251{
252#if BC_ENABLED
253	uint16_t flag = 0;
254#endif // BC_ENABLED
255
256	BC_SIG_ASSERT_LOCKED;
257
258	assert(p != NULL && prog != NULL);
259
260#if BC_ENABLED
261	if (BC_IS_BC)
262	{
263		// We always want at least one flag set on the flags stack.
264		bc_vec_init(&p->flags, sizeof(uint16_t), BC_DTOR_NONE);
265		bc_vec_push(&p->flags, &flag);
266
267		bc_vec_init(&p->exits, sizeof(BcInstPtr), BC_DTOR_NONE);
268		bc_vec_init(&p->conds, sizeof(size_t), BC_DTOR_NONE);
269		bc_vec_init(&p->ops, sizeof(BcLexType), BC_DTOR_NONE);
270		bc_vec_init(&p->buf, sizeof(char), BC_DTOR_NONE);
271
272		p->auto_part = false;
273	}
274#endif // BC_ENABLED
275
276	bc_lex_init(&p->l);
277
278	// Set up the function.
279	p->prog = prog;
280	bc_parse_updateFunc(p, func);
281}
282