mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Wrote the interpreter
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
229
source/interpreter.c
Normal file
229
source/interpreter.c
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "interpreter.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
20
source/interpreter.h
Normal file
20
source/interpreter.h
Normal file
@@ -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);
|
||||
@@ -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("<toy function>\n");
|
||||
printf("%.*s", STRLEN(literal), AS_STRING(literal));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user