diff --git a/.notes/bytecode-format.txt b/.notes/bytecode-format.txt index 914b222..6923c64 100644 --- a/.notes/bytecode-format.txt +++ b/.notes/bytecode-format.txt @@ -23,6 +23,8 @@ An additional note: The contents of the build string may be anything, such as: * identification information, such as the developer's copyright * a link to Risk Astley's "Never Gonna Give You Up" on YouTube +Please note that in the final bytecode, if the null terminator of TOY_VERSION_BUILD is not 4-byte aligned, extra space will be allocated to round out the header's size to a multiple of 4. The contents of the extra bytes are undefined. + === At this time, a 'module' consists of a single 'routine', which acts as its global scope. @@ -39,6 +41,7 @@ Additional information may be added later, or multiple 'modules' listed sequenti .header: N total size # size of this routine, including all data and subroutines N .param count # the number of parameter fields expected + N .jumps count # the number of entries in the jump table (should be data count + routine count) N .data count # the number of data fields expected N .routine count # the number of routines present .param start # absolute addess of .param; omitted if not needed @@ -57,7 +60,7 @@ Additional information may be added later, or multiple 'modules' listed sequenti LOAD 0 ASSERT -.datatable: +.jumptable: # a 'symbol -> pointer' jumptable for quickly looking up values in .data and .routines 0 -> {string, 0x00} 1 -> {fn, 0xFF} diff --git a/source/toy_bytecode.c b/source/toy_bytecode.c index 7d8b54c..dcb4d01 100644 --- a/source/toy_bytecode.c +++ b/source/toy_bytecode.c @@ -34,6 +34,11 @@ static void writeBytecodeHeader(Toy_Bytecode* bc) { const char* build = Toy_private_version_build(); int len = (int)strlen(build) + 1; + //BUGFIX: ensure the end of the header has 4-byte alignment + if (len % 4 != 1) { //1 to fill the 4th byte above + len += 4 - (len % 4) +1; //ceil + } + expand(bc, len); memcpy(bc->ptr + bc->count, build, len); bc->count += len; diff --git a/source/toy_routine.c b/source/toy_routine.c index 3ff9b07..09687ba 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -60,14 +60,29 @@ static void writeInstructionValue(Toy_Routine** rt, Toy_AstValue ast) { //emit the raw value based on the type if (TOY_VALUE_IS_NULL(ast.value)) { //NOTHING - null's type data is enough + + //BUGFIX: 4-byte alignment + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); } else if (TOY_VALUE_IS_BOOLEAN(ast.value)) { EMIT_BYTE(rt, TOY_VALUE_AS_BOOLEAN(ast.value)); + + //BUGFIX: 4-byte alignment + EMIT_BYTE(rt, 0); } else if (TOY_VALUE_IS_INTEGER(ast.value)) { + //BUGFIX: 4-byte alignment + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_INT(rt, code, TOY_VALUE_AS_INTEGER(ast.value)); } else if (TOY_VALUE_IS_FLOAT(ast.value)) { + //BUGFIX: 4-byte alignment + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_FLOAT(rt, code, TOY_VALUE_AS_FLOAT(ast.value)); } else { @@ -82,6 +97,11 @@ static void writeInstructionUnary(Toy_Routine** rt, Toy_AstUnary ast) { if (ast.flag == TOY_AST_FLAG_NEGATE) { EMIT_BYTE(rt, TOY_OPCODE_NEGATE); + + //BUGFIX: 4-byte alignment + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); } else { fprintf(stderr, TOY_CC_ERROR "Invalid AST unary flag found\n" TOY_CC_RESET); @@ -145,7 +165,16 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) { } else if (ast.flag == TOY_AST_FLAG_COMPARE_NOT) { EMIT_BYTE(rt, TOY_OPCODE_COMPARE_EQUAL); - EMIT_BYTE(rt, TOY_OPCODE_NEGATE); + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + + EMIT_BYTE(rt, TOY_OPCODE_NEGATE); //TODO: squeeze these into one word + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + + return; } else if (ast.flag == TOY_AST_FLAG_COMPARE_LESS) { EMIT_BYTE(rt, TOY_OPCODE_COMPARE_LESS); @@ -170,6 +199,11 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) { fprintf(stderr, TOY_CC_ERROR "Invalid AST binary flag found\n" TOY_CC_RESET); exit(-1); } + + //BUGFIX: 4-byte alignment (covers most cases) + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); } //routine structure @@ -236,12 +270,15 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) { static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) { //build the routine's parts - //param + //TODO: param //code writeRoutineCode(&rt, ast); EMIT_BYTE(&rt, TOY_OPCODE_RETURN); //temp terminator - //jumps - //data + EMIT_BYTE(&rt, 0); //BUGFIX: 4-byte alignment + EMIT_BYTE(&rt, 0); + EMIT_BYTE(&rt, 0); + //TODO: jumps + //TODO: data //write the header and combine the parts void* buffer = TOY_ALLOCATE(unsigned char, 16); @@ -251,6 +288,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) { emitInt(&buffer, &capacity, &count, 0); //total size (overwritten later) emitInt(&buffer, &capacity, &count, rt->paramCount); //param count + emitInt(&buffer, &capacity, &count, rt->jumpsCount); //jumps count emitInt(&buffer, &capacity, &count, rt->dataCount); //data count emitInt(&buffer, &capacity, &count, rt->subsCount); //routine count @@ -285,7 +323,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) { count += rt->codeCount; } - //finally, record the total size, and return the result + //finally, record the total size within the header, and return the result *((int*)buffer) = count; return buffer; diff --git a/source/toy_routine.h b/source/toy_routine.h index 3eb447c..90ac882 100644 --- a/source/toy_routine.h +++ b/source/toy_routine.h @@ -3,9 +3,9 @@ #include "toy_common.h" #include "toy_ast.h" -//routine - internal structure that holds the individual parts of a compiled routine +//internal structure that holds the individual parts of a compiled routine typedef struct Toy_Routine { - unsigned char* param; //c-string params in sequence + unsigned char* param; //c-string params in sequence (could be moved below the jump table?) int paramCapacity; int paramCount; diff --git a/tests/cases/test_bytecode.c b/tests/cases/test_bytecode.c index 861c36e..b2959b5 100644 --- a/tests/cases/test_bytecode.c +++ b/tests/cases/test_bytecode.c @@ -1,6 +1,10 @@ #include "toy_bytecode.h" #include "toy_console_colors.h" +#include "toy_opcodes.h" +#include "toy_lexer.h" +#include "toy_parser.h" + #include #include @@ -30,6 +34,135 @@ int test_bytecode_header(Toy_Bucket* bucket) { return -1; } + if (bc.count % 4 != 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: bytecode size is not a multiple of 4, size is: %d\n" TOY_CC_RESET, bc.count); + + //cleanup and return + Toy_freeBytecode(bc); + return -1; + } + + //cleanup + Toy_freeBytecode(bc); + } + + return 0; +} + +int test_bytecode_from_source(Toy_Bucket* bucket) { + { + //setup + const char* source = "(1 + 2) * (3 + 4);"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + Toy_Bytecode bc = Toy_compileBytecode(ast); + + //check bytecode alignment + if (bc.count % 4 != 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: bytecode alignment is not a multiple of 4 (size is %d), source: %s\n" TOY_CC_RESET, bc.count, source); + + //cleanup and return + Toy_freeBytecode(bc); + return -1; + } + + //check bytecode header + if (bc.ptr[0] != TOY_VERSION_MAJOR || + bc.ptr[1] != TOY_VERSION_MINOR || + bc.ptr[2] != TOY_VERSION_PATCH || + strcmp((char*)(bc.ptr + 3), TOY_VERSION_BUILD) != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to write the bytecode header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + Toy_freeBytecode(bc); + return -1; + } + + //check contents of the routine (this is copy/pasted from test_routine.c, and tweaked with the offset) + int offset = strlen(TOY_VERSION_BUILD) + 3; + if (offset % 4 != 0) { + offset += 4 - (offset % 4); //ceil + } + + int* ptr = (int*)(bc.ptr + offset); + + if ((ptr++)[0] != 72 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header within bytecode, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + Toy_freeBytecode(bc); + return -1; + } + + //check code + if ( + //left hand side + *((unsigned char*)(offset + bc.ptr + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(offset + bc.ptr + 25)) != TOY_VALUE_INTEGER || + *((unsigned char*)(offset + bc.ptr + 26)) != 0 || + *((unsigned char*)(offset + bc.ptr + 27)) != 0 || + *(int*)(offset + bc.ptr + 28) != 1 || + + *((unsigned char*)(offset + bc.ptr + 32)) != TOY_OPCODE_READ || + *((unsigned char*)(offset + bc.ptr + 33)) != TOY_VALUE_INTEGER || + *((unsigned char*)(offset + bc.ptr + 34)) != 0 || + *((unsigned char*)(offset + bc.ptr + 35)) != 0 || + *(int*)(offset + bc.ptr + 36) != 2 || + + *((unsigned char*)(offset + bc.ptr + 40)) != TOY_OPCODE_ADD || + *((unsigned char*)(offset + bc.ptr + 41)) != 0 || + *((unsigned char*)(offset + bc.ptr + 42)) != 0 || + *((unsigned char*)(offset + bc.ptr + 43)) != 0 || + + //right hand side + *((unsigned char*)(offset + bc.ptr + 44)) != TOY_OPCODE_READ || + *((unsigned char*)(offset + bc.ptr + 45)) != TOY_VALUE_INTEGER || + *((unsigned char*)(offset + bc.ptr + 46)) != 0 || + *((unsigned char*)(offset + bc.ptr + 47)) != 0 || + *(int*)(offset + bc.ptr + 48) != 3 || + + *((unsigned char*)(offset + bc.ptr + 52)) != TOY_OPCODE_READ || + *((unsigned char*)(offset + bc.ptr + 53)) != TOY_VALUE_INTEGER || + *((unsigned char*)(offset + bc.ptr + 54)) != 0 || + *((unsigned char*)(offset + bc.ptr + 55)) != 0 || + *(int*)(offset + bc.ptr + 56) != 4 || + + *((unsigned char*)(offset + bc.ptr + 60)) != TOY_OPCODE_ADD || + *((unsigned char*)(offset + bc.ptr + 61)) != 0 || + *((unsigned char*)(offset + bc.ptr + 62)) != 0 || + *((unsigned char*)(offset + bc.ptr + 63)) != 0 || + + //multiply the two values + *((unsigned char*)(offset + bc.ptr + 64)) != TOY_OPCODE_MULTIPLY || + *((unsigned char*)(offset + bc.ptr + 65)) != 0 || + *((unsigned char*)(offset + bc.ptr + 66)) != 0 || + *((unsigned char*)(offset + bc.ptr + 67)) != 0 || + + *((unsigned char*)(offset + bc.ptr + 68)) != TOY_OPCODE_RETURN || + *((unsigned char*)(offset + bc.ptr + 69)) != 0 || + *((unsigned char*)(offset + bc.ptr + 70)) != 0 || + *((unsigned char*)(offset + bc.ptr + 71)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code within bytecode, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + Toy_freeBytecode(bc); + return -1; + } + //cleanup Toy_freeBytecode(bc); } @@ -52,5 +185,16 @@ int main() { total += res; } + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_bytecode_from_source(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 diff --git a/tests/cases/test_routine.c b/tests/cases/test_routine.c index f932518..848455c 100644 --- a/tests/cases/test_routine.c +++ b/tests/cases/test_routine.c @@ -1,12 +1,16 @@ #include "toy_routine.h" #include "toy_console_colors.h" +#include "toy_opcodes.h" +#include "toy_lexer.h" +#include "toy_parser.h" + #include #include //tests -int test_routine_header(Toy_Bucket* bucket) { - //simple test to ensure the header looks right +int test_routine_header_and_values(Toy_Bucket* bucket) { + //simple test to ensure the header looks right with an empty ast { //setup Toy_Ast* ast = NULL; @@ -16,8 +20,601 @@ int test_routine_header(Toy_Bucket* bucket) { void* buffer = Toy_compileRoutine(ast); int len = ((int*)buffer)[0]; - //check - //TODO + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 28 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, ast: PASS\n" TOY_CC_RESET); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 25)) != 0 || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, ast: PASS\n" TOY_CC_RESET); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //rerun the test with a more complex ast, derived from a snippet of source + { + //setup + const char* source = ";"; //interestingly, different ASTs will produce the same output + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 28 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 25)) != 0 || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce a null value + { + //setup + const char* source = "null;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 32 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_NULL || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *((unsigned char*)(buffer + 28)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 29)) != 0 || + *((unsigned char*)(buffer + 30)) != 0 || + *((unsigned char*)(buffer + 31)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce a boolean value + { + //setup + const char* source = "true;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 32 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_BOOLEAN || + *((unsigned char*)(buffer + 26)) != 1 || + *((unsigned char*)(buffer + 27)) != 0 || + *((unsigned char*)(buffer + 28)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 29)) != 0 || + *((unsigned char*)(buffer + 30)) != 0 || + *((unsigned char*)(buffer + 31)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce an integer value + { + //setup + const char* source = "42;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 36 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *(int*)(buffer + 28) != 42 || + *((unsigned char*)(buffer + 32)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 23)) != 0 || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce a float value + { + //setup + const char* source = "3.1415;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 36 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_FLOAT || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *(float*)(buffer + 28) != 3.1415f || + *((unsigned char*)(buffer + 32)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 23)) != 0 || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + return 0; +} + +// int test_routine_unary(Toy_Bucket* bucket) { +// //TODO: Nothing produces a unary instruction yet +// } + +int test_routine_binary(Toy_Bucket* bucket) { + //produce a simple algorithm + { + //setup + const char* source = "3 + 5;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 48 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *(int*)(buffer + 28) != 3 || + + *((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 || + *(int*)(buffer + 36) != 5 || + + *((unsigned char*)(buffer + 40)) != TOY_OPCODE_ADD || + *((unsigned char*)(buffer + 41)) != 0 || + *((unsigned char*)(buffer + 42)) != 0 || + *((unsigned char*)(buffer + 43)) != 0 || + + *((unsigned char*)(buffer + 44)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 45)) != 0 || + *((unsigned char*)(buffer + 46)) != 0 || + *((unsigned char*)(buffer + 47)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce a simple comparison + { + //setup + const char* source = "3 == 5;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 48 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *(int*)(buffer + 28) != 3 || + + *((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 || + *(int*)(buffer + 36) != 5 || + + *((unsigned char*)(buffer + 40)) != TOY_OPCODE_COMPARE_EQUAL || + *((unsigned char*)(buffer + 41)) != 0 || + *((unsigned char*)(buffer + 42)) != 0 || + *((unsigned char*)(buffer + 43)) != 0 || + + *((unsigned char*)(buffer + 44)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 45)) != 0 || + *((unsigned char*)(buffer + 46)) != 0 || + *((unsigned char*)(buffer + 47)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce a simple comparison + { + //setup + const char* source = "3 != 5;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 52 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *(int*)(buffer + 28) != 3 || + + *((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 || + *(int*)(buffer + 36) != 5 || + + *((unsigned char*)(buffer + 40)) != TOY_OPCODE_COMPARE_EQUAL || + *((unsigned char*)(buffer + 41)) != 0 || + *((unsigned char*)(buffer + 42)) != 0 || + *((unsigned char*)(buffer + 43)) != 0 || + + *((unsigned char*)(buffer + 44)) != TOY_OPCODE_NEGATE || + *((unsigned char*)(buffer + 45)) != 0 || + *((unsigned char*)(buffer + 46)) != 0 || + *((unsigned char*)(buffer + 47)) != 0 || + + *((unsigned char*)(buffer + 48)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 49)) != 0 || + *((unsigned char*)(buffer + 50)) != 0 || + *((unsigned char*)(buffer + 51)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //cleanup + TOY_FREE_ARRAY(unsigned char, buffer, len); + } + + //produce a more complex algorithm + { + //setup + const char* source = "(1 + 2) * (3 + 4);"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 72 || //total size + (ptr++)[0] != 0 || //param count + (ptr++)[0] != 0 || //jump count + (ptr++)[0] != 0 || //data count + (ptr++)[0] != 0) //subs count + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } + + //check code + if ( + //left hand side + *((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 25)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 26)) != 0 || + *((unsigned char*)(buffer + 27)) != 0 || + *(int*)(buffer + 28) != 1 || + + *((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 || + *(int*)(buffer + 36) != 2 || + + *((unsigned char*)(buffer + 40)) != TOY_OPCODE_ADD || + *((unsigned char*)(buffer + 41)) != 0 || + *((unsigned char*)(buffer + 42)) != 0 || + *((unsigned char*)(buffer + 43)) != 0 || + + //right hand side + *((unsigned char*)(buffer + 44)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 45)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 46)) != 0 || + *((unsigned char*)(buffer + 47)) != 0 || + *(int*)(buffer + 48) != 3 || + + *((unsigned char*)(buffer + 52)) != TOY_OPCODE_READ || + *((unsigned char*)(buffer + 53)) != TOY_VALUE_INTEGER || + *((unsigned char*)(buffer + 54)) != 0 || + *((unsigned char*)(buffer + 55)) != 0 || + *(int*)(buffer + 56) != 4 || + + *((unsigned char*)(buffer + 60)) != TOY_OPCODE_ADD || + *((unsigned char*)(buffer + 61)) != 0 || + *((unsigned char*)(buffer + 62)) != 0 || + *((unsigned char*)(buffer + 63)) != 0 || + + //multiply the two values + *((unsigned char*)(buffer + 64)) != TOY_OPCODE_MULTIPLY || + *((unsigned char*)(buffer + 65)) != 0 || + *((unsigned char*)(buffer + 66)) != 0 || + *((unsigned char*)(buffer + 67)) != 0 || + + *((unsigned char*)(buffer + 68)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 69)) != 0 || + *((unsigned char*)(buffer + 70)) != 0 || + *((unsigned char*)(buffer + 71)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + TOY_FREE_ARRAY(unsigned char, buffer, len); + return -1; + } //cleanup TOY_FREE_ARRAY(unsigned char, buffer, len); @@ -27,15 +624,24 @@ int test_routine_header(Toy_Bucket* bucket) { } int main() { - fprintf(stderr, TOY_CC_WARN "WARNING: Routine tests incomplete\n" TOY_CC_RESET); - //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_routine_header(bucket); + res = test_routine_header_and_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_routine_binary(bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);