/** * @file grammar_leaves.c * @brief Tests AST leaf construction, rendering and deep cloning. */ #include #include #include #include "testutil.h" static akbasic_ASTLeaf LEAVES[8]; static akbasic_ASTLeaf STORAGE[AKBASIC_MAX_LEAVES]; static akbasic_LeafPool POOL; int main(void) { akbasic_ASTLeaf *copy = NULL; char rendered[AKBASIC_MAX_STRING_LENGTH]; TEST_REQUIRE_OK(akbasic_error_register()); POOL.next = 0; POOL.capacity = AKBASIC_MAX_LEAVES; POOL.leaves = STORAGE; /* Literals render the way PRINT renders them. */ TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[0], "42")); TEST_REQUIRE_INT(LEAVES[0].literal_int, 42); TEST_REQUIRE_OK(akbasic_leaf_to_string(&LEAVES[0], rendered, sizeof(rendered))); TEST_REQUIRE_STR(rendered, "42"); /* 0x is hex. */ TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[1], "0xff")); TEST_REQUIRE_INT(LEAVES[1].literal_int, 255); /* * A leading zero is padding, not a radix -- TODO.md section 6 item 10's * regression test. The reference selects base 8 for any lexeme starting with * '0' (basicgrammar.go:224), so 010 was 8 and 08 would not parse at all. * Commodore BASIC has no octal literals. */ TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[1], "010")); TEST_REQUIRE_INT(LEAVES[1].literal_int, 10); TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[1], "08")); TEST_REQUIRE_INT(LEAVES[1].literal_int, 8); TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[1], "0")); TEST_REQUIRE_INT(LEAVES[1].literal_int, 0); /* 0x still selects base 16, which is the one prefix that means anything. */ TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[1], "0x10")); TEST_REQUIRE_INT(LEAVES[1].literal_int, 16); TEST_REQUIRE_OK(akbasic_leaf_new_literal_float(&LEAVES[2], "2.5")); TEST_REQUIRE_FEQ(LEAVES[2].literal_float, 2.5); TEST_REQUIRE_OK(akbasic_leaf_to_string(&LEAVES[2], rendered, sizeof(rendered))); TEST_REQUIRE_STR(rendered, "2.500000"); TEST_REQUIRE_OK(akbasic_leaf_new_literal_string(&LEAVES[3], "HELLO")); TEST_REQUIRE_STR(LEAVES[3].literal_string, "HELLO"); /* Identifier predicates. */ TEST_REQUIRE_OK(akbasic_leaf_new_identifier(&LEAVES[4], AKBASIC_LEAF_IDENTIFIER_INT, "A#")); TEST_REQUIRE(akbasic_leaf_is_identifier(&LEAVES[4]), "A# should be an identifier"); TEST_REQUIRE(!akbasic_leaf_is_literal(&LEAVES[4]), "A# is not a literal"); TEST_REQUIRE(akbasic_leaf_is_literal(&LEAVES[0]), "42 should be a literal"); TEST_REQUIRE(!akbasic_leaf_is_identifier(NULL), "NULL is not an identifier"); /* Binary and unary render in prefix form. */ TEST_REQUIRE_OK(akbasic_leaf_new_binary(&LEAVES[5], &LEAVES[4], AKBASIC_TOK_PLUS, &LEAVES[0])); TEST_REQUIRE_OK(akbasic_leaf_to_string(&LEAVES[5], rendered, sizeof(rendered))); TEST_REQUIRE_STR(rendered, "(+ A# 42)"); TEST_REQUIRE_OK(akbasic_leaf_new_unary(&LEAVES[6], AKBASIC_TOK_MINUS, &LEAVES[0])); TEST_REQUIRE_OK(akbasic_leaf_to_string(&LEAVES[6], rendered, sizeof(rendered))); TEST_REQUIRE_STR(rendered, "(- 42)"); TEST_REQUIRE_OK(akbasic_leaf_new_grouping(&LEAVES[7], &LEAVES[5])); TEST_REQUIRE_OK(akbasic_leaf_to_string(&LEAVES[7], rendered, sizeof(rendered))); TEST_REQUIRE_STR(rendered, "(group (+ A# 42))"); /* A deep clone copies the whole tree into pool storage. */ TEST_REQUIRE_OK(akbasic_leaf_clone(&LEAVES[7], &POOL, ©)); TEST_REQUIRE(copy != NULL && copy != &LEAVES[7], "clone must be a distinct leaf"); TEST_REQUIRE(copy->expr != NULL && copy->expr != &LEAVES[5], "clone must be deep"); TEST_REQUIRE(copy->expr->left != NULL && copy->expr->left != &LEAVES[4], "clone must recurse into .left"); TEST_REQUIRE_OK(akbasic_leaf_to_string(copy, rendered, sizeof(rendered))); TEST_REQUIRE_STR(rendered, "(group (+ A# 42))"); /* Cloning NULL yields NULL, not an error: the reference does the same. */ TEST_REQUIRE_OK(akbasic_leaf_clone(NULL, &POOL, ©)); TEST_REQUIRE(copy == NULL, "cloning NULL must yield NULL"); /* Pool exhaustion is diagnosed, not a buffer overrun. */ POOL.next = POOL.capacity; TEST_REQUIRE_STATUS(akbasic_leaf_clone(&LEAVES[7], &POOL, ©), AKBASIC_ERR_BOUNDS); /* Constructors reject the nil arguments the reference rejects. */ TEST_REQUIRE_STATUS(akbasic_leaf_new_binary(&LEAVES[0], NULL, AKBASIC_TOK_PLUS, &LEAVES[1]), AKERR_NULLPOINTER); TEST_REQUIRE_STATUS(akbasic_leaf_new_unary(&LEAVES[0], AKBASIC_TOK_MINUS, NULL), AKERR_NULLPOINTER); TEST_REQUIRE_STATUS(akbasic_leaf_new_grouping(&LEAVES[0], NULL), AKERR_NULLPOINTER); TEST_REQUIRE_STATUS(akbasic_leaf_new_branch(&LEAVES[0], NULL, NULL, NULL), AKERR_NULLPOINTER); TEST_REQUIRE_STATUS(akbasic_leaf_new_comparison(&LEAVES[0], &LEAVES[1], AKBASIC_TOK_PLUS, &LEAVES[2]), AKBASIC_ERR_SYNTAX); return akbasic_test_failures; }