From 7a3986af33a840bb590bf40139d76d0033d7115b Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 6 Aug 2022 07:58:32 +0100 Subject: [PATCH] Wrote the interpreter --- source/compiler.c | 8 +- source/debug.c | 2 +- source/interpreter.c | 229 +++++++++++++++++++++++++++++++++++++++++ source/interpreter.h | 20 ++++ source/literal.c | 14 +-- source/literal.h | 6 +- source/literal_array.c | 20 +++- source/literal_array.h | 4 +- source/makefile | 2 +- source/repl_main.c | 14 +-- 10 files changed, 292 insertions(+), 27 deletions(-) create mode 100644 source/interpreter.c create mode 100644 source/interpreter.h diff --git a/source/compiler.c b/source/compiler.c index b650714..a134fc0 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -13,9 +13,9 @@ void initCompiler(Compiler* compiler) { Literal t = TO_BOOLEAN_LITERAL(true); Literal f = TO_BOOLEAN_LITERAL(false); - writeLiteralArray(&compiler->literalCache, n); - writeLiteralArray(&compiler->literalCache, t); - writeLiteralArray(&compiler->literalCache, f); + pushLiteralArray(&compiler->literalCache, n); + pushLiteralArray(&compiler->literalCache, t); + pushLiteralArray(&compiler->literalCache, f); } void writeCompiler(Compiler* compiler, Node* node) { @@ -33,7 +33,7 @@ void writeCompiler(Compiler* compiler, Node* node) { //ensure the literal is in the cache int index = findLiteralIndex(&compiler->literalCache, node->atomic.literal); if (index < 0) { - index = writeLiteralArray(&compiler->literalCache, node->atomic.literal); + index = pushLiteralArray(&compiler->literalCache, node->atomic.literal); } //push the node opcode to the bytecode diff --git a/source/debug.c b/source/debug.c index f07c8b0..3999550 100644 --- a/source/debug.c +++ b/source/debug.c @@ -51,7 +51,7 @@ static void consumeByte(unsigned char byte, const char* str, int* count) { static void consumeShort(unsigned short bytes, const char* str, int* count) { if (bytes != *(unsigned short*)(str + *count)) { - printf("Failed to consume the correct byte"); + printf("Failed to consume the correct bytes"); } *count += 2; } diff --git a/source/interpreter.c b/source/interpreter.c new file mode 100644 index 0000000..e0fdfb0 --- /dev/null +++ b/source/interpreter.c @@ -0,0 +1,229 @@ +#include "interpreter.h" + +#include "common.h" +#include "memory.h" + +#include +#include + +void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) { + initLiteralArray(&interpreter->literalCache); + interpreter->bytecode = bytecode; + interpreter->length = length; + interpreter->count = 0; + + initLiteralArray(&interpreter->stack); +} + +void freeInterpreter(Interpreter* interpreter) { + freeLiteralArray(&interpreter->literalCache); + FREE_ARRAY(char, interpreter->bytecode, interpreter->length); + freeLiteralArray(&interpreter->stack); +} + +//utils +static unsigned char readByte(unsigned char* tb, int* count) { + unsigned char ret = *(unsigned char*)(tb + *count); + *count += 1; + return ret; +} + +static unsigned short readShort(unsigned char* tb, int* count) { + unsigned short ret = *(unsigned short*)(tb + *count); + *count += 2; + return ret; +} + +static int readInt(unsigned char* tb, int* count) { + int ret = *(int*)(tb + *count); + *count += 4; + return ret; +} + +static float readFloat(unsigned char* tb, int* count) { + float ret = *(float*)(tb + *count); + *count += 4; + return ret; +} + +static char* readString(unsigned char* tb, int* count) { + char* ret = tb + *count; + *count += strlen(ret) + 1; //+1 for null character + return ret; +} + +static void consumeByte(unsigned char byte, unsigned char* tb, int* count) { + if (byte != tb[*count]) { + printf("Failed to consume the correct byte"); + } + *count += 1; +} + +static void consumeShort(unsigned short bytes, unsigned char* tb, int* count) { + if (bytes != *(unsigned short*)(tb + *count)) { + printf("Failed to consume the correct bytes"); + } + *count += 2; +} + +//each available statement +static void execPrint(Interpreter* interpreter) { + //print what is on top of the stack, then pop it + Literal lit = popLiteralArray(&interpreter->stack); + + printLiteral(lit); + printf("\n"); + + freeLiteral(lit); +} + +static void execPushLiteral(Interpreter* interpreter, bool lng) { + //read the index in the cache + int index = 0; + + if (lng) { + index = (int)readShort(interpreter->bytecode, &interpreter->count); + } + else { + index = (int)readByte(interpreter->bytecode, &interpreter->count); + } + + //push from cache to stack + pushLiteralArray(&interpreter->stack, interpreter->literalCache.literals[index]); +} + +static void execNegate(Interpreter* interpreter) { + //negate the top literal on the stack + Literal lit = popLiteralArray(&interpreter->stack); + + if (IS_INTEGER(lit)) { + lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit)); + } + else if (IS_FLOAT(lit)) { + lit = TO_FLOAT_LITERAL(-AS_FLOAT(lit)); + } + else { + printf("[internal] The interpreter can't negate that literal: "); + printLiteral(lit); + printf("\n"); + } + + pushLiteralArray(&interpreter->stack, lit); +} + +//the heart of toy +static void execInterpreter(Interpreter* interpreter) { + unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); + + while(opcode != OP_EOF && opcode != OP_SECTION_END) { + switch(opcode) { + case OP_PRINT: + execPrint(interpreter); + break; + + case OP_LITERAL: + case OP_LITERAL_LONG: + execPushLiteral(interpreter, opcode == OP_LITERAL_LONG); + break; + + default: + printf("Unknown opcode found %d, terminating\n", opcode); + printLiteralArray(&interpreter->stack, "\n"); + return; + } + + opcode = readByte(interpreter->bytecode, &interpreter->count); + } +} + +void runInterpreter(Interpreter* interpreter) { + //header section + const unsigned char major = readByte(interpreter->bytecode, &interpreter->count); + const unsigned char minor = readByte(interpreter->bytecode, &interpreter->count); + const unsigned char patch = readByte(interpreter->bytecode, &interpreter->count); + const char* build = readString(interpreter->bytecode, &interpreter->count); + + if (command.verbose) { + if (major != TOY_VERSION_MAJOR || minor != TOY_VERSION_MINOR || patch != TOY_VERSION_PATCH) { + printf("Warning: interpreter/bytecode version mismatch\n"); + } + + if (!strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD))) { + printf("Warning: interpreter/bytecode build mismatch\n"); + } + } + + consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count); + + //data section + const short literalCount = readShort(interpreter->bytecode, &interpreter->count); + + if (command.verbose) { + printf("Reading %d literals\n", literalCount); + } + + for (int i = 0; i < literalCount; i++) { + const unsigned char literalType = readByte(interpreter->bytecode, &interpreter->count); + + switch(literalType) { + case LITERAL_NULL: + //read the null + pushLiteralArray(&interpreter->literalCache, TO_NULL_LITERAL); + + if (command.verbose) { + printf("(null)\n"); + } + break; + + case LITERAL_BOOLEAN: { + //read the booleans + const bool b = readByte(interpreter->bytecode, &interpreter->count); + pushLiteralArray(&interpreter->literalCache, TO_BOOLEAN_LITERAL(b)); + + if (command.verbose) { + printf("(boolean %s)\n", b ? "true" : "false"); + } + } + break; + + case LITERAL_INTEGER: { + const int d = readInt(interpreter->bytecode, &interpreter->count); + pushLiteralArray(&interpreter->literalCache, TO_INTEGER_LITERAL(d)); + + if (command.verbose) { + printf("(integer %d)\n", d); + } + } + break; + + case LITERAL_FLOAT: { + const float f = readFloat(interpreter->bytecode, &interpreter->count); + pushLiteralArray(&interpreter->literalCache, TO_FLOAT_LITERAL(f)); + + if (command.verbose) { + printf("(float %f)\n", f); + } + } + break; + + case LITERAL_STRING: { + char* s = readString(interpreter->bytecode, &interpreter->count); + pushLiteralArray(&interpreter->literalCache, TO_STRING_LITERAL(s)); + + if (command.verbose) { + printf("(string \"%s\")\n", s); + } + } + break; + } + } + + consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count); + + //code section + if (command.verbose) { + printf("executing bytecode\n"); + } + + execInterpreter(interpreter); +} diff --git a/source/interpreter.h b/source/interpreter.h new file mode 100644 index 0000000..1921ff1 --- /dev/null +++ b/source/interpreter.h @@ -0,0 +1,20 @@ +#pragma once + +#include "opcodes.h" + +#include "literal_array.h" + +//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; +} Interpreter; + +void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); +void freeInterpreter(Interpreter* interpreter); + +void runInterpreter(Interpreter* interpreter); diff --git a/source/literal.c b/source/literal.c index 208bdc1..2590906 100644 --- a/source/literal.c +++ b/source/literal.c @@ -7,27 +7,23 @@ void printLiteral(Literal literal) { switch(literal.type) { case LITERAL_NULL: - printf("null\n"); + printf("null"); break; case LITERAL_BOOLEAN: - printf(AS_BOOLEAN(literal) ? "true\n" : "false\n"); + printf(AS_BOOLEAN(literal) ? "true" : "false"); break; case LITERAL_INTEGER: - printf("%d\n", AS_INTEGER(literal)); + printf("%d", AS_INTEGER(literal)); break; case LITERAL_FLOAT: - printf("%g\n", AS_FLOAT(literal)); + printf("%g", AS_FLOAT(literal)); break; case LITERAL_STRING: - printf("%.*s (%d)\n", STRLEN(literal), AS_STRING(literal), STRLEN(literal)); - break; - - case LITERAL_FUNCTION: - printf("\n"); + printf("%.*s", STRLEN(literal), AS_STRING(literal)); break; default: diff --git a/source/literal.h b/source/literal.h index 9c4d881..c13bed1 100644 --- a/source/literal.h +++ b/source/literal.h @@ -10,9 +10,9 @@ typedef enum { LITERAL_INTEGER, LITERAL_FLOAT, LITERAL_STRING, - LITERAL_ARRAY, - LITERAL_DICTIONARY, - LITERAL_FUNCTION, + // LITERAL_ARRAY, + // LITERAL_DICTIONARY, + // LITERAL_FUNCTION, } LiteralType; typedef struct { diff --git a/source/literal_array.c b/source/literal_array.c index 6181e31..e77308e 100644 --- a/source/literal_array.c +++ b/source/literal_array.c @@ -12,7 +12,7 @@ void initLiteralArray(LiteralArray* array) { array->literals = NULL; } -int writeLiteralArray(LiteralArray* array, Literal literal) { +int pushLiteralArray(LiteralArray* array, Literal literal) { if (array->capacity < array->count + 1) { int oldCapacity = array->capacity; @@ -29,6 +29,17 @@ int writeLiteralArray(LiteralArray* array, Literal literal) { return array->count++; } +Literal popLiteralArray(LiteralArray* array) { + //get the return + Literal ret = array->literals[array->count-1]; + + //null the existing data + array->literals[array->count-1] = TO_NULL_LITERAL; + + array->count--; + return ret; +} + void freeLiteralArray(LiteralArray* array) { //clean up memory for(int i = 0; i < array->count; i++) { @@ -84,3 +95,10 @@ int findLiteralIndex(LiteralArray* array, Literal literal) { return -1; } + +void printLiteralArray(LiteralArray* array, const char* delim) { + for (int i = 0; i < array->count; i++) { + printLiteral(array->literals[i]); + printf("%s", delim); + } +} \ No newline at end of file diff --git a/source/literal_array.h b/source/literal_array.h index 9039bf6..29a7e24 100644 --- a/source/literal_array.h +++ b/source/literal_array.h @@ -9,8 +9,10 @@ typedef struct LiteralArray { } LiteralArray; void initLiteralArray(LiteralArray* array); -int writeLiteralArray(LiteralArray* array, Literal literal); +int pushLiteralArray(LiteralArray* array, Literal literal); +Literal popLiteralArray(LiteralArray* array); void freeLiteralArray(LiteralArray* array); int findLiteralIndex(LiteralArray* array, Literal literal); +void printLiteralArray(LiteralArray* array, const char* delim); \ No newline at end of file diff --git a/source/makefile b/source/makefile index 2eef851..0f2c134 100644 --- a/source/makefile +++ b/source/makefile @@ -1,7 +1,7 @@ CC=gcc IDIR =. -CFLAGS=$(addprefix -I,$(IDIR)) -g -Wall -W -pedantic +CFLAGS=$(addprefix -I,$(IDIR)) -g # -Wall -W -pedantic LIBS= ODIR=obj diff --git a/source/repl_main.c b/source/repl_main.c index ab5380e..f2edc56 100644 --- a/source/repl_main.c +++ b/source/repl_main.c @@ -3,7 +3,7 @@ #include "lexer.h" #include "parser.h" #include "compiler.h" -//#include "toy.h" +#include "interpreter.h" #include "memory.h" @@ -135,6 +135,7 @@ void debug() { Lexer lexer; Parser parser; Compiler compiler; + Interpreter interpreter; char* source = readFile(command.filename); @@ -146,22 +147,21 @@ void debug() { Node* node = scanParser(&parser); while(node != NULL) { writeCompiler(&compiler, node); - freeNode(node); - node = scanParser(&parser); } //get the data dump int size = 0; - const char* tb = collateCompiler(&compiler, &size); - - dissectBytecode(tb, size); + char* tb = collateCompiler(&compiler, &size); //cleanup - FREE_ARRAY(char, tb, size); freeCompiler(&compiler); freeParser(&parser); + + initInterpreter(&interpreter, tb, size); + runInterpreter(&interpreter); + freeInterpreter(&interpreter); } //entry point