blob: f65c91756ddb8ed2e691a7b504ba09e42506a219 [file] [log] [blame]
/*
* *****************************************************************************
*
* Copyright 2018 Gavin D. Howard
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* *****************************************************************************
*
* The parser for bc.
*
*/
#if BC_ENABLED
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <lex.h>
#include <parse.h>
#include <bc.h>
#include <vm.h>
static BcStatus bc_parse_else(BcParse *p);
static BcStatus bc_parse_stmt(BcParse *p);
static BcStatus 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_SQRT) ||
#if BC_ENABLE_EXTRA_MATH
t == BC_INST_TRUNC ||
#endif // BC_ENABLE_EXTRA_MATH
t == BC_INST_INC_POST || t == BC_INST_DEC_POST;
}
static size_t bc_parse_addFunc(BcParse *p, char *name) {
size_t idx = bc_program_insertFunc(p->prog, name);
// Make sure that this pointer was not invalidated.
p->func = bc_vec_item(&p->prog->fns, p->fidx);
return idx;
}
static void bc_parse_operator(BcParse *p, BcLexType type,
size_t start, size_t *nexprs)
{
BcLexType t;
uchar l, r = BC_PARSE_OP_PREC(bc_parse_ops[type - BC_LEX_OP_INC]);
uchar left = BC_PARSE_OP_LEFT(bc_parse_ops[type - BC_LEX_OP_INC]);
while (p->ops.len > start) {
t = BC_PARSE_TOP_OP(p);
if (t == BC_LEX_LPAREN) break;
l = BC_PARSE_OP_PREC(bc_parse_ops[t - BC_LEX_OP_INC]);
if (l >= r && (l != r || !left)) break;
bc_parse_push(p, BC_PARSE_TOKEN_INST(t));
bc_vec_pop(&p->ops);
*nexprs -= t != BC_LEX_OP_BOOL_NOT && t != BC_LEX_NEG;
}
bc_vec_push(&p->ops, &type);
}
static BcStatus bc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs) {
BcLexType top;
if (p->ops.len <= ops_bgn) return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
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 -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
if (p->ops.len <= ops_bgn) return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
}
bc_vec_pop(&p->ops);
return bc_lex_next(&p->l);
}
static BcStatus bc_parse_params(BcParse *p, uint8_t flags) {
BcStatus s;
bool comma = false;
size_t nparams;
s = bc_lex_next(&p->l);
if (s) return s;
for (nparams = 0; p->l.t != BC_LEX_RPAREN; ++nparams) {
flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
s = bc_parse_expr_status(p, flags, bc_parse_next_param);
if (s) return s;
comma = p->l.t == BC_LEX_COMMA;
if (comma) {
s = bc_lex_next(&p->l);
if (s) return s;
}
}
if (comma) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
bc_parse_push(p, BC_INST_CALL);
bc_parse_pushIndex(p, nparams);
return BC_STATUS_SUCCESS;
}
static BcStatus bc_parse_call(BcParse *p, char *name, uint8_t flags) {
BcStatus s;
BcId id, *id_ptr;
size_t idx;
id.name = name;
s = bc_parse_params(p, flags);
if (s) goto err;
if (p->l.t != BC_LEX_RPAREN) {
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
goto err;
}
idx = bc_map_index(&p->prog->fn_map, &id);
if (idx == BC_VEC_INVALID_IDX) {
bc_parse_addFunc(p, name);
idx = bc_map_index(&p->prog->fn_map, &id);
assert(idx != BC_VEC_INVALID_IDX);
}
else free(name);
assert(idx != BC_VEC_INVALID_IDX);
id_ptr = bc_vec_item(&p->prog->fn_map, idx);
assert(id_ptr);
bc_parse_pushIndex(p, id_ptr->idx);
return bc_lex_next(&p->l);
err:
free(name);
return s;
}
static BcStatus bc_parse_name(BcParse *p, BcInst *type, uint8_t flags) {
BcStatus s;
char *name;
name = bc_vm_strdup(p->l.str.v);
s = bc_lex_next(&p->l);
if (s) goto err;
if (p->l.t == BC_LEX_LBRACKET) {
s = bc_lex_next(&p->l);
if (s) goto err;
if (p->l.t == BC_LEX_RBRACKET) {
if (!(flags & BC_PARSE_ARRAY)) {
s = bc_parse_err(p, BC_ERROR_PARSE_EXPR);
goto err;
}
*type = BC_INST_ARRAY;
}
else {
*type = BC_INST_ARRAY_ELEM;
flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
s = bc_parse_expr_status(p, flags, bc_parse_next_elem);
if (s) goto err;
if (p->l.t != BC_LEX_RBRACKET) {
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
goto err;
}
}
s = bc_lex_next(&p->l);
if (s) goto err;
bc_parse_push(p, *type);
bc_parse_pushName(p, name);
}
else if (p->l.t == BC_LEX_LPAREN) {
if (flags & BC_PARSE_NOCALL) {
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
goto err;
}
*type = BC_INST_CALL;
// Return early because bc_parse_call() frees the name.
return bc_parse_call(p, name, flags);
}
else {
*type = BC_INST_VAR;
bc_parse_push(p, BC_INST_VAR);
bc_parse_pushName(p, name);
}
err:
free(name);
return s;
}
static BcStatus bc_parse_read(BcParse *p) {
BcStatus s;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_RPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
bc_parse_push(p, BC_INST_READ);
return bc_lex_next(&p->l);
}
static BcStatus bc_parse_builtin(BcParse *p, BcLexType type,
uint8_t flags, BcInst *prev)
{
BcStatus s;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
s = bc_lex_next(&p->l);
if (s) return s;
s = bc_parse_expr_status(p, flags, bc_parse_next_rel);
if (s) return s;
if (p->l.t != BC_LEX_RPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
*prev = type - BC_LEX_KEY_LENGTH + BC_INST_LENGTH;
bc_parse_push(p, *prev);
return bc_lex_next(&p->l);
}
static BcStatus bc_parse_scale(BcParse *p, BcInst *type, uint8_t flags) {
BcStatus s;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LPAREN) {
*type = BC_INST_SCALE;
bc_parse_push(p, BC_INST_SCALE);
return BC_STATUS_SUCCESS;
}
*type = BC_INST_SCALE_FUNC;
flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
s = bc_lex_next(&p->l);
if (s) return s;
s = bc_parse_expr_status(p, flags, bc_parse_next_rel);
if (s) return s;
if (p->l.t != BC_LEX_RPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
bc_parse_push(p, BC_INST_SCALE_FUNC);
return bc_lex_next(&p->l);
}
static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev,
size_t *nexs, uint8_t flags)
{
BcStatus s;
BcLexType type;
uchar inst;
BcInst etype = *prev;
BcLexType last = p->l.last;
if (last == BC_LEX_OP_INC || last == BC_LEX_OP_DEC || last == BC_LEX_RPAREN)
return s = bc_parse_err(p, BC_ERROR_PARSE_ASSIGN);
if (BC_PARSE_INST_VAR(etype)) {
*prev = inst = BC_INST_INC_POST + (p->l.t != BC_LEX_OP_INC);
bc_parse_push(p, inst);
s = bc_lex_next(&p->l);
}
else {
*prev = inst = BC_INST_INC_PRE + (p->l.t != BC_LEX_OP_INC);
s = bc_lex_next(&p->l);
if (s) return s;
type = p->l.t;
// Because we parse the next part of the expression
// right here, we need to increment this.
*nexs = *nexs + 1;
switch (type) {
case BC_LEX_NAME:
{
s = bc_parse_name(p, prev, flags | BC_PARSE_NOCALL);
break;
}
case BC_LEX_KEY_LAST:
case BC_LEX_KEY_IBASE:
case BC_LEX_KEY_OBASE:
{
bc_parse_push(p, type - BC_LEX_KEY_LAST + BC_INST_LAST);
s = bc_lex_next(&p->l);
break;
}
case BC_LEX_KEY_SCALE:
{
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t == BC_LEX_LPAREN)
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
else bc_parse_push(p, BC_INST_SCALE);
break;
}
default:
{
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
break;
}
}
if (!s) bc_parse_push(p, inst);
}
return s;
}
static BcStatus bc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn,
bool rparen, bool bin_last, size_t *nexprs)
{
BcStatus s;
BcLexType type;
s = bc_lex_next(&p->l);
if (s) return s;
type = BC_PARSE_LEAF(*prev, bin_last, 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);
return s;
}
static BcStatus bc_parse_str(BcParse *p, char inst) {
bc_parse_string(p);
bc_parse_push(p, inst);
return bc_lex_next(&p->l);
}
static BcStatus bc_parse_delimiter(BcParse *p) {
BcStatus s = BC_STATUS_SUCCESS;
if (!BC_PARSE_VALID_END_TOKEN(p->l.t))
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
else if (p->l.t == BC_LEX_SCOLON || p->l.t == BC_LEX_NLINE)
s = bc_lex_next(&p->l);
// I need to check for right brace and keywords specifically.
else {
size_t i;
bool good = false;
if (p->l.t == BC_LEX_RBRACE) {
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);
}
}
else good = !BC_PARSE_BRACE(p);
if (!good) s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
}
return s;
}
static BcStatus bc_parse_print(BcParse *p) {
BcStatus s;
BcLexType t;
bool comma = false;
s = bc_lex_next(&p->l);
if (s) return s;
t = p->l.t;
if (BC_PARSE_VALID_END_TOKEN(t)) return bc_parse_err(p, BC_ERROR_PARSE_PRINT);
do {
if (t == BC_LEX_STR) s = bc_parse_str(p, BC_INST_PRINT_POP);
else {
s = bc_parse_expr_status(p, 0, bc_parse_next_print);
if (s) return s;
bc_parse_push(p, BC_INST_PRINT_POP);
}
if (s) return s;
comma = p->l.t == BC_LEX_COMMA;
if (comma) s = bc_lex_next(&p->l);
t = p->l.t;
} while (!s && !BC_PARSE_VALID_END_TOKEN(t));
if (s) return s;
if (comma) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
return bc_parse_delimiter(p);
}
static BcStatus bc_parse_return(BcParse *p) {
BcStatus s;
BcLexType t;
bool paren;
uchar inst = BC_INST_RET0;
if (!BC_PARSE_FUNC(p)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
if (p->func->voidfn) inst = BC_INST_RET_VOID;
s = bc_lex_next(&p->l);
if (s) return s;
t = p->l.t;
paren = t == BC_LEX_LPAREN;
if (BC_PARSE_VALID_END_TOKEN(t)) bc_parse_push(p, inst);
else {
s = bc_parse_expr_err(p, 0, bc_parse_next_expr);
if (s && s != BC_STATUS_EMPTY_EXPR) return s;
else if (s == BC_STATUS_EMPTY_EXPR) {
bc_parse_push(p, inst);
s = bc_lex_next(&p->l);
if (s) return s;
}
if (!paren || p->l.last != BC_LEX_RPAREN) {
s = bc_parse_posixErr(p, BC_ERROR_POSIX_RET);
if (s) return s;
}
else if (p->func->voidfn)
return bc_parse_verr(p, BC_ERROR_PARSE_RET_VOID, p->func->name);
bc_parse_push(p, BC_INST_RET);
}
return bc_parse_delimiter(p);
}
static BcStatus bc_parse_endBody(BcParse *p, bool brace) {
BcStatus s = BC_STATUS_SUCCESS;
bool has_brace, new_else = false;
if (p->flags.len <= 1) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
if (brace) {
if (p->l.t == BC_LEX_RBRACE) {
s = bc_lex_next(&p->l);
if (s) return s;
}
else return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
}
has_brace = (BC_PARSE_BRACE(p) != 0);
do {
size_t len = p->flags.len;
if (has_brace && !brace) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
if (BC_PARSE_ELSE(p)) {
BcInstPtr *ip;
size_t *label;
bc_vec_pop(&p->flags);
ip = bc_vec_top(&p->exits);
label = bc_vec_item(&p->func->labels, ip->idx);
*label = p->func->code.len;
bc_vec_pop(&p->exits);
}
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_IF(p)) {
BcInstPtr *ip = bc_vec_top(&p->exits);
size_t *label = bc_vec_top(&p->conds);
bc_parse_push(p, BC_INST_JUMP);
bc_parse_pushIndex(p, *label);
label = bc_vec_item(&p->func->labels, ip->idx);
*label = p->func->code.len;
bc_vec_pop(&p->flags);
bc_vec_pop(&p->exits);
bc_vec_pop(&p->conds);
}
// 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) {
s = bc_lex_next(&p->l);
if (s) return s;
}
bc_vec_pop(&p->flags);
*(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_IF_END;
new_else = (p->l.t == BC_LEX_KEY_ELSE);
if (new_else) s = bc_parse_else(p);
}
} while (p->flags.len > 1 && !new_else && !BC_PARSE_IF_END(p) &&
!(has_brace = (BC_PARSE_BRACE(p) != 0)));
return s;
}
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_noElse(BcParse *p) {
BcInstPtr *ip;
size_t *label;
uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
*flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END));
ip = bc_vec_top(&p->exits);
assert(!ip->func && !ip->len);
assert(p->func == bc_vec_item(&p->prog->fns, p->fidx));
label = bc_vec_item(&p->func->labels, ip->idx);
*label = p->func->code.len;
bc_vec_pop(&p->exits);
}
static BcStatus bc_parse_if(BcParse *p) {
BcStatus s;
BcInstPtr ip;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
s = bc_parse_expr_status(p, BC_PARSE_REL, bc_parse_next_rel);
if (s) return s;
if (p->l.t != BC_LEX_RPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
bc_parse_push(p, BC_INST_JUMP_ZERO);
ip.idx = p->func->labels.len;
ip.func = ip.len = 0;
bc_parse_pushIndex(p, ip.idx);
bc_vec_push(&p->exits, &ip);
bc_vec_push(&p->func->labels, &ip.idx);
bc_parse_startBody(p, BC_PARSE_FLAG_IF);
return BC_STATUS_SUCCESS;
}
static BcStatus bc_parse_else(BcParse *p) {
BcInstPtr ip;
if (!BC_PARSE_IF_END(p)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
ip.idx = p->func->labels.len;
ip.func = ip.len = 0;
bc_parse_push(p, BC_INST_JUMP);
bc_parse_pushIndex(p, ip.idx);
bc_parse_noElse(p);
bc_vec_push(&p->exits, &ip);
bc_vec_push(&p->func->labels, &ip.idx);
bc_parse_startBody(p, BC_PARSE_FLAG_ELSE);
return bc_lex_next(&p->l);
}
static BcStatus bc_parse_while(BcParse *p) {
BcStatus s;
BcInstPtr ip;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
ip.idx = p->func->labels.len;
bc_vec_push(&p->func->labels, &p->func->code.len);
bc_vec_push(&p->conds, &ip.idx);
ip.idx = p->func->labels.len;
ip.func = 1;
ip.len = 0;
bc_vec_push(&p->exits, &ip);
bc_vec_push(&p->func->labels, &ip.idx);
s = bc_parse_expr_status(p, BC_PARSE_REL, bc_parse_next_rel);
if (s) return s;
if (p->l.t != BC_LEX_RPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
bc_parse_push(p, BC_INST_JUMP_ZERO);
bc_parse_pushIndex(p, ip.idx);
bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
return BC_STATUS_SUCCESS;
}
static BcStatus bc_parse_for(BcParse *p) {
BcStatus s;
BcInstPtr ip;
size_t cond_idx, exit_idx, body_idx, update_idx;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_SCOLON) {
s = bc_parse_expr_status(p, 0, bc_parse_next_for);
if (!s) bc_parse_push(p, BC_INST_POP);
}
else s = bc_parse_posixErr(p, BC_ERROR_POSIX_FOR1);
if (s) return s;
if (p->l.t != BC_LEX_SCOLON) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
cond_idx = p->func->labels.len;
update_idx = cond_idx + 1;
body_idx = update_idx + 1;
exit_idx = body_idx + 1;
bc_vec_push(&p->func->labels, &p->func->code.len);
if (p->l.t != BC_LEX_SCOLON)
s = bc_parse_expr_status(p, BC_PARSE_REL, 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, strlen(bc_parse_const1), bc_parse_const1);
bc_parse_number(p);
s = bc_parse_posixErr(p, BC_ERROR_POSIX_FOR2);
}
if (s) return s;
if (p->l.t != BC_LEX_SCOLON) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
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);
ip.idx = p->func->labels.len;
bc_vec_push(&p->conds, &update_idx);
bc_vec_push(&p->func->labels, &p->func->code.len);
if (p->l.t != BC_LEX_RPAREN) {
s = bc_parse_expr_status(p, 0, bc_parse_next_rel);
if (!s) bc_parse_push(p, BC_INST_POP);
}
else s = bc_parse_posixErr(p, BC_ERROR_POSIX_FOR3);
if (s) return s;
if (p->l.t != BC_LEX_RPAREN) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
bc_parse_push(p, BC_INST_JUMP);
bc_parse_pushIndex(p, cond_idx);
bc_vec_push(&p->func->labels, &p->func->code.len);
ip.idx = exit_idx;
ip.func = 1;
ip.len = 0;
bc_vec_push(&p->exits, &ip);
bc_vec_push(&p->func->labels, &ip.idx);
s = bc_lex_next(&p->l);
if (!s) bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
return s;
}
static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type) {
BcStatus s;
size_t i;
BcInstPtr *ip;
if (!BC_PARSE_LOOP(p)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
if (type == BC_LEX_KEY_BREAK) {
if (!p->exits.len) return bc_parse_err(p, BC_ERROR_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);
if (i >= p->exits.len && !ip->func)
return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
i = ip->idx;
}
else i = *((size_t*) bc_vec_top(&p->conds));
bc_parse_push(p, BC_INST_JUMP);
bc_parse_pushIndex(p, i);
s = bc_lex_next(&p->l);
if (s) return s;
return bc_parse_delimiter(p);
}
static BcStatus bc_parse_func(BcParse *p) {
BcStatus s;
bool comma = false, voidfn;
uint16_t flags;
char *name;
size_t idx;
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_NAME) return bc_parse_err(p, BC_ERROR_PARSE_FUNC);
voidfn = (!BC_S && !BC_W && p->l.t == BC_LEX_NAME &&
!strcmp(p->l.str.v, "void"));
s = bc_lex_next(&p->l);
if (s) return s;
voidfn = (voidfn && p->l.t == BC_LEX_NAME);
if (voidfn) {
s = bc_lex_next(&p->l);
if (s) return s;
}
if (p->l.t != BC_LEX_LPAREN) return bc_parse_err(p, BC_ERROR_PARSE_FUNC);
assert(p->prog->fns.len == p->prog->fn_map.len);
name = bc_vm_strdup(p->l.str.v);
idx = bc_program_insertFunc(p->prog, name);
assert(idx);
bc_parse_updateFunc(p, idx);
p->func->voidfn = voidfn;
s = bc_lex_next(&p->l);
if (s) return s;
while (p->l.t != BC_LEX_RPAREN) {
BcType t = BC_TYPE_VAR;
#if BC_ENABLE_REFERENCES
if (p->l.t == BC_LEX_OP_MULTIPLY) {
t = BC_TYPE_REF;
s = bc_lex_next(&p->l);
if (s) return s;
s = bc_parse_posixErr(p, BC_ERROR_POSIX_REF);
if (s) return s;
}
#endif // BC_ENABLE_REFERENCES
if (p->l.t != BC_LEX_NAME) return bc_parse_err(p, BC_ERROR_PARSE_FUNC);
++p->func->nparams;
name = bc_vm_strdup(p->l.str.v);
s = bc_lex_next(&p->l);
if (s) goto err;
if (p->l.t == BC_LEX_LBRACKET) {
if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY;
s = bc_lex_next(&p->l);
if (s) goto err;
if (p->l.t != BC_LEX_RBRACKET) {
s = bc_parse_err(p, BC_ERROR_PARSE_FUNC);
goto err;
}
s = bc_lex_next(&p->l);
if (s) goto err;
}
#if BC_ENABLE_REFERENCES
else if (t == BC_TYPE_REF) {
s = bc_parse_verr(p, BC_ERROR_PARSE_REF_VAR, name);
goto err;
}
#endif // BC_ENABLE_REFERENCES
comma = p->l.t == BC_LEX_COMMA;
if (comma) {
s = bc_lex_next(&p->l);
if (s) goto err;
}
s = bc_func_insert(p->func, name, t, p->l.line);
if (s) goto err;
}
if (comma) return bc_parse_err(p, BC_ERROR_PARSE_FUNC);
flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_BODY;
bc_parse_startBody(p, flags);
s = bc_lex_next(&p->l);
if (s) return s;
if (p->l.t != BC_LEX_LBRACE) s = bc_parse_posixErr(p, BC_ERROR_POSIX_BRACE);
return s;
err:
free(name);
return s;
}
static BcStatus bc_parse_auto(BcParse *p) {
BcStatus s;
bool comma, one;
char *name;
if (!p->auto_part) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_lex_next(&p->l);
if (s) return s;
p->auto_part = comma = false;
one = p->l.t == BC_LEX_NAME;
while (p->l.t == BC_LEX_NAME) {
BcType t;
name = bc_vm_strdup(p->l.str.v);
s = bc_lex_next(&p->l);
if (s) goto err;
if (p->l.t == BC_LEX_LBRACKET) {
t = BC_TYPE_ARRAY;
s = bc_lex_next(&p->l);
if (s) goto err;
if (p->l.t != BC_LEX_RBRACKET) {
s = bc_parse_err(p, BC_ERROR_PARSE_FUNC);
goto err;
}
s = bc_lex_next(&p->l);
if (s) goto err;
}
else t = BC_TYPE_VAR;
comma = p->l.t == BC_LEX_COMMA;
if (comma) {
s = bc_lex_next(&p->l);
if (s) goto err;
}
s = bc_func_insert(p->func, name, t, p->l.line);
if (s) goto err;
}
if (comma) return bc_parse_err(p, BC_ERROR_PARSE_FUNC);
if (!one) return bc_parse_err(p, BC_ERROR_PARSE_NO_AUTO);
return bc_parse_delimiter(p);
err:
free(name);
return s;
}
static BcStatus bc_parse_body(BcParse *p, bool brace) {
BcStatus s = BC_STATUS_SUCCESS;
uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
assert(p->flags.len >= 2);
*flag_ptr &= ~(BC_PARSE_FLAG_BODY);
if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) {
if (!brace) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
p->auto_part = p->l.t != BC_LEX_KEY_AUTO;
if (!p->auto_part) {
// Make sure this is true to not get a parse error.
p->auto_part = true;
s = bc_parse_auto(p);
if (s) return s;
}
if (p->l.t == BC_LEX_NLINE) s = bc_lex_next(&p->l);
}
else {
assert(*flag_ptr);
s = bc_parse_stmt(p);
if (!s && !brace && !BC_PARSE_BODY(p)) s = bc_parse_endBody(p, false);
}
return s;
}
static BcStatus bc_parse_stmt(BcParse *p) {
BcStatus s = BC_STATUS_SUCCESS;
switch (p->l.t) {
case BC_LEX_NLINE:
{
return bc_lex_next(&p->l);
}
case BC_LEX_KEY_ELSE:
{
p->auto_part = false;
break;
}
case BC_LEX_LBRACE:
{
if (!BC_PARSE_BODY(p)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
*(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_BRACE;
s = bc_lex_next(&p->l);
if (s) return s;
return bc_parse_body(p, true);
}
case BC_LEX_KEY_AUTO:
{
return bc_parse_auto(p);
}
default:
{
p->auto_part = false;
if (BC_PARSE_IF_END(p)) {
bc_parse_noElse(p);
if (p->flags.len > 1 && !BC_PARSE_BRACE(p))
s = bc_parse_endBody(p, false);
return s;
}
else if (BC_PARSE_BODY(p)) return bc_parse_body(p, false);
break;
}
}
switch (p->l.t) {
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_KEY_IBASE:
case BC_LEX_KEY_LAST:
case BC_LEX_KEY_LENGTH:
case BC_LEX_KEY_OBASE:
case BC_LEX_KEY_READ:
case BC_LEX_KEY_SCALE:
case BC_LEX_KEY_SQRT:
{
s = bc_parse_expr_status(p, BC_PARSE_PRINT, bc_parse_next_expr);
break;
}
case BC_LEX_KEY_ELSE:
{
s = bc_parse_else(p);
break;
}
case BC_LEX_SCOLON:
{
while (!s && p->l.t == BC_LEX_SCOLON) s = bc_lex_next(&p->l);
break;
}
case BC_LEX_RBRACE:
{
s = bc_parse_endBody(p, true);
break;
}
case BC_LEX_STR:
{
s = bc_parse_str(p, BC_INST_PRINT_STR);
break;
}
case BC_LEX_KEY_BREAK:
case BC_LEX_KEY_CONTINUE:
{
s = bc_parse_loopExit(p, p->l.t);
break;
}
case BC_LEX_KEY_FOR:
{
s = bc_parse_for(p);
break;
}
case BC_LEX_KEY_HALT:
{
bc_parse_push(p, BC_INST_HALT);
s = bc_lex_next(&p->l);
break;
}
case BC_LEX_KEY_IF:
{
s = bc_parse_if(p);
break;
}
case BC_LEX_KEY_LIMITS:
{
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);
bc_vm_printf("MAX Exponent = %lu\n", BC_MAX_EXP);
bc_vm_printf("Number of vars = %lu\n", BC_MAX_VARS);
s = bc_lex_next(&p->l);
break;
}
case BC_LEX_KEY_PRINT:
{
s = bc_parse_print(p);
break;
}
case BC_LEX_KEY_QUIT:
{
// Quit is a compile-time command. We don't exit directly,
// so the vm can clean up. Limits do the same thing.
s = BC_STATUS_QUIT;
break;
}
case BC_LEX_KEY_RETURN:
{
s = bc_parse_return(p);
break;
}
case BC_LEX_KEY_WHILE:
{
s = bc_parse_while(p);
break;
}
default:
{
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
break;
}
}
// Make sure semicolons are eaten.
while (!s && p->l.t == BC_LEX_SCOLON) s = bc_lex_next(&p->l);
return s;
}
BcStatus bc_parse_parse(BcParse *p) {
BcStatus s;
assert(p);
if (p->l.t == BC_LEX_EOF) s = bc_parse_err(p, BC_ERROR_PARSE_EOF);
else if (p->l.t == BC_LEX_KEY_DEFINE) {
if (BC_PARSE_NO_EXEC(p)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
s = bc_parse_func(p);
}
else s = bc_parse_stmt(p);
if ((s && s != BC_STATUS_QUIT) || BC_SIGNAL) s = bc_parse_reset(p, s);
return s;
}
static BcStatus bc_parse_expr_err(BcParse *p, uint8_t flags, BcParseNext next)
{
BcStatus s = BC_STATUS_SUCCESS;
BcInst prev = BC_INST_PRINT;
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;
pfirst = p->l.t == BC_LEX_LPAREN;
nparens = nrelops = 0;
rprn = done = get_token = assign = incdec = 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.
while (!s && (t = p->l.t) == BC_LEX_NLINE) s = bc_lex_next(&p->l);
for (; !BC_SIGNAL && !s && !done && BC_PARSE_EXPR(t); t = p->l.t) {
switch (t) {
case BC_LEX_OP_INC:
case BC_LEX_OP_DEC:
{
if (incdec) return bc_parse_err(p, BC_ERROR_PARSE_ASSIGN);
s = bc_parse_incdec(p, &prev, &nexprs, flags);
rprn = get_token = bin_last = false;
incdec = true;
break;
}
#if BC_ENABLE_EXTRA_MATH
case BC_LEX_OP_TRUNC:
{
if (!BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
// I can just add the instruction because
// negative will already be taken care of.
bc_parse_push(p, BC_INST_TRUNC);
rprn = false;
get_token = true;
break;
}
#endif // BC_ENABLE_EXTRA_MATH
case BC_LEX_OP_MINUS:
{
s = bc_parse_minus(p, &prev, ops_bgn, rprn, bin_last, &nexprs);
rprn = get_token = false;
bin_last = (prev == BC_INST_MINUS);
if (bin_last) incdec = false;
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)) {
s = bc_parse_err(p, BC_ERROR_PARSE_ASSIGN);
break;
}
}
// 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 (t == BC_LEX_OP_BOOL_NOT) {
if (!bin_last && p->l.last != BC_LEX_OP_BOOL_NOT)
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
}
else if (prev == BC_INST_BOOL_NOT)
return bc_parse_err(p, BC_ERROR_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 = false;
get_token = true;
bin_last = t != BC_LEX_OP_BOOL_NOT;
break;
}
case BC_LEX_LPAREN:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
++nparens;
rprn = incdec = 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 (p->l.last == BC_LEX_LPAREN) return BC_STATUS_EMPTY_EXPR;
if (bin_last || prev == BC_INST_BOOL_NOT)
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
if (!nparens) {
s = BC_STATUS_SUCCESS;
done = true;
get_token = false;
break;
}
--nparens;
rprn = true;
get_token = bin_last = incdec = false;
s = bc_parse_rightParen(p, ops_bgn, &nexprs);
break;
}
case BC_LEX_NAME:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
get_token = bin_last = false;
s = bc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL);
rprn = (prev == BC_INST_CALL);
++nexprs;
break;
}
case BC_LEX_NUMBER:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
bc_parse_number(p);
nexprs += 1;
prev = BC_INST_NUM;
get_token = true;
rprn = bin_last = false;
break;
}
case BC_LEX_KEY_IBASE:
case BC_LEX_KEY_LAST:
case BC_LEX_KEY_OBASE:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
prev = (uchar) (t - BC_LEX_KEY_LAST + BC_INST_LAST);
bc_parse_push(p, (uchar) prev);
get_token = true;
rprn = bin_last = false;
++nexprs;
break;
}
case BC_LEX_KEY_LENGTH:
case BC_LEX_KEY_SQRT:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
s = bc_parse_builtin(p, t, flags, &prev);
rprn = get_token = bin_last = incdec = false;
++nexprs;
break;
}
case BC_LEX_KEY_READ:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
else if (flags & BC_PARSE_NOREAD)
s = bc_parse_err(p, BC_ERROR_EXEC_REC_READ);
else s = bc_parse_read(p);
rprn = get_token = bin_last = incdec = false;
++nexprs;
prev = BC_INST_READ;
break;
}
case BC_LEX_KEY_SCALE:
{
if (BC_PARSE_LEAF(prev, bin_last, rprn))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
s = bc_parse_scale(p, &prev, flags);
rprn = get_token = bin_last = false;
++nexprs;
break;
}
default:
{
s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN);
break;
}
}
if (!s && get_token) s = bc_lex_next(&p->l);
}
if (s) return s;
if (BC_SIGNAL) return BC_STATUS_SIGNAL;
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 (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
bc_parse_push(p, BC_PARSE_TOKEN_INST(top));
nexprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
bc_vec_pop(&p->ops);
}
if (prev == BC_INST_BOOL_NOT || nexprs != 1)
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
for (i = 0; i < next.len && t != next.tokens[i]; ++i);
if (i == next.len && !BC_PARSE_VALID_END_TOKEN(t))
return bc_parse_err(p, BC_ERROR_PARSE_EXPR);
if (!(flags & BC_PARSE_REL) && nrelops) {
s = bc_parse_posixErr(p, BC_ERROR_POSIX_REL_POS);
if (s) return s;
}
else if ((flags & BC_PARSE_REL) && nrelops > 1) {
s = bc_parse_posixErr(p, BC_ERROR_POSIX_MULTIREL);
if (s) return s;
}
if (flags & BC_PARSE_PRINT) {
if (pfirst || !assign) bc_parse_push(p, BC_INST_PRINT);
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 (!s && p->l.t == BC_LEX_NLINE) s = bc_lex_next(&p->l);
}
return s;
}
BcStatus bc_parse_expr_status(BcParse *p, uint8_t flags, BcParseNext next) {
BcStatus s = bc_parse_expr_err(p, flags, next);
if (s == BC_STATUS_EMPTY_EXPR) s = bc_parse_err(p, BC_ERROR_PARSE_EMPTY_EXPR);
return s;
}
BcStatus bc_parse_expr(BcParse *p, uint8_t flags) {
assert(p);
return bc_parse_expr_status(p, flags, bc_parse_next_read);
}
#endif // BC_ENABLED