106 lines
4.5 KiB
C
106 lines
4.5 KiB
C
|
|
/**
|
||
|
|
* @file grammar_leaves.c
|
||
|
|
* @brief Tests AST leaf construction, rendering and deep cloning.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
#include <akbasic/error.h>
|
||
|
|
#include <akbasic/grammar.h>
|
||
|
|
|
||
|
|
#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 selects base 8, so 010 is 8 and 08 will not parse.
|
||
|
|
* Commodore BASIC has no octal literals; this is TODO.md section 12 item 10,
|
||
|
|
* reproduced deliberately and pinned here so the eventual fix is a
|
||
|
|
* deliberate change to this assertion.
|
||
|
|
*/
|
||
|
|
TEST_REQUIRE_OK(akbasic_leaf_new_literal_int(&LEAVES[1], "010"));
|
||
|
|
TEST_REQUIRE_INT(LEAVES[1].literal_int, 8);
|
||
|
|
TEST_REQUIRE_ANY_ERROR(akbasic_leaf_new_literal_int(&LEAVES[1], "08"));
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|