Breaking up functionality into more modules.

This commit is contained in:
Calvin Rose 2017-02-23 17:21:13 -05:00
parent 5ec6e46f1a
commit fd34837265
19 changed files with 701 additions and 387 deletions

15
.gitignore vendored
View File

@ -60,3 +60,18 @@ Mkfile.old
dkms.conf
# End of https://www.gitignore.io/api/c
# Created by https://www.gitignore.io/api/cmake
### CMake ###
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
# End of https://www.gitignore.io/api/cmake

2
.idea/interpreter.iml Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

4
.idea/misc.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/interpreter.iml" filepath="$PROJECT_DIR$/.idea/interpreter.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

211
.idea/workspace.xml Normal file
View File

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeRunConfigurationManager" shouldGenerate="true" buildAllGenerated="true">
<generated>
<config projectName="interpreter" targetName="interpreter" />
</generated>
</component>
<component name="CMakeSettings">
<configurations>
<configuration CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="aa7ed09b-d1cc-4d38-bb04-299b1e1504d1" name="Default" comment="">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/misc.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/vcs.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/main.c" afterPath="$PROJECT_DIR$/main.c" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/parse.c" afterPath="$PROJECT_DIR$/parse.c" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/vm.h" afterPath="$PROJECT_DIR$/vm.h" />
</list>
<ignored path="$PROJECT_DIR$/cmake-build-debug/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="CMakeLists.txt" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CMakeLists.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45">
<caret line="3" column="13" lean-forward="false" selection-start-line="3" selection-start-column="13" selection-end-line="3" selection-end-column="13" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="ds.c" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/ds.c">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-918">
<caret line="31" column="54" lean-forward="false" selection-start-line="31" selection-start-column="54" selection-end-line="31" selection-end-column="54" />
<folding>
<element signature="e#0#15#0" expanded="true" />
<element signature="e#435#821#0" expanded="false" />
<element signature="e#823#1032#0" expanded="false" />
<element signature="e#1034#1280#0" expanded="false" />
<element signature="e#1282#1642#0" expanded="false" />
<element signature="e#1644#2007#0" expanded="false" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="-1" />
<option name="y" value="25" />
<option name="width" value="1922" />
<option name="height" value="1060" />
</component>
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="interpreter" />
<option name="myItemType" value="com.jetbrains.cidr.projectView.CidrFilesViewHelper$MyProjectTreeStructure$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="interpreter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
</component>
<component name="RunManager" selected="Application.Build All">
<configuration default="true" type="CMakeRunConfiguration" factoryName="Application" PASS_PARENT_ENVS_2="true" PROJECT_NAME="interpreter" TARGET_NAME="interpreter" CONFIG_NAME="Debug">
<envs />
<method />
</configuration>
<configuration default="false" name="interpreter" type="CMakeRunConfiguration" factoryName="Application" PASS_PARENT_ENVS_2="true" PROJECT_NAME="interpreter" TARGET_NAME="interpreter" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="interpreter" RUN_TARGET_NAME="interpreter">
<envs />
<method />
</configuration>
<configuration default="false" name="Build All" type="CMakeRunConfiguration" factoryName="Application" WORKING_DIR="" PASS_PARENT_ENVS_2="true" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="interpreter" RUN_TARGET_NAME="interpreter" EXPLICIT_BUILD_TARGET_NAME="all">
<envs />
<method />
</configuration>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="Application.interpreter" />
<item index="1" class="java.lang.String" itemvalue="Application.Build All" />
</list>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="aa7ed09b-d1cc-4d38-bb04-299b1e1504d1" name="Default" comment="" />
<created>1487818367037</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1487818367037</updated>
<workItem from="1487818369430" duration="237000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="237000" />
</component>
<component name="ToolWindowManager">
<frame x="-1" y="25" width="1922" height="1060" extended-state="6" />
<editor active="false" />
<layout>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.18177083" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.24816754" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="CMake" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="LuaJ" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32984293" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="processedProjectFiles" value="true" />
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager />
<watches-manager />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/CMakeLists.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45">
<caret line="3" column="13" lean-forward="false" selection-start-line="3" selection-start-column="13" selection-end-line="3" selection-end-column="13" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/ds.c">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-918">
<caret line="31" column="54" lean-forward="false" selection-start-line="31" selection-start-column="54" selection-end-line="31" selection-end-column="54" />
<folding>
<element signature="e#0#15#0" expanded="true" />
<element signature="e#435#821#0" expanded="false" />
<element signature="e#823#1032#0" expanded="false" />
<element signature="e#1034#1280#0" expanded="false" />
<element signature="e#1282#1642#0" expanded="false" />
<element signature="e#1644#2007#0" expanded="false" />
</folding>
</state>
</provider>
</entry>
</component>
</project>

View File

@ -1,13 +1,13 @@
# TIL
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -O3
TARGET=interp
PREFIX=/usr/local
# C sources
HEADERS=vm.h ds.h compile.h parse.h value.h disasm.h datatypes.h
SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c
HEADERS=vm.h ds.h compile.h parse.h value.h disasm.h datatypes.h gc.h thread.h
SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c gc.c thread.c
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET)

