mirror of
https://github.com/janet-lang/janet
synced 2024-12-25 07:50:27 +00:00
Breaking up functionality into more modules.
This commit is contained in:
parent
5ec6e46f1a
commit
fd34837265
15
.gitignore
vendored
15
.gitignore
vendored
@ -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
generated
Normal file
2
.idea/interpreter.iml
generated
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal 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
generated
Normal file
8
.idea/modules.xml
generated
Normal 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
generated
Normal file
6
.idea/vcs.xml
generated
Normal 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
generated
Normal file
211
.idea/workspace.xml
generated
Normal 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>
|
6
Makefile
6
Makefile
@ -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)
|
||||
|
@ -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
232
gc.c
Normal 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
29
gc.h
Normal 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
61
main.c
@ -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;
|
||||
}
|
||||
|
4
parse.c
4
parse.c
@ -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;
|
||||
|
11
temp.script
11
temp.script
@ -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
78
thread.c
Normal 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
25
thread.h
Normal 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
16
value.c
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
6
value.h
6
value.h
@ -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
370
vm.c
@ -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
2
vm.h
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user