/* * Evaluation tests * (C) 2006, Pascal Schmidt * see file ../doc/LICENSE for license */ #include #include #include "test.h" #include "../libparser/parser.h" #include "../libeval/eval.h" /* * Dummy function */ value *myfunc(arena_state *s, unsigned int argc, value **argv) { return value_make_int((argc + (int) argv)*0 + 21); } void eval_test(void) { arena_state *s = state_alloc(); expr ex, ex2, vari, varj, five, init, guard; expr etrue, efalse, zero, one, two, three, eincj; expr *exlist = { &five }; stmt st, strue, sfalse, sincj; value *val, *val2, *val3; signature *sig; symtab_entry *entry; stmt_list *slist; /* add myfunc to symbol table */ sig = call_sig_builtin("myfunc", 0, "", myfunc); symtab_stack_init(s); symtab_stack_enter(s); symtab_stack_add_function(s, "func", sig); /* * initialize some expressions and statements */ vari.type = EXPR_REF; vari.name = "i"; varj.type = EXPR_REF; varj.name = "j"; zero.type = EXPR_CONST_INT; zero.name = "0"; three.type = EXPR_CONST_INT; three.name = "3"; five.type = EXPR_CONST_INT; five.name = "5"; init.type = EXPR_ASSIGN; init.name = "i"; init.inner = &zero; guard.type = EXPR_POSTFIX; guard.op = OPTYPE_POSTINC; guard.inner = &vari; eincj.type = EXPR_POSTFIX; eincj.op = OPTYPE_POSTINC; eincj.inner = &varj; sincj.type = STMT_EXPR; sincj.expr = &eincj; test_banner("libeval"); test_section("simple expressions"); /* constant bool true should yield true */ test_start("eval_expr (true)"); ex.type = EXPR_CONST_BOOL; ex.name = "true"; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_BOOL && val->value_u.bool_val == 1); value_free(val); /* constant bool false should yield false */ test_start("eval_expr (false)"); ex.name = "false"; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_BOOL && val->value_u.bool_val == 0); value_free(val); /* constant int should yield identical int value */ test_start("eval_expr (42)"); ex.type = EXPR_CONST_INT; ex.name = "42"; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 42); symtab_stack_add_variable(s, "i", val); value_free(val); /* constant float should yield identical float value */ test_start("eval_expr (4.2)"); ex.type = EXPR_CONST_FLOAT; ex.name = "4.2"; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_FLOAT && val->value_u.float_val == 4.2); value_free(val); /* constant string should yield identical string value */ test_start("eval_expr (hello)"); ex.type = EXPR_CONST_STRING; ex.name = "hello"; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_STRING && val->value_u.string_val.len == 5 && strcmp(val->value_u.string_val.value, ex.name) == 0); value_free(val); /* casting 5 to string should yield "5" */ test_start("eval_expr (cast)"); ex.type = EXPR_CAST; ex.name = "string"; ex.inner = &five; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_STRING && val->value_u.string_val.len == 1 && strcmp(val->value_u.string_val.value, "5") == 0); value_free(val); /* calling user defined function should yield its result */ test_start("eval_expr (call)"); ex.type = EXPR_CALL; ex.name = "func"; ex.argc = 0; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 21); value_free(val); /* variable reference should return previously stored value */ test_start("eval_expr (ref)"); ex.type = EXPR_REF; ex.name = "i"; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 42); value_free(val); /* store int value 420 at a[5] */ val = value_make_array(); val2 = value_make_int(420); value_set_array(val, 5, val2); value_free(val2); symtab_stack_add_variable(s, "a", val); value_free(val); /* a[5] should yield value previously stored at that index */ test_start("eval_expr (ref array)"); ex.type = EXPR_REF_ARRAY; ex.name = "a"; ex.argc = 1; ex.argv = &exlist; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 420); value_free(val); /* assignment i=5 should make i contain the value 5 */ test_start("eval_expr (assign)"); ex.type = EXPR_ASSIGN; ex.name = "i"; ex.inner = &five; val = eval_expr(s, &ex); entry = symtab_stack_lookup(s, "i"); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 5 && entry && entry->type == SYMTAB_ENTRY_VAR && entry->entry_u.var->type == VALUE_TYPE_INT && entry->entry_u.var->value_u.int_val == 5); value_free(val); /* assignment a[5]=5 should make a[5] yield 5 */ test_start("eval_expr (assign array)"); ex.type = EXPR_ASSIGN_ARRAY; ex.name = "a"; ex.argc = 1; ex.argv = &exlist; ex.inner = &five; val = eval_expr(s, &ex); ex.type = EXPR_REF_ARRAY; val2 = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_INT && val2->value_u.int_val == 5 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 5); value_free(val); value_free(val2); /* nested assignment should assign to both variables */ test_start("eval_expr (nested assign)"); ex.type = EXPR_ASSIGN; ex.name = "i"; ex.inner = &ex2; ex2.type = EXPR_ASSIGN; ex2.name = "j"; ex2.inner = &three; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); val3 = eval_expr(s, &varj); test_assert(val && val->type == VALUE_TYPE_INT && val2->value_u.int_val == 3 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 3 && val3 && val3->type == VALUE_TYPE_INT && val3->value_u.int_val == 3); value_free(val); value_free(val2); value_free(val3); /* multiplication of 5 by 5 should yield 25 */ test_start("eval_expr (infix)"); ex.type = EXPR_INFIX; ex.inner = ex.index = &five; ex.op = OPTYPE_MUL; val = eval_expr(s, &ex); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 25); value_free(val); test_section("mixfix expressions"); symtab_stack_add_variable(s, "i", value_make_int(42)); /* preincrement value should match variable value */ test_start("eval_expr (preinc)"); ex.type = EXPR_PREFIX; ex.inner = &vari; ex.op = OPTYPE_PREINC; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 43 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 43); value_free(val); value_free(val2); /* predecrement value should match variable value */ test_start("eval_expr (predec)"); ex.type = EXPR_PREFIX; ex.inner = &vari; ex.op = OPTYPE_PREDEC; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 42 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 42); value_free(val); value_free(val2); /* unary minus should change sign of variable value */ test_start("eval_expr (unary minus)"); ex.type = EXPR_PREFIX; ex.inner = &vari; ex.op = OPTYPE_MINUS; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == -42 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 42); value_free(val); value_free(val2); /* bitwise negation */ test_start("eval_expr (bit negation)"); ex.type = EXPR_PREFIX; ex.inner = &vari; ex.op = OPTYPE_NEG; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == -43 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 42); value_free(val); value_free(val2); /* postincrement should change variable after returning original value */ test_start("eval_expr (postinc)"); ex.type = EXPR_POSTFIX; ex.inner = &vari; ex.op = OPTYPE_POSTINC; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 42 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 43); value_free(val); value_free(val2); /* postdecrement should change variable after returning original value */ test_start("eval_expr (postdec)"); ex.type = EXPR_POSTFIX; ex.inner = &vari; ex.op = OPTYPE_POSTDEC; val = eval_expr(s, &ex); val2 = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 43 && val2 && val2->type == VALUE_TYPE_INT && val2->value_u.int_val == 42); value_free(val); value_free(val2); test_section("simple statements"); /* empty expression should run okay */ test_start("eval_stmt (nop)"); st.type = STMT_NOP; eval_stmt(s, &st, 0); test_assert(1); /* expression statement should run okay */ test_start("eval_stmt (expr)"); st.type = STMT_EXPR; st.expr = &ex; eval_stmt(s, &st, 0); test_assert(1); /* * initialize some extra expressions and statements */ symtab_stack_add_variable(s, "i", value_make_int(6)); ex.type = EXPR_INFIX; ex.inner = &vari; ex.index = &five; ex.op = OPTYPE_SMALLER; one.type = EXPR_CONST_INT; one.name = "1"; two.type = EXPR_CONST_INT; two.name = "2"; etrue.type = EXPR_ASSIGN; etrue.name = "i"; etrue.inner = &one; efalse.type = EXPR_ASSIGN; efalse.name = "i"; efalse.inner = &two; strue.type = STMT_EXPR; strue.expr = &etrue; sfalse.type = STMT_EXPR; sfalse.expr = &efalse; /* if without else and false condition, true block is i=1 */ test_start("eval_stmt (if false)"); st.type = STMT_IF; st.expr = &ex; st.true_case = &strue; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 6); value_free(val); /* if without else and true condition, true block is i=1 */ test_start("eval_stmt (if true)"); ex.op = OPTYPE_LARGER; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 1); value_free(val); /* if with else and false condition, false block is i=2 */ test_start("eval_stmt (if_else false)"); st.type = STMT_IF_ELSE; st.false_case = &sfalse; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 2); value_free(val); /* if with else and true condition, true block is i=1 */ test_start("eval_stmt (if_else true)"); ex.op = OPTYPE_SMALLER; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 1); value_free(val); /* while block that should run four times, changing i from 1 to 5 */ test_start("eval_stmt (while)"); eval_expr(s, &etrue); ex.type = EXPR_INFIX; ex.op = OPTYPE_SMALLER; ex.inner = &vari; ex.index = &five; sfalse.type = STMT_EXPR; sfalse.expr = &guard; st.type = STMT_WHILE; st.expr = &ex; st.true_case = &sfalse; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 5); value_free(val); /* do block with false condition executes once */ test_start("eval_stmt (do)"); st.type = STMT_DO; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 6); value_free(val); /* for block executing 5 times j++ changes j from 0 to 5 */ test_start("eval_stmt (for)"); symtab_stack_add_variable(s, "j", value_make_int(0)); st.type = STMT_FOR; st.init = &init; st.expr = &ex; st.guard = &guard; st.true_case = &sincj; eval_stmt(s, &st, 0); val = eval_expr(s, &varj); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 5); value_free(val); /* for block with break causes counter to stay at init value */ test_start("eval_stmt (break)"); sincj.type = STMT_BREAK; eval_stmt(s, &st, 0); val = eval_expr(s, &vari); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 0); value_free(val); /* for block with continue and j++ must not change j */ test_start("eval_stmt (continue)"); symtab_stack_add_variable(s, "j", value_make_int(0)); sincj.type = STMT_EXPR; sfalse.type = STMT_CONTINUE; slist = stmt_list_alloc(); stmt_list_push(slist, &sfalse); stmt_list_push(slist, &sincj); strue.type = STMT_BLOCK; strue.block = (STMT_LIST *) slist; st.true_case = &strue; eval_stmt(s, &st, 0); val = eval_expr(s, &varj); test_assert(val && val->type == VALUE_TYPE_INT && val->value_u.int_val == 0); value_free(val); symtab_stack_leave(s); symtab_stack_teardown(s); state_free(s); }