From 48072f0dd188567ffc0a957d781cc269ecf3fc22 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Tue, 7 Apr 2026 20:06:31 +1000 Subject: [PATCH] AST test is passing --- source/toy_ast.h | 2 +- source/toy_compiler.c | 2 +- tests/units/test_ast.c | 491 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 490 insertions(+), 5 deletions(-) diff --git a/source/toy_ast.h b/source/toy_ast.h index 249fb36..c57fb76 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -185,9 +185,9 @@ typedef struct Toy_AstPrint { typedef struct Toy_AstVarDeclare { Toy_AstType type; Toy_String* name; + Toy_Ast* expr; Toy_ValueType valueType; bool constant; - Toy_Ast* expr; } Toy_AstVarDeclare; typedef struct Toy_AstVarAssign { diff --git a/source/toy_compiler.c b/source/toy_compiler.c index b943ffb..d82f56b 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -795,7 +795,7 @@ static unsigned int writeInstructionVarDeclare(Toy_Bytecode** mb, Toy_AstVarDecl static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment) { unsigned int result = 0; - //target is a name string + //target is a variable name if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) { //name string Toy_String* target = TOY_VALUE_AS_STRING(ast.target->value.value); diff --git a/tests/units/test_ast.c b/tests/units/test_ast.c index 934dfe3..9df3803 100644 --- a/tests/units/test_ast.c +++ b/tests/units/test_ast.c @@ -2,8 +2,493 @@ #include "toy_console_colors.h" #include +#include + +#if TOY_BITNESS == 32 + +#define TEST_SIZEOF(type, bit32, bit64) \ + if (sizeof(type) != bit32) { \ + fprintf(stderr, TOY_CC_ERROR "ERROR: sizeof(" #type ") is %d, expected %d (bitness %d)\n" TOY_CC_RESET, (int)sizeof(type), bit32, TOY_BITNESS); \ + ++err; \ + } + +#elif TOY_BITNESS == 64 + +#define TEST_SIZEOF(type, bit32, bit64) \ + if (sizeof(type) != bit64) { \ + fprintf(stderr, TOY_CC_ERROR "ERROR: sizeof(" #type ") is %d, expected %d (bitness %d)\n" TOY_CC_RESET, (int)sizeof(type), bit64, TOY_BITNESS); \ + ++err; \ + } + +#else + +#pragma message("Unable to test the size of Toy_Ast members, as TOY_BITNESS is not recognized") +#define TEST_SIZEOF(type, bit32, bit64) + +#endif + +int test_sizeof_ast(void) { + int err = 0; + + //run for each type + TEST_SIZEOF(Toy_AstType, 4 , 4); + TEST_SIZEOF(Toy_AstBlock, 20 , 32); + TEST_SIZEOF(Toy_AstValue, 12 , 24); + TEST_SIZEOF(Toy_AstUnary, 12 , 16); + TEST_SIZEOF(Toy_AstBinary, 16 , 24); + TEST_SIZEOF(Toy_AstBinaryShortCircuit, 16 , 24); + TEST_SIZEOF(Toy_AstCompare, 16 , 24); + TEST_SIZEOF(Toy_AstGroup, 8 , 16); + TEST_SIZEOF(Toy_AstCompound, 12 , 16); + TEST_SIZEOF(Toy_AstAggregate, 16 , 24); + TEST_SIZEOF(Toy_AstAssert, 12 , 24); + TEST_SIZEOF(Toy_AstIfThenElse, 16 , 32); + TEST_SIZEOF(Toy_AstWhileThen, 12 , 24); + TEST_SIZEOF(Toy_AstBreak, 4 , 4); + TEST_SIZEOF(Toy_AstContinue, 4 , 4); + TEST_SIZEOF(Toy_AstReturn, 8 , 16); + TEST_SIZEOF(Toy_AstPrint, 8 , 16); + TEST_SIZEOF(Toy_AstVarDeclare, 16 , 32); + TEST_SIZEOF(Toy_AstVarAssign, 16 , 24); + TEST_SIZEOF(Toy_AstVarAccess, 8 , 16); + TEST_SIZEOF(Toy_AstFnDeclare, 16 , 32); + TEST_SIZEOF(Toy_AstFnInvoke, 12 , 24); + TEST_SIZEOF(Toy_AstPass, 4 , 4); + TEST_SIZEOF(Toy_AstError, 4 , 4); + TEST_SIZEOF(Toy_AstEnd, 4 , 4); + TEST_SIZEOF(Toy_Ast, 20 , 32); + + return -err; +} + +#undef TEST_SIZEOF + +int test_type_emission(Toy_Bucket** bucketHandle) { + //emit value + { + //emit to an AST + Toy_Ast* ast = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->value.value) != 42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a value as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit unary + { + //build the AST + Toy_Ast* ast = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstUnary(bucketHandle, &ast, TOY_AST_FLAG_NEGATE); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_UNARY || + ast->unary.flag != TOY_AST_FLAG_NEGATE || + ast->unary.child->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->unary.child->value.value) != 42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a unary as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit binary + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BINARY || + ast->binary.flag != TOY_AST_FLAG_ADD || + ast->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->binary.left->value.value) != 42 || + ast->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->binary.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a binary as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit compare + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); //technically, not a valid value + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstCompare(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_COMPARE || + ast->compare.flag != TOY_AST_FLAG_ADD || + ast->compare.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->compare.left->value.value) != 42 || + ast->compare.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->compare.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a compare as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit group + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstGroup(bucketHandle, &ast); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_GROUP || + ast->group.child == NULL || + ast->group.child->type != TOY_AST_BINARY || + ast->group.child->binary.flag != TOY_AST_FLAG_ADD || + ast->group.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->group.child->binary.left->value.value) != 42 || + ast->group.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->group.child->binary.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a group as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit aggregate + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstAggregate(bucketHandle, &ast, TOY_AST_FLAG_COLLECTION, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_AGGREGATE || + + ast->aggregate.left == NULL || + ast->aggregate.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->aggregate.left->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->aggregate.left->value.value) != 42 || + + ast->aggregate.right == NULL || + ast->aggregate.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->aggregate.right->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->aggregate.right->value.value) != 69 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an aggregate as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit keyword assert + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* child = NULL; + Toy_Ast* msg = NULL; + + Toy_private_emitAstValue(bucketHandle, &child, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &msg, TOY_VALUE_FROM_INTEGER(69)); + + Toy_private_emitAstAssert(bucketHandle, &ast, child, msg); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_ASSERT || + ast->assert.child == NULL || + ast->assert.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->assert.child->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->assert.child->value.value) != 42 || + + ast->assert.message == NULL || + ast->assert.message->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->assert.message->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->assert.message->value.value) != 69 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'assert' as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit keyword if-then + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* condBranch = NULL; + Toy_Ast* thenBranch = NULL; + + Toy_private_emitAstValue(bucketHandle, &condBranch, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &thenBranch, TOY_VALUE_FROM_INTEGER(69)); + + Toy_private_emitAstIfThenElse(bucketHandle, &ast, condBranch, thenBranch, NULL); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_IF_THEN_ELSE || + ast->ifThenElse.condBranch == NULL || + ast->ifThenElse.condBranch->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->ifThenElse.condBranch->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->ifThenElse.condBranch->value.value) != 42 || + + ast->ifThenElse.thenBranch == NULL || + ast->ifThenElse.thenBranch->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->ifThenElse.thenBranch->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->ifThenElse.thenBranch->value.value) != 69 || + + ast->ifThenElse.elseBranch != NULL || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'if-then' as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit keyword if-then-else + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* condBranch = NULL; + Toy_Ast* thenBranch = NULL; + Toy_Ast* elseBranch = NULL; + + Toy_private_emitAstValue(bucketHandle, &condBranch, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &thenBranch, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstValue(bucketHandle, &elseBranch, TOY_VALUE_FROM_INTEGER(420)); + + Toy_private_emitAstIfThenElse(bucketHandle, &ast, condBranch, thenBranch, elseBranch); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_IF_THEN_ELSE || + ast->ifThenElse.condBranch == NULL || + ast->ifThenElse.condBranch->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->ifThenElse.condBranch->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->ifThenElse.condBranch->value.value) != 42 || + + ast->ifThenElse.thenBranch == NULL || + ast->ifThenElse.thenBranch->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->ifThenElse.thenBranch->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->ifThenElse.thenBranch->value.value) != 69 || + + ast->ifThenElse.elseBranch == NULL || + ast->ifThenElse.elseBranch->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->ifThenElse.elseBranch->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->ifThenElse.elseBranch->value.value) != 420 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'if-then-else' as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit keyword print + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstPrint(bucketHandle, &ast); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_PRINT || + ast->print.child == NULL || + ast->print.child->type != TOY_AST_BINARY || + ast->print.child->binary.flag != TOY_AST_FLAG_ADD || + ast->print.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->print.child->binary.left->value.value) != 42 || + ast->print.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->print.child->binary.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'print' as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit var declare + { + //build the AST + Toy_Ast* ast = NULL; + Toy_String* name = Toy_createStringLength(bucketHandle, "foobar", 6); + + Toy_private_emitAstVariableDeclaration(bucketHandle, &ast, name, TOY_VALUE_ANY, NULL, false); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_VAR_DECLARE || + + ast->varDeclare.name == NULL || + ast->varDeclare.name->info.type != TOY_STRING_LEAF || + strcmp(ast->varDeclare.name->leaf.data, "foobar") != 0 || + + ast->varDeclare.expr != NULL) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a var declare as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + Toy_freeString(name); + return -1; + } + + //cleanup + Toy_freeString(name); + } + + //emit assign + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_String* name = Toy_createStringLength(bucketHandle, "foobar", 6); + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_STRING(name)); //target must be a string value, because it could also be an aggregate + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstVariableAssignment(bucketHandle, &ast, TOY_AST_FLAG_ASSIGN, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_VAR_ASSIGN || + ast->varAssign.flag != TOY_AST_FLAG_ASSIGN || + ast->varAssign.target == NULL || + ast->varAssign.target->type != TOY_AST_VALUE || + TOY_VALUE_IS_STRING(ast->varAssign.target->value.value) != true || + TOY_VALUE_AS_STRING(ast->varAssign.target->value.value)->info.type != TOY_STRING_LEAF || + strcmp(TOY_VALUE_AS_STRING(ast->varAssign.target->value.value)->leaf.data, "foobar") != 0 || + + ast->varAssign.expr->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->varAssign.expr->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an assign as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit access + { + //build the AST + Toy_Ast* ast = NULL; + Toy_String* name = Toy_createStringLength(bucketHandle, "foobar", 6); + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_STRING(name)); //access with a string value for technical reasons + Toy_private_emitAstVariableAccess(bucketHandle, &ast); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_VAR_ACCESS || + ast->varAccess.child == NULL || + ast->varAccess.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_STRING(ast->varAccess.child->value.value) != true || + TOY_VALUE_AS_STRING(ast->varAccess.child->value.value)->info.type != TOY_STRING_LEAF || + strcmp(TOY_VALUE_AS_STRING(ast->varAccess.child->value.value)->leaf.data, "foobar") != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an access as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit and append blocks of code (at the bottom of this test function, so everything else is checked first) + { + //initialize the root block + Toy_Ast* block = NULL; + Toy_private_initAstBlock(bucketHandle, &block); + + //loop over the ast emissions, appending each one as you go + for (int i = 0; i < 5; i++) { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstGroup(bucketHandle, &ast); + + Toy_private_appendAstBlock(bucketHandle, block, ast); + } + + //check if it worked + Toy_Ast* iter = block; + + while(iter != NULL) { + if ( + iter->type != TOY_AST_BLOCK || + iter->block.child == NULL || + iter->block.child->type != TOY_AST_GROUP || + iter->block.child->group.child == NULL || + iter->block.child->group.child->type != TOY_AST_BINARY || + iter->block.child->group.child->binary.flag != TOY_AST_FLAG_ADD || + iter->block.child->group.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.left->value.value) != 42 || + iter->block.child->group.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a block as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + + iter = iter->block.next; + } + } + + return 0; +} int main(void) { - printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); - return -1; -} + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_sizeof_ast(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucketHandle = Toy_allocateBucket(TOY_BUCKET_IDEAL); + res = test_type_emission(&bucketHandle); + Toy_freeBucket(&bucketHandle); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} \ No newline at end of file