Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/bc/src/parse.c
39492 views
1
/*
2
* *****************************************************************************
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*
6
* Copyright (c) 2018-2025 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
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
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
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
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
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
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
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
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
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
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
283