View File

@ -164,8 +164,6 @@ struct Gst {
jmp_buf jump;
GstValue error;
GstValue ret; /* Returned value from VMStart. Also holds errors. */
/* Object definitions */
GstValue metas[GST_OBJECT];
};
struct GstParser {

232
gc.c Normal file
View File

@ -0,0 +1,232 @@
#include "datatypes.h"
#include "gc.h"
#include "vm.h"
#include "thread.h"
#include <stdlib.h>
/* The metadata header associated with an allocated block of memory */
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1)
/* Memory header struct. Node of a linked list of memory blocks. */
typedef struct GCMemoryHeader GCMemoryHeader;
struct GCMemoryHeader {
GCMemoryHeader * next;
uint32_t color : 1;
};
/* Helper to mark function environments */
static void gst_mark_funcenv(Gst *vm, GstFuncEnv *env) {
if (gc_header(env)->color != vm->black) {
GstValue temp;
gc_header(env)->color = vm->black;
if (env->thread) {
temp.type = GST_THREAD;
temp.data.thread = env->thread;
gst_mark(vm, &temp);
}
if (env->values) {
uint32_t count = env->stackOffset;
uint32_t i;
gc_header(env->values)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark(vm, env->values + i);
}
}
}
/* GC helper to mark a FuncDef */
static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
if (gc_header(def)->color != vm->black) {
gc_header(def)->color = vm->black;
gc_header(def->byteCode)->color = vm->black;
uint32_t count, i;
if (def->literals) {
count = def->literalsLen;
gc_header(def->literals)->color = vm->black;
for (i = 0; i < count; ++i) {
/* If the literal is a NIL type, it actually
* contains a FuncDef */
if (def->literals[i].type == GST_NIL) {
gst_mark_funcdef(vm, (GstFuncDef *) def->literals[i].data.pointer);
} else {
gst_mark(vm, def->literals + i);
}
}
}
}
}
/* Helper to mark a stack frame. Returns the next frame. */
static GstStackFrame *gst_mark_stackframe(Gst *vm, GstStackFrame *frame) {
uint32_t i;
GstValue *stack = (GstValue *)frame + GST_FRAME_SIZE;
gst_mark(vm, &frame->callee);
if (frame->env)
gst_mark_funcenv(vm, frame->env);
for (i = 0; i < frame->size; ++i)
gst_mark(vm, stack + i);
return (GstStackFrame *)(stack + frame->size);
}
/* Mark allocated memory associated with a value. This is
* the main function for doing the garbage collection mark phase. */
void gst_mark(Gst *vm, GstValue *x) {
switch (x->type) {
case GST_NIL:
case GST_BOOLEAN:
case GST_NUMBER:
case GST_CFUNCTION:
break;
case GST_STRING:
gc_header(gst_string_raw(x->data.string))->color = vm->black;
break;
case GST_BYTEBUFFER:
gc_header(x->data.buffer)->color = vm->black;
gc_header(x->data.buffer->data)->color = vm->black;
break;
case GST_ARRAY:
if (gc_header(x->data.array)->color != vm->black) {
uint32_t i, count;
count = x->data.array->count;
gc_header(x->data.array)->color = vm->black;
gc_header(x->data.array->data)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark(vm, x->data.array->data + i);
}
break;
case GST_THREAD:
if (gc_header(x->data.thread)->color != vm->black) {
GstThread *thread = x->data.thread;
GstStackFrame *frame = (GstStackFrame *)thread->data;
GstStackFrame *end = gst_thread_frame(thread);
gc_header(thread)->color = vm->black;
gc_header(thread->data)->color = vm->black;
while (frame <= end)
frame = gst_mark_stackframe(vm, frame);
}
break;
case GST_FUNCTION:
if (gc_header(x->data.function)->color != vm->black) {
GstFunction *f = x->data.function;
gc_header(f)->color = vm->black;
gst_mark_funcdef(vm, f->def);
if (f->env)
gst_mark_funcenv(vm, f->env);
if (f->parent) {
GstValue temp;
temp.type = GST_FUNCTION;
temp.data.function = f->parent;
gst_mark(vm, &temp);
}
}
break;
case GST_OBJECT:
if (gc_header(x->data.object)->color != vm->black) {
uint32_t i;
GstBucket *bucket;
gc_header(x->data.object)->color = vm->black;
gc_header(x->data.object->buckets)->color = vm->black;
for (i = 0; i < x->data.object->capacity; ++i) {
bucket = x->data.object->buckets[i];
while (bucket) {
gc_header(bucket)->color = vm->black;
gst_mark(vm, &bucket->key);
gst_mark(vm, &bucket->value);
bucket = bucket->next;
}
}
}
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void gst_sweep(Gst *vm) {
GCMemoryHeader *previous = NULL;
GCMemoryHeader *current = vm->blocks;
GCMemoryHeader *next;
while (current) {
next = current->next;
if (current->color != vm->black) {
if (previous) {
previous->next = next;
} else {
vm->blocks = next;
}
free(current);
} else {
previous = current;
}
current = next;
}
/* Rotate flag */
vm->black = !vm->black;
}
/* Prepare a memory block */
static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
GCMemoryHeader *mdata;
if (rawBlock == NULL) {
gst_crash(vm, "out of memory");
}
vm->nextCollection += size;
mdata = (GCMemoryHeader *)rawBlock;
mdata->next = vm->blocks;
vm->blocks = mdata;
mdata->color = !vm->black;
return rawBlock + sizeof(GCMemoryHeader);
}
/* Allocate some memory that is tracked for garbage collection */
void *gst_alloc(Gst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return gst_alloc_prepare(vm, malloc(totalSize), totalSize);
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void *gst_zalloc(Gst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return gst_alloc_prepare(vm, calloc(1, totalSize), totalSize);
}
/* Run garbage collection */
void gst_collect(Gst *vm) {
if (vm->lock > 0) return;
/* Thread can be null */
if (vm->thread) {
GstValue thread;
thread.type = GST_THREAD;
thread.data.thread = vm->thread;
gst_mark(vm, &thread);
}
gst_mark(vm, &vm->ret);
gst_mark(vm, &vm->error);
gst_sweep(vm);
vm->nextCollection = 0;
}
/* Run garbage collection if needed */
void gst_maybe_collect(Gst *vm) {
if (vm->nextCollection >= vm->memoryInterval)
gst_collect(vm);
}
/* Free all allocated memory */
void gst_clear_memory(Gst *vm) {
GCMemoryHeader *current = vm->blocks;
while (current) {
GCMemoryHeader *next = current->next;
free(current);
current = next;
}
vm->blocks = NULL;
}

29
gc.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef gc_h_INCLUDED
#define gc_h_INCLUDED
#include "datatypes.h"
/* Makr a value as reachable */
void gst_mark(Gst *vm, GstValue *x);
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void gst_sweep(Gst *vm);
/* Allocate a chunk memory that will be garbage collected. */
void *gst_alloc(Gst *vm, uint32_t size);
/* Allocate zeroed memory to be garbage collected */
void *gst_zalloc(Gst *vm, uint32_t size);
/* Run a collection */
void gst_collect(Gst *vm);
/* Run a collection if we have alloctaed enough memory since the last
collection */
void gst_maybe_collect(Gst *vm);
/* Clear all memory */
void gst_clear_memory(Gst *vm);
#endif

61
main.c
View File

@ -7,11 +7,11 @@
#include "value.h"
#include "disasm.h"
void string_put(uint8_t * string) {
void string_put(FILE *out, uint8_t * string) {
uint32_t i;
uint32_t len = gst_string_length(string);
for (i = 0; i < len; ++i)
fputc(string[i], stdout);
fputc(string[i], out);
}
/* Test c function */
@ -20,7 +20,7 @@ GstValue print(Gst *vm) {
GstValue nil;
count = gst_count_args(vm);
for (j = 0; j < count; ++j) {
string_put(gst_to_string(vm, gst_arg(vm, j)));
string_put(stdout, gst_to_string(vm, gst_arg(vm, j)));
fputc('\n', stdout);
}
nil.type = GST_NIL;
@ -28,7 +28,7 @@ GstValue print(Gst *vm) {
}
/* A simple repl for debugging */
void debugRepl() {
void debug_repl(FILE *in, FILE *out) {
char buffer[1024] = {0};
const char * reader = buffer;
GstValue func;
@ -47,8 +47,9 @@ void debugRepl() {
while (p.status == GST_PARSER_PENDING) {
/* Get some input if we are done */
if (*reader == '\0') {
printf(">> ");
if (!fgets(buffer, sizeof(buffer), stdin)) {
if (out)
fprintf(out, ">> ");
if (!fgets(buffer, sizeof(buffer), in)) {
return;
}
p.index = 0;
@ -60,13 +61,15 @@ void debugRepl() {
/* Check for parsing errors */
if (p.error) {
unsigned i;
printf("\n");
printf("%s\n", buffer);
for (i = 0; i < p.index; ++i) {
printf(" ");
if (out) {
fprintf(out, "\n");
fprintf(out, "%s\n", buffer);
for (i = 0; i < p.index; ++i) {
fprintf(out, " ");
}
fprintf(out, "^\n");
fprintf(out, "\nParse error: %s\n", p.error);
}
printf("^\n");
printf("\nParse error: %s\n", p.error);
reader = buffer; /* Flush the input buffer */
buffer[0] = '\0';
continue;
@ -80,33 +83,39 @@ void debugRepl() {
/* Check for compilation errors */
if (c.error) {
printf("Compiler error: %s\n", c.error);
if (out) {
fprintf(out, "Compiler error: %s\n", c.error);
}
reader = buffer;
buffer[0] = 0;
continue;
}
/* Print asm */
/* printf("\n");
gst_dasm_function(stdout, func.data.function);
printf("\n");*/
if (out) {
fprintf(out, "\n");
gst_dasm_function(out, func.data.function);
fprintf(out, "\n");
}
/* Execute function */
gst_load(&vm, func);
if (gst_start(&vm)) {
if (vm.crash) {
printf("VM crash: %s\n", vm.crash);
} else {
printf("VM error: ");
string_put(gst_to_string(&vm, vm.error));
printf("\n");
if (out) {
if (vm.crash) {
fprintf(out, "VM crash: %s\n", vm.crash);
} else {
fprintf(out, "VM error: ");
string_put(out, gst_to_string(&vm, vm.error));
printf("\n");
}
}
reader = buffer;
buffer[0] = 0;
continue;
} else {
string_put(gst_to_string(&vm, vm.ret));
printf("\n");
} else if (out) {
string_put(out, gst_to_string(&vm, vm.ret));
fprintf(out, "\n");
}
}
@ -114,6 +123,6 @@ void debugRepl() {
int main() {
printf("Super cool interpreter v0.0\n");
debugRepl();
debug_repl(stdin, stdout);
return 0;
}

View File

@ -44,7 +44,7 @@ struct GstParseState {
/* Get the top ParseState in the parse stack */
static GstParseState *parser_peek(GstParser *p) {
if (!p->count) {
p_error(p, "Parser stack underflow. (Peek)");
p_error(p, "parser stack underflow");
return NULL;
}
return p->data + p->count - 1;
@ -53,7 +53,7 @@ static GstParseState *parser_peek(GstParser *p) {
/* Remove the top state from the ParseStack */
static GstParseState *parser_pop(GstParser * p) {
if (!p->count) {
p_error(p, "Parser stack underflow. (Pop)");
p_error(p, "parser stack underflow");
return NULL;
}
return p->data + --p->count;

View File

@ -1 +1,10 @@
(do (:= a 0) (while (< a 10) (:= a (+ a 1)) (print a)) a)
(do
(:= fib (fn (n)
(if (< n 2)
1
(+ (fib (- n 1)) (fib (- n 2))))))
(print 10)
(print (fib 33))
)

78
thread.c Normal file
View File

@ -0,0 +1,78 @@
#include "thread.h"
#include "vm.h"
#include <string.h>
/* Get the stack frame pointer for a thread */
GstStackFrame *gst_thread_frame(GstThread * thread) {
return (GstStackFrame *)(thread->data + thread->count - GST_FRAME_SIZE);
}
/* Ensure that a thread has enough space in it */
void gst_thread_ensure(Gst *vm, GstThread *thread, uint32_t size) {
if (size > thread->capacity) {
uint32_t newCap = size * 2;
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
memcpy(newData, thread->data, thread->capacity * sizeof(GstValue));
thread->data = newData;
thread->capacity = newCap;
}
}
/* Push a stack frame onto a thread */
void gst_thread_push(Gst *vm, GstThread *thread, GstValue callee, uint32_t size) {
uint16_t oldSize;
uint32_t nextCount, i;
GstStackFrame *frame;
if (thread->count) {
frame = gst_thread_frame(thread);
oldSize = frame->size;
} else {
oldSize = 0;
}
nextCount = thread->count + oldSize + GST_FRAME_SIZE;
gst_thread_ensure(vm, thread, nextCount + size);
thread->count = nextCount;
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + size; ++i)
thread->data[i].type = GST_NIL;
vm->base = thread->data + thread->count;
vm->frame = frame = (GstStackFrame *)(vm->base - GST_FRAME_SIZE);
/* Set up the new stack frame */
frame->prevSize = oldSize;
frame->size = size;
frame->env = NULL;
frame->callee = callee;
frame->errorJump = NULL;
}
/* Copy the current function stack to the current closure
environment. Call when exiting function with closures. */
void gst_thread_split_env(Gst *vm) {
GstStackFrame *frame = vm->frame;
GstFuncEnv *env = frame->env;
/* Check for closures */
if (env) {
GstThread *thread = vm->thread;
uint32_t size = frame->size;
env->thread = NULL;
env->stackOffset = size;
env->values = gst_alloc(vm, sizeof(GstValue) * size);
memcpy(env->values, thread->data + thread->count, size * sizeof(GstValue));
}
}
/* Pop the top-most stack frame from stack */
void gst_thread_pop(Gst *vm) {
GstThread *thread = vm->thread;
GstStackFrame *frame = vm->frame;
uint32_t delta = GST_FRAME_SIZE + frame->prevSize;
if (thread->count) {
gst_thread_split_env(vm);
} else {
gst_crash(vm, "stack underflow");
}
thread->count -= delta;
vm->base -= delta;
vm->frame = (GstStackFrame *)(vm->base - GST_FRAME_SIZE);
}

25
thread.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef THREAD_H
#define THREAD_H
#include "datatypes.h"
/* The size of a StackFrame in units of Values. */
#define GST_FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) - 1) / sizeof(GstValue))
/* Get the stack frame pointer for a thread */
GstStackFrame *gst_thread_frame(GstThread * thread);
/* Ensure that a thread has enough space in it */
void gst_thread_ensure(Gst *vm, GstThread *thread, uint32_t size);
/* Push a stack frame onto a thread */
void gst_thread_push(Gst *vm, GstThread *thread, GstValue callee, uint32_t size);
/* Copy the current function stack to the current closure
environment. Call when exiting function with closures. */
void gst_thread_split_env(Gst *vm);
/* Pop the top-most stack frame from stack */
void gst_thread_pop(Gst *vm);
#endif

16
value.c
View File

@ -5,6 +5,11 @@
#include "ds.h"
#include "vm.h"
/* Boolean truth definition */
int gst_truthy(GstValue v) {
return v.type != GST_NIL && !(v.type == GST_BOOLEAN && !v.data.boolean);
}
static uint8_t * load_cstring(Gst *vm, const char *string, uint32_t len) {
uint8_t *data = gst_alloc(vm, len + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
@ -344,13 +349,4 @@ void gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
default:
gst_error(vm, "Cannot set.");
}
}
/* Get the meta value associated with a value */
GstValue gst_meta(Gst *vm, GstValue x) {
switch (x.type) {
default: return vm->metas[x.type];
case GST_OBJECT:
return x.data.object->meta;
}
}
}

View File

@ -3,6 +3,9 @@
#include "datatypes.h"
/* Check for boolean truthiness */
int gst_truthy(GstValue x);
/* Compare two gst values. All gst values are comparable and strictly
* ordered by default. Return 0 if equal, -1 if x is less than y, and
* 1 and x is greater than y. */
@ -26,7 +29,4 @@ uint8_t *gst_to_string(Gst *vm, GstValue x);
/* Generate a hash value for a gst object */
uint32_t gst_hash(GstValue x);
/* Get the meta value for a given value */
GstValue gst_meta(Gst *vm, GstValue x);
#endif /* end of include guard: VALUE_H_1RJPQKFM */

370
vm.c
View File

@ -4,310 +4,13 @@
#include "vm.h"
#include "value.h"
#include "ds.h"
#include "gc.h"
#include "thread.h"
static const char OOM[] = "out of memory";
static const char NO_UPVALUE[] = "no upvalue";
static const char EXPECTED_FUNCTION[] = "expected function";
static const char VMS_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
static const char VMS_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
/* The size of a StackFrame in units of Values. */
#define FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) - 1) / sizeof(GstValue))
/* Get the stack frame pointer for a thread */
static GstStackFrame *thread_frame(GstThread * thread) {
return (GstStackFrame *)(thread->data + thread->count - FRAME_SIZE);
}
/* Ensure that a thread has enough space in it */
static void thread_ensure(Gst *vm, GstThread *thread, uint32_t size) {
if (size > thread->capacity) {
uint32_t newCap = size * 2;
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
memcpy(newData, thread->data, thread->capacity * sizeof(GstValue));
thread->data = newData;
thread->capacity = newCap;
}
}
/* Push a stack frame onto a thread */
static void thread_push(Gst *vm, GstThread *thread, GstValue callee, uint32_t size) {
uint16_t oldSize;
uint32_t nextCount, i;
GstStackFrame *frame;
if (thread->count) {
frame = thread_frame(thread);
oldSize = frame->size;
} else {
oldSize = 0;
}
nextCount = thread->count + oldSize + FRAME_SIZE;
thread_ensure(vm, thread, nextCount + size);
thread->count = nextCount;
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + size; ++i)
thread->data[i].type = GST_NIL;
vm->base = thread->data + thread->count;
vm->frame = frame = (GstStackFrame *)(vm->base - FRAME_SIZE);
/* Set up the new stack frame */
frame->prevSize = oldSize;
frame->size = size;
frame->env = NULL;
frame->callee = callee;
frame->errorJump = NULL;
}
/* Copy the current function stack to the current closure
environment. Call when exiting function with closures. */
static void thread_split_env(Gst *vm) {
GstStackFrame *frame = vm->frame;
GstFuncEnv *env = frame->env;
/* Check for closures */
if (env) {
GstThread *thread = vm->thread;
uint32_t size = frame->size;
env->thread = NULL;
env->stackOffset = size;
env->values = gst_alloc(vm, sizeof(GstValue) * size);
memcpy(env->values, thread->data + thread->count, size * sizeof(GstValue));
}
}
/* Pop the top-most stack frame from stack */
static void thread_pop(Gst *vm) {
GstThread *thread = vm->thread;
GstStackFrame *frame = vm->frame;
uint32_t delta = FRAME_SIZE + frame->prevSize;
if (thread->count) {
thread_split_env(vm);
} else {
gst_crash(vm, "stack underflow");
}
thread->count -= delta;
vm->base -= delta;
vm->frame = (GstStackFrame *)(vm->base - FRAME_SIZE);
}
/* The metadata header associated with an allocated block of memory */
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1)
/* Memory header struct. Node of a linked list of memory blocks. */
typedef struct GCMemoryHeader GCMemoryHeader;
struct GCMemoryHeader {
GCMemoryHeader * next;
uint32_t color : 1;
};
/* Forward declaration */
static void gst_mark(Gst *vm, GstValue *x);
/* Helper to mark function environments */
static void gst_mark_funcenv(Gst *vm, GstFuncEnv *env) {
if (gc_header(env)->color != vm->black) {
GstValue temp;
gc_header(env)->color = vm->black;
if (env->thread) {
temp.type = GST_THREAD;
temp.data.thread = env->thread;
gst_mark(vm, &temp);
}
if (env->values) {
uint32_t count = env->stackOffset;
uint32_t i;
gc_header(env->values)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark(vm, env->values + i);
}
}
}
/* GC helper to mark a FuncDef */
static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
if (gc_header(def)->color != vm->black) {
gc_header(def)->color = vm->black;
gc_header(def->byteCode)->color = vm->black;
uint32_t count, i;
if (def->literals) {
count = def->literalsLen;
gc_header(def->literals)->color = vm->black;
for (i = 0; i < count; ++i) {
/* If the literal is a NIL type, it actually
* contains a FuncDef */
if (def->literals[i].type == GST_NIL) {
gst_mark_funcdef(vm, (GstFuncDef *) def->literals[i].data.pointer);
} else {
gst_mark(vm, def->literals + i);
}
}
}
}
}
/* Helper to mark a stack frame. Returns the next frame. */
static GstStackFrame *gst_mark_stackframe(Gst *vm, GstStackFrame *frame) {
uint32_t i;
GstValue *stack = (GstValue *)frame + FRAME_SIZE;
gst_mark(vm, &frame->callee);
if (frame->env)
gst_mark_funcenv(vm, frame->env);
for (i = 0; i < frame->size; ++i)
gst_mark(vm, stack + i);
return (GstStackFrame *)(stack + frame->size);
}
/* Mark allocated memory associated with a value. This is
* the main function for doing the garbage collection mark phase. */
static void gst_mark(Gst *vm, GstValue *x) {
switch (x->type) {
case GST_NIL:
case GST_BOOLEAN:
case GST_NUMBER:
case GST_CFUNCTION:
break;
case GST_STRING:
gc_header(gst_string_raw(x->data.string))->color = vm->black;
break;
case GST_BYTEBUFFER:
gc_header(x->data.buffer)->color = vm->black;
gc_header(x->data.buffer->data)->color = vm->black;
break;
case GST_ARRAY:
if (gc_header(x->data.array)->color != vm->black) {
uint32_t i, count;
count = x->data.array->count;
gc_header(x->data.array)->color = vm->black;
gc_header(x->data.array->data)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark(vm, x->data.array->data + i);
}
break;
case GST_THREAD:
if (gc_header(x->data.thread)->color != vm->black) {
GstThread *thread = x->data.thread;
GstStackFrame *frame = (GstStackFrame *)thread->data;
GstStackFrame *end = thread_frame(thread);
gc_header(thread)->color = vm->black;
gc_header(thread->data)->color = vm->black;
while (frame <= end)
frame = gst_mark_stackframe(vm, frame);
}
break;
case GST_FUNCTION:
if (gc_header(x->data.function)->color != vm->black) {
GstFunction *f = x->data.function;
gc_header(f)->color = vm->black;
gst_mark_funcdef(vm, f->def);
if (f->env)
gst_mark_funcenv(vm, f->env);
if (f->parent) {
GstValue temp;
temp.type = GST_FUNCTION;
temp.data.function = f->parent;
gst_mark(vm, &temp);
}
}
break;
case GST_OBJECT:
if (gc_header(x->data.object)->color != vm->black) {
uint32_t i;
GstBucket *bucket;
gc_header(x->data.object)->color = vm->black;
gc_header(x->data.object->buckets)->color = vm->black;
for (i = 0; i < x->data.object->capacity; ++i) {
bucket = x->data.object->buckets[i];
while (bucket) {
gc_header(bucket)->color = vm->black;
gst_mark(vm, &bucket->key);
gst_mark(vm, &bucket->value);
bucket = bucket->next;
}
}
}
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
static void gst_sweep(Gst *vm) {
GCMemoryHeader *previous = NULL;
GCMemoryHeader *current = vm->blocks;
GCMemoryHeader *next;
while (current) {
next = current->next;
if (current->color != vm->black) {
if (previous) {
previous->next = next;
} else {
vm->blocks = next;
}
free(current);
} else {
previous = current;
}
current = next;
}
/* Rotate flag */
vm->black = !vm->black;
}
/* Prepare a memory block */
static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
GCMemoryHeader *mdata;
if (rawBlock == NULL) {
gst_crash(vm, OOM);
}
vm->nextCollection += size;
mdata = (GCMemoryHeader *)rawBlock;
mdata->next = vm->blocks;
vm->blocks = mdata;
mdata->color = !vm->black;
return rawBlock + sizeof(GCMemoryHeader);
}
/* Allocate some memory that is tracked for garbage collection */
void *gst_alloc(Gst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return gst_alloc_prepare(vm, malloc(totalSize), totalSize);
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void *gst_zalloc(Gst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return gst_alloc_prepare(vm, calloc(1, totalSize), totalSize);
}
/* Run garbage collection */
void gst_collect(Gst *vm) {
if (vm->lock > 0) return;
/* Thread can be null */
if (vm->thread) {
GstValue thread;
thread.type = GST_THREAD;
thread.data.thread = vm->thread;
gst_mark(vm, &thread);
}
gst_mark(vm, &vm->ret);
gst_mark(vm, &vm->error);
gst_sweep(vm);
vm->nextCollection = 0;
}
/* Run garbage collection if needed */
void gst_maybe_collect(Gst *vm) {
if (vm->nextCollection >= vm->memoryInterval)
gst_collect(vm);
}
static const char GST_NO_UPVALUE[] = "no upvalue";
static const char GST_EXPECTED_FUNCTION[] = "expected function";
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
/* Get an upvalue */
static GstValue *gst_vm_upvalue_location(Gst *vm, GstFunction *fn, uint16_t level, uint16_t index) {
@ -317,7 +20,7 @@ static GstValue *gst_vm_upvalue_location(Gst *vm, GstFunction *fn, uint16_t leve
return vm->base + index;
while (fn && --level)
fn = fn->parent;
gst_assert(vm, fn, NO_UPVALUE);
gst_assert(vm, fn, GST_NO_UPVALUE);
env = fn->env;
if (env->thread)
stack = env->thread->data + env->stackOffset;
@ -329,19 +32,14 @@ static GstValue *gst_vm_upvalue_location(Gst *vm, GstFunction *fn, uint16_t leve
/* Get a literal */
static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) {
if (index > fn->def->literalsLen) {
gst_error(vm, NO_UPVALUE);
gst_error(vm, GST_NO_UPVALUE);
}
return fn->def->literals[index];
}
/* Boolean truth definition */
static int truthy(GstValue v) {
return v.type != GST_NIL && !(v.type == GST_BOOLEAN && !v.data.boolean);
}
/* Return from the vm */
static void gst_vm_return(Gst *vm, GstValue ret) {
thread_pop(vm);
gst_thread_pop(vm);
if (vm->thread->count == 0) {
gst_exit(vm, ret);
}
@ -361,11 +59,11 @@ static void gst_vm_call(Gst *vm) {
vm->frame->ret = vm->pc[2];
if (callee.type == GST_FUNCTION) {
GstFunction *fn = callee.data.function;
thread_push(vm, thread, callee, fn->def->locals);
gst_thread_push(vm, thread, callee, fn->def->locals);
} else if (callee.type == GST_CFUNCTION) {
thread_push(vm, thread, callee, arity);
gst_thread_push(vm, thread, callee, arity);
} else {
gst_error(vm, EXPECTED_FUNCTION);
gst_error(vm, GST_EXPECTED_FUNCTION);
}
oldBase = thread->data + oldCount;
if (callee.type == GST_CFUNCTION) {
@ -393,18 +91,18 @@ static void gst_vm_tailcall(Gst *vm) {
uint16_t newFrameSize, currentFrameSize;
uint32_t i;
/* Check for closures */
thread_split_env(vm);
gst_thread_split_env(vm);
if (callee.type == GST_CFUNCTION) {
newFrameSize = arity;
} else if (callee.type == GST_FUNCTION) {
GstFunction * f = callee.data.function;
newFrameSize = f->def->locals;
} else {
gst_error(vm, EXPECTED_FUNCTION);
gst_error(vm, GST_EXPECTED_FUNCTION);
}
/* Ensure stack has enough space for copies of arguments */
currentFrameSize = vm->frame->size;
thread_ensure(vm, thread, thread->count + currentFrameSize + arity);
gst_thread_ensure(vm, thread, thread->count + currentFrameSize + arity);
vm->base = thread->data + thread->count;
/* Copy the arguments into the extra space */
for (i = 0; i < arity; ++i)
@ -432,7 +130,7 @@ static void gst_vm_tailcall(Gst *vm) {
static GstValue gst_vm_closure(Gst *vm, uint16_t literal) {
GstThread *thread = vm->thread;
if (vm->frame->callee.type != GST_FUNCTION) {
gst_error(vm, EXPECTED_FUNCTION);
gst_error(vm, GST_EXPECTED_FUNCTION);
} else {
GstValue constant, ret;
GstFunction *fn, *current;
@ -471,9 +169,9 @@ int gst_start(Gst *vm) {
vm->lock = 0;
return 0;
} else if (n == 2) {
/* Error. Handling TODO. */
/* Error. */
while (vm->thread->count && !vm->frame->errorJump) {
thread_pop(vm);
gst_thread_pop(vm);
}
if (vm->thread->count == 0)
return n;
@ -499,8 +197,8 @@ int gst_start(Gst *vm) {
#define DO_BINARY_MATH(op) \
v1 = vm->base[vm->pc[2]]; \
v2 = vm->base[vm->pc[3]]; \
gst_assert(vm, v1.type == GST_NUMBER, VMS_EXPECTED_NUMBER_LOP); \
gst_assert(vm, v2.type == GST_NUMBER, VMS_EXPECTED_NUMBER_ROP); \
gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP); \
gst_assert(vm, v2.type == GST_NUMBER, GST_EXPECTED_NUMBER_ROP); \
temp.type = GST_NUMBER; \
temp.data.number = v1.data.number op v2.data.number; \
vm->base[vm->pc[1]] = temp; \
@ -523,7 +221,7 @@ int gst_start(Gst *vm) {
case GST_OP_NOT: /* Boolean unary (Boolean not) */
temp.type = GST_BOOLEAN;
temp.data.boolean = !truthy(vm->base[vm->pc[2]]);
temp.data.boolean = !gst_truthy(vm->base[vm->pc[2]]);
vm->base[vm->pc[1]] = temp;
vm->pc += 3;
break;
@ -571,13 +269,13 @@ int gst_start(Gst *vm) {
case GST_OP_UPV: /* Load Up Value */
temp = vm->frame->callee;
gst_assert(vm, temp.type == GST_FUNCTION, EXPECTED_FUNCTION);
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
vm->base[vm->pc[1]] = *gst_vm_upvalue_location(vm, temp.data.function, vm->pc[2], vm->pc[3]);
vm->pc += 4;
break;
case GST_OP_JIF: /* Jump If */
if (truthy(vm->base[vm->pc[1]])) {
if (gst_truthy(vm->base[vm->pc[1]])) {
vm->pc += 4;
} else {
vm->pc += *((int32_t *)(vm->pc + 2));
@ -598,14 +296,14 @@ int gst_start(Gst *vm) {
case GST_OP_SUV: /* Set Up Value */
temp = vm->frame->callee;
gst_assert(vm, temp.type == GST_FUNCTION, EXPECTED_FUNCTION);
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
*gst_vm_upvalue_location(vm, temp.data.function, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]];
vm->pc += 4;
break;
case GST_OP_CST: /* Load constant value */
temp = vm->frame->callee;
gst_assert(vm, temp.type == GST_FUNCTION, EXPECTED_FUNCTION);
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
vm->base[vm->pc[1]] = gst_vm_literal(vm, temp.data.function, vm->pc[2]);
vm->pc += 3;
break;
@ -699,7 +397,7 @@ int gst_start(Gst *vm) {
GstNumber accum = start; \
for (i = 0; i < count; ++i) { \
v1 = vm->base[vm->pc[3 + i]]; \
gst_assert(vm, v1.type == GST_NUMBER, "Expected number"); \
gst_assert(vm, v1.type == GST_NUMBER, "expected number"); \
accum = accum op v1.data.number; \
} \
temp.type = GST_NUMBER; \
@ -762,7 +460,7 @@ int gst_start(Gst *vm) {
}
/* Move collection only to places that allocate memory */
/* This, however, is good for testing */
/* This, however, is good for testing to ensure no memory leaks */
gst_maybe_collect(vm);
}
}
@ -801,7 +499,7 @@ void gst_init(Gst *vm) {
* a collection pretty much every cycle, which is
* obviously horrible for performance. It helps ensure
* there are no memory bugs during dev */
vm->memoryInterval = 0;
vm->memoryInterval = 2000;
vm->black = 0;
vm->lock = 0;
/* Add thread */
@ -819,10 +517,10 @@ void gst_load(Gst *vm, GstValue callee) {
vm->thread = thread;
if (callee.type == GST_FUNCTION) {
GstFunction *fn = callee.data.function;
thread_push(vm, thread, callee, fn->def->locals);
gst_thread_push(vm, thread, callee, fn->def->locals);
vm->pc = fn->def->byteCode;
} else if (callee.type == GST_CFUNCTION) {
thread_push(vm, thread, callee, 0);
gst_thread_push(vm, thread, callee, 0);
vm->pc = NULL;
} else {
return;
@ -831,11 +529,5 @@ void gst_load(Gst *vm, GstValue callee) {
/* Clear all memory associated with the VM */
void gst_deinit(Gst *vm) {
GCMemoryHeader *current = vm->blocks;
while (current) {
GCMemoryHeader *next = current->next;
free(current);
current = next;
}
vm->blocks = NULL;
gst_clear_memory(vm);
}

2
vm.h
View File

@ -7,7 +7,7 @@
/* Exit from the VM normally */
#define gst_exit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1))
/* Bail from the VM with an error. */
/* Bail from the VM with an error string. */
#define gst_error(vm, e) ((vm)->error = gst_load_cstring((vm), (e)), longjmp((vm)->jump, 2))
/* Crash. Not catchable, unlike error. */