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 * The parser for dc. 33 * 34 */ 35 36 #if DC_ENABLED 37 38 #include <assert.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <setjmp.h> 42 43 #include <dc.h> 44 #include <program.h> 45 #include <vm.h> 46 47 /** 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. 52 */ 53 static void 54 dc_parse_register(BcParse* p, bool var) 55 { 56 bc_lex_next(&p->l); 57 if (p->l.t != BC_LEX_NAME) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 58 59 bc_parse_pushName(p, p->l.str.v, var); 60 } 61 62 /** 63 * Parses a dc string. 64 * @param p The parser. 65 */ 66 static inline void 67 dc_parse_string(BcParse* p) 68 { 69 bc_parse_addString(p); 70 bc_lex_next(&p->l); 71 } 72 73 /** 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 78 * a global. 79 * @param store True if the operation is a store, false otherwise. 80 */ 81 static void 82 dc_parse_mem(BcParse* p, uchar inst, bool name, bool store) 83 { 84 // Push the instruction. 85 bc_parse_push(p, inst); 86 87 // Parse the register if necessary. 88 if (name) dc_parse_register(p, inst != BC_INST_ARRAY_ELEM); 89 90 // Stores use the bc assign infrastructure, but they need to do a swap 91 // first. 92 if (store) 93 { 94 bc_parse_push(p, BC_INST_SWAP); 95 bc_parse_push(p, BC_INST_ASSIGN_NO_VAL); 96 } 97 98 bc_lex_next(&p->l); 99 } 100 101 /** 102 * Parses a conditional execution instruction. 103 * @param p The parser. 104 * @param inst The instruction for the condition. 105 */ 106 static void 107 dc_parse_cond(BcParse* p, uchar inst) 108 { 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); 112 113 // Parse the register. 114 dc_parse_register(p, true); 115 116 bc_lex_next(&p->l); 117 118 // If the next token is an else, parse the else. 119 if (p->l.t == BC_LEX_KW_ELSE) 120 { 121 dc_parse_register(p, true); 122 bc_lex_next(&p->l); 123 } 124 // Otherwise, push a marker for no else. 125 else bc_parse_pushIndex(p, SIZE_MAX); 126 } 127 128 /** 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. 133 */ 134 static void 135 dc_parse_token(BcParse* p, BcLexType t, uint8_t flags) 136 { 137 uchar inst; 138 bool assign, get_token = false; 139 140 switch (t) 141 { 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: 148 { 149 inst = (uchar) (t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ); 150 dc_parse_cond(p, inst); 151 break; 152 } 153 154 case BC_LEX_SCOLON: 155 case BC_LEX_COLON: 156 { 157 dc_parse_mem(p, BC_INST_ARRAY_ELEM, true, t == BC_LEX_COLON); 158 break; 159 } 160 161 case BC_LEX_STR: 162 { 163 dc_parse_string(p); 164 break; 165 } 166 167 case BC_LEX_NEG: 168 { 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)) 173 { 174 bc_parse_push(p, BC_INST_NEG); 175 get_token = true; 176 break; 177 } 178 179 bc_lex_next(&p->l); 180 181 // Fallthrough. 182 BC_FALLTHROUGH 183 } 184 185 case BC_LEX_NUMBER: 186 { 187 bc_parse_number(p); 188 189 // Push the negative instruction if we fell through from above. 190 if (t == BC_LEX_NEG) bc_parse_push(p, BC_INST_NEG); 191 get_token = true; 192 193 break; 194 } 195 196 case BC_LEX_KW_READ: 197 { 198 // Make sure the read is not recursive. 199 if (BC_ERR(flags & BC_PARSE_NOREAD)) 200 { 201 bc_parse_err(p, BC_ERR_EXEC_REC_READ); 202 } 203 else bc_parse_push(p, BC_INST_READ); 204 205 get_token = true; 206 207 break; 208 } 209 210 case BC_LEX_OP_ASSIGN: 211 case BC_LEX_STORE_PUSH: 212 { 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); 216 break; 217 } 218 219 case BC_LEX_LOAD: 220 case BC_LEX_LOAD_POP: 221 { 222 inst = t == BC_LEX_LOAD_POP ? BC_INST_PUSH_VAR : BC_INST_LOAD; 223 dc_parse_mem(p, inst, true, false); 224 break; 225 } 226 227 case BC_LEX_REG_STACK_LEVEL: 228 { 229 dc_parse_mem(p, BC_INST_REG_STACK_LEN, true, false); 230 break; 231 } 232 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 239 { 240 inst = (uchar) (t - BC_LEX_STORE_IBASE + BC_INST_IBASE); 241 dc_parse_mem(p, inst, false, true); 242 break; 243 } 244 245 case BC_LEX_ARRAY_LENGTH: 246 { 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); 250 251 bc_parse_push(p, BC_INST_LENGTH); 252 253 get_token = true; 254 255 break; 256 } 257 258 case BC_LEX_EOF: 259 case BC_LEX_INVALID: 260 #if BC_ENABLED 261 case BC_LEX_OP_INC: 262 case BC_LEX_OP_DEC: 263 #endif // BC_ENABLED 264 case BC_LEX_OP_BOOL_NOT: 265 #if BC_ENABLE_EXTRA_MATH 266 case BC_LEX_OP_TRUNC: 267 #endif // BC_ENABLE_EXTRA_MATH 268 case BC_LEX_OP_POWER: 269 case BC_LEX_OP_MULTIPLY: 270 case BC_LEX_OP_DIVIDE: 271 case BC_LEX_OP_MODULUS: 272 case BC_LEX_OP_PLUS: 273 case BC_LEX_OP_MINUS: 274 #if BC_ENABLE_EXTRA_MATH 275 case BC_LEX_OP_PLACES: 276 case BC_LEX_OP_LSHIFT: 277 case BC_LEX_OP_RSHIFT: 278 #endif // BC_ENABLE_EXTRA_MATH 279 case BC_LEX_OP_BOOL_OR: 280 case BC_LEX_OP_BOOL_AND: 281 #if BC_ENABLED 282 case BC_LEX_OP_ASSIGN_POWER: 283 case BC_LEX_OP_ASSIGN_MULTIPLY: 284 case BC_LEX_OP_ASSIGN_DIVIDE: 285 case BC_LEX_OP_ASSIGN_MODULUS: 286 case BC_LEX_OP_ASSIGN_PLUS: 287 case BC_LEX_OP_ASSIGN_MINUS: 288 #if BC_ENABLE_EXTRA_MATH 289 case BC_LEX_OP_ASSIGN_PLACES: 290 case BC_LEX_OP_ASSIGN_LSHIFT: 291 case BC_LEX_OP_ASSIGN_RSHIFT: 292 #endif // BC_ENABLE_EXTRA_MATH 293 #endif // BC_ENABLED 294 case BC_LEX_NLINE: 295 case BC_LEX_WHITESPACE: 296 case BC_LEX_LPAREN: 297 case BC_LEX_RPAREN: 298 case BC_LEX_LBRACKET: 299 case BC_LEX_COMMA: 300 case BC_LEX_RBRACKET: 301 case BC_LEX_LBRACE: 302 case BC_LEX_NAME: 303 case BC_LEX_RBRACE: 304 #if BC_ENABLED 305 case BC_LEX_KW_AUTO: 306 case BC_LEX_KW_BREAK: 307 case BC_LEX_KW_CONTINUE: 308 case BC_LEX_KW_DEFINE: 309 case BC_LEX_KW_FOR: 310 case BC_LEX_KW_IF: 311 case BC_LEX_KW_LIMITS: 312 case BC_LEX_KW_RETURN: 313 case BC_LEX_KW_WHILE: 314 case BC_LEX_KW_HALT: 315 case BC_LEX_KW_LAST: 316 #endif // BC_ENABLED 317 case BC_LEX_KW_IBASE: 318 case BC_LEX_KW_OBASE: 319 case BC_LEX_KW_SCALE: 320 #if BC_ENABLE_EXTRA_MATH 321 case BC_LEX_KW_SEED: 322 #endif // BC_ENABLE_EXTRA_MATH 323 case BC_LEX_KW_LENGTH: 324 case BC_LEX_KW_PRINT: 325 case BC_LEX_KW_SQRT: 326 case BC_LEX_KW_ABS: 327 case BC_LEX_KW_IS_NUMBER: 328 case BC_LEX_KW_IS_STRING: 329 #if BC_ENABLE_EXTRA_MATH 330 case BC_LEX_KW_IRAND: 331 #endif // BC_ENABLE_EXTRA_MATH 332 case BC_LEX_KW_ASCIIFY: 333 case BC_LEX_KW_MODEXP: 334 case BC_LEX_KW_DIVMOD: 335 case BC_LEX_KW_QUIT: 336 #if BC_ENABLE_EXTRA_MATH 337 case BC_LEX_KW_RAND: 338 #endif // BC_ENABLE_EXTRA_MATH 339 case BC_LEX_KW_MAXIBASE: 340 case BC_LEX_KW_MAXOBASE: 341 case BC_LEX_KW_MAXSCALE: 342 #if BC_ENABLE_EXTRA_MATH 343 case BC_LEX_KW_MAXRAND: 344 #endif // BC_ENABLE_EXTRA_MATH 345 case BC_LEX_KW_LINE_LENGTH: 346 #if BC_ENABLED 347 case BC_LEX_KW_GLOBAL_STACKS: 348 #endif // BC_ENABLED 349 case BC_LEX_KW_LEADING_ZERO: 350 case BC_LEX_KW_STREAM: 351 case BC_LEX_KW_ELSE: 352 case BC_LEX_EXTENDED_REGISTERS: 353 case BC_LEX_EQ_NO_REG: 354 case BC_LEX_EXECUTE: 355 case BC_LEX_PRINT_STACK: 356 case BC_LEX_CLEAR_STACK: 357 case BC_LEX_STACK_LEVEL: 358 case BC_LEX_DUPLICATE: 359 case BC_LEX_SWAP: 360 case BC_LEX_POP: 361 case BC_LEX_PRINT_POP: 362 case BC_LEX_NQUIT: 363 case BC_LEX_EXEC_STACK_LENGTH: 364 case BC_LEX_SCALE_FACTOR: 365 { 366 // All other tokens should be taken care of by the caller, or they 367 // actually *are* invalid. 368 bc_parse_err(p, BC_ERR_PARSE_TOKEN); 369 } 370 } 371 372 if (get_token) bc_lex_next(&p->l); 373 } 374 375 void 376 dc_parse_expr(BcParse* p, uint8_t flags) 377 { 378 BcInst inst; 379 BcLexType t; 380 bool need_expr, have_expr = false; 381 382 need_expr = ((flags & BC_PARSE_NOREAD) != 0); 383 384 // dc can just keep parsing forever basically, unlike bc, which has to have 385 // a whole bunch of complicated nonsense because its language was horribly 386 // designed. 387 388 // While we don't have EOF... 389 while ((t = p->l.t) != BC_LEX_EOF) 390 { 391 // Eat newline. 392 if (t == BC_LEX_NLINE) 393 { 394 bc_lex_next(&p->l); 395 continue; 396 } 397 398 // Get the instruction that corresponds to the token. 399 inst = dc_parse_insts[t]; 400 401 // If the instruction is invalid, that means we have to do some harder 402 // parsing. So if not invalid, just push the instruction; otherwise, 403 // parse the token. 404 if (inst != BC_INST_INVALID) 405 { 406 bc_parse_push(p, inst); 407 bc_lex_next(&p->l); 408 } 409 else dc_parse_token(p, t, flags); 410 411 have_expr = true; 412 } 413 414 // If we don't have an expression and need one, barf. Otherwise, just push a 415 // BC_INST_POP_EXEC if we have EOF and BC_PARSE_NOCALL, which dc uses to 416 // indicate that it is executing a string. 417 if (BC_ERR(need_expr && !have_expr)) bc_err(BC_ERR_EXEC_READ_EXPR); 418 else if (p->l.t == BC_LEX_EOF && (flags & BC_PARSE_NOCALL)) 419 { 420 bc_parse_push(p, BC_INST_POP_EXEC); 421 } 422 } 423 424 void 425 dc_parse_parse(BcParse* p) 426 { 427 assert(p != NULL); 428 429 BC_SETJMP_LOCKED(vm, exit); 430 431 // If we have EOF, someone called this function one too many times. 432 // Otherwise, parse. 433 if (BC_ERR(p->l.t == BC_LEX_EOF)) bc_parse_err(p, BC_ERR_PARSE_EOF); 434 else dc_parse_expr(p, 0); 435 436 exit: 437 438 // Need to reset if there was an error. 439 if (BC_SIG_EXC(vm)) bc_parse_reset(p); 440 441 BC_LONGJMP_CONT(vm); 442 BC_SIG_MAYLOCK; 443 } 444 #endif // DC_ENABLED 445