/* * ***************************************************************************** * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2021 Gavin D. Howard and contributors. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ***************************************************************************** * * The parser for bc. * */ #if BC_ENABLED #include #include #include #include #include #include #include #include static void bc_parse_else(BcParse *p); static void bc_parse_stmt(BcParse *p); static BcParseStatus bc_parse_expr_err(BcParse *p, uint8_t flags, BcParseNext next); static bool bc_parse_inst_isLeaf(BcInst t) { return (t >= BC_INST_NUM && t <= BC_INST_MAXSCALE) || #if BC_ENABLE_EXTRA_MATH t == BC_INST_TRUNC || #endif // BC_ENABLE_EXTRA_MATH t <= BC_INST_DEC; } static bool bc_parse_isDelimiter(const BcParse *p) { BcLexType t = p->l.t; bool good = false; if (BC_PARSE_DELIMITER(t)) return true; if (t == BC_LEX_KW_ELSE) { size_t i; uint16_t *fptr = NULL, flags = BC_PARSE_FLAG_ELSE; for (i = 0; i < p->flags.len && BC_PARSE_BLOCK_STMT(flags); ++i) { fptr = bc_vec_item_rev(&p->flags, i); flags = *fptr; if ((flags & BC_PARSE_FLAG_BRACE) && p->l.last != BC_LEX_RBRACE) return false; } good = ((flags & BC_PARSE_FLAG_IF) != 0); } else if (t == BC_LEX_RBRACE) { size_t i; for (i = 0; !good && i < p->flags.len; ++i) { uint16_t *fptr = bc_vec_item_rev(&p->flags, i); good = (((*fptr) & BC_PARSE_FLAG_BRACE) != 0); } } return good; } static void bc_parse_setLabel(BcParse *p) { BcFunc *func = p->func; BcInstPtr *ip = bc_vec_top(&p->exits); size_t *label; assert(func == bc_vec_item(&p->prog->fns, p->fidx)); label = bc_vec_item(&func->labels, ip->idx); *label = func->code.len; bc_vec_pop(&p->exits); } static void bc_parse_createLabel(BcParse *p, size_t idx) { bc_vec_push(&p->func->labels, &idx); } static void bc_parse_createCondLabel(BcParse *p, size_t idx) { bc_parse_createLabel(p, p->func->code.len); bc_vec_push(&p->conds, &idx); } static void bc_parse_createExitLabel(BcParse *p, size_t idx, bool loop) { BcInstPtr ip; assert(p->func == bc_vec_item(&p->prog->fns, p->fidx)); ip.func = loop; ip.idx = idx; ip.len = 0; bc_vec_push(&p->exits, &ip); bc_parse_createLabel(p, SIZE_MAX); } static void bc_parse_operator(BcParse *p, BcLexType type, size_t start, size_t *nexprs) { BcLexType t; uchar l, r = BC_PARSE_OP_PREC(type); uchar left = BC_PARSE_OP_LEFT(type); while (p->ops.len > start) { t = BC_PARSE_TOP_OP(p); if (t == BC_LEX_LPAREN) break; l = BC_PARSE_OP_PREC(t); if (l >= r && (l != r || !left)) break; bc_parse_push(p, BC_PARSE_TOKEN_INST(t)); bc_vec_pop(&p->ops); *nexprs -= !BC_PARSE_OP_PREFIX(t); } bc_vec_push(&p->ops, &type); } static void bc_parse_rightParen(BcParse *p, size_t *nexs) { BcLexType top; while ((top = BC_PARSE_TOP_OP(p)) != BC_LEX_LPAREN) { bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); bc_vec_pop(&p->ops); *nexs -= !BC_PARSE_OP_PREFIX(top); } bc_vec_pop(&p->ops); bc_lex_next(&p->l); } static void bc_parse_params(BcParse *p, uint8_t flags) { bool comma = false; size_t nparams; bc_lex_next(&p->l); flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= (BC_PARSE_ARRAY | BC_PARSE_NEEDVAL); for (nparams = 0; p->l.t != BC_LEX_RPAREN; ++nparams) { bc_parse_expr_status(p, flags, bc_parse_next_param); comma = (p->l.t == BC_LEX_COMMA); if (comma) bc_lex_next(&p->l); } if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_parse_push(p, BC_INST_CALL); bc_parse_pushIndex(p, nparams); } static void bc_parse_call(BcParse *p, const char *name, uint8_t flags) { size_t idx; bc_parse_params(p, flags); // We just assert this because bc_parse_params() should // ensure that the next token is what it should be. assert(p->l.t == BC_LEX_RPAREN); // We cannot use bc_program_insertFunc() here // because it will overwrite an existing function. idx = bc_map_index(&p->prog->fn_map, name); if (idx == BC_VEC_INVALID_IDX) { BC_SIG_LOCK; idx = bc_program_insertFunc(p->prog, name); BC_SIG_UNLOCK; assert(idx != BC_VEC_INVALID_IDX); // Make sure that this pointer was not invalidated. p->func = bc_vec_item(&p->prog->fns, p->fidx); } else idx = ((BcId*) bc_vec_item(&p->prog->fn_map, idx))->idx; bc_parse_pushIndex(p, idx); bc_lex_next(&p->l); } static void bc_parse_name(BcParse *p, BcInst *type, bool *can_assign, uint8_t flags) { char *name; BC_SIG_LOCK; name = bc_vm_strdup(p->l.str.v); BC_SETJMP_LOCKED(err); BC_SIG_UNLOCK; bc_lex_next(&p->l); if (p->l.t == BC_LEX_LBRACKET) { bc_lex_next(&p->l); if (p->l.t == BC_LEX_RBRACKET) { if (BC_ERR(!(flags & BC_PARSE_ARRAY))) bc_parse_err(p, BC_ERR_PARSE_EXPR); *type = BC_INST_ARRAY; *can_assign = false; } else { uint8_t flags2 = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_NEEDVAL; bc_parse_expr_status(p, flags2, bc_parse_next_elem); if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); *type = BC_INST_ARRAY_ELEM; *can_assign = true; } bc_lex_next(&p->l); bc_parse_push(p, *type); bc_parse_pushName(p, name, false); } else if (p->l.t == BC_LEX_LPAREN) { if (BC_ERR(flags & BC_PARSE_NOCALL)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); *type = BC_INST_CALL; *can_assign = false; bc_parse_call(p, name, flags); } else { *type = BC_INST_VAR; *can_assign = true; bc_parse_push(p, BC_INST_VAR); bc_parse_pushName(p, name, true); } err: BC_SIG_MAYLOCK; free(name); BC_LONGJMP_CONT; } static void bc_parse_noArgBuiltin(BcParse *p, BcInst inst) { bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); if ((p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_parse_push(p, inst); bc_lex_next(&p->l); } static void bc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags, BcInst *prev) { bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= BC_PARSE_NEEDVAL; if (type == BC_LEX_KW_LENGTH) flags |= BC_PARSE_ARRAY; bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); *prev = type - BC_LEX_KW_LENGTH + BC_INST_LENGTH; bc_parse_push(p, *prev); bc_lex_next(&p->l); } static void bc_parse_scale(BcParse *p, BcInst *type, bool *can_assign, uint8_t flags) { bc_lex_next(&p->l); if (p->l.t != BC_LEX_LPAREN) { *type = BC_INST_SCALE; *can_assign = true; bc_parse_push(p, BC_INST_SCALE); return; } *type = BC_INST_SCALE_FUNC; *can_assign = false; flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= BC_PARSE_NEEDVAL; bc_lex_next(&p->l); bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_parse_push(p, BC_INST_SCALE_FUNC); bc_lex_next(&p->l); } static void bc_parse_incdec(BcParse *p, BcInst *prev, bool *can_assign, size_t *nexs, uint8_t flags) { BcLexType type; uchar inst; BcInst etype = *prev; BcLexType last = p->l.last; assert(prev != NULL && can_assign != NULL); if (BC_ERR(last == BC_LEX_OP_INC || last == BC_LEX_OP_DEC || last == BC_LEX_RPAREN)) { bc_parse_err(p, BC_ERR_PARSE_ASSIGN); } if (BC_PARSE_INST_VAR(etype)) { if (!*can_assign) bc_parse_err(p, BC_ERR_PARSE_ASSIGN); *prev = inst = BC_INST_INC + (p->l.t != BC_LEX_OP_INC); bc_parse_push(p, inst); bc_lex_next(&p->l); *can_assign = false; } else { *prev = inst = BC_INST_ASSIGN_PLUS + (p->l.t != BC_LEX_OP_INC); bc_lex_next(&p->l); type = p->l.t; // Because we parse the next part of the expression // right here, we need to increment this. *nexs = *nexs + 1; if (type == BC_LEX_NAME) { uint8_t flags2 = flags & ~BC_PARSE_ARRAY; bc_parse_name(p, prev, can_assign, flags2 | BC_PARSE_NOCALL); } else if (type >= BC_LEX_KW_LAST && type <= BC_LEX_KW_OBASE) { bc_parse_push(p, type - BC_LEX_KW_LAST + BC_INST_LAST); bc_lex_next(&p->l); } else if (BC_NO_ERR(type == BC_LEX_KW_SCALE)) { bc_lex_next(&p->l); if (BC_ERR(p->l.t == BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); else bc_parse_push(p, BC_INST_SCALE); } else bc_parse_err(p, BC_ERR_PARSE_TOKEN); *can_assign = false; bc_parse_push(p, BC_INST_ONE); bc_parse_push(p, inst); } } static void bc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn, bool rparen, bool binlast, size_t *nexprs) { BcLexType type; bc_lex_next(&p->l); type = BC_PARSE_LEAF(*prev, binlast, rparen) ? BC_LEX_OP_MINUS : BC_LEX_NEG; *prev = BC_PARSE_TOKEN_INST(type); // We can just push onto the op stack because this is the largest // precedence operator that gets pushed. Inc/dec does not. if (type != BC_LEX_OP_MINUS) bc_vec_push(&p->ops, &type); else bc_parse_operator(p, type, ops_bgn, nexprs); } static void bc_parse_str(BcParse *p, char inst) { bc_parse_addString(p); bc_parse_push(p, inst); bc_lex_next(&p->l); } static void bc_parse_print(BcParse *p) { BcLexType t; bool comma = false; bc_lex_next(&p->l); t = p->l.t; if (bc_parse_isDelimiter(p)) bc_parse_err(p, BC_ERR_PARSE_PRINT); do { if (t == BC_LEX_STR) bc_parse_str(p, BC_INST_PRINT_POP); else { bc_parse_expr_status(p, BC_PARSE_NEEDVAL, bc_parse_next_print); bc_parse_push(p, BC_INST_PRINT_POP); } comma = (p->l.t == BC_LEX_COMMA); if (comma) bc_lex_next(&p->l); else { if (!bc_parse_isDelimiter(p)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); else break; } t = p->l.t; } while (true); if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); } static void bc_parse_return(BcParse *p) { BcLexType t; bool paren; uchar inst = BC_INST_RET0; if (BC_ERR(!BC_PARSE_FUNC(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); if (p->func->voidfn) inst = BC_INST_RET_VOID; bc_lex_next(&p->l); t = p->l.t; paren = t == BC_LEX_LPAREN; if (bc_parse_isDelimiter(p)) bc_parse_push(p, inst); else { BcParseStatus s; s = bc_parse_expr_err(p, BC_PARSE_NEEDVAL, bc_parse_next_expr); if (s == BC_PARSE_STATUS_EMPTY_EXPR) { bc_parse_push(p, inst); bc_lex_next(&p->l); } if (!paren || p->l.last != BC_LEX_RPAREN) { bc_parse_err(p, BC_ERR_POSIX_RET); } else if (BC_ERR(p->func->voidfn)) bc_parse_verr(p, BC_ERR_PARSE_RET_VOID, p->func->name); bc_parse_push(p, BC_INST_RET); } } static void bc_parse_noElse(BcParse *p) { uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END)); bc_parse_setLabel(p); } static void bc_parse_endBody(BcParse *p, bool brace) { bool has_brace, new_else = false; if (BC_ERR(p->flags.len <= 1)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); if (brace) { assert(p->l.t == BC_LEX_RBRACE); bc_lex_next(&p->l); if (BC_ERR(!bc_parse_isDelimiter(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); } has_brace = (BC_PARSE_BRACE(p) != 0); do { size_t len = p->flags.len; bool loop; if (has_brace && !brace) bc_parse_err(p, BC_ERR_PARSE_TOKEN); loop = (BC_PARSE_LOOP_INNER(p) != 0); if (loop || BC_PARSE_ELSE(p)) { if (loop) { size_t *label = bc_vec_top(&p->conds); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, *label); bc_vec_pop(&p->conds); } bc_parse_setLabel(p); bc_vec_pop(&p->flags); } else if (BC_PARSE_FUNC_INNER(p)) { BcInst inst = (p->func->voidfn ? BC_INST_RET_VOID : BC_INST_RET0); bc_parse_push(p, inst); bc_parse_updateFunc(p, BC_PROG_MAIN); bc_vec_pop(&p->flags); } else if (BC_PARSE_BRACE(p) && !BC_PARSE_IF(p)) bc_vec_pop(&p->flags); // This needs to be last to parse nested if's properly. if (BC_PARSE_IF(p) && (len == p->flags.len || !BC_PARSE_BRACE(p))) { while (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l); bc_vec_pop(&p->flags); if (!BC_S) { *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_IF_END; new_else = (p->l.t == BC_LEX_KW_ELSE); if (new_else) bc_parse_else(p); else if (!has_brace && (!BC_PARSE_IF_END(p) || brace)) bc_parse_noElse(p); } else bc_parse_noElse(p); } if (brace && has_brace) brace = false; } while (p->flags.len > 1 && !new_else && (!BC_PARSE_IF_END(p) || brace) && !(has_brace = (BC_PARSE_BRACE(p) != 0))); if (BC_ERR(p->flags.len == 1 && brace)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); else if (brace && BC_PARSE_BRACE(p)) { uint16_t flags = BC_PARSE_TOP_FLAG(p); if (!(flags & (BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_LOOP_INNER)) && !(flags & (BC_PARSE_FLAG_IF | BC_PARSE_FLAG_ELSE)) && !(flags & (BC_PARSE_FLAG_IF_END))) { bc_vec_pop(&p->flags); } } } static void bc_parse_startBody(BcParse *p, uint16_t flags) { assert(flags); flags |= (BC_PARSE_TOP_FLAG(p) & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP)); flags |= BC_PARSE_FLAG_BODY; bc_vec_push(&p->flags, &flags); } static void bc_parse_if(BcParse *p) { size_t idx; uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); bc_parse_push(p, BC_INST_JUMP_ZERO); idx = p->func->labels.len; bc_parse_pushIndex(p, idx); bc_parse_createExitLabel(p, idx, false); bc_parse_startBody(p, BC_PARSE_FLAG_IF); } static void bc_parse_else(BcParse *p) { size_t idx = p->func->labels.len; if (BC_ERR(!BC_PARSE_IF_END(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, idx); bc_parse_noElse(p); bc_parse_createExitLabel(p, idx, false); bc_parse_startBody(p, BC_PARSE_FLAG_ELSE); bc_lex_next(&p->l); } static void bc_parse_while(BcParse *p) { size_t idx; uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); bc_parse_createCondLabel(p, p->func->labels.len); idx = p->func->labels.len; bc_parse_createExitLabel(p, idx, true); bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); bc_parse_push(p, BC_INST_JUMP_ZERO); bc_parse_pushIndex(p, idx); bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); } static void bc_parse_for(BcParse *p) { size_t cond_idx, exit_idx, body_idx, update_idx; bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); if (p->l.t != BC_LEX_SCOLON) bc_parse_expr_status(p, 0, bc_parse_next_for); else bc_parse_err(p, BC_ERR_POSIX_FOR); if (BC_ERR(p->l.t != BC_LEX_SCOLON)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); cond_idx = p->func->labels.len; update_idx = cond_idx + 1; body_idx = update_idx + 1; exit_idx = body_idx + 1; bc_parse_createLabel(p, p->func->code.len); if (p->l.t != BC_LEX_SCOLON) { uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); bc_parse_expr_status(p, flags, bc_parse_next_for); } else { // Set this for the next call to bc_parse_number. // This is safe to set because the current token // is a semicolon, which has no string requirement. bc_vec_string(&p->l.str, sizeof(bc_parse_one) - 1, bc_parse_one); bc_parse_number(p); bc_parse_err(p, BC_ERR_POSIX_FOR); } if (BC_ERR(p->l.t != BC_LEX_SCOLON)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); bc_parse_push(p, BC_INST_JUMP_ZERO); bc_parse_pushIndex(p, exit_idx); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, body_idx); bc_parse_createCondLabel(p, update_idx); if (p->l.t != BC_LEX_RPAREN) bc_parse_expr_status(p, 0, bc_parse_next_rel); else bc_parse_err(p, BC_ERR_POSIX_FOR); if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, cond_idx); bc_parse_createLabel(p, p->func->code.len); bc_parse_createExitLabel(p, exit_idx, true); bc_lex_next(&p->l); bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); } static void bc_parse_loopExit(BcParse *p, BcLexType type) { size_t i; BcInstPtr *ip; if (BC_ERR(!BC_PARSE_LOOP(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); if (type == BC_LEX_KW_BREAK) { if (BC_ERR(!p->exits.len)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); i = p->exits.len - 1; ip = bc_vec_item(&p->exits, i); while (!ip->func && i < p->exits.len) ip = bc_vec_item(&p->exits, i--); assert(ip != NULL && (i < p->exits.len || ip->func)); i = ip->idx; } else i = *((size_t*) bc_vec_top(&p->conds)); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, i); bc_lex_next(&p->l); } static void bc_parse_func(BcParse *p) { bool comma = false, voidfn; uint16_t flags; size_t idx; bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_FUNC); voidfn = (!BC_IS_POSIX && p->l.t == BC_LEX_NAME && !strcmp(p->l.str.v, "void")); bc_lex_next(&p->l); voidfn = (voidfn && p->l.t == BC_LEX_NAME); if (voidfn) { bc_parse_err(p, BC_ERR_POSIX_VOID); bc_lex_next(&p->l); } if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_FUNC); assert(p->prog->fns.len == p->prog->fn_map.len); BC_SIG_LOCK; idx = bc_program_insertFunc(p->prog, p->l.str.v); BC_SIG_UNLOCK; assert(idx); bc_parse_updateFunc(p, idx); p->func->voidfn = voidfn; bc_lex_next(&p->l); while (p->l.t != BC_LEX_RPAREN) { BcType t = BC_TYPE_VAR; if (p->l.t == BC_LEX_OP_MULTIPLY) { t = BC_TYPE_REF; bc_lex_next(&p->l); bc_parse_err(p, BC_ERR_POSIX_REF); } if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_FUNC); p->func->nparams += 1; bc_vec_string(&p->buf, p->l.str.len, p->l.str.v); bc_lex_next(&p->l); if (p->l.t == BC_LEX_LBRACKET) { if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY; bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) bc_parse_err(p, BC_ERR_PARSE_FUNC); bc_lex_next(&p->l); } else if (BC_ERR(t == BC_TYPE_REF)) bc_parse_verr(p, BC_ERR_PARSE_REF_VAR, p->buf.v); comma = (p->l.t == BC_LEX_COMMA); if (comma) { bc_lex_next(&p->l); } bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line); } if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_FUNC); flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER; bc_parse_startBody(p, flags); bc_lex_next(&p->l); if (p->l.t != BC_LEX_LBRACE) bc_parse_err(p, BC_ERR_POSIX_BRACE); } static void bc_parse_auto(BcParse *p) { bool comma, one; if (BC_ERR(!p->auto_part)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_lex_next(&p->l); p->auto_part = comma = false; one = p->l.t == BC_LEX_NAME; while (p->l.t == BC_LEX_NAME) { BcType t; bc_vec_string(&p->buf, p->l.str.len - 1, p->l.str.v); bc_lex_next(&p->l); if (p->l.t == BC_LEX_LBRACKET) { t = BC_TYPE_ARRAY; bc_lex_next(&p->l); if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) bc_parse_err(p, BC_ERR_PARSE_FUNC); bc_lex_next(&p->l); } else t = BC_TYPE_VAR; comma = (p->l.t == BC_LEX_COMMA); if (comma) bc_lex_next(&p->l); bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line); } if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_FUNC); if (BC_ERR(!one)) bc_parse_err(p, BC_ERR_PARSE_NO_AUTO); if (BC_ERR(!bc_parse_isDelimiter(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); } static void bc_parse_body(BcParse *p, bool brace) { uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); assert(flag_ptr != NULL); assert(p->flags.len >= 2); *flag_ptr &= ~(BC_PARSE_FLAG_BODY); if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) { if (BC_ERR(!brace)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); p->auto_part = (p->l.t != BC_LEX_KW_AUTO); if (!p->auto_part) { // Make sure this is true to not get a parse error. p->auto_part = true; bc_parse_auto(p); } if (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l); } else { size_t len = p->flags.len; assert(*flag_ptr); bc_parse_stmt(p); if (!brace && !BC_PARSE_BODY(p) && len <= p->flags.len) bc_parse_endBody(p, false); } } static void bc_parse_stmt(BcParse *p) { size_t len; uint16_t flags; BcLexType type = p->l.t; if (type == BC_LEX_NLINE) { bc_lex_next(&p->l); return; } if (type == BC_LEX_KW_AUTO) { bc_parse_auto(p); return; } p->auto_part = false; if (type != BC_LEX_KW_ELSE) { if (BC_PARSE_IF_END(p)) { bc_parse_noElse(p); if (p->flags.len > 1 && !BC_PARSE_BRACE(p)) bc_parse_endBody(p, false); return; } else if (type == BC_LEX_LBRACE) { if (!BC_PARSE_BODY(p)) { bc_parse_startBody(p, BC_PARSE_FLAG_BRACE); bc_lex_next(&p->l); } else { *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_BRACE; bc_lex_next(&p->l); bc_parse_body(p, true); } return; } else if (BC_PARSE_BODY(p) && !BC_PARSE_BRACE(p)) { bc_parse_body(p, false); return; } } len = p->flags.len; flags = BC_PARSE_TOP_FLAG(p); switch (type) { case BC_LEX_OP_INC: case BC_LEX_OP_DEC: case BC_LEX_OP_MINUS: case BC_LEX_OP_BOOL_NOT: case BC_LEX_LPAREN: case BC_LEX_NAME: case BC_LEX_NUMBER: case BC_LEX_KW_IBASE: case BC_LEX_KW_LAST: case BC_LEX_KW_LENGTH: case BC_LEX_KW_OBASE: case BC_LEX_KW_SCALE: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_SEED: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_SQRT: case BC_LEX_KW_ABS: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_IRAND: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_READ: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_RAND: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_MAXIBASE: case BC_LEX_KW_MAXOBASE: case BC_LEX_KW_MAXSCALE: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_MAXRAND: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND { bc_parse_expr_status(p, BC_PARSE_PRINT, bc_parse_next_expr); break; } case BC_LEX_KW_ELSE: { bc_parse_else(p); break; } case BC_LEX_SCOLON: { // Do nothing. break; } case BC_LEX_RBRACE: { bc_parse_endBody(p, true); break; } case BC_LEX_STR: { bc_parse_str(p, BC_INST_PRINT_STR); break; } case BC_LEX_KW_BREAK: case BC_LEX_KW_CONTINUE: { bc_parse_loopExit(p, p->l.t); break; } case BC_LEX_KW_FOR: { bc_parse_for(p); break; } case BC_LEX_KW_HALT: { bc_parse_push(p, BC_INST_HALT); bc_lex_next(&p->l); break; } case BC_LEX_KW_IF: { bc_parse_if(p); break; } case BC_LEX_KW_LIMITS: { bc_vm_printf("BC_LONG_BIT = %lu\n", (ulong) BC_LONG_BIT); bc_vm_printf("BC_BASE_DIGS = %lu\n", (ulong) BC_BASE_DIGS); bc_vm_printf("BC_BASE_POW = %lu\n", (ulong) BC_BASE_POW); bc_vm_printf("BC_OVERFLOW_MAX = %lu\n", (ulong) BC_NUM_BIGDIG_MAX); bc_vm_printf("\n"); bc_vm_printf("BC_BASE_MAX = %lu\n", BC_MAX_OBASE); bc_vm_printf("BC_DIM_MAX = %lu\n", BC_MAX_DIM); bc_vm_printf("BC_SCALE_MAX = %lu\n", BC_MAX_SCALE); bc_vm_printf("BC_STRING_MAX = %lu\n", BC_MAX_STRING); bc_vm_printf("BC_NAME_MAX = %lu\n", BC_MAX_NAME); bc_vm_printf("BC_NUM_MAX = %lu\n", BC_MAX_NUM); #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND bc_vm_printf("BC_RAND_MAX = %lu\n", BC_MAX_RAND); #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND bc_vm_printf("MAX Exponent = %lu\n", BC_MAX_EXP); bc_vm_printf("Number of vars = %lu\n", BC_MAX_VARS); bc_lex_next(&p->l); break; } case BC_LEX_KW_PRINT: { bc_parse_print(p); break; } case BC_LEX_KW_QUIT: { // Quit is a compile-time command. We don't exit directly, // so the vm can clean up. Limits do the same thing. vm.status = BC_STATUS_QUIT; BC_VM_JMP; break; } case BC_LEX_KW_RETURN: { bc_parse_return(p); break; } case BC_LEX_KW_WHILE: { bc_parse_while(p); break; } default: { bc_parse_err(p, BC_ERR_PARSE_TOKEN); } } if (len == p->flags.len && flags == BC_PARSE_TOP_FLAG(p)) { if (BC_ERR(!bc_parse_isDelimiter(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); } // Make sure semicolons are eaten. while (p->l.t == BC_LEX_SCOLON) bc_lex_next(&p->l); } void bc_parse_parse(BcParse *p) { assert(p); BC_SETJMP(exit); if (BC_ERR(p->l.t == BC_LEX_EOF)) bc_parse_err(p, BC_ERR_PARSE_EOF); else if (p->l.t == BC_LEX_KW_DEFINE) { if (BC_ERR(BC_PARSE_NO_EXEC(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); bc_parse_func(p); } else bc_parse_stmt(p); exit: BC_SIG_MAYLOCK; if (BC_ERR(((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig))) bc_parse_reset(p); BC_LONGJMP_CONT; } static BcParseStatus bc_parse_expr_err(BcParse *p, uint8_t flags, BcParseNext next) { BcInst prev = BC_INST_PRINT; uchar inst = BC_INST_INVALID; BcLexType top, t = p->l.t; size_t nexprs = 0, ops_bgn = p->ops.len; uint32_t i, nparens, nrelops; bool pfirst, rprn, done, get_token, assign, bin_last, incdec, can_assign; assert(!(flags & BC_PARSE_PRINT) || !(flags & BC_PARSE_NEEDVAL)); pfirst = (p->l.t == BC_LEX_LPAREN); nparens = nrelops = 0; rprn = done = get_token = assign = incdec = can_assign = false; bin_last = true; // We want to eat newlines if newlines are not a valid ending token. // This is for spacing in things like for loop headers. if (!(flags & BC_PARSE_NOREAD)) { while ((t = p->l.t) == BC_LEX_NLINE) bc_lex_next(&p->l); } for (; !done && BC_PARSE_EXPR(t); t = p->l.t) { switch (t) { case BC_LEX_OP_INC: case BC_LEX_OP_DEC: { if (BC_ERR(incdec)) bc_parse_err(p, BC_ERR_PARSE_ASSIGN); bc_parse_incdec(p, &prev, &can_assign, &nexprs, flags); rprn = get_token = bin_last = false; incdec = true; flags &= ~(BC_PARSE_ARRAY); break; } #if BC_ENABLE_EXTRA_MATH case BC_LEX_OP_TRUNC: { if (BC_ERR(!BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); // I can just add the instruction because // negative will already be taken care of. bc_parse_push(p, BC_INST_TRUNC); rprn = can_assign = incdec = false; get_token = true; flags &= ~(BC_PARSE_ARRAY); break; } #endif // BC_ENABLE_EXTRA_MATH case BC_LEX_OP_MINUS: { bc_parse_minus(p, &prev, ops_bgn, rprn, bin_last, &nexprs); rprn = get_token = can_assign = false; bin_last = (prev == BC_INST_MINUS); if (bin_last) incdec = false; flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_OP_ASSIGN_POWER: case BC_LEX_OP_ASSIGN_MULTIPLY: case BC_LEX_OP_ASSIGN_DIVIDE: case BC_LEX_OP_ASSIGN_MODULUS: case BC_LEX_OP_ASSIGN_PLUS: case BC_LEX_OP_ASSIGN_MINUS: #if BC_ENABLE_EXTRA_MATH case BC_LEX_OP_ASSIGN_PLACES: case BC_LEX_OP_ASSIGN_LSHIFT: case BC_LEX_OP_ASSIGN_RSHIFT: #endif // BC_ENABLE_EXTRA_MATH case BC_LEX_OP_ASSIGN: { if (!BC_PARSE_INST_VAR(prev)) bc_parse_err(p, BC_ERR_PARSE_ASSIGN); } // Fallthrough. BC_FALLTHROUGH case BC_LEX_OP_POWER: case BC_LEX_OP_MULTIPLY: case BC_LEX_OP_DIVIDE: case BC_LEX_OP_MODULUS: case BC_LEX_OP_PLUS: #if BC_ENABLE_EXTRA_MATH case BC_LEX_OP_PLACES: case BC_LEX_OP_LSHIFT: case BC_LEX_OP_RSHIFT: #endif // BC_ENABLE_EXTRA_MATH case BC_LEX_OP_REL_EQ: case BC_LEX_OP_REL_LE: case BC_LEX_OP_REL_GE: case BC_LEX_OP_REL_NE: case BC_LEX_OP_REL_LT: case BC_LEX_OP_REL_GT: case BC_LEX_OP_BOOL_NOT: case BC_LEX_OP_BOOL_OR: case BC_LEX_OP_BOOL_AND: { if (BC_PARSE_OP_PREFIX(t)) { if (BC_ERR(!bin_last && !BC_PARSE_OP_PREFIX(p->l.last))) bc_parse_err(p, BC_ERR_PARSE_EXPR); } else if (BC_ERR(BC_PARSE_PREV_PREFIX(prev) || bin_last)) bc_parse_err(p, BC_ERR_PARSE_EXPR); nrelops += (t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT); prev = BC_PARSE_TOKEN_INST(t); bc_parse_operator(p, t, ops_bgn, &nexprs); rprn = incdec = can_assign = false; get_token = true; bin_last = !BC_PARSE_OP_PREFIX(t); flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_LPAREN: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); nparens += 1; rprn = incdec = can_assign = false; get_token = true; bc_vec_push(&p->ops, &t); break; } case BC_LEX_RPAREN: { // This needs to be a status. The error // is handled in bc_parse_expr_status(). if (BC_ERR(p->l.last == BC_LEX_LPAREN)) return BC_PARSE_STATUS_EMPTY_EXPR; if (BC_ERR(bin_last || BC_PARSE_PREV_PREFIX(prev))) bc_parse_err(p, BC_ERR_PARSE_EXPR); if (!nparens) { done = true; get_token = false; break; } nparens -= 1; rprn = true; get_token = bin_last = incdec = false; bc_parse_rightParen(p, &nexprs); break; } case BC_LEX_NAME: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); get_token = bin_last = false; bc_parse_name(p, &prev, &can_assign, flags & ~BC_PARSE_NOCALL); rprn = (prev == BC_INST_CALL); nexprs += 1; flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_NUMBER: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); bc_parse_number(p); nexprs += 1; prev = BC_INST_NUM; get_token = true; rprn = bin_last = can_assign = false; flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_KW_IBASE: case BC_LEX_KW_LAST: case BC_LEX_KW_OBASE: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_SEED: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); prev = t - BC_LEX_KW_LAST + BC_INST_LAST; bc_parse_push(p, prev); get_token = can_assign = true; rprn = bin_last = false; nexprs += 1; flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_KW_LENGTH: case BC_LEX_KW_SQRT: case BC_LEX_KW_ABS: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_IRAND: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); bc_parse_builtin(p, t, flags, &prev); rprn = get_token = bin_last = incdec = can_assign = false; nexprs += 1; flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_KW_READ: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_RAND: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_MAXIBASE: case BC_LEX_KW_MAXOBASE: case BC_LEX_KW_MAXSCALE: #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND case BC_LEX_KW_MAXRAND: #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); else if (t == BC_LEX_KW_READ && BC_ERR(flags & BC_PARSE_NOREAD)) bc_parse_err(p, BC_ERR_EXEC_REC_READ); else { prev = t - BC_LEX_KW_READ + BC_INST_READ; bc_parse_noArgBuiltin(p, prev); } rprn = get_token = bin_last = incdec = can_assign = false; nexprs += 1; flags &= ~(BC_PARSE_ARRAY); break; } case BC_LEX_KW_SCALE: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) bc_parse_err(p, BC_ERR_PARSE_EXPR); bc_parse_scale(p, &prev, &can_assign, flags); rprn = get_token = bin_last = false; nexprs += 1; flags &= ~(BC_PARSE_ARRAY); break; } default: { #ifndef NDEBUG bc_parse_err(p, BC_ERR_PARSE_TOKEN); break; #endif // NDEBUG } } if (get_token) bc_lex_next(&p->l); } while (p->ops.len > ops_bgn) { top = BC_PARSE_TOP_OP(p); assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN; if (BC_ERR(top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_EXPR); bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); nexprs -= !BC_PARSE_OP_PREFIX(top); bc_vec_pop(&p->ops); incdec = false; } if (BC_ERR(nexprs != 1)) bc_parse_err(p, BC_ERR_PARSE_EXPR); for (i = 0; i < next.len && t != next.tokens[i]; ++i); if (BC_ERR(i == next.len && !bc_parse_isDelimiter(p))) bc_parse_err(p, BC_ERR_PARSE_EXPR); if (!(flags & BC_PARSE_REL) && nrelops) bc_parse_err(p, BC_ERR_POSIX_REL_POS); else if ((flags & BC_PARSE_REL) && nrelops > 1) bc_parse_err(p, BC_ERR_POSIX_MULTIREL); if (!(flags & BC_PARSE_NEEDVAL) && !pfirst) { if (assign) { inst = *((uchar*) bc_vec_top(&p->func->code)); inst += (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER); incdec = false; } else if (incdec && !(flags & BC_PARSE_PRINT)) { inst = *((uchar*) bc_vec_top(&p->func->code)); incdec = (inst <= BC_INST_DEC); inst = BC_INST_ASSIGN_PLUS_NO_VAL + (inst != BC_INST_INC && inst != BC_INST_ASSIGN_PLUS); } if (inst >= BC_INST_ASSIGN_POWER_NO_VAL && inst <= BC_INST_ASSIGN_NO_VAL) { bc_vec_pop(&p->func->code); if (incdec) bc_parse_push(p, BC_INST_ONE); bc_parse_push(p, inst); } } if ((flags & BC_PARSE_PRINT)) { if (pfirst || !assign) bc_parse_push(p, BC_INST_PRINT); } else if (!(flags & BC_PARSE_NEEDVAL) && (inst < BC_INST_ASSIGN_POWER_NO_VAL || inst > BC_INST_ASSIGN_NO_VAL)) { bc_parse_push(p, BC_INST_POP); } // We want to eat newlines if newlines are not a valid ending token. // This is for spacing in things like for loop headers. for (incdec = true, i = 0; i < next.len && incdec; ++i) incdec = (next.tokens[i] != BC_LEX_NLINE); if (incdec) { while (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l); } return BC_PARSE_STATUS_SUCCESS; } void bc_parse_expr_status(BcParse *p, uint8_t flags, BcParseNext next) { BcParseStatus s = bc_parse_expr_err(p, flags, next); if (BC_ERR(s == BC_PARSE_STATUS_EMPTY_EXPR)) bc_parse_err(p, BC_ERR_PARSE_EMPTY_EXPR); } void bc_parse_expr(BcParse *p, uint8_t flags) { assert(p); bc_parse_expr_status(p, flags, bc_parse_next_read); } #endif // BC_ENABLED