diff --git a/source/toy_ast.c b/source/toy_ast.c index 104d63f..5d4a6de 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -82,3 +82,9 @@ void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle) { (*handle)->error.type = TOY_AST_ERROR; } + +void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle) { + (*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast)); + + (*handle)->error.type = TOY_AST_END; +} diff --git a/source/toy_ast.h b/source/toy_ast.h index d9eec08..7d5ff6b 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -16,6 +16,7 @@ typedef enum Toy_AstType { TOY_AST_PASS, TOY_AST_ERROR, + TOY_AST_END, } Toy_AstType; //flags are handled differently by different types @@ -64,6 +65,7 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucket, Toy_Ast** handle); void Toy_private_emitAstPass(Toy_Bucket** bucket, Toy_Ast** handle); void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle); +void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle); typedef struct Toy_AstBlock { Toy_AstType type; @@ -85,8 +87,8 @@ typedef struct Toy_AstUnary { typedef struct Toy_AstBinary { Toy_AstType type; - Toy_Ast* left; Toy_AstFlag flag; + Toy_Ast* left; Toy_Ast* right; } Toy_AstBinary; @@ -104,6 +106,10 @@ typedef struct Toy_AstError { //TODO: more data regarding the error } Toy_AstError; +typedef struct Toy_AstEnd { + Toy_AstType type; +} Toy_AstEnd; + union Toy_Ast { Toy_AstType type; //4 Toy_AstBlock block; //12 @@ -113,4 +119,5 @@ union Toy_Ast { Toy_AstGroup group; //8 Toy_AstPass pass; //4 Toy_AstError error; //4 + Toy_AstEnd end; //4 }; //16 diff --git a/source/toy_memory.h b/source/toy_memory.h index 55d4c81..95d0b9f 100644 --- a/source/toy_memory.h +++ b/source/toy_memory.h @@ -24,8 +24,8 @@ TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize); //immobile "bucket" memory structure for custom allocators -#define TOY_BUCKET_INIT(type, bucket, capacity) \ - Toy_initBucket(&(bucket), sizeof(type)*(capacity)) +#define TOY_BUCKET_INIT(type, bucket, count) \ + Toy_initBucket(&(bucket), sizeof(type)*(count)) #define TOY_BUCKET_PART(type, bucket) \ (type*)Toy_partBucket(&(bucket), sizeof(type)) diff --git a/source/toy_parser.c b/source/toy_parser.c index ab45cb4..c195af4 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -235,7 +235,8 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo do { buffer[i] = parser->previous.lexeme[o]; if (buffer[i] != '_') i++; - } while (parser->previous.lexeme[o++]); + } while (parser->previous.lexeme[o++] && i < parser->previous.length); + buffer[i] = '\0'; //BUGFIX int value = 0; sscanf(buffer, "%d", &value); @@ -251,7 +252,8 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo do { buffer[i] = parser->previous.lexeme[o]; if (buffer[i] != '_') i++; - } while (parser->previous.lexeme[o++]); + } while (parser->previous.lexeme[o++] && i < parser->previous.length); + buffer[i] = '\0'; //BUGFIX float value = 0; sscanf(buffer, "%f", &value); @@ -267,12 +269,36 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo } static Toy_AstFlag unary(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** root) { - if (parser->previous.type == TOY_TOKEN_OPERATOR_SUBTRACT || parser->previous.type == TOY_TOKEN_OPERATOR_NEGATE) { - //read what to negate + //'subtract' can only be applied to numbers and groups, while 'negate' can only be applied to booleans and groups + //this function takes the libery of peeking into the uppermost node, to see if it can apply this to it + + if (parser->previous.type == TOY_TOKEN_OPERATOR_SUBTRACT) { parsePrecedence(bucket, parser, root, PREC_UNARY); - //actually emit the negation node - Toy_private_emitAstUnary(bucket, root, TOY_AST_FLAG_NEGATE); + //negative numbers + if ((*root)->type == TOY_AST_VALUE && TOY_VALUE_IS_INTEGER((*root)->value.value)) { + (*root)->value.value = TOY_VALUE_TO_INTEGER( -TOY_VALUE_AS_INTEGER((*root)->value.value) ); + } + else if ((*root)->type == TOY_AST_VALUE && TOY_VALUE_IS_FLOAT((*root)->value.value)) { + (*root)->value.value = TOY_VALUE_TO_FLOAT( -TOY_VALUE_AS_FLOAT((*root)->value.value) ); + } + else { + //actually emit the negation node + Toy_private_emitAstUnary(bucket, root, TOY_AST_FLAG_NEGATE); + } + } + + else if (parser->previous.type == TOY_TOKEN_OPERATOR_NEGATE) { + parsePrecedence(bucket, parser, root, PREC_UNARY); + + //inverted booleans + if ((*root)->type == TOY_AST_VALUE && TOY_VALUE_IS_BOOLEAN((*root)->value.value)) { + (*root)->value.value = TOY_VALUE_TO_BOOLEAN( !TOY_VALUE_AS_BOOLEAN((*root)->value.value) ); + } + else { + //actually emit the negation node + Toy_private_emitAstUnary(bucket, root, TOY_AST_FLAG_NEGATE); + } } else { @@ -300,17 +326,17 @@ static Toy_AstFlag binary(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo } case TOY_TOKEN_OPERATOR_MULTIPLY: { - parsePrecedence(bucket, parser, root, PREC_TERM + 1); + parsePrecedence(bucket, parser, root, PREC_FACTOR + 1); return TOY_AST_FLAG_MULTIPLY; } case TOY_TOKEN_OPERATOR_DIVIDE: { - parsePrecedence(bucket, parser, root, PREC_TERM + 1); + parsePrecedence(bucket, parser, root, PREC_FACTOR + 1); return TOY_AST_FLAG_DIVIDE; } case TOY_TOKEN_OPERATOR_MODULO: { - parsePrecedence(bucket, parser, root, PREC_TERM + 1); + parsePrecedence(bucket, parser, root, PREC_FACTOR + 1); return TOY_AST_FLAG_MODULO; } @@ -524,17 +550,20 @@ static void makeBlockStmt(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer) { Toy_resetParser(parser); parser->lexer = lexer; + advance(parser); } Toy_Ast* Toy_scanParser(Toy_Bucket** bucket, Toy_Parser* parser) { + Toy_Ast* root = NULL; + //check for EOF if (match(parser, TOY_TOKEN_EOF)) { - return NULL; + Toy_private_emitAstEnd(bucket, &root); + return root; } //TODO: better errors, check for unbound parser, etc. - Toy_Ast* root = NULL; makeBlockStmt(bucket, parser, &root); return root; diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index 5e73c77..be56386 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -29,13 +29,9 @@ int test_sizeof_ast_32bit() { return -err; } -int test_type_emission() { +int test_type_emission(Toy_Bucket* bucket) { //emit value { - //bucket setup - Toy_Bucket* bucket = NULL; - TOY_BUCKET_INIT(Toy_Ast, bucket, 8); - //emit to an AST Toy_Ast* ast = NULL; Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); @@ -49,17 +45,10 @@ int test_type_emission() { fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a value as 'Toy_Ast', state unknown\n" TOY_CC_RESET); return -1; } - - //bucket free - TOY_BUCKET_FREE(bucket); } //emit unary { - //bucket setup - Toy_Bucket* bucket = NULL; - TOY_BUCKET_INIT(Toy_Ast, bucket, 8); - //build the AST Toy_Ast* ast = NULL; Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); @@ -76,17 +65,10 @@ int test_type_emission() { fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a unary as 'Toy_Ast', state unknown\n" TOY_CC_RESET); return -1; } - - //bucket free - TOY_BUCKET_FREE(bucket); } //emit binary { - //bucket setup - Toy_Bucket* bucket = NULL; - TOY_BUCKET_INIT(Toy_Ast, bucket, 8); - //build the AST Toy_Ast* ast = NULL; Toy_Ast* right = NULL; @@ -107,17 +89,10 @@ int test_type_emission() { fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a binary as 'Toy_Ast', state unknown\n" TOY_CC_RESET); return -1; } - - //bucket free - TOY_BUCKET_FREE(bucket); } //emit group { - //bucket setup - Toy_Bucket* bucket = NULL; - TOY_BUCKET_INIT(Toy_Ast, bucket, 8); - //build the AST Toy_Ast* ast = NULL; Toy_Ast* right = NULL; @@ -141,17 +116,10 @@ int test_type_emission() { fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a group as 'Toy_Ast', state unknown\n" TOY_CC_RESET); return -1; } - - //bucket free - TOY_BUCKET_FREE(bucket); } //emit and append blocks of code { - //bucket setup - Toy_Bucket* bucket = NULL; - TOY_BUCKET_INIT(Toy_Ast, bucket, 8); - //initialize the root block Toy_Ast* block = NULL; Toy_private_initAstBlock(&bucket, &block); @@ -191,23 +159,6 @@ int test_type_emission() { iter = iter->block.next; } - - //additional check: count the bucket's total allocations - Toy_Bucket* biter = bucket; - int total = 0; - - while(biter != NULL) { - total += biter->count; - biter = biter->next; - } - - if (total != 25 * (int)sizeof(Toy_Ast)) { - fprintf(stderr, TOY_CC_ERROR "ERROR: unexpected number of allocations found in a bucket, expected %d, found %d\n" TOY_CC_RESET, 25 * (int)sizeof(Toy_Ast), total); - return -1; - } - - //bucket free - TOY_BUCKET_FREE(bucket); } return 0; @@ -228,11 +179,15 @@ int main() { fprintf(stderr, TOY_CC_WARN "WARNING: Skipping test_sizeof_ast_32bit(); Can't determine the 'bitness' of this platform\n" TOY_CC_RESET); #endif - res = test_type_emission(); - total += res; - - if (res == 0) { - printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_type_emission(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; } return total; diff --git a/tests/cases/test_parser.c b/tests/cases/test_parser.c index f2c2b89..34b9fb9 100644 --- a/tests/cases/test_parser.c +++ b/tests/cases/test_parser.c @@ -3,7 +3,630 @@ #include -int main() { - fprintf(stderr, TOY_CC_WARN "WARNING: Test suite for Toy_Parser is not yet implemented\n" TOY_CC_RESET); +//utils +Toy_Ast* makeAstFromSource(Toy_Bucket** bucket, const char* source) { + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + return Toy_scanParser(bucket, &parser); +} + +//tests +int test_simple_empty_parsers(Toy_Bucket* bucket) { + //simple parser setup and cleanup + { + //raw source code and lexer + const char* source = ""; + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + //parser + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_END) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with empty source\n" TOY_CC_RESET); + return -1; + } + } + + //repeat above, but with one semicolon + { + //raw source code and lexer + const char* source = ";"; + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + //parser + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_PASS) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with one semicolon\n" TOY_CC_RESET); + return -1; + } + } + + //repeat above, but with multiple semicolons + { + //raw source code and lexer + const char* source = ";;;;;"; + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + //parser + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + Toy_Ast* iter = ast; + + while(iter != NULL) { + //check each link and child + if ( + iter == NULL || + iter->type != TOY_AST_BLOCK || + iter->block.child == NULL || + iter->block.child->type != TOY_AST_PASS) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with multiple semicolons\n" TOY_CC_RESET); + return -1; + } + + iter = iter->block.next; + } + } + + return 0; +} + +int test_values(Toy_Bucket* bucket) { + //test boolean true + { + Toy_Ast* ast = makeAstFromSource(&bucket, "true;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_BOOLEAN(ast->block.child->value.value) == false || + TOY_VALUE_AS_BOOLEAN(ast->block.child->value.value) != true) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with boolean value true\n" TOY_CC_RESET); + return -1; + } + } + + //test boolean false (just to be safe) + { + Toy_Ast* ast = makeAstFromSource(&bucket, "false;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_BOOLEAN(ast->block.child->value.value) == false || + TOY_VALUE_AS_BOOLEAN(ast->block.child->value.value) != false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with boolean value false\n" TOY_CC_RESET); + return -1; + } + } + + //test integer + { + Toy_Ast* ast = makeAstFromSource(&bucket, "42;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->value.value) != 42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with integer value 42\n" TOY_CC_RESET); + return -1; + } + } + + //test float + { + Toy_Ast* ast = makeAstFromSource(&bucket, "3.1415;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_FLOAT(ast->block.child->value.value) == false || + TOY_VALUE_AS_FLOAT(ast->block.child->value.value) != 3.1415f) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with float value 3.1415\n" TOY_CC_RESET); + return -1; + } + } + + //test integer with separators + { + Toy_Ast* ast = makeAstFromSource(&bucket, "1_234_567_890;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->value.value) != 1234567890) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with integer value 42 with separators\n" TOY_CC_RESET); + return -1; + } + } + + //test float with separators + { + Toy_Ast* ast = makeAstFromSource(&bucket, "3.141_592_65;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_FLOAT(ast->block.child->value.value) == false || + TOY_VALUE_AS_FLOAT(ast->block.child->value.value) != 3.14159265f) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with float value 3.1415 with separators\n" TOY_CC_RESET); + return -1; + } + } + + return 0; +} + +int test_unary(Toy_Bucket* bucket) { + //test unary boolean negation (!true) + { + Toy_Ast* ast = makeAstFromSource(&bucket, "!true;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_BOOLEAN(ast->block.child->value.value) == false || + TOY_VALUE_AS_BOOLEAN(ast->block.child->value.value) != false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with boolean value !true (unary negation)\n" TOY_CC_RESET); + return -1; + } + } + + //test unary boolean negation (!false, just to be safe) + { + Toy_Ast* ast = makeAstFromSource(&bucket, "!false;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_BOOLEAN(ast->block.child->value.value) == false || + TOY_VALUE_AS_BOOLEAN(ast->block.child->value.value) != true) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with boolean value !false (unary negation)\n" TOY_CC_RESET); + return -1; + } + } + + //test unary integer negation + { + Toy_Ast* ast = makeAstFromSource(&bucket, "-42;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->value.value) != -42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with integer value -42 (unary negation)\n" TOY_CC_RESET); + return -1; + } + } + + //test unary float negation + { + Toy_Ast* ast = makeAstFromSource(&bucket, "-3.1415;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_FLOAT(ast->block.child->value.value) == false || + TOY_VALUE_AS_FLOAT(ast->block.child->value.value) != -3.1415f) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with float value -3.1415 (unary negation)\n" TOY_CC_RESET); + return -1; + } + } + + //ensure unary negation doesn't affect other things (such as group) + { + Toy_Ast* ast = makeAstFromSource(&bucket, "-(42);"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_UNARY || + ast->block.child->unary.child == NULL || + ast->block.child->unary.child->type != TOY_AST_GROUP || + ast->block.child->unary.child->group.child == NULL || + ast->block.child->unary.child->group.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->unary.child->group.child->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->unary.child->group.child->value.value) != 42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: unexpected successful unary negation in parser with grouped value -(42)\n" TOY_CC_RESET); + return -1; + } + } + + return 0; +} + +int test_binary(Toy_Bucket* bucket) { + //test binary add (term); also covers subtract + { + Toy_Ast* ast = makeAstFromSource(&bucket, "1 + 2;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_ADD || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 1 || + + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 2) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary add '1 + 2' (term)\n" TOY_CC_RESET); + return -1; + } + } + + //test binary multiply (factor); also covers divide and modulo + { + Toy_Ast* ast = makeAstFromSource(&bucket, "3 * 5;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_MULTIPLY || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 3 || + + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 5) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary multiply '3 * 5' (factor)\n" TOY_CC_RESET); + return -1; + } + } + + //test binary assign (using numbers for now, as identifiers aren't coded yet) + { + Toy_Ast* ast = makeAstFromSource(&bucket, "1 = 2;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_ASSIGN || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 1 || + + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 2) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary assign '1 = 2'\n" TOY_CC_RESET); + return -1; + } + } + + //test binary compare (equality) + { + Toy_Ast* ast = makeAstFromSource(&bucket, "42 == 69;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_COMPARE_EQUAL || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 42 || + + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary compare '42 == 69'\n" TOY_CC_RESET); + return -1; + } + } + + return 0; +} + +int test_precedence(Toy_Bucket* bucket) { + //test term-factor precedence + { + Toy_Ast* ast = makeAstFromSource(&bucket, "1 * 2 + 3 * 4;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_ADD || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_BINARY || + ast->block.child->binary.left->binary.flag != TOY_AST_FLAG_MULTIPLY || + ast->block.child->binary.left->binary.left == NULL || + ast->block.child->binary.left->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->value.value) != 1 || + ast->block.child->binary.left->binary.right == NULL || + ast->block.child->binary.left->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.right->value.value) != 2 || + + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_BINARY || + ast->block.child->binary.right->binary.flag != TOY_AST_FLAG_MULTIPLY || + ast->block.child->binary.right->binary.left == NULL || + ast->block.child->binary.right->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->binary.left->value.value) != 3 || + ast->block.child->binary.right->binary.right == NULL || + ast->block.child->binary.right->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->binary.right->value.value) != 4) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser precedence '1 * 2 + 3 * 4' (term-factor)\n" TOY_CC_RESET); + return -1; + } + } + + //test left-recrusive precedence + { + Toy_Ast* ast = makeAstFromSource(&bucket, "1 + 2 + 3 + 4 + 5 + 6;"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_ADD || + + // start from the right and work backwards + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 6 || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_BINARY || + ast->block.child->binary.left->binary.right == NULL || + ast->block.child->binary.left->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.right->value.value) != 5 || + + ast->block.child->binary.left->binary.left == NULL || + ast->block.child->binary.left->binary.left->type != TOY_AST_BINARY || + ast->block.child->binary.left->binary.left->binary.right == NULL || + ast->block.child->binary.left->binary.left->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.right->value.value) != 4 || + + ast->block.child->binary.left->binary.left->binary.left == NULL || + ast->block.child->binary.left->binary.left->binary.left->type != TOY_AST_BINARY || + ast->block.child->binary.left->binary.left->binary.left->binary.right == NULL || + ast->block.child->binary.left->binary.left->binary.left->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.right->value.value) != 3 || + + ast->block.child->binary.left->binary.left->binary.left->binary.left == NULL || + ast->block.child->binary.left->binary.left->binary.left->binary.left->type != TOY_AST_BINARY || + ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right == NULL || + ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right->value.value) != 2 || + + ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left == NULL || + ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left->value.value) != 1) + + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser precedence '1 + 2 + 3 + 4 + 5 + 6' (left-recursive)\n" TOY_CC_RESET); + return -1; + } + } + + //test group precedence + { + Toy_Ast* ast = makeAstFromSource(&bucket, "(1 + 2) * (3 + 4);"); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_BINARY || + ast->block.child->binary.flag != TOY_AST_FLAG_MULTIPLY || + + ast->block.child->binary.left == NULL || + ast->block.child->binary.left->type != TOY_AST_GROUP || + ast->block.child->binary.left->group.child == NULL || + + ast->block.child->binary.left->group.child->type != TOY_AST_BINARY || + ast->block.child->binary.left->group.child->binary.flag != TOY_AST_FLAG_ADD || + ast->block.child->binary.left->group.child->binary.left == NULL || + ast->block.child->binary.left->group.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->group.child->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->group.child->binary.left->value.value) != 1 || + ast->block.child->binary.left->group.child->binary.right == NULL || + ast->block.child->binary.left->group.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->group.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->group.child->binary.right->value.value) != 2 || + + ast->block.child->binary.right == NULL || + ast->block.child->binary.right->type != TOY_AST_GROUP || + ast->block.child->binary.right->group.child == NULL || + + ast->block.child->binary.right->group.child->type != TOY_AST_BINARY || + ast->block.child->binary.right->group.child->binary.flag != TOY_AST_FLAG_ADD || + ast->block.child->binary.right->group.child->binary.left == NULL || + ast->block.child->binary.right->group.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->group.child->binary.left->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->group.child->binary.left->value.value) != 3 || + ast->block.child->binary.right->group.child->binary.right == NULL || + ast->block.child->binary.right->group.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->group.child->binary.right->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->group.child->binary.right->value.value) != 4) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser precedence '(1 + 2) * (3 + 4)' (group)\n" TOY_CC_RESET); + return -1; + } + } + return 0; } + +int main() { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_simple_empty_parsers(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_values(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_unary(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_binary(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_precedence(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} \ No newline at end of file