From d7fda480fda367e3e63f5a68208ebae27506ee33 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 7 Aug 2022 12:26:04 +0100 Subject: [PATCH] Added assert keyword, with re-routable print and assert outputs --- source/compiler.c | 2 ++ source/interpreter.c | 51 ++++++++++++++++++++++++++++++++++++++++++-- source/interpreter.h | 9 +++++++- source/literal.c | 43 ++++++++++++++++++++++++++----------- source/literal.h | 1 + source/opcodes.h | 3 ++- source/parser.c | 20 +++++++++++++++-- 7 files changed, 110 insertions(+), 19 deletions(-) diff --git a/source/compiler.c b/source/compiler.c index a134fc0..8e4f492 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -29,6 +29,8 @@ void writeCompiler(Compiler* compiler, Node* node) { //determine node type switch(node->type) { + //TODO: more types, like variables, etc. + case NODE_LITERAL: { //ensure the literal is in the cache int index = findLiteralIndex(&compiler->literalCache, node->atomic.literal); diff --git a/source/interpreter.c b/source/interpreter.c index 059c581..c6026c7 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -6,6 +6,17 @@ #include #include +static void stdoutWrapper(const char* output) { + fprintf(stdout, output); + fprintf(stdout, "\n"); //default new line +} + +static void stderrWrapper(const char* output) { + fprintf(stderr, "Assertion failure: "); + fprintf(stderr, output); + fprintf(stderr, "\n"); //default new line +} + void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) { initLiteralArray(&interpreter->literalCache); interpreter->bytecode = bytecode; @@ -13,6 +24,9 @@ void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int leng interpreter->count = 0; initLiteralArray(&interpreter->stack); + + setInterpreterPrint(interpreter, stdoutWrapper); + setInterpreterAssert(interpreter, stderrWrapper); } void freeInterpreter(Interpreter* interpreter) { @@ -21,6 +35,15 @@ void freeInterpreter(Interpreter* interpreter) { freeLiteralArray(&interpreter->stack); } +//utilities for the host program +void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput) { + interpreter->printOutput = printOutput; +} + +void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput) { + interpreter->assertOutput = assertOutput; +} + //utils static unsigned char readByte(unsigned char* tb, int* count) { unsigned char ret = *(unsigned char*)(tb + *count); @@ -67,12 +90,30 @@ static void consumeShort(unsigned short bytes, unsigned char* tb, int* count) { } //each available statement +static bool execAssert(Interpreter* interpreter) { + Literal rhs = popLiteralArray(&interpreter->stack); + Literal lhs = popLiteralArray(&interpreter->stack); + + if (!IS_STRING(rhs)) { + printf("[internal] The interpreter's assert keyword needs a string as the second argument, received: "); + printLiteral(rhs); + printf("\n"); + return false; + } + + if (!IS_TRUTHY(lhs)) { + (*interpreter->assertOutput)(AS_STRING(rhs)); + return false; + } + + return true; +} + static bool execPrint(Interpreter* interpreter) { //print what is on top of the stack, then pop it Literal lit = popLiteralArray(&interpreter->stack); - printLiteral(lit); - printf("\n"); + printLiteralCustom(lit, interpreter->printOutput); freeLiteral(lit); @@ -193,6 +234,12 @@ static void execInterpreter(Interpreter* interpreter) { while(opcode != OP_EOF && opcode != OP_SECTION_END) { switch(opcode) { + case OP_ASSERT: + if (!execAssert(interpreter)) { + return; + } + break; + case OP_PRINT: if (!execPrint(interpreter)) { return; diff --git a/source/interpreter.h b/source/interpreter.h index 1921ff1..4903ad8 100644 --- a/source/interpreter.h +++ b/source/interpreter.h @@ -4,17 +4,24 @@ #include "literal_array.h" +typedef void (*PrintFn)(const char*); + //the interpreter acts depending on the bytecode instructions typedef struct Interpreter { LiteralArray literalCache; //generally doesn't change after initialization unsigned char* bytecode; int length; int count; - LiteralArray stack; + PrintFn printOutput; + PrintFn assertOutput; } Interpreter; void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); void freeInterpreter(Interpreter* interpreter); +//utilities for the host program +void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput); +void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput); + void runInterpreter(Interpreter* interpreter); diff --git a/source/literal.c b/source/literal.c index 2590906..91ed2d0 100644 --- a/source/literal.c +++ b/source/literal.c @@ -4,27 +4,44 @@ #include #include +static void stdoutWrapper(const char* output) { + fprintf(stdout, output); +} + void printLiteral(Literal literal) { + printLiteralCustom(literal, stdoutWrapper); +} + +void printLiteralCustom(Literal literal, void (printFn)(const char*)) { switch(literal.type) { case LITERAL_NULL: - printf("null"); - break; + printFn("null"); + break; case LITERAL_BOOLEAN: - printf(AS_BOOLEAN(literal) ? "true" : "false"); - break; + printFn(AS_BOOLEAN(literal) ? "true" : "false"); + break; - case LITERAL_INTEGER: - printf("%d", AS_INTEGER(literal)); - break; + case LITERAL_INTEGER: { + char buffer[256]; + snprintf(buffer, 256, "%d", AS_INTEGER(literal)); + printFn(buffer); + } + break; - case LITERAL_FLOAT: - printf("%g", AS_FLOAT(literal)); - break; + case LITERAL_FLOAT: { + char buffer[256]; + snprintf(buffer, 256, "%g", AS_FLOAT(literal)); + printFn(buffer); + } + break; - case LITERAL_STRING: - printf("%.*s", STRLEN(literal), AS_STRING(literal)); - break; + case LITERAL_STRING: { + char buffer[256]; + snprintf(buffer, 256, "%.*s", STRLEN(literal), AS_STRING(literal)); + printFn(buffer); + } + break; default: //should never bee seen diff --git a/source/literal.h b/source/literal.h index c13bed1..7d6be6c 100644 --- a/source/literal.h +++ b/source/literal.h @@ -60,6 +60,7 @@ typedef struct { // #define TO_FUNCTION_PTR(value) ((Literal){LITERAL_FUNCTION, { .function = (Function*)value }}) void printLiteral(Literal literal); +void printLiteralCustom(Literal literal, void (printFn)(const char*)); void freeLiteral(Literal literal); #define IS_TRUTHY(x) _isTruthy(x) diff --git a/source/opcodes.h b/source/opcodes.h index 2542fee..c16a42e 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -3,7 +3,8 @@ typedef enum Opcode { OP_EOF, - //basic operations + //basic statements + OP_ASSERT, OP_PRINT, //data diff --git a/source/parser.c b/source/parser.c index 933de54..04f1a52 100644 --- a/source/parser.c +++ b/source/parser.c @@ -486,8 +486,6 @@ static void expression(Parser* parser, Node** nodeHandle) { //statements static void printStmt(Parser* parser, Node* node) { - int line = parser->previous.line; - //set the node info node->type = NODE_UNARY; node->unary.opcode = OP_PRINT; @@ -496,6 +494,18 @@ static void printStmt(Parser* parser, Node* node) { consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of print statement"); } +static void assertStmt(Parser* parser, Node* node) { + //set the node info + node->type = NODE_BINARY; + node->unary.opcode = OP_ASSERT; + + expression(parser, &(node->binary.left)); + consume(parser, TOKEN_COMMA, "Expected ',' in assert statement"); + expression(parser, &(node->binary.right)); + + consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement"); +} + //precedence functions static void expressionStmt(Parser* parser, Node* node) { error(parser, parser->previous, "Expression statements not yet implemented"); @@ -508,6 +518,12 @@ static void statement(Parser* parser, Node* node) { return; } + //assert + if (match(parser, TOKEN_ASSERT)) { + assertStmt(parser, node); + return; + } + //default expressionStmt(parser, node); }