124 lines
4.3 KiB
C
124 lines
4.3 KiB
C
|
|
/**
|
||
|
|
* @file variable_subscript.c
|
||
|
|
* @brief Tests variable storage, typing by suffix, and subscript arithmetic.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
#include <akbasic/error.h>
|
||
|
|
#include <akbasic/variable.h>
|
||
|
|
|
||
|
|
#include "testutil.h"
|
||
|
|
|
||
|
|
static akbasic_ValuePool POOL;
|
||
|
|
static akbasic_Variable VAR;
|
||
|
|
|
||
|
|
static void name_it(const char *name)
|
||
|
|
{
|
||
|
|
memset(&VAR, 0, sizeof(VAR));
|
||
|
|
strncpy(VAR.name, name, sizeof(VAR.name) - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
akbasic_Value *slot = NULL;
|
||
|
|
akerr_ErrorContext *e = NULL;
|
||
|
|
int64_t sizes1[1] = { 5 };
|
||
|
|
int64_t sizes2[2] = { 8, 8 };
|
||
|
|
int64_t sizes3[3] = { 2, 3, 4 };
|
||
|
|
int64_t at[3] = { 0, 0, 0 };
|
||
|
|
|
||
|
|
TEST_REQUIRE_OK(akbasic_error_register());
|
||
|
|
TEST_REQUIRE_OK(akbasic_valuepool_init(&POOL));
|
||
|
|
|
||
|
|
/* The suffix picks the type. Note % is float here, inverting Commodore. */
|
||
|
|
name_it("A#");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes1, 1));
|
||
|
|
TEST_REQUIRE_INT(VAR.valuetype, AKBASIC_TYPE_INTEGER);
|
||
|
|
TEST_REQUIRE_INT(VAR.valuecount, 5);
|
||
|
|
|
||
|
|
name_it("A%");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes1, 1));
|
||
|
|
TEST_REQUIRE_INT(VAR.valuetype, AKBASIC_TYPE_FLOAT);
|
||
|
|
|
||
|
|
name_it("A$");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes1, 1));
|
||
|
|
TEST_REQUIRE_INT(VAR.valuetype, AKBASIC_TYPE_STRING);
|
||
|
|
|
||
|
|
/* One dimension: round-trip every element. */
|
||
|
|
name_it("B#");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes1, 1));
|
||
|
|
for ( at[0] = 0; at[0] < 5; at[0]++ ) {
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_set_integer(&VAR, at[0] * 10, at, 1));
|
||
|
|
}
|
||
|
|
for ( at[0] = 0; at[0] < 5; at[0]++ ) {
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_get_subscript(&VAR, at, 1, &slot));
|
||
|
|
TEST_REQUIRE_INT(slot->intval, at[0] * 10);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Two dimensions, the shape array_multidimensional.bas uses. */
|
||
|
|
name_it("C#");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes2, 2));
|
||
|
|
TEST_REQUIRE_INT(VAR.valuecount, 64);
|
||
|
|
at[0] = 0; at[1] = 7;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_set_integer(&VAR, 31337, at, 2));
|
||
|
|
at[0] = 1; at[1] = 7;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_set_integer(&VAR, 65535, at, 2));
|
||
|
|
at[0] = 0; at[1] = 7;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_get_subscript(&VAR, at, 2, &slot));
|
||
|
|
TEST_REQUIRE_INT(slot->intval, 31337);
|
||
|
|
at[0] = 1; at[1] = 7;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_get_subscript(&VAR, at, 2, &slot));
|
||
|
|
TEST_REQUIRE_INT(slot->intval, 65535);
|
||
|
|
|
||
|
|
/* Three dimensions, to prove the flattening multiplier walks correctly. */
|
||
|
|
name_it("D#");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes3, 3));
|
||
|
|
TEST_REQUIRE_INT(VAR.valuecount, 24);
|
||
|
|
at[0] = 1; at[1] = 2; at[2] = 3;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_set_integer(&VAR, 99, at, 3));
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_get_subscript(&VAR, at, 3, &slot));
|
||
|
|
TEST_REQUIRE_INT(slot->intval, 99);
|
||
|
|
at[0] = 0; at[1] = 0; at[2] = 0;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_get_subscript(&VAR, at, 3, &slot));
|
||
|
|
TEST_REQUIRE_INT(slot->intval, 0);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* The out-of-bounds message is compared with strcmp, not merely for status:
|
||
|
|
* tests/language/array_outofbounds.txt reproduces it verbatim, so a reworded
|
||
|
|
* message is a failing golden case.
|
||
|
|
*/
|
||
|
|
name_it("A#");
|
||
|
|
sizes1[0] = 3;
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes1, 1));
|
||
|
|
at[0] = 4;
|
||
|
|
e = akbasic_variable_get_subscript(&VAR, at, 1, &slot);
|
||
|
|
TEST_REQUIRE(e != NULL, "an out-of-bounds subscript must raise");
|
||
|
|
if ( e != NULL ) {
|
||
|
|
TEST_REQUIRE_INT(e->status, AKBASIC_ERR_BOUNDS);
|
||
|
|
TEST_REQUIRE_STR(e->message,
|
||
|
|
"Variable index access out of bounds at dimension 0: 4 (max 2)");
|
||
|
|
test_discard_error(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Wrong subscript count. */
|
||
|
|
name_it("E#");
|
||
|
|
TEST_REQUIRE_OK(akbasic_variable_init(&VAR, &POOL, sizes2, 2));
|
||
|
|
at[0] = 0;
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_variable_get_subscript(&VAR, at, 1, &slot), AKBASIC_ERR_BOUNDS);
|
||
|
|
|
||
|
|
/* A dimension must be positive. */
|
||
|
|
name_it("F#");
|
||
|
|
sizes1[0] = 0;
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_variable_init(&VAR, &POOL, sizes1, 1), AKBASIC_ERR_VALUE);
|
||
|
|
sizes1[0] = -1;
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_variable_init(&VAR, &POOL, sizes1, 1), AKBASIC_ERR_VALUE);
|
||
|
|
|
||
|
|
/* An unnamed variable has no type to derive. */
|
||
|
|
memset(&VAR, 0, sizeof(VAR));
|
||
|
|
sizes1[0] = 1;
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_variable_init(&VAR, &POOL, sizes1, 1), AKBASIC_ERR_VALUE);
|
||
|
|
|
||
|
|
return akbasic_test_failures;
|
||
|
|
}
|