/** * @file variable_subscript.c * @brief Tests variable storage, typing by suffix, and subscript arithmetic. */ #include #include #include #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; }