197 lines
7.4 KiB
C
197 lines
7.4 KiB
C
|
|
/**
|
||
|
|
* @file runtime_evaluate.c
|
||
|
|
* @brief Tests the evaluator against hand-built and hand-parsed trees.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "harness.h"
|
||
|
|
|
||
|
|
/* Parse an expression-bearing line and evaluate what it produced. */
|
||
|
|
static akerr_ErrorContext AKERR_NOIGNORE *eval_line(const char *line, akbasic_Value **dest)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(errctx);
|
||
|
|
akbasic_ASTLeaf *leaf = NULL;
|
||
|
|
|
||
|
|
PASS(errctx, akbasic_environment_zero(HARNESS_RUNTIME.environment));
|
||
|
|
PASS(errctx, harness_parse(line, &leaf));
|
||
|
|
PASS(errctx, akbasic_runtime_evaluate(&HARNESS_RUNTIME, leaf, dest));
|
||
|
|
SUCCEED_RETURN(errctx);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void expect_int(const char *line, int64_t want)
|
||
|
|
{
|
||
|
|
akbasic_Value *out = NULL;
|
||
|
|
akerr_ErrorContext *e = eval_line(line, &out);
|
||
|
|
|
||
|
|
if ( e != NULL ) {
|
||
|
|
fprintf(stderr, "FAIL: \"%s\": %s\n", line, e->message);
|
||
|
|
akbasic_test_failures += 1;
|
||
|
|
test_discard_error(e);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
TEST_REQUIRE(out->valuetype == AKBASIC_TYPE_INTEGER,
|
||
|
|
"\"%s\" produced type %d, expected integer", line, (int)out->valuetype);
|
||
|
|
TEST_REQUIRE(out->intval == want,
|
||
|
|
"\"%s\" produced %lld, expected %lld",
|
||
|
|
line, (long long)out->intval, (long long)want);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void expect_bool(const char *line, bool want)
|
||
|
|
{
|
||
|
|
akbasic_Value *out = NULL;
|
||
|
|
akerr_ErrorContext *e = eval_line(line, &out);
|
||
|
|
|
||
|
|
if ( e != NULL ) {
|
||
|
|
fprintf(stderr, "FAIL: \"%s\": %s\n", line, e->message);
|
||
|
|
akbasic_test_failures += 1;
|
||
|
|
test_discard_error(e);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
TEST_REQUIRE(akbasic_value_is_true(out) == want,
|
||
|
|
"\"%s\" expected %s", line, (want ? "true" : "false"));
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
akbasic_Value *out = NULL;
|
||
|
|
akerr_ErrorContext *e = NULL;
|
||
|
|
|
||
|
|
TEST_REQUIRE_OK(harness_start(NULL));
|
||
|
|
|
||
|
|
/* Literals and arithmetic. */
|
||
|
|
expect_int("A# = 42", 42);
|
||
|
|
expect_int("A# = 1 + 2 * 3", 7);
|
||
|
|
expect_int("A# = (1 + 2) * 3", 9);
|
||
|
|
expect_int("A# = -5 + 10", 5);
|
||
|
|
expect_int("A# = 8 / 2", 4);
|
||
|
|
|
||
|
|
/* Identifiers read back what was assigned. */
|
||
|
|
expect_int("A# = 7", 7);
|
||
|
|
expect_int("B# = A# + 1", 8);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Comparisons yield BASIC booleans. They are evaluated bare rather than
|
||
|
|
* assigned: assign() accepts only INTEGER or FLOAT into a `#` variable, so
|
||
|
|
* `A# = 1 == 1` is "Incompatible types in variable assignment" -- which is
|
||
|
|
* also why IF works on the comparison directly and never through a variable.
|
||
|
|
*
|
||
|
|
* They are wrapped in parens because a bare leading integer in token
|
||
|
|
* position 0 is consumed as a *line number*, not as a literal.
|
||
|
|
*/
|
||
|
|
expect_bool("(1 == 1)", true);
|
||
|
|
expect_bool("(1 == 2)", false);
|
||
|
|
expect_bool("(2 > 1)", true);
|
||
|
|
expect_bool("(1 <> 2)", true);
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_zero(HARNESS_RUNTIME.environment));
|
||
|
|
TEST_REQUIRE_STATUS(eval_line("A# = 1 == 1", &out), AKBASIC_ERR_TYPE);
|
||
|
|
|
||
|
|
/* Bitwise operators. */
|
||
|
|
expect_int("A# = 12 AND 10", 8);
|
||
|
|