1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2024 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
47 void
bc_parse_updateFunc(BcParse * p,size_t fidx)48 bc_parse_updateFunc(BcParse* p, size_t fidx)
49 {
50 p->fidx = fidx;
51 p->func = bc_vec_item(&p->prog->fns, fidx);
52 }
53
54 inline void
bc_parse_pushName(const BcParse * p,char * name,bool var)55 bc_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 */
67 static inline void
bc_parse_pushInstIdx(BcParse * p,uchar inst,size_t idx)68 bc_parse_pushInstIdx(BcParse* p, uchar inst, size_t idx)
69 {
70 bc_parse_push(p, inst);
71 bc_parse_pushIndex(p, idx);
72 }
73
74 void
bc_parse_addString(BcParse * p)75 bc_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
85 static void
bc_parse_addNum(BcParse * p,const char * string)86 bc_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
145 void
bc_parse_number(BcParse * p)146 bc_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
179 void
bc_parse_text(BcParse * p,const char * text,BcMode mode)180 bc_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
191 void
bc_parse_reset(BcParse * p)192 bc_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
227 void
bc_parse_free(BcParse * p)228 bc_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
249 void
bc_parse_init(BcParse * p,BcProgram * prog,size_t func)250 bc_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