blob: b93b71bbece4fc044c766a7b8412cbaff28f145d [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 dc.
*
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <status.h>
#include <parse.h>
#include <dc.h>
#include <program.h>
#include <vm.h>
BcStatus dc_parse_inst(BcParse *p, BcInst inst) {
if (p->nbraces >= dc_inst_noperands[inst]) {
p->nbraces -= dc_inst_noperands[inst] - dc_inst_nresults[inst];
return bc_vec_pushByte(p->code, inst);
}
return BC_STATUS_PARSE_BAD_EXP;
}
BcStatus dc_parse_register(BcParse *p) {
BcStatus s;
char *name;
if ((s = bc_lex_next(&p->l))) return s;
if (p->l.t.t != BC_LEX_NAME) return BC_STATUS_PARSE_BAD_TOKEN;
if (!(name = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
if ((s = bc_parse_pushName(p, name))) free(name);
return s;
}
BcStatus dc_parse_string(BcParse *p) {
BcStatus s = BC_STATUS_ALLOC_ERR;
char *str, *name, b[DC_PARSE_BUF_LEN + 1];
size_t idx, len = p->prog->strs.len;
if (sprintf(b, "%0*zu", DC_PARSE_BUF_LEN, len) < 0) return BC_STATUS_IO_ERR;
if (!(name = strdup(b))) return s;
if (!(str = strdup(p->l.t.v.v))) goto str_err;
if ((s = dc_parse_inst(p, BC_INST_STR))) goto err;
if ((s = bc_parse_pushIndex(p, len))) goto err;
if ((s = bc_vec_push(&p->prog->strs, &str))) goto err;
if ((s = bc_program_addFunc(p->prog, name, &idx))) return s;
if ((s = bc_lex_next(&p->l))) return s;
// Update possibly invalidated pointer.
p->code = &(((BcFunc*) bc_vec_item(&p->prog->fns, p->func))->code);
assert(idx == len + BC_PROG_REQ_FUNCS);
return s;
err:
free(str);
str_err:
free(name);
return s;
}
BcStatus dc_parse_mem(BcParse *p, uint8_t inst, bool name, bool store) {
BcStatus s;
if ((s = dc_parse_inst(p, inst))) return s;
if (name && (s = dc_parse_register(p))) return s;
if (store) {
if ((s = dc_parse_inst(p, BC_INST_SWAP))) return s;
if ((s = dc_parse_inst(p, BC_INST_ASSIGN))) return s;
if ((s = dc_parse_inst(p, BC_INST_POP))) return s;
}
return bc_lex_next(&p->l);
}
BcStatus dc_parse_cond(BcParse *p, uint8_t inst) {
BcStatus s;
if ((s = dc_parse_inst(p, inst))) return s;
if ((s = dc_parse_inst(p, BC_INST_EXEC_COND))) return s;
if ((s = dc_parse_register(p))) return s;
if ((s = bc_lex_next(&p->l))) return s;
if (p->l.t.t == BC_LEX_ELSE) {
if ((s = dc_parse_register(p))) return s;
s = bc_lex_next(&p->l);
}
else s = bc_vec_pushByte(p->code, BC_PARSE_STREND);
return s;
}
BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) {
BcStatus s = BC_STATUS_SUCCESS;
BcInst prev;
bool get_token = false;
switch (t) {
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:
{
s = dc_parse_cond(p, t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ);
break;
}
case BC_LEX_SCOLON:
case BC_LEX_COLON:
{
bool assign = t == BC_LEX_COLON;
s = dc_parse_mem(p, BC_INST_ARRAY_ELEM, true, assign);
break;
}
case BC_LEX_STR:
{
s = dc_parse_string(p);
break;
}
case BC_LEX_NEG:
case BC_LEX_NUMBER:
{
if (t == BC_LEX_NEG) {
if ((s = bc_lex_next(&p->l))) return s;
if (p->l.t.t != BC_LEX_NUMBER) return BC_STATUS_PARSE_BAD_TOKEN;
}
s = bc_parse_number(p, &prev, &p->nbraces);
if (t == BC_LEX_NEG && !s) s = dc_parse_inst(p, BC_INST_NEG);
get_token = true;
break;
}
case BC_LEX_KEY_READ:
{
if (flags & BC_PARSE_NOREAD) s = BC_STATUS_EXEC_REC_READ;
else s = dc_parse_inst(p, BC_INST_READ);
get_token = true;
break;
}
case BC_LEX_OP_ASSIGN:
case BC_LEX_STORE_PUSH:
{
bool assign = t == BC_LEX_OP_ASSIGN;
uint8_t i = assign ? BC_INST_VAR : BC_INST_PUSH_TO_VAR;
s = dc_parse_mem(p, i, true, assign);
break;
}
case BC_LEX_LOAD:
case BC_LEX_LOAD_POP:
{
uint8_t i = t == BC_LEX_LOAD_POP ? BC_INST_PUSH_VAR : BC_INST_LOAD;
s = dc_parse_mem(p, i, true, false);
break;
}
case BC_LEX_STORE_IBASE:
{
s = dc_parse_mem(p, BC_INST_IBASE, false, true);
break;
}
case BC_LEX_STORE_SCALE:
{
s = dc_parse_mem(p, BC_INST_SCALE, false, true);
break;
}
case BC_LEX_STORE_OBASE:
{
s = dc_parse_mem(p, BC_INST_OBASE, false, true);
break;
}
default:
{
s = BC_STATUS_PARSE_BAD_TOKEN;
get_token = true;
break;
}
}
if (!s && get_token) s = bc_lex_next(&p->l);
return s;
}
BcStatus dc_parse_expr(BcParse *p, uint8_t flags) {
BcStatus s = BC_STATUS_SUCCESS;
BcInst inst;
BcLexType t;
if (flags & BC_PARSE_NOCALL) p->nbraces = p->prog->results.len;
while (!s && (t = p->l.t.t) != BC_LEX_EOF) {
if ((inst = dc_parse_insts[t]) != BC_INST_INVALID) {
if ((s = dc_parse_inst(p, inst))) return s;
if ((s = bc_lex_next(&p->l))) return s;
}
else if ((s = dc_parse_token(p, t, flags))) return s;
}
if (p->l.t.t == BC_LEX_EOF && (flags & BC_PARSE_NOCALL))
s = dc_parse_inst(p, BC_INST_POP_EXEC);
return s;
}
BcStatus dc_parse_parse(BcParse *p) {
BcStatus s;
assert(p);
if (p->l.t.t == BC_LEX_EOF) s = BC_STATUS_LEX_EOF;
else s = dc_parse_expr(p, 0);
if (s || bcg.signe) s = bc_parse_reset(p, s);
return s;
}
BcStatus dc_parse_init(BcParse *p, BcProgram *prog, size_t func) {
assert(p && prog);
return bc_parse_create(p, prog, func, dc_parse_parse, dc_lex_token);
}