summaryrefslogtreecommitdiff
path: root/src/bc_parse.c
diff options
context:
space:
mode:
authorStefan Eßer <se@FreeBSD.org>2020-11-26 17:27:07 +0000
committerStefan Eßer <se@FreeBSD.org>2020-11-26 17:27:07 +0000
commit2e4772e82335c32e07d0605e5edefb0ac6ea857b (patch)
treed7a054971d1ea18056d0ddb9e0bf0aeb93fb274f /src/bc_parse.c
parent907a6834f73ccf2839853eb67a29726275ed04b4 (diff)
Notes
Diffstat (limited to 'src/bc_parse.c')
-rw-r--r--src/bc_parse.c1532
1 files changed, 1532 insertions, 0 deletions
diff --git a/src/bc_parse.c b/src/bc_parse.c
new file mode 100644
index 000000000000..35d3e45c581c
--- /dev/null
+++ b/src/bc_parse.c
@@ -0,0 +1,1532 @@
+/*
+ * *****************************************************************************
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018-2020 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 <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <setjmp.h>
+
+#include <bc.h>
+#include <num.h>
+#include <vm.h>
+
+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