blob: 8532ebc66d7d82a7433e3376d922a73c99257339 [file] [log] [blame]
/*
* *****************************************************************************
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018-2021 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* *****************************************************************************
*
* Code to manipulate data structures in programs.
*
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <lang.h>
#include <program.h>
#include <vm.h>
void bc_const_free(void *constant) {
BcConst *c = constant;
BC_SIG_ASSERT_LOCKED;
assert(c->val != NULL);
bc_num_free(&c->num);
}
#if BC_ENABLED
void bc_func_insert(BcFunc *f, BcProgram *p, char *name,
BcType type, size_t line)
{
BcAuto a;
size_t i, idx;
// The function must *always* be valid.
assert(f != NULL);
// Get the index of the variable.
idx = bc_program_search(p, name, type == BC_TYPE_VAR);
// Search through all of the other autos/parameters.
for (i = 0; i < f->autos.len; ++i) {
// Get the auto.
BcAuto *aptr = bc_vec_item(&f->autos, i);
// If they match, barf.
if (BC_ERR(idx == aptr->idx && type == aptr->type)) {
const char *array = type == BC_TYPE_ARRAY ? "[]" : "";
bc_error(BC_ERR_PARSE_DUP_LOCAL, line, name, array);
}
}
// Set the auto.
a.idx = idx;
a.type = type;
// Push it.
bc_vec_push(&f->autos, &a);
}
#endif // BC_ENABLED
void bc_func_init(BcFunc *f, const char *name) {
BC_SIG_ASSERT_LOCKED;
assert(f != NULL && name != NULL);
bc_vec_init(&f->code, sizeof(uchar), BC_DTOR_NONE);
bc_vec_init(&f->consts, sizeof(BcConst), BC_DTOR_CONST);
bc_vec_init(&f->strs, sizeof(char*), BC_DTOR_NONE);
#if BC_ENABLED
// Only bc needs these things.
if (BC_IS_BC) {
bc_vec_init(&f->autos, sizeof(BcAuto), BC_DTOR_NONE);
bc_vec_init(&f->labels, sizeof(size_t), BC_DTOR_NONE);
f->nparams = 0;
f->voidfn = false;
}
#endif // BC_ENABLED
f->name = name;
}
void bc_func_reset(BcFunc *f) {
BC_SIG_ASSERT_LOCKED;
assert(f != NULL);
bc_vec_popAll(&f->code);
bc_vec_popAll(&f->consts);
bc_vec_popAll(&f->strs);
#if BC_ENABLED
if (BC_IS_BC) {
bc_vec_popAll(&f->autos);
bc_vec_popAll(&f->labels);
f->nparams = 0;
f->voidfn = false;
}
#endif // BC_ENABLED
}
#ifndef NDEBUG
void bc_func_free(void *func) {
BcFunc *f = (BcFunc*) func;
BC_SIG_ASSERT_LOCKED;
assert(f != NULL);
bc_vec_free(&f->code);
bc_vec_free(&f->consts);
bc_vec_free(&f->strs);
#if BC_ENABLED
if (BC_IS_BC) {
bc_vec_free(&f->autos);
bc_vec_free(&f->labels);
}
#endif // BC_ENABLED
}
#endif // NDEBUG
void bc_array_init(BcVec *a, bool nums) {
BC_SIG_ASSERT_LOCKED;
// Set the proper vector.
if (nums) bc_vec_init(a, sizeof(BcNum), BC_DTOR_NUM);
else bc_vec_init(a, sizeof(BcVec), BC_DTOR_VEC);
// We always want at least one item in the array.
bc_array_expand(a, 1);
}
void bc_array_copy(BcVec *d, const BcVec *s) {
size_t i;
BC_SIG_ASSERT_LOCKED;
assert(d != NULL && s != NULL);
assert(d != s && d->size == s->size && d->dtor == s->dtor);
// Make sure to destroy everything currently in d. This will put a lot of
// temps on the reuse list, so allocating later is not going to be as
// expensive as it seems. Also, it makes it easier to copy numbers that are
// strings.
bc_vec_popAll(d);
// Preexpand.
bc_vec_expand(d, s->cap);
d->len = s->len;
for (i = 0; i < s->len; ++i) {
BcNum *dnum, *snum;
dnum = bc_vec_item(d, i);
snum = bc_vec_item(s, i);
// We have to create a copy of the number as well.
if (BC_PROG_STR(snum)) memcpy(dnum, snum, sizeof(BcNum));
else bc_num_createCopy(dnum, snum);
}
}
void bc_array_expand(BcVec *a, size_t len) {
assert(a != NULL);
BC_SIG_ASSERT_LOCKED;
bc_vec_expand(a, len);
// If this is true, then we have a num array.
if (a->size == sizeof(BcNum) && a->dtor == BC_DTOR_NUM) {
// Initialize numbers until we reach the target.
while (len > a->len) {
BcNum *n = bc_vec_pushEmpty(a);
bc_num_init(n, BC_NUM_DEF_SIZE);
}
}
else {
assert(a->size == sizeof(BcVec) && a->dtor == BC_DTOR_VEC);
// Recursively initialize arrays until we reach the target. Having the
// second argument of bc_array_init() be true will activate the base
// case, so we're safe.
while (len > a->len) {
BcVec *v = bc_vec_pushEmpty(a);
bc_array_init(v, true);
}
}
}
void bc_result_clear(BcResult *r) {
r->t = BC_RESULT_TEMP;
bc_num_clear(&r->d.n);
}
#if DC_ENABLED
void bc_result_copy(BcResult *d, BcResult *src) {
assert(d != NULL && src != NULL);
BC_SIG_ASSERT_LOCKED;
// d is assumed to not be valid yet.
d->t = src->t;
// Yes, it depends on what type.
switch (d->t) {
case BC_RESULT_TEMP:
case BC_RESULT_IBASE:
case BC_RESULT_SCALE:
case BC_RESULT_OBASE:
#if BC_ENABLE_EXTRA_MATH
case BC_RESULT_SEED:
#endif // BC_ENABLE_EXTRA_MATH
{
bc_num_createCopy(&d->d.n, &src->d.n);
break;
}
case BC_RESULT_VAR:
case BC_RESULT_ARRAY:
case BC_RESULT_ARRAY_ELEM:
{
memcpy(&d->d.loc, &src->d.loc, sizeof(BcLoc));
break;
}
case BC_RESULT_STR:
{
memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
break;
}
case BC_RESULT_ZERO:
case BC_RESULT_ONE:
{
// Do nothing.
break;
}
#if BC_ENABLED
case BC_RESULT_VOID:
case BC_RESULT_LAST:
{
#ifndef NDEBUG
// We should *never* try copying either of these.
abort();
#endif // NDEBUG
}
#endif // BC_ENABLED
}
}
#endif // DC_ENABLED
void bc_result_free(void *result) {
BcResult *r = (BcResult*) result;
BC_SIG_ASSERT_LOCKED;
assert(r != NULL);
switch (r->t) {
case BC_RESULT_TEMP:
case BC_RESULT_IBASE:
case BC_RESULT_SCALE:
case BC_RESULT_OBASE:
#if BC_ENABLE_EXTRA_MATH
case BC_RESULT_SEED:
#endif // BC_ENABLE_EXTRA_MATH
{
bc_num_free(&r->d.n);
break;
}
case BC_RESULT_VAR:
case BC_RESULT_ARRAY:
case BC_RESULT_ARRAY_ELEM:
case BC_RESULT_STR:
case BC_RESULT_ZERO:
case BC_RESULT_ONE:
#if BC_ENABLED
case BC_RESULT_VOID:
case BC_RESULT_LAST:
#endif // BC_ENABLED
{
// Do nothing.
break;
}
}
}