blob: cf824995d65714ecf2dd3c7427b2cfcdb98c019d [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.
*
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <lang.h>
#include <lex.h>
#include <parse.h>
#include <bc.h>
BcStatus bc_parse_else(BcParse *p, BcVec *code);
BcStatus bc_parse_stmt(BcParse *p, BcVec *code);
BcStatus bc_parse_pushName(BcVec *code, char *name) {
BcStatus status = BC_STATUS_SUCCESS;
size_t i, len = strlen(name);
for (i = 0; !status && i < len; ++i)
status = bc_vec_pushByte(code, (uint8_t) name[i]);
free(name);
if (status) return status;
return bc_vec_pushByte(code, (uint8_t) ':');
}
BcStatus bc_parse_pushIndex(BcVec *code, size_t idx) {
BcStatus status;
uint8_t amt, i, nums[sizeof(size_t)];
for (amt = 0; idx; ++amt) {
nums[amt] = (uint8_t) idx;
idx = (idx & ((unsigned long) ~(UINT8_MAX))) >> sizeof(uint8_t) * CHAR_BIT;
}
if ((status = bc_vec_pushByte(code, amt))) return status;
for (i = 0; !status && i < amt; ++i) status = bc_vec_pushByte(code, nums[i]);
return status;
}
BcStatus bc_parse_operator(BcParse *p, BcVec *code, BcVec *ops, BcLexToken t,
uint32_t *num_exprs, bool next)
{
BcStatus status;
BcLexToken top;
uint8_t lp, rp = bc_parse_ops[t].prec;
bool rleft = bc_parse_ops[t].left;
while (ops->len &&
(top = *((BcLexToken*) bc_vec_top(ops))) != BC_LEX_LEFT_PAREN &&
((lp = bc_parse_ops[top].prec) < rp || (lp == rp && rleft)))
{
status = bc_vec_pushByte(code, (uint8_t) BC_PARSE_TOKEN_TO_INST(top));
if (status) return status;
bc_vec_pop(ops);
*num_exprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_OP_NEG;
}
if ((status = bc_vec_push(ops, 1, &t))) return status;
if (next) status = bc_lex_next(&p->lex);
return status;
}
BcStatus bc_parse_rightParen(BcParse *p, BcVec *code,
BcVec *ops, uint32_t *nexs)
{
BcStatus status;
BcLexToken top;
if (!ops->len) return BC_STATUS_PARSE_BAD_EXPR;
while ((top = *((BcLexToken*) bc_vec_top(ops))) != BC_LEX_LEFT_PAREN) {
status = bc_vec_pushByte(code, (uint8_t) BC_PARSE_TOKEN_TO_INST(top));
if (status) return status;
bc_vec_pop(ops);
*nexs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_OP_NEG;
if (!ops->len) return BC_STATUS_PARSE_BAD_EXPR;
}
bc_vec_pop(ops);
return bc_lex_next(&p->lex);
}
BcStatus bc_parse_params(BcParse *p, BcVec *code, uint8_t flags) {
BcStatus status;
bool comma = false;
size_t nparams;
if ((status = bc_lex_next(&p->lex))) return status;
for (nparams = 0; p->lex.token.type != BC_LEX_RIGHT_PAREN; ++nparams) {
flags = (flags & ~BC_PARSE_PRINT) | BC_PARSE_ARRAY;
status = bc_parse_expr(p, code, flags, bc_parse_next_param);
if (status) return status;
if (p->lex.token.type == BC_LEX_COMMA) {
comma = true;
if ((status = bc_lex_next(&p->lex))) return status;
}
else comma = false;
}
if (comma) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_vec_pushByte(code, BC_INST_CALL))) return status;
return bc_parse_pushIndex(code, nparams);
}
BcStatus bc_parse_call(BcParse *p, BcVec *code, char *name, uint8_t flags) {
BcStatus status;
BcEntry entry, *entry_ptr;
size_t idx;
entry.name = name;
if ((status = bc_parse_params(p, code, flags))) goto err;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) {
status = BC_STATUS_PARSE_BAD_TOKEN;
goto err;
}
idx = bc_veco_index(&p->prog->func_map, &entry);
if (idx == BC_INVALID_IDX) {
if ((status = bc_program_addFunc(p->prog, name, &idx))) return status;
name = NULL;
idx = bc_veco_index(&p->prog->func_map, &entry);
assert(idx != BC_INVALID_IDX);
}
else free(name);
entry_ptr = bc_veco_item(&p->prog->func_map, idx);
assert(entry_ptr);
if ((status = bc_parse_pushIndex(code, entry_ptr->idx))) return status;
return bc_lex_next(&p->lex);
err:
if (name) free(name);
return status;
}
BcStatus bc_parse_name(BcParse *p, BcVec *code, BcInst *type, uint8_t flags)
{
BcStatus status;
char *name;
if ((status = bc_vec_string(&p->lex.token.data, &name))) return status;
if ((status = bc_lex_next(&p->lex))) goto err;
if (p->lex.token.type == BC_LEX_LEFT_BRACKET) {
if ((status = bc_lex_next(&p->lex))) goto err;
if (p->lex.token.type == BC_LEX_RIGHT_BRACKET) {
if (!(flags & BC_PARSE_ARRAY)) {
status = BC_STATUS_PARSE_BAD_EXPR;
goto err;
}
*type = BC_INST_PUSH_ARRAY;
}
else {
*type = BC_INST_PUSH_ARRAY_ELEM;
flags &= ~(BC_PARSE_PRINT);
if ((status = bc_parse_expr(p, code, flags, bc_parse_next_elem)))
goto err;
if (p->lex.token.type != BC_LEX_RIGHT_BRACKET) {
status = BC_STATUS_PARSE_BAD_TOKEN;
goto err;
}
}
if ((status = bc_lex_next(&p->lex))) goto err;
if ((status = bc_vec_pushByte(code, (uint8_t) *type))) goto err;
status = bc_parse_pushName(code, name);
}
else if (p->lex.token.type == BC_LEX_LEFT_PAREN) {
if (flags & BC_PARSE_NOCALL) {
status = BC_STATUS_PARSE_BAD_TOKEN;
goto err;
}
*type = BC_INST_CALL;
status = bc_parse_call(p, code, name, flags);
}
else {
*type = BC_INST_PUSH_VAR;
if ((status = bc_vec_pushByte(code, BC_INST_PUSH_VAR))) goto err;
status = bc_parse_pushName(code, name);
}
return status;
err:
free(name);
return status;
}
BcStatus bc_parse_read(BcParse *p, BcVec *code) {
BcStatus status;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_vec_pushByte(code, BC_INST_READ))) return status;
return bc_lex_next(&p->lex);
}
BcStatus bc_parse_builtin(BcParse *p, BcVec *code, BcLexToken type,
uint8_t flags, BcInst *prev)
{
BcStatus status;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
flags = (flags & ~BC_PARSE_PRINT) | BC_PARSE_ARRAY;
if ((status = bc_lex_next(&p->lex))) return status;
if ((status = bc_parse_expr(p, code, flags, bc_parse_next_cond)))
return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
*prev = (type == BC_LEX_KEY_LENGTH) ? BC_INST_LENGTH : BC_INST_SQRT;
if ((status = bc_vec_pushByte(code, (uint8_t) *prev))) return status;
return bc_lex_next(&p->lex);
}
BcStatus bc_parse_scale(BcParse *p, BcVec *code, BcInst *type, uint8_t flags) {
BcStatus status;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) {
*type = BC_INST_PUSH_SCALE;
return bc_vec_pushByte(code, BC_INST_PUSH_SCALE);
}
*type = BC_INST_SCALE_FUNC;
flags &= ~(BC_PARSE_PRINT);
if ((status = bc_lex_next(&p->lex))) return status;
if ((status = bc_parse_expr(p, code, flags, bc_parse_next_cond)))
return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_vec_pushByte(code, BC_INST_SCALE_FUNC))) return status;
return bc_lex_next(&p->lex);
}
BcStatus bc_parse_incdec(BcParse *p, BcVec *code, BcInst *prev,
uint32_t *nexprs, uint8_t flags)
{
BcStatus status;
BcLexToken type;
uint8_t inst;
BcInst etype = *prev;
if (etype == BC_INST_PUSH_VAR || etype == BC_INST_PUSH_ARRAY_ELEM ||
etype == BC_INST_PUSH_SCALE || etype == BC_INST_PUSH_LAST ||
etype == BC_INST_PUSH_IBASE || etype == BC_INST_PUSH_OBASE)
{
*prev = inst = BC_INST_INC_POST + (p->lex.token.type != BC_LEX_OP_INC);
if ((status = bc_vec_pushByte(code, inst))) return status;
status = bc_lex_next(&p->lex);
}
else {
*prev = inst = BC_INST_INC_PRE + (p->lex.token.type != BC_LEX_OP_INC);
if ((status = bc_lex_next(&p->lex))) return status;
type = p->lex.token.type;
// Because we parse the next part of the expression
// right here, we need to increment this.
*nexprs = *nexprs + 1;
switch (type) {
case BC_LEX_NAME:
{
status = bc_parse_name(p, code, prev, flags | BC_PARSE_NOCALL);
break;
}
case BC_LEX_KEY_IBASE:
{
if ((status = bc_vec_pushByte(code, BC_INST_PUSH_IBASE))) return status;
status = bc_lex_next(&p->lex);
break;
}
case BC_LEX_KEY_LAST:
{
if ((status = bc_vec_pushByte(code, BC_INST_PUSH_LAST))) return status;
status = bc_lex_next(&p->lex);
break;
}
case BC_LEX_KEY_OBASE:
{
if ((status = bc_vec_pushByte(code, BC_INST_PUSH_OBASE))) return status;
status = bc_lex_next(&p->lex);
break;
}
case BC_LEX_KEY_SCALE:
{
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type == BC_LEX_LEFT_PAREN)
return BC_STATUS_PARSE_BAD_TOKEN;
status = bc_vec_pushByte(code, BC_INST_PUSH_SCALE);
break;
}
default:
{
return BC_STATUS_PARSE_BAD_TOKEN;
}
}
if (status) return status;
status = bc_vec_pushByte(code, inst);
}
return status;
}
BcStatus bc_parse_minus(BcParse *p, BcVec *exs, BcVec *ops, BcInst *prev,
bool rparen, uint32_t *nexprs)
{
BcStatus status;
BcLexToken type;
BcInst etype = *prev;
if ((status = bc_lex_next(&p->lex))) return status;
type = p->lex.token.type;
if (type != BC_LEX_NAME && type != BC_LEX_NUMBER &&
type != BC_LEX_KEY_SCALE && type != BC_LEX_KEY_LAST &&
type != BC_LEX_KEY_IBASE && type != BC_LEX_KEY_OBASE &&
type != BC_LEX_LEFT_PAREN && type != BC_LEX_OP_MINUS &&
type != BC_LEX_OP_INC && type != BC_LEX_OP_DEC &&
type != BC_LEX_OP_BOOL_NOT)
{
return BC_STATUS_PARSE_BAD_TOKEN;
}
type = rparen || etype == BC_INST_INC_POST || etype == BC_INST_DEC_POST ||
(etype >= BC_INST_PUSH_NUM && etype <= BC_INST_SQRT) ?
BC_LEX_OP_MINUS : BC_LEX_OP_NEG;
*prev = BC_PARSE_TOKEN_TO_INST(type);
if (type == BC_LEX_OP_MINUS)
status = bc_parse_operator(p, exs, ops, type, nexprs, false);
else
// We can just push onto the op stack because this is the largest
// precedence operator that gets pushed. Inc/dec does not.
status = bc_vec_push(ops, 1, &type);
return status;
}
BcStatus bc_parse_string(BcParse *p, BcVec *code, uint8_t inst) {
BcStatus status;
char *str;
if ((status = bc_vec_string(&p->lex.token.data, &str))) return status;
if ((status = bc_vec_pushByte(code, inst))) goto err;
if ((status = bc_parse_pushIndex(code, p->prog->strings.len))) goto err;
if ((status = bc_vec_push(&p->prog->strings, 1, &str))) goto err;
return bc_lex_next(&p->lex);
err:
free(str);
return status;
}
BcStatus bc_parse_print(BcParse *p, BcVec *code) {
BcStatus status;
BcLexToken type;
bool comma = false;
if ((status = bc_lex_next(&p->lex))) return status;
type = p->lex.token.type;
if (type == BC_LEX_SEMICOLON || type == BC_LEX_NEWLINE)
return BC_STATUS_PARSE_BAD_PRINT;
while (!status && type != BC_LEX_SEMICOLON && type != BC_LEX_NEWLINE) {
if (type == BC_LEX_STRING) {
status = bc_parse_string(p, code, BC_INST_PRINT_STR);
}
else {
if ((status = bc_parse_expr(p, code, 0, bc_parse_next_print)))
return status;
status = bc_vec_pushByte(code, BC_INST_PRINT_EXPR);
}
if (status) return status;
comma = p->lex.token.type == BC_LEX_COMMA;
if (comma) status = bc_lex_next(&p->lex);
type = p->lex.token.type;
}
if (status) return status;
if (comma) return BC_STATUS_PARSE_BAD_TOKEN;
return bc_lex_next(&p->lex);
}
BcStatus bc_parse_return(BcParse *p, BcVec *code) {
BcStatus status;
if (!BC_PARSE_FUNC(p)) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_NEWLINE &&
p->lex.token.type != BC_LEX_SEMICOLON &&
p->lex.token.type != BC_LEX_LEFT_PAREN &&
(status = bc_posix_error(BC_STATUS_POSIX_RETURN_PARENS,
p->lex.file, p->lex.line, NULL)))
{
return status;
}
if (p->lex.token.type == BC_LEX_NEWLINE ||
p->lex.token.type == BC_LEX_SEMICOLON)
{
status = bc_vec_pushByte(code, BC_INST_RETURN_ZERO);
}
else {
if ((status = bc_parse_expr(p, code, 0, bc_parse_next_expr))) return status;
status = bc_vec_pushByte(code, BC_INST_RETURN);
}
return status;
}
BcStatus bc_parse_endBody(BcParse *p, BcVec *code, bool brace) {
BcStatus status = BC_STATUS_SUCCESS;
if (p->flags.len <= 1 || p->num_braces == 0) return BC_STATUS_PARSE_BAD_TOKEN;
if (brace) {
if (p->lex.token.type == BC_LEX_RIGHT_BRACE) {
if (!p->num_braces) return BC_STATUS_PARSE_BAD_TOKEN;
--p->num_braces;
if ((status = bc_lex_next(&p->lex))) return status;
}
else return BC_STATUS_PARSE_BAD_TOKEN;
}
if (BC_PARSE_IF(p)) {
uint8_t *flag_ptr;
while (p->lex.token.type == BC_LEX_NEWLINE) {
if ((status = bc_lex_next(&p->lex))) return status;
}
bc_vec_pop(&p->flags);
flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
*flag_ptr = (*flag_ptr | BC_PARSE_FLAG_IF_END);
if (p->lex.token.type == BC_LEX_KEY_ELSE) status = bc_parse_else(p, code);
}
else if (BC_PARSE_ELSE(p)) {
BcInstPtr *ip;
BcFunc *func;
size_t *label;
bc_vec_pop(&p->flags);
ip = bc_vec_top(&p->exits);
func = bc_vec_item(&p->prog->funcs, p->func);
label = bc_vec_item(&func->labels, ip->idx);
*label = code->len;
bc_vec_pop(&p->exits);
}
else if (BC_PARSE_FUNC_INNER(p)) {
p->func = 0;
if ((status = bc_vec_pushByte(code, BC_INST_RETURN_ZERO))) return status;
bc_vec_pop(&p->flags);
}
else {
BcInstPtr *ip;
BcFunc *func;
size_t *label;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP))) return status;
ip = bc_vec_top(&p->exits);
label = bc_vec_top(&p->conds);
if ((status = bc_parse_pushIndex(code, *label))) return status;
func = bc_vec_item(&p->prog->funcs, p->func);
label = bc_vec_item(&func->labels, ip->idx);
*label = code->len;
bc_vec_pop(&p->flags);
bc_vec_pop(&p->exits);
bc_vec_pop(&p->conds);
}
return status;
}
BcStatus bc_parse_startBody(BcParse *p, uint8_t flags) {
uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
flags |= (*flag_ptr & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP));
flags |= BC_PARSE_FLAG_BODY;
return bc_vec_push(&p->flags, 1, &flags);
}
void bc_parse_noElse(BcParse *p, BcVec *code) {
BcInstPtr *ip;
BcFunc *func;
size_t *label;
uint8_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);
func = bc_vec_item(&p->prog->funcs, p->func);
assert(code == &func->code);
label = bc_vec_item(&func->labels, ip->idx);
*label = code->len;
bc_vec_pop(&p->exits);
}
BcStatus bc_parse_if(BcParse *p, BcVec *code) {
BcStatus status;
BcInstPtr ip;
BcFunc *func;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if ((status = bc_parse_expr(p, code, BC_PARSE_POSIX_REL, bc_parse_next_cond)))
return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO))) return status;
func = bc_vec_item(&p->prog->funcs, p->func);
ip.idx = func->labels.len;
ip.func = 0;
ip.len = 0;
if ((status = bc_parse_pushIndex(code, ip.idx))) return status;
if ((status = bc_vec_push(&p->exits, 1, &ip))) return status;
if ((status = bc_vec_push(&func->labels, 1, &ip.idx))) return status;
return bc_parse_startBody(p, BC_PARSE_FLAG_IF);
}
BcStatus bc_parse_else(BcParse *p, BcVec *code) {
BcStatus status;
BcInstPtr ip;
BcFunc *func;
if (!BC_PARSE_IF_END(p)) return BC_STATUS_PARSE_BAD_TOKEN;
func = bc_vec_item(&p->prog->funcs, p->func);
ip.idx = func->labels.len;
ip.func = 0;
ip.len = 0;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP))) return status;
if ((status = bc_parse_pushIndex(code, ip.idx))) return status;
bc_parse_noElse(p, code);
if ((status = bc_vec_push(&p->exits, 1, &ip))) return status;
if ((status = bc_vec_push(&func->labels, 1, &ip.idx))) return status;
if ((status = bc_lex_next(&p->lex))) return status;
return bc_parse_startBody(p, BC_PARSE_FLAG_ELSE);
}
BcStatus bc_parse_while(BcParse *p, BcVec *code) {
BcStatus status;
BcFunc *func;
BcInstPtr ip;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
func = bc_vec_item(&p->prog->funcs, p->func);
ip.idx = func->labels.len;
if ((status = bc_vec_push(&func->labels, 1, &code->len))) return status;
if ((status = bc_vec_push(&p->conds, 1, &ip.idx))) return status;
ip.idx = func->labels.len;
ip.func = 1;
ip.len = 0;
if ((status = bc_vec_push(&p->exits, 1, &ip))) return status;
if ((status = bc_vec_push(&func->labels, 1, &ip.idx))) return status;
if ((status = bc_parse_expr(p, code, BC_PARSE_POSIX_REL, bc_parse_next_cond)))
return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO))) return status;
if ((status = bc_parse_pushIndex(code, ip.idx))) return status;
return bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
}
BcStatus bc_parse_for(BcParse *p, BcVec *code) {
BcStatus status;
BcFunc *func;
BcInstPtr ip;
size_t cond_idx, exit_idx, body_idx, update_idx;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_SEMICOLON)
status = bc_parse_expr(p, code, 0, bc_parse_next_for);
else
status = bc_posix_error(BC_STATUS_POSIX_NO_FOR_INIT,
p->lex.file, p->lex.line, NULL);
if (status) return status;
if (p->lex.token.type != BC_LEX_SEMICOLON) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
func = bc_vec_item(&p->prog->funcs, p->func);
cond_idx = func->labels.len;
update_idx = cond_idx + 1;
body_idx = update_idx + 1;
exit_idx = body_idx + 1;
if ((status = bc_vec_push(&func->labels, 1, &code->len))) return status;
if (p->lex.token.type != BC_LEX_SEMICOLON)
status = bc_parse_expr(p, code, BC_PARSE_POSIX_REL, bc_parse_next_for);
else status = bc_posix_error(BC_STATUS_POSIX_NO_FOR_COND,
p->lex.file, p->lex.line, NULL);
if (status) return status;
if (p->lex.token.type != BC_LEX_SEMICOLON) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO))) return status;
if ((status = bc_parse_pushIndex(code, exit_idx))) return status;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP))) return status;
if ((status = bc_parse_pushIndex(code, body_idx))) return status;
ip.idx = func->labels.len;
if ((status = bc_vec_push(&p->conds, 1, &update_idx))) return status;
if ((status = bc_vec_push(&func->labels, 1, &code->len))) return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN)
status = bc_parse_expr(p, code, 0, bc_parse_next_cond);
else
status = bc_posix_error(BC_STATUS_POSIX_NO_FOR_UPDATE,
p->lex.file, p->lex.line, NULL);
if (status) return status;
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) {
status = bc_parse_expr(p, code, BC_PARSE_POSIX_REL, bc_parse_next_cond);
if (status) return status;
}
if (p->lex.token.type != BC_LEX_RIGHT_PAREN) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_vec_pushByte(code, BC_INST_JUMP))) return status;
if ((status = bc_parse_pushIndex(code, cond_idx))) return status;
if ((status = bc_vec_push(&func->labels, 1, &code->len))) return status;
ip.idx = exit_idx;
ip.func = 1;
ip.len = 0;
if ((status = bc_vec_push(&p->exits, 1, &ip))) return status;
if ((status = bc_vec_push(&func->labels, 1, &ip.idx))) return status;
if ((status = bc_lex_next(&p->lex))) return status;
return bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
}
BcStatus bc_parse_loopExit(BcParse *p, BcVec *code, BcLexToken type) {
BcStatus status;
size_t idx;
BcInstPtr *ip;
if (!BC_PARSE_LOOP(p)) return BC_STATUS_PARSE_BAD_TOKEN;
if (type == BC_LEX_KEY_BREAK) {
size_t top;
if (!p->exits.len) return BC_STATUS_PARSE_BAD_TOKEN;
top = p->exits.len - 1;
ip = bc_vec_item(&p->exits, top);
while (top < p->exits.len && ip && !ip->func)
ip = bc_vec_item(&p->exits, top--);
if (top >= p->exits.len || !ip) return BC_STATUS_PARSE_BAD_TOKEN;
idx = ip->idx;
}
else idx = *((size_t*) bc_vec_top(&p->conds));
if ((status = bc_vec_pushByte(code, BC_INST_JUMP))) return status;
if ((status = bc_parse_pushIndex(code, idx))) return status;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_SEMICOLON &&
p->lex.token.type != BC_LEX_NEWLINE)
{
return BC_STATUS_PARSE_BAD_TOKEN;
}
return bc_lex_next(&p->lex);
}
BcStatus bc_parse_func(BcParse *p) {
BcStatus status;
BcFunc *fptr;
bool var, comma = false;
uint8_t flags;
char *name;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_NAME) return BC_STATUS_PARSE_BAD_FUNC;
assert(p->prog->funcs.len == p->prog->func_map.vec.len);
if ((status = bc_vec_string(&p->lex.token.data, &name))) return status;
if ((status = bc_program_addFunc(p->prog, name, &p->func))) return status;
assert(p->func);
fptr = bc_vec_item(&p->prog->funcs, p->func);
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_PAREN) return BC_STATUS_PARSE_BAD_FUNC;
if ((status = bc_lex_next(&p->lex))) return status;
while (!status && p->lex.token.type != BC_LEX_RIGHT_PAREN) {
if (p->lex.token.type != BC_LEX_NAME) return BC_STATUS_PARSE_BAD_FUNC;
++fptr->nparams;
if ((status = bc_vec_string(&p->lex.token.data, &name))) return status;
if ((status = bc_lex_next(&p->lex))) goto err;
var = p->lex.token.type != BC_LEX_LEFT_BRACKET;
if (!var) {
if ((status = bc_lex_next(&p->lex))) goto err;
if (p->lex.token.type != BC_LEX_RIGHT_BRACKET) {
status = BC_STATUS_PARSE_BAD_FUNC;
goto err;
}
if ((status = bc_lex_next(&p->lex))) goto err;
}
comma = p->lex.token.type == BC_LEX_COMMA;
if (comma && (status = bc_lex_next(&p->lex))) goto err;
if ((status = bc_func_insert(fptr, name, var))) goto err;
}
if (comma) return BC_STATUS_PARSE_BAD_FUNC;
flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_BODY;
if ((status = bc_parse_startBody(p, flags))) return status;
if ((status = bc_lex_next(&p->lex))) return status;
if (p->lex.token.type != BC_LEX_LEFT_BRACE)
return bc_posix_error(BC_STATUS_POSIX_HEADER_BRACE,
p->lex.file, p->lex.line, NULL);
return status;
err:
free(name);
return status;
}
BcStatus bc_parse_auto(BcParse *p) {
BcStatus status;
bool comma, var, one;
char *name;
BcFunc *func;
if (!p->auto_part) return BC_STATUS_PARSE_BAD_TOKEN;
if ((status = bc_lex_next(&p->lex))) return status;
p->auto_part = comma = one = false;
func = bc_vec_item(&p->prog->funcs, p->func);
while (!status && p->lex.token.type == BC_LEX_NAME) {
if ((status = bc_vec_string(&p->lex.token.data, &name))) return status;
if ((status = bc_lex_next(&p->lex))) goto err;
one = true;
var = p->lex.token.type != BC_LEX_LEFT_BRACKET;
if (!var) {
if ((status = bc_lex_next(&p->lex))) goto err;
if (p->lex.token.type != BC_LEX_RIGHT_BRACKET) {
status = BC_STATUS_PARSE_BAD_FUNC;
goto err;
}
if ((status = bc_lex_next(&p->lex))) goto err;
}
comma = p->lex.token.type == BC_LEX_COMMA;
if (comma && (status = bc_lex_next(&p->lex))) goto err;
if ((status = bc_func_insert(func, name, var))) goto err;
}
if (comma) return BC_STATUS_PARSE_BAD_FUNC;
if (!one) return BC_STATUS_PARSE_NO_AUTO;
if (p->lex.token.type != BC_LEX_NEWLINE &&
p->lex.token.type != BC_LEX_SEMICOLON)
{
return BC_STATUS_PARSE_BAD_TOKEN;
}
return bc_lex_next(&p->lex);
err:
free(name);
return status;
}
BcStatus bc_parse_body(BcParse *p, BcVec *code, bool brace) {
BcStatus status;
uint8_t *flag_ptr = bc_vec_top(&p->flags);
assert(p->flags.len >= 2);
*flag_ptr &= ~(BC_PARSE_FLAG_BODY);
if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) {
if (!brace) return BC_STATUS_PARSE_BAD_TOKEN;
p->auto_part = true;
if (p->lex.token.type == BC_LEX_KEY_AUTO) {
if ((status = bc_parse_auto(p))) return status;
p->auto_part = true;
}
status = bc_lex_next(&p->lex);
}
else {
assert(*flag_ptr);
if ((status = bc_parse_stmt(p, code))) return status;
if (!brace) status = bc_parse_endBody(p, code, false);
}
return status;
}
BcStatus bc_parse_stmt(BcParse *p, BcVec *code) {
BcStatus status;
switch (p->lex.token.type) {
case BC_LEX_NEWLINE:
{
return bc_lex_next(&p->lex);
}
case BC_LEX_KEY_ELSE:
{
p->auto_part = false;
break;
}
case BC_LEX_LEFT_BRACE:
{
if (!BC_PARSE_BODY(p)) return BC_STATUS_PARSE_BAD_TOKEN;
++p->num_braces;
if ((status = bc_lex_next(&p->lex))) return status;
return bc_parse_body(p, code, 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, code);
return BC_STATUS_SUCCESS;
}
else if (BC_PARSE_BODY(p)) return bc_parse_body(p, code, false);
break;
}
}
switch (p->lex.token.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_LEFT_PAREN:
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:
{
status = bc_parse_expr(p, code, BC_PARSE_PRINT, bc_parse_next_expr);
break;
}
case BC_LEX_KEY_ELSE:
{
status = bc_parse_else(p, code);
break;
}
case BC_LEX_SEMICOLON:
{
status = BC_STATUS_SUCCESS;
while (!status && p->lex.token.type == BC_LEX_SEMICOLON)
status = bc_lex_next(&p->lex);
break;
}
case BC_LEX_RIGHT_BRACE:
{
status = bc_parse_endBody(p, code, true);
break;
}
case BC_LEX_STRING:
{
status = bc_parse_string(p, code, BC_INST_STR);
break;
}
case BC_LEX_KEY_BREAK:
case BC_LEX_KEY_CONTINUE:
{
status = bc_parse_loopExit(p, code, p->lex.token.type);
break;
}
case BC_LEX_KEY_FOR:
{
status = bc_parse_for(p, code);
break;
}
case BC_LEX_KEY_HALT:
{
if ((status = bc_vec_pushByte(code, BC_INST_HALT))) return status;
status = bc_lex_next(&p->lex);
break;
}
case BC_LEX_KEY_IF:
{
status = bc_parse_if(p, code);
break;
}
case BC_LEX_KEY_LIMITS:
{
if ((status = bc_lex_next(&p->lex))) return status;
status = BC_STATUS_LIMITS;
break;
}
case BC_LEX_KEY_PRINT:
{
status = bc_parse_print(p, code);
break;
}
case BC_LEX_KEY_QUIT:
{
// Quit is a compile-time command, so we send an exit command. We don't
// exit directly, so the vm can clean up. Limits do the same thing.
status = BC_STATUS_QUIT;
break;
}
case BC_LEX_KEY_RETURN:
{
if ((status = bc_parse_return(p, code))) return status;
break;
}
case BC_LEX_KEY_WHILE:
{
status = bc_parse_while(p, code);
break;
}
case BC_LEX_EOF:
{
status = (p->flags.len > 0) * BC_STATUS_PARSE_NO_BLOCK_END;
break;
}
default:
{
status = BC_STATUS_PARSE_BAD_TOKEN;
break;
}
}
return status;
}
BcStatus bc_parse_init(BcParse *p, BcProgram *program) {
BcStatus status;
uint8_t flags = 0;
assert(p && program);
if ((status = bc_lex_init(&p->lex))) return status;
if ((status = bc_vec_init(&p->flags, sizeof(uint8_t), NULL))) goto flags_err;
if ((status = bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL))) goto exit_err;
if ((status = bc_vec_init(&p->conds, sizeof(size_t), NULL))) goto cond_err;
if ((status = bc_vec_push(&p->flags, 1, &flags))) goto push_err;
if ((status = bc_vec_init(&p->ops, sizeof(BcLexToken), NULL))) goto push_err;
p->prog = program;
p->func = p->num_braces = 0;
p->auto_part = false;
return status;
push_err:
bc_vec_free(&p->conds);
cond_err:
bc_vec_free(&p->exits);
exit_err:
bc_vec_free(&p->flags);
flags_err:
bc_lex_free(&p->lex);
return status;
}
BcStatus bc_parse_parse(BcParse *p) {
BcStatus status;
assert(p);
if (p->lex.token.type == BC_LEX_EOF) status = BC_STATUS_LEX_EOF;
else if (p->lex.token.type == BC_LEX_KEY_DEFINE) {
if (!BC_PARSE_CAN_EXEC(p)) return BC_STATUS_PARSE_BAD_TOKEN;
status = bc_parse_func(p);
}
else {
BcFunc *func = bc_vec_item(&p->prog->funcs, p->func);
status = bc_parse_stmt(p, &func->code);
}
if (status || bcg.signe) {
if (p->func) {
BcFunc *func = bc_vec_item(&p->prog->funcs, p->func);
func->nparams = 0;
bc_vec_npop(&func->code, func->code.len);
bc_vec_npop(&func->autos, func->autos.len);
bc_vec_npop(&func->labels, func->labels.len);
p->func = 0;
}
p->lex.idx = p->lex.len;
p->lex.token.type = BC_LEX_EOF;
p->auto_part = false;
p->num_braces = 0;
bc_vec_npop(&p->flags, p->flags.len - 1);
bc_vec_npop(&p->exits, p->exits.len);
bc_vec_npop(&p->conds, p->conds.len);
bc_vec_npop(&p->ops, p->ops.len);
status = bc_program_reset(p->prog, status);
}
return status;
}
void bc_parse_free(BcParse *p) {
assert(p);
bc_vec_free(&p->flags);
bc_vec_free(&p->exits);
bc_vec_free(&p->conds);
bc_vec_free(&p->ops);
bc_lex_free(&p->lex);
}
BcStatus bc_parse_expr(BcParse *p, BcVec *code, uint8_t flags, BcNext next) {
BcStatus status = BC_STATUS_SUCCESS;
BcInst prev = BC_INST_PRINT;
BcLexToken top, type = p->lex.token.type;
uint32_t i, nexprs, nparens, nrelops, ops_start = (uint32_t) p->ops.len;
bool paren_first, paren_expr, rparen, done, get_token, assign, bin_last;
paren_first = p->lex.token.type == BC_LEX_LEFT_PAREN;
nexprs = nparens = nrelops = 0;
paren_expr = rparen = done = get_token = assign = false;
bin_last = true;
while (!bcg.signe && !status && !done && bc_parse_token_exprs[type]) {
switch (type) {
case BC_LEX_OP_INC:
case BC_LEX_OP_DEC:
{
status = bc_parse_incdec(p, code, &prev, &nexprs, flags);
rparen = get_token = bin_last = false;
break;
}
case BC_LEX_OP_MINUS:
{
status = bc_parse_minus(p, code, &p->ops, &prev, rparen, &nexprs);
rparen = get_token = false;
bin_last = prev == BC_INST_MINUS;
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:
case BC_LEX_OP_ASSIGN:
if (prev != BC_INST_PUSH_VAR && prev != BC_INST_PUSH_ARRAY_ELEM &&
prev != BC_INST_PUSH_SCALE && prev != BC_INST_PUSH_IBASE &&
prev != BC_INST_PUSH_OBASE && prev != BC_INST_PUSH_LAST)
{
status = BC_STATUS_PARSE_BAD_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:
case BC_LEX_OP_REL_EQUAL:
case BC_LEX_OP_REL_LESS_EQ:
case BC_LEX_OP_REL_GREATER_EQ:
case BC_LEX_OP_REL_NOT_EQ:
case BC_LEX_OP_REL_LESS:
case BC_LEX_OP_REL_GREATER:
case BC_LEX_OP_BOOL_NOT:
case BC_LEX_OP_BOOL_OR:
case BC_LEX_OP_BOOL_AND:
{
if (((type == BC_LEX_OP_BOOL_NOT) != bin_last) ||
(type != BC_LEX_OP_BOOL_NOT && prev == BC_INST_BOOL_NOT))
{
return BC_STATUS_PARSE_BAD_EXPR;
}
nrelops += type >= BC_LEX_OP_REL_EQUAL && type <= BC_LEX_OP_REL_GREATER;
prev = BC_PARSE_TOKEN_TO_INST(type);
status = bc_parse_operator(p, code, &p->ops, type, &nexprs, true);
rparen = get_token = false;
bin_last = type != BC_LEX_OP_BOOL_NOT;
break;
}
case BC_LEX_LEFT_PAREN:
{
++nparens;
paren_expr = rparen = bin_last = false;
get_token = true;
status = bc_vec_push(&p->ops, 1, &type);
break;
}
case BC_LEX_RIGHT_PAREN:
{
if (nparens == 0) {
status = BC_STATUS_SUCCESS;
done = true;
get_token = false;
break;
}
else if (!paren_expr) return BC_STATUS_PARSE_BAD_EXPR;
--nparens;
paren_expr = rparen = true;
get_token = bin_last = false;
status = bc_parse_rightParen(p, code, &p->ops, &nexprs);
break;
}
case BC_LEX_NAME:
{
if (!BC_PARSE_LEAF(prev, paren_expr)) return BC_STATUS_PARSE_BAD_EXPR;
paren_expr = true;
rparen = get_token = bin_last = false;
status = bc_parse_name(p, code, &prev, flags & ~BC_PARSE_NOCALL);
++nexprs;
break;
}
case BC_LEX_NUMBER:
{
char *num;
size_t idx = p->prog->constants.len;
if (!BC_PARSE_LEAF(prev, paren_expr)) return BC_STATUS_PARSE_BAD_EXPR;
if ((status = bc_vec_string(&p->lex.token.data, &num))) return status;
if ((status = bc_vec_push(&p->prog->constants, 1, &num))) {
free(num);
return status;
}
if ((status = bc_vec_pushByte(code, BC_INST_PUSH_NUM))) return status;
if ((status = bc_parse_pushIndex(code, idx))) return status;
paren_expr = get_token = true;
rparen = bin_last = false;
++nexprs;
prev = BC_INST_PUSH_NUM;
break;
}
case BC_LEX_KEY_IBASE:
case BC_LEX_KEY_LAST:
case BC_LEX_KEY_OBASE:
{
if (!BC_PARSE_LEAF(prev, paren_expr)) return BC_STATUS_PARSE_BAD_EXPR;
prev = (uint8_t) (type - BC_LEX_KEY_IBASE + BC_INST_PUSH_IBASE);
status = bc_vec_pushByte(code, (uint8_t) prev);
paren_expr = get_token = true;
rparen = bin_last = false;
++nexprs;
break;
}
case BC_LEX_KEY_LENGTH:
case BC_LEX_KEY_SQRT:
{
if (!BC_PARSE_LEAF(prev, paren_expr)) return BC_STATUS_PARSE_BAD_EXPR;
status = bc_parse_builtin(p, code, type, flags, &prev);
paren_expr = true;
rparen = get_token = bin_last = false;
++nexprs;
break;
}
case BC_LEX_KEY_READ:
{
if (!BC_PARSE_LEAF(prev, paren_expr)) return BC_STATUS_PARSE_BAD_EXPR;
else if (flags & BC_PARSE_NOREAD) status = BC_STATUS_EXEC_NESTED_READ;
else status = bc_parse_read(p, code);
paren_expr = true;
rparen = get_token = bin_last = false;
++nexprs;
prev = BC_INST_READ;
break;
}
case BC_LEX_KEY_SCALE:
{
if (!BC_PARSE_LEAF(prev, paren_expr)) return BC_STATUS_PARSE_BAD_EXPR;
status = bc_parse_scale(p, code, &prev, flags);
paren_expr = true;
rparen = get_token = bin_last = false;
++nexprs;
prev = BC_INST_PUSH_SCALE;
break;
}
default:
{
status = BC_STATUS_PARSE_BAD_TOKEN;
break;
}
}
if (status) return status;
if (get_token) status = bc_lex_next(&p->lex);
type = p->lex.token.type;
}
if (status) return status;
if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
while (!status && p->ops.len > ops_start) {
top = *((BcLexToken*) bc_vec_top(&p->ops));
assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN;
if (top == BC_LEX_LEFT_PAREN || top == BC_LEX_RIGHT_PAREN)
return BC_STATUS_PARSE_BAD_EXPR;
if ((status = bc_vec_pushByte(code, (uint8_t) BC_PARSE_TOKEN_TO_INST(top))))
return status;
nexprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_OP_NEG;
bc_vec_pop(&p->ops);
}
status = BC_STATUS_PARSE_BAD_EXPR;
if (prev == BC_INST_BOOL_NOT || nexprs != 1) return status;
for (i = 0; status && i < next.len; ++i) status *= type != next.tokens[i];
if (status) return status;
if (!(flags & BC_PARSE_POSIX_REL) && nrelops &&
(status = bc_posix_error(BC_STATUS_POSIX_REL_OUTSIDE,
p->lex.file, p->lex.line, NULL)))
{
return status;
}
else if ((flags & BC_PARSE_POSIX_REL) && nrelops != 1 &&
(status = bc_posix_error(BC_STATUS_POSIX_MULTIPLE_REL,
p->lex.file, p->lex.line, NULL)))
{
return status;
}
if (flags & BC_PARSE_PRINT) {
if (paren_first || !assign) status = bc_vec_pushByte(code, BC_INST_PRINT);
else status = bc_vec_pushByte(code, BC_INST_POP);
}
return status;
}