From ff13b5cf384a6ba88f2fac1c4c20926cf8848363 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Mon, 7 Oct 2024 12:13:06 +1100 Subject: [PATCH] Implemented print keyword and associated tests --- .notes/SECD-concept.txt | 2 +- README.md | 6 ++- repl/main.c | 69 +++++++++++++++++--------------- scripts/example-print.toy | 5 +++ scripts/example.toy | 3 +- source/toy_ast.c | 9 +++++ source/toy_ast.h | 10 +++++ source/toy_opcodes.h | 4 ++ source/toy_parser.c | 32 ++++++++++----- source/toy_print.c | 2 +- source/toy_routine.c | 31 +++++++++++---- source/toy_value.h | 1 - source/toy_vm.c | 58 +++++++++++++++++++++++++-- tests/cases/test_ast.c | 29 ++++++++++++++ tests/cases/test_routine.c | 80 ++++++++++++++++++++++++++++++++++++-- tests/cases/test_vm.c | 65 +++++++++++++++++++++++++++++++ 16 files changed, 346 insertions(+), 60 deletions(-) create mode 100644 scripts/example-print.toy diff --git a/.notes/SECD-concept.txt b/.notes/SECD-concept.txt index 616a35a..1584580 100644 --- a/.notes/SECD-concept.txt +++ b/.notes/SECD-concept.txt @@ -87,7 +87,7 @@ FN_CALL FN_RETURN *read a list of return values specified in C into 'R', pop S, restore (S, E, C, D) from D(0) popping it, store the contents of 'R' in E or S based on the next few parts of C -//bespoke utility instructions +//various action instructions ASSERT if S(-1) is falsy, print S(0) and exit PRINT diff --git a/README.md b/README.md index 6119e47..6481910 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,11 @@ This repository holds the reference implementation for Toy version 2.x, written # Syntax -*Coming Soon.* +```toy +print 42; //print is a built-in keyword + +//more examples to be added as the features are implemented +``` # Building diff --git a/repl/main.c b/repl/main.c index b195e1f..385dd96 100644 --- a/repl/main.c +++ b/repl/main.c @@ -157,16 +157,21 @@ CmdLine parseCmdLine(int argc, const char* argv[]) { return cmd; } -//callback -static void errorAndExit(const char* msg) { - fprintf(stderr, "%s", msg); +//callbacks +static void printCallback(const char* msg) { + fprintf(stdout, "%s\n", msg); +} + +static void errorAndExitCallback(const char* msg) { + fprintf(stderr, "%s\n", msg); exit(-1); } //main file int main(int argc, const char* argv[]) { - Toy_setErrorCallback(errorAndExit); - Toy_setAssertFailureCallback(errorAndExit); + Toy_setPrintCallback(printCallback); + Toy_setErrorCallback(errorAndExitCallback); + Toy_setAssertFailureCallback(errorAndExitCallback); CmdLine cmd = parseCmdLine(argc, argv); @@ -226,39 +231,41 @@ int main(int argc, const char* argv[]) { Toy_runVM(&vm); //debugging result - printf("printing the stack result\n\ntype\tvalue\n"); - for (int i = 0; i < vm.stack->count; i++) { - Toy_Value v = ((Toy_Value*)(vm.stack + 1))[i]; + if (vm.stack->count > 0) { + printf("Debug output of the stack after execution\n\ntype\tvalue\n"); + for (int i = 0; i < vm.stack->count; i++) { + Toy_Value v = ((Toy_Value*)(vm.stack + 1))[i]; - printf(" %d\t ", v.type); + printf(" %d\t ", v.type); - switch(v.type) { - case TOY_VALUE_NULL: - printf("null"); - break; + switch(v.type) { + case TOY_VALUE_NULL: + printf("null"); + break; - case TOY_VALUE_BOOLEAN: - printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false"); - break; + case TOY_VALUE_BOOLEAN: + printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false"); + break; - case TOY_VALUE_INTEGER: - printf("%d", TOY_VALUE_AS_INTEGER(v)); - break; + case TOY_VALUE_INTEGER: + printf("%d", TOY_VALUE_AS_INTEGER(v)); + break; - case TOY_VALUE_FLOAT: - printf("%f", TOY_VALUE_AS_FLOAT(v)); - break; + case TOY_VALUE_FLOAT: + printf("%f", TOY_VALUE_AS_FLOAT(v)); + break; - case TOY_VALUE_STRING: - case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: - case TOY_VALUE_FUNCTION: - case TOY_VALUE_OPAQUE: - printf("???"); - break; + case TOY_VALUE_STRING: + case TOY_VALUE_ARRAY: + case TOY_VALUE_DICTIONARY: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + printf("???"); + break; + } + + printf("\n"); } - - printf("\n"); } //cleanup diff --git a/scripts/example-print.toy b/scripts/example-print.toy new file mode 100644 index 0000000..8f7023a --- /dev/null +++ b/scripts/example-print.toy @@ -0,0 +1,5 @@ +//print statement +print 42; + +//it can handle complex expressions +print 3 * 5; diff --git a/scripts/example.toy b/scripts/example.toy index 82246fc..fe33835 100644 --- a/scripts/example.toy +++ b/scripts/example.toy @@ -1 +1,2 @@ -(1 + 2) * (3 + 4); \ No newline at end of file +//expression +(1 + 2) * (3 + 4); diff --git a/source/toy_ast.c b/source/toy_ast.c index 0001a5f..e4ccbf9 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -72,6 +72,15 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { (*astHandle) = tmp; } +void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_PRINT; + tmp->print.child = (*astHandle); + + (*astHandle) = tmp; +} + void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); diff --git a/source/toy_ast.h b/source/toy_ast.h index 30c9b1d..7b356da 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -14,6 +14,8 @@ typedef enum Toy_AstType { TOY_AST_BINARY, TOY_AST_GROUP, + TOY_AST_PRINT, + TOY_AST_PASS, TOY_AST_ERROR, TOY_AST_END, @@ -85,6 +87,11 @@ typedef struct Toy_AstGroup { Toy_Ast* child; } Toy_AstGroup; +typedef struct Toy_AstPrint { + Toy_AstType type; + Toy_Ast* child; +} Toy_AstPrint; + typedef struct Toy_AstPass { Toy_AstType type; } Toy_AstPass; @@ -105,6 +112,7 @@ union Toy_Ast { //32 | 64 BITNESS Toy_AstUnary unary; //12 | 16 Toy_AstBinary binary; //16 | 24 Toy_AstGroup group; //8 | 16 + Toy_AstPrint print; //8 | 16 Toy_AstPass pass; //4 | 4 Toy_AstError error; //4 | 4 Toy_AstEnd end; //4 | 4 @@ -118,6 +126,8 @@ void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, To void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right); void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); +void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); + void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); \ No newline at end of file diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 176964d..4f1e867 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -33,6 +33,10 @@ typedef enum Toy_OpcodeType { //control instructions TOY_OPCODE_RETURN, + //various action instructions + TOY_OPCODE_PRINT, + //TODO: clear the program stack + //meta instructions TOY_OPCODE_PASS, TOY_OPCODE_ERROR, diff --git a/source/toy_parser.c b/source/toy_parser.c index 428251c..0eab50f 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -482,20 +482,20 @@ static void makeExpr(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro parsePrecedence(bucketHandle, parser, rootHandle, PREC_ASSIGNMENT); } -static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { - //check for empty lines - if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) { - Toy_private_emitAstPass(bucketHandle, rootHandle); - return; - } +static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + makeExpr(bucketHandle, parser, rootHandle); + Toy_private_emitAstPrint(bucketHandle, rootHandle); + consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of print statement"); +} + +static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { makeExpr(bucketHandle, parser, rootHandle); consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of expression statement"); } static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { //block - //print //assert //if-then-else //while-then @@ -505,8 +505,22 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro //return //import - //default - makeExprStmt(bucketHandle, parser, rootHandle); + //check for empty lines + if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) { + Toy_private_emitAstPass(bucketHandle, rootHandle); + return; + } + + else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) { + makePrintStmt(bucketHandle, parser, rootHandle); + return; + } + + else { + //default + makeExprStmt(bucketHandle, parser, rootHandle); + return; + } } static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { diff --git a/source/toy_print.c b/source/toy_print.c index d57eb18..1a75c78 100644 --- a/source/toy_print.c +++ b/source/toy_print.c @@ -33,6 +33,7 @@ void Toy_setPrintCallback(Toy_callbackType cb) { void Toy_setErrorCallback(Toy_callbackType cb) { errorCallback = cb; } + void Toy_setAssertFailureCallback(Toy_callbackType cb) { assertCallback = cb; } @@ -48,4 +49,3 @@ void Toy_resetErrorCallback() { void Toy_resetAssertFailureCallback() { assertCallback = errDefault; } - diff --git a/source/toy_routine.c b/source/toy_routine.c index f876cbb..8efc34e 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -63,25 +63,25 @@ static void writeInstructionValue(Toy_Routine** rt, Toy_AstValue ast) { if (TOY_VALUE_IS_NULL(ast.value)) { //NOTHING - null's type data is enough - //BUGFIX: 4-byte alignment + //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 + //4-byte alignment EMIT_BYTE(rt, 0); } else if (TOY_VALUE_IS_INTEGER(ast.value)) { - //BUGFIX: 4-byte alignment + //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 + //4-byte alignment EMIT_BYTE(rt, 0); EMIT_BYTE(rt, 0); @@ -100,7 +100,7 @@ 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 + //4-byte alignment EMIT_BYTE(rt, 0); EMIT_BYTE(rt, 0); EMIT_BYTE(rt, 0); @@ -197,7 +197,20 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) { exit(-1); } - //BUGFIX: 4-byte alignment (covers most cases) + //4-byte alignment (covers most cases) + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); + EMIT_BYTE(rt, 0); +} + +static void writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) { + //the thing to print + writeRoutineCode(rt, ast.child); + + //output the print opcode + EMIT_BYTE(rt, TOY_OPCODE_PRINT); + + //4-byte alignment EMIT_BYTE(rt, 0); EMIT_BYTE(rt, 0); EMIT_BYTE(rt, 0); @@ -232,6 +245,10 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) { writeInstructionBinary(rt, ast->binary); break; + case TOY_AST_PRINT: + writeInstructionPrint(rt, ast->print); + break; + //other disallowed instructions case TOY_AST_GROUP: fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST type found: Group shouldn't be used\n" TOY_CC_RESET); @@ -271,7 +288,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) { //code writeRoutineCode(&rt, ast); EMIT_BYTE(&rt, TOY_OPCODE_RETURN); //temp terminator - EMIT_BYTE(&rt, 0); //BUGFIX: 4-byte alignment + EMIT_BYTE(&rt, 0); //4-byte alignment EMIT_BYTE(&rt, 0); EMIT_BYTE(&rt, 0); //TODO: jumps diff --git a/source/toy_value.h b/source/toy_value.h index bc7cc8d..f6bfe58 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -58,4 +58,3 @@ TOY_API bool Toy_private_isTruthy(Toy_Value value); TOY_API bool Toy_private_isEqual(Toy_Value left, Toy_Value right); unsigned int Toy_hashValue(Toy_Value value); - diff --git a/source/toy_vm.c b/source/toy_vm.c index af019b5..7b730c2 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -1,6 +1,7 @@ #include "toy_vm.h" #include "toy_console_colors.h" +#include "toy_print.h" #include "toy_opcodes.h" #include "toy_value.h" @@ -235,15 +236,55 @@ static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) { } } +static void processPrint(Toy_VM* vm) { + //print the value on top of the stack, popping it + Toy_Value value = Toy_popStack(&vm->stack); + + //NOTE: don't append a newline - leave that choice to the host + switch(value.type) { + case TOY_VALUE_NULL: + Toy_print("null"); + break; + + case TOY_VALUE_BOOLEAN: + Toy_print(TOY_VALUE_AS_BOOLEAN(value) ? "true" : "false"); + break; + + case TOY_VALUE_INTEGER: { + char buffer[16]; + sprintf(buffer, "%d", TOY_VALUE_AS_INTEGER(value)); + Toy_print(buffer); + break; + } + + case TOY_VALUE_FLOAT: { + char buffer[16]; + sprintf(buffer, "%f", TOY_VALUE_AS_FLOAT(value)); + Toy_print(buffer); + break; + } + + case TOY_VALUE_STRING: //TODO: decide on how long strings, etc. live for in memory + case TOY_VALUE_ARRAY: + case TOY_VALUE_DICTIONARY: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type %d passed to processPrint, exiting\n" TOY_CC_RESET, value.type); + exit(-1); + } +} + static void process(Toy_VM* vm) { while(true) { Toy_OpcodeType opcode = READ_BYTE(vm); switch(opcode) { + //variable instructions case TOY_OPCODE_READ: processRead(vm); break; + //arithmetic instructions case TOY_OPCODE_ADD: case TOY_OPCODE_SUBTRACT: case TOY_OPCODE_MULTIPLY: @@ -252,6 +293,7 @@ static void process(Toy_VM* vm) { processArithmetic(vm, opcode); break; + //comparison instructions case TOY_OPCODE_COMPARE_EQUAL: case TOY_OPCODE_COMPARE_LESS: case TOY_OPCODE_COMPARE_LESS_EQUAL: @@ -260,6 +302,7 @@ static void process(Toy_VM* vm) { processComparison(vm, opcode); break; + //logical instructions case TOY_OPCODE_AND: case TOY_OPCODE_OR: case TOY_OPCODE_TRUTHY: @@ -267,6 +310,17 @@ static void process(Toy_VM* vm) { processLogical(vm, opcode); break; + //control instructions + case TOY_OPCODE_RETURN: + //temp terminator + return; + + //various action instructions + case TOY_OPCODE_PRINT: + processPrint(vm); + break; + + //not yet implemented case TOY_OPCODE_LOAD: case TOY_OPCODE_LOAD_LONG: case TOY_OPCODE_DECLARE: @@ -277,10 +331,6 @@ static void process(Toy_VM* vm) { case TOY_OPCODE_EOF: fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d found, exiting\n" TOY_CC_RESET, opcode); exit(-1); - - case TOY_OPCODE_RETURN: //temp terminator, temp position - // - return; } //prepare for the next instruction diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index 2a6b926..1addca1 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -20,6 +20,7 @@ int test_sizeof_ast_64bit() { TEST_SIZEOF(Toy_AstUnary, 16); TEST_SIZEOF(Toy_AstBinary, 24); TEST_SIZEOF(Toy_AstGroup, 16); + TEST_SIZEOF(Toy_AstPrint, 16); TEST_SIZEOF(Toy_AstPass, 4); TEST_SIZEOF(Toy_AstError, 4); TEST_SIZEOF(Toy_AstEnd, 4); @@ -47,6 +48,7 @@ int test_sizeof_ast_32bit() { TEST_SIZEOF(Toy_AstUnary, 12); TEST_SIZEOF(Toy_AstBinary, 16); TEST_SIZEOF(Toy_AstGroup, 8); + TEST_SIZEOF(Toy_AstPrint, 8); TEST_SIZEOF(Toy_AstPass, 4); TEST_SIZEOF(Toy_AstError, 4); TEST_SIZEOF(Toy_AstEnd, 4); @@ -146,6 +148,33 @@ int test_type_emission(Toy_Bucket** bucketHandle) { } } + //emit print keyword + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_TO_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_TO_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 print as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + //emit and append blocks of code { //initialize the root block diff --git a/tests/cases/test_routine.c b/tests/cases/test_routine.c index c31177b..8f93f9c 100644 --- a/tests/cases/test_routine.c +++ b/tests/cases/test_routine.c @@ -10,7 +10,7 @@ #include //tests -int test_routine_header_and_values(Toy_Bucket** bucketHandle) { +int test_routine_expressions(Toy_Bucket** bucketHandle) { //simple test to ensure the header looks right with an empty ast { //setup @@ -248,7 +248,7 @@ int test_routine_header_and_values(Toy_Bucket** bucketHandle) { *((unsigned char*)(buffer + 27)) != 0 || *(int*)(buffer + 28) != 42 || *((unsigned char*)(buffer + 32)) != TOY_OPCODE_RETURN || - *((unsigned char*)(buffer + 23)) != 0 || + *((unsigned char*)(buffer + 33)) != 0 || *((unsigned char*)(buffer + 34)) != 0 || *((unsigned char*)(buffer + 35)) != 0 ) @@ -302,7 +302,7 @@ int test_routine_header_and_values(Toy_Bucket** bucketHandle) { *((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 + 33)) != 0 || *((unsigned char*)(buffer + 34)) != 0 || *((unsigned char*)(buffer + 35)) != 0 ) @@ -619,13 +619,75 @@ int test_routine_binary(Toy_Bucket** bucketHandle) { return 0; } +int test_routine_keywords(Toy_Bucket** bucketHandle) { + //print + { + //setup + const char* source = "print 42;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* ptr = (int*)buffer; + + if ((ptr++)[0] != 40 || //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 + free(buffer); + 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_PRINT || + *((unsigned char*)(buffer + 33)) != 0 || + *((unsigned char*)(buffer + 34)) != 0 || + *((unsigned char*)(buffer + 35)) != 0 || + *((unsigned char*)(buffer + 36)) != TOY_OPCODE_RETURN || + *((unsigned char*)(buffer + 37)) != 0 || + *((unsigned char*)(buffer + 38)) != 0 || + *((unsigned char*)(buffer + 39)) != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + free(buffer); + return -1; + } + + //cleanup + free(buffer); + } + + return 0; +} + int main() { //run each test set, returning the total errors given int total = 0, res = 0; { Toy_Bucket* bucket = Toy_allocateBucket(sizeof(Toy_Ast) * 32); - res = test_routine_header_and_values(&bucket); + res = test_routine_expressions(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -643,5 +705,15 @@ int main() { total += res; } + { + Toy_Bucket* bucket = Toy_allocateBucket(sizeof(Toy_Ast) * 32); + res = test_routine_keywords(&bucket); + Toy_freeBucket(&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_vm.c b/tests/cases/test_vm.c index a64b580..bf8b641 100644 --- a/tests/cases/test_vm.c +++ b/tests/cases/test_vm.c @@ -4,8 +4,10 @@ #include "toy_lexer.h" #include "toy_parser.h" #include "toy_bytecode.h" +#include "toy_print.h" #include +#include #include //utils @@ -161,6 +163,59 @@ int test_opcode_not_equal(Toy_Bucket** bucketHandle) { return 0; } +static char* callbackUtilReceived = NULL; +static void callbackUtil(const char* msg) { + if (msg != NULL) { + free(callbackUtilReceived); + callbackUtilReceived = (char*)malloc(strlen(msg) + 1); + strcpy(callbackUtilReceived, msg); + } +} + +int test_keywords(Toy_Bucket** bucketHandle) { + //test print + { + //setup + Toy_setPrintCallback(callbackUtil); + const char* source = "print 42;"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + Toy_Bytecode bc = Toy_compileBytecode(ast); + + Toy_VM vm; + Toy_bindVM(&vm, bc.ptr); + + //run + Toy_runVM(&vm); + + //check the final state of the stack + if (callbackUtilReceived == NULL || + strcmp(callbackUtilReceived, "42") != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); + + //cleanup and return + Toy_resetPrintCallback(); + free(callbackUtilReceived); + Toy_freeVM(&vm); + return -1; + } + + //teadown + Toy_resetPrintCallback(); + free(callbackUtilReceived); + Toy_freeVM(&vm); + } + + return 0; +} + int main() { //run each test set, returning the total errors given int total = 0, res = 0; @@ -195,5 +250,15 @@ int main() { total += res; } + { + Toy_Bucket* bucket = Toy_allocateBucket(sizeof(Toy_Ast) * 32); + res = test_keywords(&bucket); + Toy_freeBucket(&bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + return total; }