/**
 * @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 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, &copy));
    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, &copy));
    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, &copy), 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;
}
