mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Added assert keyword, with re-routable print and assert outputs
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user