Added assert keyword, with re-routable print and assert outputs

This commit is contained in:
2022-08-07 12:26:04 +01:00
parent 7ff232c814
commit d7fda480fd
7 changed files with 110 additions and 19 deletions

View File

@@ -29,6 +29,8 @@ void writeCompiler(Compiler* compiler, Node* node) {
//determine node type //determine node type
switch(node->type) { switch(node->type) {
//TODO: more types, like variables, etc.
case NODE_LITERAL: { case NODE_LITERAL: {
//ensure the literal is in the cache //ensure the literal is in the cache
int index = findLiteralIndex(&compiler->literalCache, node->atomic.literal); int index = findLiteralIndex(&compiler->literalCache, node->atomic.literal);

View File

@@ -6,6 +6,17 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
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) { void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) {
initLiteralArray(&interpreter->literalCache); initLiteralArray(&interpreter->literalCache);
interpreter->bytecode = bytecode; interpreter->bytecode = bytecode;
@@ -13,6 +24,9 @@ void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int leng
interpreter->count = 0; interpreter->count = 0;
initLiteralArray(&interpreter->stack); initLiteralArray(&interpreter->stack);
setInterpreterPrint(interpreter, stdoutWrapper);
setInterpreterAssert(interpreter, stderrWrapper);
} }
void freeInterpreter(Interpreter* interpreter) { void freeInterpreter(Interpreter* interpreter) {
@@ -21,6 +35,15 @@ void freeInterpreter(Interpreter* interpreter) {
freeLiteralArray(&interpreter->stack); 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 //utils
static unsigned char readByte(unsigned char* tb, int* count) { static unsigned char readByte(unsigned char* tb, int* count) {
unsigned char ret = *(unsigned char*)(tb + *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 //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) { static bool execPrint(Interpreter* interpreter) {
//print what is on top of the stack, then pop it //print what is on top of the stack, then pop it
Literal lit = popLiteralArray(&interpreter->stack); Literal lit = popLiteralArray(&interpreter->stack);
printLiteral(lit); printLiteralCustom(lit, interpreter->printOutput);
printf("\n");
freeLiteral(lit); freeLiteral(lit);
@@ -193,6 +234,12 @@ static void execInterpreter(Interpreter* interpreter) {
while(opcode != OP_EOF && opcode != OP_SECTION_END) { while(opcode != OP_EOF && opcode != OP_SECTION_END) {
switch(opcode) { switch(opcode) {
case OP_ASSERT:
if (!execAssert(interpreter)) {
return;
}
break;
case OP_PRINT: case OP_PRINT:
if (!execPrint(interpreter)) { if (!execPrint(interpreter)) {
return; return;

View File

@@ -4,17 +4,24 @@
#include "literal_array.h" #include "literal_array.h"
typedef void (*PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions //the interpreter acts depending on the bytecode instructions
typedef struct Interpreter { typedef struct Interpreter {
LiteralArray literalCache; //generally doesn't change after initialization LiteralArray literalCache; //generally doesn't change after initialization
unsigned char* bytecode; unsigned char* bytecode;
int length; int length;
int count; int count;
LiteralArray stack; LiteralArray stack;
PrintFn printOutput;
PrintFn assertOutput;
} Interpreter; } Interpreter;
void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length);
void freeInterpreter(Interpreter* interpreter); 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); void runInterpreter(Interpreter* interpreter);

View File

@@ -4,26 +4,43 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
static void stdoutWrapper(const char* output) {
fprintf(stdout, output);
}
void printLiteral(Literal literal) { void printLiteral(Literal literal) {
printLiteralCustom(literal, stdoutWrapper);
}
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
switch(literal.type) { switch(literal.type) {
case LITERAL_NULL: case LITERAL_NULL:
printf("null"); printFn("null");
break; break;
case LITERAL_BOOLEAN: case LITERAL_BOOLEAN:
printf(AS_BOOLEAN(literal) ? "true" : "false"); printFn(AS_BOOLEAN(literal) ? "true" : "false");
break; break;
case LITERAL_INTEGER: case LITERAL_INTEGER: {
printf("%d", AS_INTEGER(literal)); char buffer[256];
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
printFn(buffer);
}
break; break;
case LITERAL_FLOAT: case LITERAL_FLOAT: {
printf("%g", AS_FLOAT(literal)); char buffer[256];
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
printFn(buffer);
}
break; break;
case LITERAL_STRING: case LITERAL_STRING: {
printf("%.*s", STRLEN(literal), AS_STRING(literal)); char buffer[256];
snprintf(buffer, 256, "%.*s", STRLEN(literal), AS_STRING(literal));
printFn(buffer);
}
break; break;
default: default:

View File

@@ -60,6 +60,7 @@ typedef struct {
// #define TO_FUNCTION_PTR(value) ((Literal){LITERAL_FUNCTION, { .function = (Function*)value }}) // #define TO_FUNCTION_PTR(value) ((Literal){LITERAL_FUNCTION, { .function = (Function*)value }})
void printLiteral(Literal literal); void printLiteral(Literal literal);
void printLiteralCustom(Literal literal, void (printFn)(const char*));
void freeLiteral(Literal literal); void freeLiteral(Literal literal);
#define IS_TRUTHY(x) _isTruthy(x) #define IS_TRUTHY(x) _isTruthy(x)

View File

@@ -3,7 +3,8 @@
typedef enum Opcode { typedef enum Opcode {
OP_EOF, OP_EOF,
//basic operations //basic statements
OP_ASSERT,
OP_PRINT, OP_PRINT,
//data //data

View File

@@ -486,8 +486,6 @@ static void expression(Parser* parser, Node** nodeHandle) {
//statements //statements
static void printStmt(Parser* parser, Node* node) { static void printStmt(Parser* parser, Node* node) {
int line = parser->previous.line;
//set the node info //set the node info
node->type = NODE_UNARY; node->type = NODE_UNARY;
node->unary.opcode = OP_PRINT; 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"); 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 //precedence functions
static void expressionStmt(Parser* parser, Node* node) { static void expressionStmt(Parser* parser, Node* node) {
error(parser, parser->previous, "Expression statements not yet implemented"); error(parser, parser->previous, "Expression statements not yet implemented");
@@ -508,6 +518,12 @@ static void statement(Parser* parser, Node* node) {
return; return;
} }
//assert
if (match(parser, TOKEN_ASSERT)) {
assertStmt(parser, node);
return;
}
//default //default
expressionStmt(parser, node); expressionStmt(parser, node);
} }