2 * *****************************************************************************
4 * SPDX-License-Identifier: BSD-2-Clause
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
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.
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.
30 * *****************************************************************************
48 * Parses a register. The lexer should have already lexed the true name of the
49 * register, per extended registers and such.
50 * @param p The parser.
51 * @param var True if the parser is for a variable, false otherwise.
54 dc_parse_register(BcParse* p, bool var)
57 if (p->l.t != BC_LEX_NAME) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
59 bc_parse_pushName(p, p->l.str.v, var);
64 * @param p The parser.
67 dc_parse_string(BcParse* p)
69 bc_parse_addString(p);
74 * Parses a token that requires a memory operation, like load or store.
75 * @param p The parser.
76 * @param inst The instruction to push for the memory operation.
77 * @param name Whether the load or store is to a variable or array, and not to
79 * @param store True if the operation is a store, false otherwise.
82 dc_parse_mem(BcParse* p, uchar inst, bool name, bool store)
84 // Push the instruction.
85 bc_parse_push(p, inst);
87 // Parse the register if necessary.
88 if (name) dc_parse_register(p, inst != BC_INST_ARRAY_ELEM);
90 // Stores use the bc assign infrastructure, but they need to do a swap
94 bc_parse_push(p, BC_INST_SWAP);
95 bc_parse_push(p, BC_INST_ASSIGN_NO_VAL);
102 * Parses a conditional execution instruction.
103 * @param p The parser.
104 * @param inst The instruction for the condition.
107 dc_parse_cond(BcParse* p, uchar inst)
109 // Push the instruction for the condition and the conditional execution.
110 bc_parse_push(p, inst);
111 bc_parse_push(p, BC_INST_EXEC_COND);
113 // Parse the register.
114 dc_parse_register(p, true);
118 // If the next token is an else, parse the else.
119 if (p->l.t == BC_LEX_KW_ELSE)
121 dc_parse_register(p, true);
124 // Otherwise, push a marker for no else.
125 else bc_parse_pushIndex(p, SIZE_MAX);
129 * Parses a token for dc.
130 * @param p The parser.
131 * @param t The token to parse.
132 * @param flags The flags that say what is allowed or not.
135 dc_parse_token(BcParse* p, BcLexType t, uint8_t flags)
138 bool assign, get_token = false;
142 case BC_LEX_OP_REL_EQ:
143 case BC_LEX_OP_REL_LE:
144 case BC_LEX_OP_REL_GE:
145 case BC_LEX_OP_REL_NE:
146 case BC_LEX_OP_REL_LT:
147 case BC_LEX_OP_REL_GT:
149 inst = (uchar) (t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ);
150 dc_parse_cond(p, inst);
157 dc_parse_mem(p, BC_INST_ARRAY_ELEM, true, t == BC_LEX_COLON);
169 // This tells us whether or not the neg is for a command or at the
170 // beginning of a number. If it's a command, push it. Otherwise,
171 // fallthrough and parse the number.
172 if (dc_lex_negCommand(&p->l))
174 bc_parse_push(p, BC_INST_NEG);
189 // Push the negative instruction if we fell through from above.
190 if (t == BC_LEX_NEG) bc_parse_push(p, BC_INST_NEG);
198 // Make sure the read is not recursive.
199 if (BC_ERR(flags & BC_PARSE_NOREAD))
201 bc_parse_err(p, BC_ERR_EXEC_REC_READ);
203 else bc_parse_push(p, BC_INST_READ);
210 case BC_LEX_OP_ASSIGN:
211 case BC_LEX_STORE_PUSH:
213 assign = t == BC_LEX_OP_ASSIGN;
214 inst = assign ? BC_INST_VAR : BC_INST_PUSH_TO_VAR;
215 dc_parse_mem(p, inst, true, assign);
220 case BC_LEX_LOAD_POP:
222 inst = t == BC_LEX_LOAD_POP ? BC_INST_PUSH_VAR : BC_INST_LOAD;
223 dc_parse_mem(p, inst, true, false);
227 case BC_LEX_REG_STACK_LEVEL:
229 dc_parse_mem(p, BC_INST_REG_STACK_LEN, true, false);
233 case BC_LEX_STORE_IBASE:
234 case BC_LEX_STORE_OBASE:
235 case BC_LEX_STORE_SCALE:
236 #if BC_ENABLE_EXTRA_MATH
237 case BC_LEX_STORE_SEED:
238 #endif // BC_ENABLE_EXTRA_MATH
240 inst = (uchar) (t - BC_LEX_STORE_IBASE + BC_INST_IBASE);
241 dc_parse_mem(p, inst, false, true);
245 case BC_LEX_ARRAY_LENGTH:
247 // Need to push the array first, based on how length is implemented.
248 bc_parse_push(p, BC_INST_ARRAY);
249 dc_parse_register(p, false);
251 bc_parse_push(p, BC_INST_LENGTH);
260 // All other tokens should be taken care of by the caller, or they
261 // actually *are* invalid.
262 bc_parse_err(p, BC_ERR_PARSE_TOKEN);
266 if (get_token) bc_lex_next(&p->l);
270 dc_parse_expr(BcParse* p, uint8_t flags)
274 bool need_expr, have_expr = false;
276 need_expr = ((flags & BC_PARSE_NOREAD) != 0);
278 // dc can just keep parsing forever basically, unlike bc, which has to have
279 // a whole bunch of complicated nonsense because its language was horribly
282 // While we don't have EOF...
283 while ((t = p->l.t) != BC_LEX_EOF)
286 if (t == BC_LEX_NLINE)
292 // Get the instruction that corresponds to the token.
293 inst = dc_parse_insts[t];
295 // If the instruction is invalid, that means we have to do some harder
296 // parsing. So if not invalid, just push the instruction; otherwise,
298 if (inst != BC_INST_INVALID)
300 bc_parse_push(p, inst);
303 else dc_parse_token(p, t, flags);
308 // If we don't have an expression and need one, barf. Otherwise, just push a
309 // BC_INST_POP_EXEC if we have EOF and BC_PARSE_NOCALL, which dc uses to
310 // indicate that it is executing a string.
311 if (BC_ERR(need_expr && !have_expr)) bc_err(BC_ERR_EXEC_READ_EXPR);
312 else if (p->l.t == BC_LEX_EOF && (flags & BC_PARSE_NOCALL))
314 bc_parse_push(p, BC_INST_POP_EXEC);
319 dc_parse_parse(BcParse* p)
323 BC_SETJMP_LOCKED(exit);
325 // If we have EOF, someone called this function one too many times.
327 if (BC_ERR(p->l.t == BC_LEX_EOF)) bc_parse_err(p, BC_ERR_PARSE_EOF);
328 else dc_parse_expr(p, 0);
332 // Need to reset if there was an error.
333 if (BC_SIG_EXC) bc_parse_reset(p);