First commit.

This commit is contained in:
Calvin Rose 2017-02-09 15:02:59 -05:00
commit a80dd4bff3
25 changed files with 4263 additions and 0 deletions

57
.gitignore vendored Normal file
View File

@ -0,0 +1,57 @@
# Created by https://www.gitignore.io/api/c
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
modules.order
Module.symvers
Mkfile.old
dkms.conf
# End of https://www.gitignore.io/api/c

141
.ycm_extra_conf.py Normal file
View File

@ -0,0 +1,141 @@
# Generated by YCM Generator at 2016-05-24 22:59:35.570087
# This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
import os
import ycm_core
flags = [
'-x',
'c',
'-I/home/calvin/code/bkdoc/include',
'-I/home/calvin/code/bkdoc/src',
'-I/home/calvin/code/bkdoc/cli',
'-std=gnu99',
]
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = ''
if os.path.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
database = None
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith( '/' ):
new_flag = os.path.join( working_directory, flag )
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
new_flag = path_flag + os.path.join( working_directory, path )
break
if new_flag:
new_flags.append( new_flag )
return new_flags
def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if IsHeaderFile( filename ):
basename = os.path.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists( replacement_file ):
compilation_info = database.GetCompilationInfoForFile(
replacement_file )
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile( filename )
def FlagsForFile( filename, **kwargs ):
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
return {
'flags': final_flags,
'do_cache': True
}

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
# TIL
CFLAGS=-std=c99 -Wall -Wextra -m32 -g
TARGET=interp
PREFIX=/usr/local
# C sources
SOURCES=main.c parse.c value.c vm.c dict.c array.c buffer.c gc.c compile.c
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)
%.o : %.c
$(CC) $(CFLAGS) -o $@ -c $<
install: $(TARGET)
cp $(TARGET) $(PREFIX)/bin
clean:
rm $(TARGET) || true
rm $(OBJECTS) || true
run: $(TARGET)
./$(TARGET)
debug: $(TARGET)
gdb $(TARGET)
.PHONY: clean install run debug

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# tape
A language and vm for embeddable scripting.

78
array.c Normal file
View File

@ -0,0 +1,78 @@
#include <string.h>
#include "array.h"
#include "gc.h"
/* Creates a new array */
Array * ArrayNew(GC * gc, uint32_t capacity) {
Array * array = GCAlloc(gc, sizeof(Array));
Value * data = GCAlloc(gc, capacity * sizeof(Value));
array->data = data;
array->count = 0;
array->capacity = capacity;
return array;
}
/* Ensure the array has enough capacity for capacity elements */
void ArrayEnsure(GC * gc, Array * array, uint32_t capacity) {
Value * newData;
if (capacity <= array->capacity) return;
newData = GCAlloc(gc, capacity * sizeof(Value));
memcpy(newData, array->data, array->count * sizeof(Value));
array->data = newData;
array->capacity = capacity;
}
/* Get a value of an array with bounds checking. */
Value ArrayGet(Array * array, uint32_t index) {
if (index < array->count) {
return array->data[index];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Try to set an index in the array. Return 1 if successful, 0
* on failiure */
int ArraySet(Array * array, uint32_t index, Value x) {
if (index < array->count) {
array->data[index] = x;
return 1;
} else {
return 0;
}
}
/* Add an item to the end of the array */
void ArrayPush(GC * gc, Array * array, Value x) {
if (array->count >= array->capacity) {
ArrayEnsure(gc, array, 2 * array->count);
}
array->data[array->count++] = x;
}
/* Remove the last item from the Array and return it */
Value ArrayPop(Array * array) {
if (array->count) {
return array->data[--array->count];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Look at the last item in the Array */
Value ArrayPeek(Array * array) {
if (array->count) {
return array->data[array->count - 1];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}

30
array.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef ARRAY_H_BW48JZSK
#define ARRAY_H_BW48JZSK
#include "datatypes.h"
/* Create a new Array */
Array * ArrayNew(GC * gc, uint32_t capacity);
/* Get a value of an array with bounds checking. Returns nil if
* outside bounds. */
Value ArrayGet(Array * array, uint32_t index);
/* Set a value in the array. Does bounds checking but will not grow
* or shrink the array */
int ArraySet(Array * array, uint32_t index, Value x);
/* Ensure that the internal memory hash enough space for capacity items */
void ArrayEnsure(GC * gc, Array * array, uint32_t capacity);
/* Set a value in an array. Will also append to the array if the index is
* greater than the current max index. */
void ArrayPush(GC * gc, Array * array, Value x);
/* Pop the last item in the array, or return NIL if empty */
Value ArrayPop(Array * array);
/* Look at the top most item of an Array */
Value ArrayPeek(Array * array);
#endif /* end of include guard: ARRAY_H_BW48JZSK */

62
buffer.c Normal file
View File

@ -0,0 +1,62 @@
#include <string.h>
#include "buffer.h"
#include "gc.h"
#include "value.h"
#include "vstring.h"
void BufferInit(GC * gc, Buffer * buffer, uint32_t capacity) {
uint8_t * data;
data = GCAlloc(gc, sizeof(uint8_t) * capacity);
buffer->data = data;
buffer->count = 0;
buffer->capacity = capacity;
}
Buffer * BufferNew(GC * gc, uint32_t capacity) {
Buffer * buffer;
buffer = GCAlloc(gc, sizeof(Buffer));
BufferInit(gc, buffer, capacity);
return buffer;
}
void BufferEnsure(GC * gc, Buffer * buffer, uint32_t capacity) {
uint8_t * newData;
if (capacity <= buffer->capacity) return;
newData = GCAlloc(gc, capacity * sizeof(uint8_t));
memcpy(newData, buffer->data, buffer->count * sizeof(uint8_t));
buffer->data = newData;
buffer->capacity = capacity;
}
int32_t BufferGet(Buffer * buffer, uint32_t index) {
if (index < buffer->count) {
return buffer->data[index];
} else {
return -1;
}
}
void BufferPush(GC * gc, Buffer * buffer, uint8_t c) {
if (buffer->count >= buffer->capacity) {
BufferEnsure(gc, buffer, 2 * buffer->count);
}
buffer->data[buffer->count++] = c;
}
void BufferAppendData(GC * gc, Buffer * buffer, uint8_t * string, uint32_t length) {
uint32_t newSize = buffer->count + length;
if (newSize > buffer->capacity) {
BufferEnsure(gc, buffer, 2 * newSize);
}
memcpy(buffer->data + buffer->count, string, length);
buffer->count = newSize;
}
uint8_t * BufferToString(GC * gc, Buffer * buffer) {
uint8_t * data = GCAlloc(gc, buffer->count + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
VStringSize(data) = buffer->count;
VStringHash(data) = 0;
memcpy(data, buffer->data, buffer->count * sizeof(uint8_t));
return data;
}

26
buffer.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef BUFFER_H_OEA9T4DJ
#define BUFFER_H_OEA9T4DJ
#include "datatypes.h"
void BufferInit(GC * gc, Buffer * buffer, uint32_t capacity);
Buffer * BufferNew(GC * gc, uint32_t capacity);
void BufferEnsure(GC * gc, Buffer * buffer, uint32_t capacity);
int32_t BufferGet(Buffer * buffer, uint32_t index);
void BufferPush(GC * gc, Buffer * buffer, uint8_t c);
void BufferAppendData(GC * gc, Buffer * buffer, uint8_t * string, uint32_t length);
uint8_t * BufferToString(GC * gc, Buffer * buffer);
#define BufferDefine(name, type) \
static void BufferPush##name (GC * gc, Buffer * buffer, type x) { \
union { type t; uint8_t bytes[sizeof(type)]; } u; \
u.t = x; return BufferAppendData(gc, buffer, u.bytes, sizeof(type)); \
}
#endif /* end of include guard: BUFFER_H_OEA9T4DJ */

1163
compile.c Normal file

File diff suppressed because it is too large Load Diff

15
compile.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef COMPILE_H_9VXF71HY
#define COMPILE_H_9VXF71HY
#include "datatypes.h"
/* Initialize the Compiler */
void CompilerInit(Compiler * c, VM * vm);
/* Register a global for the compilation environment. */
void CompilerAddGlobal(Compiler * c, const char * name, Value x);
/* Compile a function that evaluates the given form. */
Func * CompilerCompile(Compiler * c, Value form);
#endif /* end of include guard: COMPILE_H_9VXF71HY */

185
datatypes.h Normal file
View File

@ -0,0 +1,185 @@
#ifndef DATATYPES_H_PJJ035NT
#define DATATYPES_H_PJJ035NT
#include <stdint.h>
#include <setjmp.h>
#define THREAD_STATUS_ALIVE 0
#define THREAD_STATUS_DEAD 1
#define THREAD_STATUS_PENDING 2
typedef enum Type {
TYPE_NIL = 0,
TYPE_NUMBER,
TYPE_BOOLEAN,
TYPE_STRING,
TYPE_SYMBOL,
TYPE_ARRAY,
TYPE_THREAD,
TYPE_FORM,
TYPE_BYTEBUFFER,
TYPE_FUNCTION,
TYPE_CFUNCTION,
TYPE_DICTIONARY,
TYPE_FUNCDEF,
TYPE_FUNCENV
} Type;
typedef double Number;
typedef uint8_t Boolean;
typedef struct VM VM;
typedef struct Value Value;
typedef Value (*CFunction)(VM * vm);
typedef struct Func Func;
typedef struct FuncDef FuncDef;
typedef struct FuncEnv FuncEnv;
typedef union ValueData ValueData;
typedef struct DictBucket DictBucket;
typedef struct Array Array;
typedef struct Buffer Buffer;
typedef struct Dictionary Dictionary;
typedef struct DictionaryIterator DictionaryIterator;
typedef struct GC GC;
typedef struct Parser Parser;
typedef struct ParseState ParseState;
typedef struct Scope Scope;
typedef struct Compiler Compiler;
union ValueData {
Boolean boolean;
Number number;
uint8_t * string;
Array * array;
Buffer * buffer;
Dictionary * dict;
Func * func;
void * pointer;
FuncDef * funcdef;
FuncEnv * funcenv;
CFunction cfunction;
uint16_t u16[4];
uint8_t u8[8];
} data;
/* Use an Array to represent the stack. A Stack frame is
* represented by a grouping of FRAME_SIZE values. */
#define FRAME_SIZE 4
#define ThreadStack(t) ((t)->data + (t)->count)
#define FrameMeta(t) (ThreadStack(t)[-1])
#define FrameReturn(t) ((ThreadStack(t) - 1)->data.u16[0])
#define FrameSize(t) ((ThreadStack(t) - 1)->data.u16[1])
#define FramePrevSize(t) ((ThreadStack(t) - 1)->data.u16[2])
#define FrameCallee(t) (ThreadStack(t)[-2])
#define FrameEnvValue(t) (ThreadStack(t)[-3])
#define FrameEnv(t) ((ThreadStack(t) - 3)->data.funcenv)
#define FramePCValue(t) (ThreadStack(t)[-4])
#define FramePC(t) ((ThreadStack(t)[-1]).data.pointer)
struct Array {
uint32_t count;
uint32_t capacity;
Value * data;
};
struct Buffer {
uint32_t count;
uint32_t capacity;
uint8_t * data;
};
struct Dictionary {
uint32_t count;
uint32_t capacity;
DictBucket ** buckets;
};
struct DictionaryIterator {
Dictionary * dict;
uint32_t index;
DictBucket * bucket;
};
struct FuncDef {
uint32_t locals;
uint32_t arity;
uint32_t literalsLen;
uint32_t byteCodeLen;
Value * literals; /* Contains strings, FuncDefs, etc. */
uint16_t * byteCode;
};
struct FuncEnv {
Array * thread; /* When nil, index the local values */
uint32_t stackOffset; /* Used as environment size when off stack */
Value * values;
};
struct Func {
FuncDef * def;
FuncEnv * env;
Func * parent;
};
struct Value {
Type type;
ValueData data;
};
struct DictBucket {
Value key;
Value value;
DictBucket * next;
};
struct GC {
void * blocks;
void * user;
void (*handleOutOfMemory)(GC * gc);
uint32_t memoryInterval;
uint32_t nextCollection;
uint32_t black : 1;
};
struct VM {
GC gc;
const char * error;
uint16_t * pc;
Array * thread;
Value * base;
jmp_buf jump;
Value tempRoot; /* Temporary GC root */
};
/* Parsing */
#define PARSER_PENDING 0
#define PARSER_FULL 1
#define PARSER_ERROR -1
struct Parser {
VM * vm;
const char * error;
ParseState * data;
Value value;
uint32_t count;
uint32_t cap;
uint32_t index;
uint32_t status;
};
/* Compiling */
struct Compiler {
VM * vm;
const char * error;
jmp_buf onError;
Scope * root;
Scope * tail;
Array * env;
Buffer * buffer;
};
#endif /* end of include guard: DATATYPES_H_PJJ035NT */

152
dict.c Normal file
View File

@ -0,0 +1,152 @@
#include "dict.h"
#include "value.h"
#include "gc.h"
/* Create a new dictionary */
Dictionary * DictNew(GC * gc, uint32_t capacity) {
Dictionary * dict = GCAlloc(gc, sizeof(Dictionary));
DictBucket ** buckets = GCZalloc(gc, capacity * sizeof(DictBucket *));
dict->buckets = buckets;
dict->capacity = capacity;
dict->count = 0;
return dict;
}
/* Resize the dictionary table. */
static void DictReHash(GC * gc, Dictionary * dict, uint32_t size) {
DictBucket ** newBuckets = GCZalloc(gc, size * sizeof(DictBucket *));
uint32_t i, count;
for (i = 0, count = dict->capacity; i < count; ++i) {
DictBucket * bucket = dict->buckets[i];
while (bucket) {
uint32_t index;
DictBucket * next = bucket->next;
index = ValueHash(&bucket->key) % size;
bucket->next = newBuckets[index];
newBuckets[index] = bucket;
bucket = next;
}
}
dict->buckets = newBuckets;
dict->capacity = size;
}
/* Find the bucket that contains the given key */
static DictBucket * DictFind(Dictionary * dict, Value * key) {
uint32_t index = ValueHash(key) % dict->capacity;
DictBucket * bucket = dict->buckets[index];
while (bucket) {
if (ValueEqual(&bucket->key, key))
return bucket;
bucket = bucket->next;
}
return (DictBucket *)0;
}
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key) {
DictBucket * bucket = DictFind(dict, key);
if (bucket) {
return bucket->value;
} else {
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Remove an entry from the dictionary */
Value DictRemove(GC * gc, Dictionary * dict, Value * key) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(gc, dict, dict->capacity / 2);
}
--dict->count;
return bucket->value;
}
previous = bucket;
bucket = bucket->next;
}
/* Return nil if we found nothing */
{
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(GC * gc, Dictionary * dict, Value * key, Value * value) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
if (key->type == TYPE_NIL) return;
/* Do a removal if value is nil */
if (value->type == TYPE_NIL) {
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(gc, dict, dict->capacity / 2);
}
--dict->count;
return;
}
previous = bucket;
bucket = bucket->next;
}
} else {
bucket = DictFind(dict, key);
if (bucket) {
bucket->value = *value;
} else {
if (dict->count >= 2 * dict->capacity) {
DictReHash(gc, dict, 2 * dict->capacity);
}
bucket = GCAlloc(gc, sizeof(DictBucket));
bucket->next = dict->buckets[index];
bucket->value = *value;
bucket->key = *key;
dict->buckets[index] = bucket;
++dict->count;
}
}
}
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator) {
iterator->index = 0;
iterator->dict = dict;
iterator->bucket = dict->buckets[0];
}
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket) {
Dictionary * dict = iterator->dict;
for (;;) {
if (iterator->bucket) {
*bucket = iterator->bucket;
iterator->bucket = iterator->bucket->next;
return 1;
}
if (++iterator->index >= dict->capacity) break;
iterator->bucket = dict->buckets[iterator->index];
}
return 0;
}

26
dict.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef DICT_H_YN2BKHUQ
#define DICT_H_YN2BKHUQ
#include "datatypes.h"
/* Create a new dictionary */
Dictionary * DictNew(GC * gc, uint32_t capacity);
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key);
/* Get a Value from the dictionary, but remove it at the same
* time. */
Value DictRemove(GC * gc, Dictionary * dict, Value * key);
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(GC * gc, Dictionary * dict, Value * key, Value * value);
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator);
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket);
#endif /* end of include guard: DICT_H_YN2BKHUQ */

209
gc.c Normal file
View File

@ -0,0 +1,209 @@
#include <stdlib.h>
#include "gc.h"
#include "dict.h"
#include "vstring.h"
#include "parse.h"
#define GCHeader(mem) ((GCMemoryHeader *)(mem) - 1)
typedef struct GCMemoryHeader GCMemoryHeader;
struct GCMemoryHeader {
GCMemoryHeader * next;
uint32_t color;
};
/* Initialize a garbage collector */
void GCInit(GC * gc, uint32_t memoryInterval) {
gc->black = 0;
gc->blocks = NULL;
gc->nextCollection = 0;
gc->memoryInterval = memoryInterval;
gc->handleOutOfMemory = NULL;
}
/* Mark some raw memory */
void GCMarkMemory(GC * gc, void * memory) {
GCHeader(memory)->color = gc->black;
}
/* Helper to mark function environments */
static void GCMarkFuncEnv(GC * gc, FuncEnv * env) {
if (GCHeader(env)->color != gc->black) {
Value temp;
GCHeader(env)->color = gc->black;
if (env->thread) {
temp.type = TYPE_THREAD;
temp.data.array = env->thread;
GCMark(gc, &temp);
} else {
uint32_t count = env->stackOffset;
uint32_t i;
GCHeader(env->values)->color = gc->black;
for (i = 0; i < count; ++i) {
GCMark(gc, env->values + i);
}
}
}
}
/* Mark allocated memory associated with a value. This is
* the main function for doing garbage collection. */
void GCMark(GC * gc, Value * x) {
switch (x->type) {
case TYPE_NIL:
case TYPE_BOOLEAN:
case TYPE_NUMBER:
case TYPE_CFUNCTION:
break;
case TYPE_STRING:
case TYPE_SYMBOL:
GCHeader(VStringRaw(x->data.string))->color = gc->black;
break;
case TYPE_BYTEBUFFER:
GCHeader(x->data.buffer)->color = gc->black;
GCHeader(x->data.buffer->data)->color = gc->black;
break;
case TYPE_ARRAY:
case TYPE_FORM:
if (GCHeader(x->data.array)->color != gc->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = gc->black;
GCHeader(x->data.array->data)->color = gc->black;
for (i = 0; i < count; ++i)
GCMark(gc, x->data.array->data + i);
}
break;
case TYPE_THREAD:
if (GCHeader(x->data.array)->color != gc->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = gc->black;
GCHeader(x->data.array->data)->color = gc->black;
if (count) {
count += FrameSize(x->data.array);
for (i = 0; i < count; ++i)
GCMark(gc, x->data.array->data + i);
}
}
break;
case TYPE_FUNCTION:
if (GCHeader(x->data.func)->color != gc->black) {
Func * f = x->data.func;
GCHeader(f)->color = gc->black;
GCMarkFuncEnv(gc, f->env);
{
Value temp;
temp.type = TYPE_FUNCDEF;
temp.data.funcdef = x->data.funcdef;
GCMark(gc, &temp);
if (f->parent) {
temp.type = TYPE_FUNCTION;
temp.data.func = f->parent;
GCMark(gc, &temp);
}
}
}
break;
case TYPE_DICTIONARY:
if (GCHeader(x->data.dict)->color != gc->black) {
DictionaryIterator iter;
DictBucket * bucket;
GCHeader(x->data.dict)->color = gc->black;
GCHeader(x->data.dict->buckets)->color = gc->black;
DictIterate(x->data.dict, &iter);
while (DictIterateNext(&iter, &bucket)) {
GCHeader(bucket)->color = gc->black;
GCMark(gc, &bucket->key);
GCMark(gc, &bucket->value);
}
}
break;
case TYPE_FUNCDEF:
if (GCHeader(x->data.funcdef)->color != gc->black) {
GCHeader(x->data.funcdef->byteCode)->color = gc->black;
uint32_t count, i;
count = x->data.funcdef->literalsLen;
if (x->data.funcdef->literals) {
GCHeader(x->data.funcdef->literals)->color = gc->black;
for (i = 0; i < count; ++i)
GCMark(gc, x->data.funcdef->literals + i);
}
}
break;
case TYPE_FUNCENV:
GCMarkFuncEnv(gc, x->data.funcenv);
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void GCSweep(GC * gc) {
GCMemoryHeader * previous = NULL;
GCMemoryHeader * current = gc->blocks;
while (current) {
if (current->color != gc->black) {
if (previous) {
previous->next = current->next;
} else {
gc->blocks = current->next;
}
free(current);
} else {
previous = current;
}
current = current->next;
}
/* Rotate flag */
gc->black = !gc->black;
}
/* Clean up all memory */
void GCClear(GC * gc) {
GCMemoryHeader * current = gc->blocks;
while (current) {
GCMemoryHeader * next = current->next;
free(current);
current = next;
}
gc->blocks = NULL;
}
/* Prepare a memory block */
static void * GCPrepare(GC * gc, char * rawBlock, uint32_t size) {
GCMemoryHeader * mdata;
if (rawBlock == NULL) {
if (gc->handleOutOfMemory != NULL)
gc->handleOutOfMemory(gc);
return NULL;
}
gc->nextCollection += size;
mdata = (GCMemoryHeader *) rawBlock;
mdata->next = gc->blocks;
gc->blocks = mdata;
mdata->color = !gc->black;
return rawBlock + sizeof(GCMemoryHeader);
}
/* Allocate some memory that is tracked for garbage collection */
void * GCAlloc(GC * gc, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return GCPrepare(gc, malloc(totalSize), totalSize);
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void * GCZalloc(GC * gc, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return GCPrepare(gc, calloc(1, totalSize), totalSize);
}

41
gc.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef GC_H_N8L3U4KK
#define GC_H_N8L3U4KK
#include "datatypes.h"
/* Initialize a GC */
void GCInit(GC * gc, uint32_t memoryInterval);
/* Iterate over all allocated memory, and frees memory that is not
* marked as reachable */
void GCSweep(GC * gc);
/* Do a depth first search of the variables and mark all reachable memory.
* Root variables are just everyting in the stack. */
void GCMark(GC * gc, Value * x);
/* Mark some raw memory as reachable */
void GCMarkMemory(GC * gc, void * memory);
/* Clean up all memory, including locked memory */
void GCClear(GC * gc);
/* Allocate some memory that is tracked for garbage collection */
void * GCAlloc(GC * gc, uint32_t size);
/* Allocate zeroed memory */
void * GCZalloc(GC * gc, uint32_t size);
/* Run a collection */
#define GCCollect(gc, root) \
(GCMark((gc), (root)), GCSweep(gc), (gc)->nextCollection = 0)
/* Check if a collection needs to be run */
#define GCNeedsCollect(gc) \
((gc)->nextCollection >= (gc)->memoryInterval)
/* Run a collection if enough memory has been allocated since last collection */
#define GCMaybeCollect(gc, root) \
(GCNeedsCollect(gc) ? GCCollect((gc), (root)) : 0)
#endif /* end of include guard: GC_H_N8L3U4KK */

90
main.c Normal file
View File

@ -0,0 +1,90 @@
#include <stdlib.h>
#include <stdio.h>
#include "datatypes.h"
#include "gc.h"
#include "vm.h"
#include "parse.h"
#include "compile.h"
#include "value.h"
/* A simple repl for debugging */
void debugRepl() {
char buffer[128] = {0};
const char * reader = buffer;
Func * func;
VM vm;
Parser p;
Compiler c;
VMInit(&vm);
for (;;) {
/* Run garbage collection */
VMMaybeCollect(&vm);
/* Reset state */
ParserInit(&p, &vm);
/* Get and parse input until we have a full form */
while (p.status == PARSER_PENDING) {
/* Get some input if we are done */
if (*reader == '\0') {
printf(">> ");
if (!fgets(buffer, sizeof(buffer), stdin)) {
return;
}
p.index = 0;
reader = buffer;
}
reader += ParserParseCString(&p, reader);
}
/* Check for parsing errors */
if (p.error) {
unsigned i;
printf("\n");
printf("%s\n", buffer);
for (i = 0; i < p.index; ++i) {
printf(" ");
}
printf("^\n");
printf("\nParse error: %s\n", p.error);
reader = buffer; /* Flush the input buffer */
buffer[0] = '\0';
continue;
}
/* Try to compile generated AST */
CompilerInit(&c, &vm);
func = CompilerCompile(&c, p.value);
/* Check for compilation errors */
if (c.error) {
printf("Compiler error: %s\n", c.error);
reader = buffer;
buffer[0] = 0;
continue;
} else {
printf("Compiled!\n");
}
/* Execute function */
VMLoad(&vm, func);
if (VMStart(&vm)) {
printf("VM error: %s\n", vm.error);
reader = buffer;
buffer[0] = 0;
continue;
} else {
ValuePrint(&vm.tempRoot, 0);
printf("\n");
}
}
}
int main() {
printf("Super cool interpreter v0.0\n");
debugRepl();
}

40
opcodes.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef OPCODES_H_EFPEYNZ0
#define OPCODES_H_EFPEYNZ0
enum OpCode {
VM_OP_ADD = 0, /* 0x0000 */
VM_OP_SUB, /* 0x0001 */
VM_OP_MUL, /* 0x0002 */
VM_OP_DIV, /* 0x0003 */
VM_OP_NOT, /* 0x0004 */
VM_OP_LD0, /* 0x0005 */
VM_OP_LD1, /* 0x0006 */
VM_OP_FLS, /* 0x0007 */
VM_OP_TRU, /* 0x0008 */
VM_OP_NIL, /* 0x0009 */
VM_OP_I16, /* 0x000a */
VM_OP_UPV, /* 0x000b */
VM_OP_JIF, /* 0x000c */
VM_OP_JMP, /* 0x000d */
VM_OP_CAL, /* 0x000e */
VM_OP_RET, /* 0x000f */
VM_OP_SUV, /* 0x0010 */
VM_OP_CST, /* 0x0011 */
VM_OP_I32, /* 0x0012 */
VM_OP_F64, /* 0x0013 */
VM_OP_MOV, /* 0x0014 */
VM_OP_CLN, /* 0x0015 */
VM_OP_EQL, /* 0x0016 */
VM_OP_LTN, /* 0x0017 */
VM_OP_LTE, /* 0x0018 */
VM_OP_ARR, /* 0x0019 */
VM_OP_DIC, /* 0x001a */
VM_OP_TCL, /* 0x001b */
VM_OP_ADM, /* 0x001c */
VM_OP_SBM, /* 0x001d */
VM_OP_MUM, /* 0x001e */
VM_OP_DVM, /* 0x001f */
VM_OP_RTN /* 0x0020 */
};
#endif /* end of include guard: OPCODES_H_EFPEYNZ0 */

456
parse.c Normal file
View File

@ -0,0 +1,456 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include "datatypes.h"
#include "array.h"
#include "dict.h"
#include "gc.h"
#include "buffer.h"
#include "parse.h"
#include "vm.h"
#include "vstring.h"
static const char UNEXPECTED_CLOSING_DELIM[] = "Unexpected closing delimiter";
/* The type of a ParseState */
typedef enum ParseType {
PTYPE_ROOT,
PTYPE_ARRAY,
PTYPE_FORM,
PTYPE_DICTIONARY,
PTYPE_STRING,
PTYPE_TOKEN
} ParseType;
/* Contain a parse state that goes on the parse stack */
struct ParseState {
ParseType type;
union {
Array * array;
struct {
Dictionary * dict;
Value key;
int keyFound;
} dictState;
struct {
Buffer * buffer;
enum {
STRING_STATE_BASE,
STRING_STATE_ESCAPE,
STRING_STATE_ESCAPE_UNICODE,
STRING_STATE_ESCAPE_HEX
} state;
} string;
} buf;
};
/* Handle error in parsing */
#define PError(p, e) ((p)->error = (e), (p)->status = PARSER_ERROR)
/* Get the top ParseState in the parse stack */
static ParseState * ParserPeek(Parser * p) {
if (!p->count) {
PError(p, "Parser stack underflow. (Peek)");
return NULL;
}
return p->data + p->count - 1;
}
/* Remove the top state from the ParseStack */
static ParseState * ParserPop(Parser * p) {
if (!p->count) {
PError(p, "Parser stack underflow. (Pop)");
return NULL;
}
return p->data + --p->count;
}
/* Add a new, empty ParseState to the ParseStack. */
static void ParserPush(Parser *p, ParseType type) {
GC * gc = &p->vm->gc;
ParseState * top;
if (p->count >= p->cap) {
uint32_t newCap = 2 * p->count;
ParseState * data = GCAlloc(gc, newCap);
p->data = data;
p->cap = newCap;
}
++p->count;
top = ParserPeek(p);
if (!top) return;
top->type = type;
switch (type) {
case PTYPE_ROOT:
break;
case PTYPE_STRING:
top->buf.string.state = STRING_STATE_BASE;
case PTYPE_TOKEN:
top->buf.string.buffer = BufferNew(gc, 10);
break;
case PTYPE_ARRAY:
case PTYPE_FORM:
top->buf.array = ArrayNew(gc, 10);
break;
case PTYPE_DICTIONARY:
top->buf.dictState.dict = DictNew(gc, 10);
top->buf.dictState.keyFound = 0;
break;
}
}
/* Append a value to the top-most state in the Parser's stack. */
static void ParserTopAppend(Parser * p, Value x) {
GC * gc = &p->vm->gc;
ParseState * top = ParserPeek(p);
if (!top) return;
switch (top->type) {
case PTYPE_ROOT:
p->value = x;
p->status = PARSER_FULL;
break;
case PTYPE_ARRAY:
case PTYPE_FORM:
ArrayPush(gc, top->buf.array, x);
break;
case PTYPE_DICTIONARY:
if (top->buf.dictState.keyFound) {
DictPut(gc, top->buf.dictState.dict, &top->buf.dictState.key, &x);
} else {
top->buf.dictState.key = x;
}
top->buf.dictState.keyFound = !top->buf.dictState.keyFound;
break;
default:
PError(p, "Expected container type.");
break;
}
}
/* Check if a character is whitespace */
static int isWhitespace(uint8_t c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0' || c == ',';
}
/* Check if a character is a valid symbol character */
static int isSymbolChar(uint8_t c) {
if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1;
if (c >= '0' && c <= '9') return 1;
if (c >= '<' && c <= '@') return 1;
if (c >= '*' && c <= '/') return 1;
if (c >= '#' && c <= '&') return 1;
if (c == '_') return 1;
if (c == '^') return 1;
if (c == '!') return 1;
return 0;
}
/* Get an integer power of 10 */
static double exp10(int power) {
if (power == 0) return 1;
if (power > 0) {
double result = 10;
int currentPower = 1;
while (currentPower * 2 <= power) {
result = result * result;
currentPower *= 2;
}
return result * exp10(power - currentPower);
} else {
return 1 / exp10(-power);
}
}
/* Read a number from a string */
static int ParseReadNumber(const uint8_t * string, const uint8_t * end, double * ret, int forceInt) {
int sign = 1, x = 0;
double accum = 0, exp = 1, place = 1;
if (*string == '-') {
sign = -1;
++string;
} else if (*string == '+') {
++string;
}
if (string >= end) return 0;
while (string < end) {
if (*string == '.' && !forceInt) {
place = 0.1;
} else if (!forceInt && (*string == 'e' || *string == 'E')) {
++string;
if (!ParseReadNumber(string, end, &exp, 1))
return 0;
exp = exp10(exp);
break;
} else {
x = *string;
if (x < '0' || x > '9') return 0;
x -= '0';
if (place < 1) {
accum += x * place;
place *= 0.1;
} else {
accum *= 10;
accum += x;
}
}
++string;
}
*ret = accum * sign * exp;
return 1;
}
/* Checks if a string slice is equal to a string constant */
static int checkStrConst(const char * ref, const uint8_t * start, const uint8_t * end) {
while (*ref && start < end) {
if (*ref != *(char *)start) return 0;
++ref;
++start;
}
return !*ref && start == end;
}
/* Handle parsing generic input */
static int ParserMainState(Parser * p, uint8_t c) {
if (c == '(') {
ParserPush(p, PTYPE_FORM);
return 1;
}
if (c == '[') {
ParserPush(p, PTYPE_ARRAY);
return 1;
}
if (c == '{') {
ParserPush(p, PTYPE_DICTIONARY);
return 1;
}
if (c == '"') {
ParserPush(p, PTYPE_STRING);
return 1;
}
if (isWhitespace(c)) return 1;
if (isSymbolChar(c)) {
ParserPush(p, PTYPE_TOKEN);
return 0;
}
PError(p, "Unexpected character.");
return 1;
}
/* Build from the token buffer */
static Value ParserBuildTokenBuffer(Parser * p, Buffer * buf) {
Value x;
Number number;
uint8_t * data = buf->data;
uint8_t * back = data + buf->count;
if (ParseReadNumber(data, back, &number, 0)) {
x.type = TYPE_NUMBER;
x.data.number = number;
} else if (checkStrConst("nil", data, back)) {
x.type = TYPE_NIL;
x.data.boolean = 0;
} else if (checkStrConst("false", data, back)) {
x.type = TYPE_BOOLEAN;
x.data.boolean = 0;
} else if (checkStrConst("true", data, back)) {
x.type = TYPE_BOOLEAN;
x.data.boolean = 1;
} else {
if (buf->data[0] >= '0' && buf->data[0] <= '9') {
PError(p, "Symbols cannot start with digits.");
x.type = TYPE_NIL;
} else {
x.type = TYPE_SYMBOL;
x.data.string = BufferToString(&p->vm->gc, buf);
}
}
return x;
}
/* Handle parsing a token */
static int ParserTokenState(Parser * p, uint8_t c) {
ParseState * top = ParserPeek(p);
Buffer * buf = top->buf.string.buffer;
if (isWhitespace(c) || c == ')' || c == ']' || c == '}') {
ParserPop(p);
ParserTopAppend(p, ParserBuildTokenBuffer(p, buf));
return !(c == ')' || c == ']' || c == '}');
} else if (isSymbolChar(c)) {
BufferPush(&p->vm->gc, buf, c);
return 1;
} else {
PError(p, "Expected symbol character.");
return 1;
}
}
/* Handle parsing a string literal */
static int ParserStringState(Parser * p, uint8_t c) {
ParseState * top = ParserPeek(p);
switch (top->buf.string.state) {
case STRING_STATE_BASE:
if (c == '\\') {
top->buf.string.state = STRING_STATE_ESCAPE;
} else if (c == '"') {
Value x;
x.type = TYPE_STRING;
x.data.string = BufferToString(&p->vm->gc, top->buf.string.buffer);
ParserPop(p);
ParserTopAppend(p, x);
} else {
BufferPush(&p->vm->gc, top->buf.string.buffer, c);
}
break;
case STRING_STATE_ESCAPE:
{
uint8_t next;
switch (c) {
case 'n': next = '\n'; break;
case 'r': next = '\r'; break;
case 't': next = '\t'; break;
case 'f': next = '\f'; break;
case '0': next = '\0'; break;
case '"': next = '"'; break;
case '\'': next = '\''; break;
case 'z': next = '\0'; break;
default:
PError(p, "Unknown string escape sequence.");
return 1;
}
BufferPush(&p->vm->gc, top->buf.string.buffer, next);
top->buf.string.state = STRING_STATE_BASE;
}
break;
case STRING_STATE_ESCAPE_HEX:
break;
case STRING_STATE_ESCAPE_UNICODE:
break;
}
return 1;
}
/* Handle parsing a form */
static int ParserFormState(Parser * p, uint8_t c) {
if (c == ')') {
ParseState * top = ParserPop(p);
Array * array = top->buf.array;
Value x;
x.type = TYPE_FORM;
x.data.array = array;
ParserTopAppend(p, x);
return 1;
} else if (c == ']' || c == '}') {
PError(p, UNEXPECTED_CLOSING_DELIM);
return 1;
} else {
return ParserMainState(p, c);
}
}
/* Handle parsing an array */
static int ParserArrayState(Parser * p, uint8_t c) {
if (c == ']') {
ParseState * top = ParserPop(p);
Array * array = top->buf.array;
Value x;
x.type = TYPE_ARRAY;
x.data.array = array;
ParserTopAppend(p, x);
return 1;
} else if (c == ')' || c == '}') {
PError(p, UNEXPECTED_CLOSING_DELIM);
return 1;
} else {
return ParserMainState(p, c);
}
}
/* Handle parsing a dictionary */
static int ParserDictState(Parser * p, uint8_t c) {
if (c == '}') {
ParseState * top = ParserPop(p);
if (!top->buf.dictState.keyFound) {
Value x;
x.type = TYPE_DICTIONARY;
x.data.dict = top->buf.dictState.dict;
ParserTopAppend(p, x);
return 1;
} else {
PError(p, "Odd number of items in dictionary literal.");
return 1;
}
} else if (c == ')' || c == ']') {
PError(p, UNEXPECTED_CLOSING_DELIM);
return 1;
} else {
return ParserMainState(p, c);
}
}
/* Root state of the parser */
static int ParserRootState(Parser * p, uint8_t c) {
if (c == ']' || c == ')' || c == '}') {
PError(p, UNEXPECTED_CLOSING_DELIM);
return 1;
} else {
return ParserMainState(p, c);
}
}
/* Handle a character */
static int ParserDispatchChar(Parser * p, uint8_t c) {
int done = 0;
while (!done && p->status == PARSER_PENDING) {
ParseState * top = ParserPeek(p);
switch (top->type) {
case PTYPE_ROOT:
done = ParserRootState(p, c);
break;
case PTYPE_TOKEN:
done = ParserTokenState(p, c);
break;
case PTYPE_FORM:
done = ParserFormState(p, c);
break;
case PTYPE_ARRAY:
done = ParserArrayState(p, c);
break;
case PTYPE_STRING:
done = ParserStringState(p, c);
break;
case PTYPE_DICTIONARY:
done = ParserDictState(p, c);
break;
}
}
++p->index;
return !done;
}
/* Parse a C style string. The first value encountered when parsed is put
* in p->value. The string variable is then updated to the next char that
* was not read. Returns 1 if any values were read, otherwise returns 0.
* Returns the number of bytes read.
*/
int ParserParseCString(Parser * p, const char * string) {
int bytesRead = 0;
p->status = PARSER_PENDING;
while ((p->status == PARSER_PENDING) && (string[bytesRead] != '\0')) {
ParserDispatchChar(p, string[bytesRead++]);
}
return bytesRead;
}
/* Parser initialization (memory allocation) */
void ParserInit(Parser * p, VM * vm) {
p->vm = vm;
ParseState * data = GCAlloc(&vm->gc, sizeof(ParseState) * 10);
p->data = data;
p->count = 0;
p->cap = 10;
p->index = 0;
p->error = NULL;
p->status = PARSER_PENDING;
p->value.type = TYPE_NIL;
ParserPush(p, PTYPE_ROOT);
}

14
parse.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef PARSE_H_ONYWMADW
#define PARSE_H_ONYWMADW
#include "datatypes.h"
#define PARSE_ERROR -1
#define PARSE_VALUE_READ 1
#define PARSE_VALUE_PENDING 0
void ParserInit(Parser * p, VM * vm);
int ParserParseCString(Parser * p, const char * string);
#endif /* end of include guard: PARSE_H_ONYWMADW */

425
tags Normal file
View File

@ -0,0 +1,425 @@
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
!_TAG_PROGRAM_VERSION 0.0.0 /d9090e1b/
$(TARGET) Makefile /^$(TARGET): $(OBJECTS)$/;" t
%.o Makefile /^%.o : %.c$/;" t
ARRAY_H_BW48JZSK array.h /^#define ARRAY_H_BW48JZSK$/;" d
Array datatypes.h /^struct Array {$/;" s
Array datatypes.h /^typedef struct Array Array;$/;" t typeref:struct:Array
ArrayEnsure array.c /^void ArrayEnsure(GC * gc, Array * array, uint32_t capacity) {$/;" f typeref:typename:void
ArrayGet array.c /^Value ArrayGet(Array * array, uint32_t index) {$/;" f typeref:typename:Value
ArrayInit array.c /^void ArrayInit(GC * gc, Array * array, uint32_t capacity) {$/;" f typeref:typename:void
ArrayNew array.c /^Array * ArrayNew(GC * gc, uint32_t capacity) {$/;" f typeref:typename:Array *
ArrayPeek array.c /^Value ArrayPeek(Array * array) {$/;" f typeref:typename:Value
ArrayPop array.c /^Value ArrayPop(Array * array) {$/;" f typeref:typename:Value
ArrayPush array.c /^void ArrayPush(GC * gc, Array * array, Value x) {$/;" f typeref:typename:void
ArraySet array.c /^int ArraySet(Array * array, uint32_t index, Value x) {$/;" f typeref:typename:int
BUFFER_H_OEA9T4DJ buffer.h /^#define BUFFER_H_OEA9T4DJ$/;" d
Boolean datatypes.h /^typedef uint8_t Boolean;$/;" t typeref:typename:uint8_t
Buffer datatypes.h /^struct Buffer {$/;" s
Buffer datatypes.h /^typedef struct Buffer Buffer;$/;" t typeref:struct:Buffer
BufferAppendData buffer.c /^void BufferAppendData(GC * gc, Buffer * buffer, uint8_t * string, uint32_t length) {$/;" f typeref:typename:void
BufferDefine buffer.h /^#define BufferDefine(/;" d
BufferEnsure buffer.c /^void BufferEnsure(GC * gc, Buffer * buffer, uint32_t capacity) {$/;" f typeref:typename:void
BufferGet buffer.c /^int32_t BufferGet(Buffer * buffer, uint32_t index) {$/;" f typeref:typename:int32_t
BufferInit buffer.c /^void BufferInit(GC * gc, Buffer * buffer, uint32_t capacity) {$/;" f typeref:typename:void
BufferNew buffer.c /^Buffer * BufferNew(GC * gc, uint32_t capacity) {$/;" f typeref:typename:Buffer *
BufferPush buffer.c /^void BufferPush(GC * gc, Buffer * buffer, uint8_t c) {$/;" f typeref:typename:void
BufferToString buffer.c /^uint8_t * BufferToString(GC * gc, Buffer * buffer) {$/;" f typeref:typename:uint8_t *
CError compile.c /^#define CError(/;" d file:
CFLAGS Makefile /^CFLAGS=-std=c99 -Wall -Wextra -m32 -g$/;" m
CFunction datatypes.h /^typedef Value (*CFunction)(VM * vm);$/;" t typeref:typename:Value (*)(VM * vm)
COMPILE_H_9VXF71HY compile.h /^#define COMPILE_H_9VXF71HY$/;" d
CompileAddition compile.c /^static Slot CompileAddition(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileArray compile.c /^static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {$/;" f typeref:typename:Slot file:
CompileAssign compile.c /^static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value right) {$/;" f typeref:typename:Slot file:
CompileBlock compile.c /^static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t startIndex) {$/;" f typeref:typename:Slot file:
CompileDict compile.c /^static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) {$/;" f typeref:typename:Slot file:
CompileDivision compile.c /^static Slot CompileDivision(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileDo compile.c /^static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileForm compile.c /^static Slot CompileForm(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileFunction compile.c /^static Slot CompileFunction(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileIf compile.c /^static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileLiteral compile.c /^static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {$/;" f typeref:typename:Slot file:
CompileMultiplication compile.c /^static Slot CompileMultiplication(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileNonReferenceType compile.c /^static Slot CompileNonReferenceType(Compiler * c, FormOptions opts, Value x) {$/;" f typeref:typename:Slot file:
CompileOperator compile.c /^static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,$/;" f typeref:typename:Slot file:
CompileQuote compile.c /^static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileSet compile.c /^static Slot CompileSet(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileSubtraction compile.c /^static Slot CompileSubtraction(Compiler * c, FormOptions opts, Array * form) {$/;" f typeref:typename:Slot file:
CompileSymbol compile.c /^static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) {$/;" f typeref:typename:Slot file:
CompileValue compile.c /^static Slot CompileValue(Compiler * c, FormOptions opts, Value x) {$/;" f typeref:typename:Slot file:
Compiler datatypes.h /^struct Compiler {$/;" s
Compiler datatypes.h /^typedef struct Compiler Compiler;$/;" t typeref:struct:Compiler
CompilerAddGlobal compile.c /^void CompilerAddGlobal(Compiler * c, const char * name, Value x) {$/;" f typeref:typename:void
CompilerAddLiteral compile.c /^static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) {$/;" f typeref:typename:uint16_t file:
CompilerCompile compile.c /^Func * CompilerCompile(Compiler * c, Value form) {$/;" f typeref:typename:Func *
CompilerDeclareSymbol compile.c /^static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {$/;" f typeref:typename:uint16_t file:
CompilerDropSlot compile.c /^static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) {$/;" f typeref:typename:void file:
CompilerFreeLocal compile.c /^static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) {$/;" f typeref:typename:void file:
CompilerGC compile.c /^#define CompilerGC(/;" d file:
CompilerGenFuncDef compile.c /^static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t arity) {$/;" f typeref:typename:FuncDef * file:
CompilerGetLocal compile.c /^static uint16_t CompilerGetLocal(Compiler * c, Scope * scope) {$/;" f typeref:typename:uint16_t file:
CompilerInit compile.c /^void CompilerInit(Compiler * c, VM * vm) {$/;" f typeref:typename:void
CompilerPopScope compile.c /^static void CompilerPopScope(Compiler * c) {$/;" f typeref:typename:void file:
CompilerPushScope compile.c /^static Scope * CompilerPushScope(Compiler * c, int sameFunction) {$/;" f typeref:typename:Scope * file:
CompilerReturnSlot compile.c /^static void CompilerReturnSlot(Compiler * c, Slot slot) {$/;" f typeref:typename:void file:
CompilerTrackerFree compile.c /^static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToB/;" f typeref:typename:void file:
CompilerTrackerInit compile.c /^static void CompilerTrackerInit(Compiler * c, SlotTracker * tracker) {$/;" f typeref:typename:void file:
CompilerTrackerPush compile.c /^static void CompilerTrackerPush(Compiler * c, SlotTracker * tracker, Slot slot) {$/;" f typeref:typename:void file:
DATATYPES_H_PJJ035NT datatypes.h /^#define DATATYPES_H_PJJ035NT$/;" d
DICT_H_YN2BKHUQ dict.h /^#define DICT_H_YN2BKHUQ$/;" d
DO_MULTI_MATH vm.c /^ #define DO_MULTI_MATH(/;" d file:
DictBucket datatypes.h /^struct DictBucket {$/;" s
DictBucket datatypes.h /^typedef struct DictBucket DictBucket;$/;" t typeref:struct:DictBucket
DictFind dict.c /^static DictBucket * DictFind(Dictionary * dict, Value * key, DictBucket ** prev) {$/;" f typeref:typename:DictBucket * file:
DictGet dict.c /^Value DictGet(Dictionary * dict, Value * key) {$/;" f typeref:typename:Value
DictInit dict.c /^void DictInit(GC * gc, Dictionary * dict, uint32_t capacity) {$/;" f typeref:typename:void
DictIterate dict.c /^void DictIterate(Dictionary * dict, DictionaryIterator * iterator) {$/;" f typeref:typename:void
DictIterateNext dict.c /^int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket) {$/;" f typeref:typename:int
DictNew dict.c /^Dictionary * DictNew(GC * gc, uint32_t capacity) {$/;" f typeref:typename:Dictionary *
DictPut dict.c /^void DictPut(GC * gc, Dictionary * dict, Value * key, Value * value) {$/;" f typeref:typename:void
DictReHash dict.c /^static void DictReHash(GC * gc, Dictionary * dict, uint32_t size) {$/;" f typeref:typename:void file:
DictRemove dict.c /^Value DictRemove(GC * gc, Dictionary * dict, Value * key) {$/;" f typeref:typename:Value
Dictionary datatypes.h /^struct Dictionary {$/;" s
Dictionary datatypes.h /^typedef struct Dictionary Dictionary;$/;" t typeref:struct:Dictionary
DictionaryIterator datatypes.h /^struct DictionaryIterator {$/;" s
DictionaryIterator datatypes.h /^typedef struct DictionaryIterator DictionaryIterator;$/;" t typeref:struct:DictionaryIterator
DirectoryOfThisScript .ycm_extra_conf.py /^def DirectoryOfThisScript():$/;" f
EXPECTED_FUNCTION vm.c /^static const char EXPECTED_FUNCTION[] = "Expected function";$/;" v typeref:typename:const char[] file:
FRAME_SIZE datatypes.h /^#define FRAME_SIZE /;" d
FlagsForFile .ycm_extra_conf.py /^def FlagsForFile( filename, **kwargs ):$/;" f
FormOptions compile.c /^struct FormOptions {$/;" s file:
FormOptions compile.c /^typedef struct FormOptions FormOptions;$/;" t typeref:struct:FormOptions file:
FormOptionsDefault compile.c /^static FormOptions FormOptionsDefault() {$/;" f typeref:typename:FormOptions file:
FrameCallee datatypes.h /^#define FrameCallee(/;" d
FrameEnv datatypes.h /^#define FrameEnv(/;" d
FrameEnvValue datatypes.h /^#define FrameEnvValue(/;" d
FrameMeta datatypes.h /^#define FrameMeta(/;" d
FramePC datatypes.h /^#define FramePC(/;" d
FramePCValue datatypes.h /^#define FramePCValue(/;" d
FramePrevSize datatypes.h /^#define FramePrevSize(/;" d
FrameReturn datatypes.h /^#define FrameReturn(/;" d
FrameSize datatypes.h /^#define FrameSize(/;" d
Func datatypes.h /^struct Func {$/;" s
Func datatypes.h /^typedef struct Func Func;$/;" t typeref:struct:Func
FuncDef datatypes.h /^struct FuncDef {$/;" s
FuncDef datatypes.h /^typedef struct FuncDef FuncDef;$/;" t typeref:struct:FuncDef
FuncEnv datatypes.h /^struct FuncEnv {$/;" s
FuncEnv datatypes.h /^typedef struct FuncEnv FuncEnv;$/;" t typeref:struct:FuncEnv
GC datatypes.h /^struct GC {$/;" s
GC datatypes.h /^typedef struct GC GC;$/;" t typeref:struct:GC
GCAlloc gc.c /^void * GCAlloc(GC * gc, uint32_t size) {$/;" f typeref:typename:void *
GCClear gc.c /^void GCClear(GC * gc) {$/;" f typeref:typename:void
GCCollect gc.h /^#define GCCollect(/;" d
GCHeader gc.c /^#define GCHeader(/;" d file:
GCInit gc.c /^void GCInit(GC * gc, uint32_t memoryInterval) {$/;" f typeref:typename:void
GCMark gc.c /^void GCMark(GC * gc, Value * x) {$/;" f typeref:typename:void
GCMarkFuncEnv gc.c /^static void GCMarkFuncEnv(GC * gc, FuncEnv * env) {$/;" f typeref:typename:void file:
GCMarkMemory gc.c /^void GCMarkMemory(GC * gc, void * memory) {$/;" f typeref:typename:void
GCMaybeCollect gc.h /^#define GCMaybeCollect(/;" d
GCMemoryHeader gc.c /^struct GCMemoryHeader {$/;" s file:
GCMemoryHeader gc.c /^typedef struct GCMemoryHeader GCMemoryHeader;$/;" t typeref:struct:GCMemoryHeader file:
GCNeedsCollect gc.h /^#define GCNeedsCollect(/;" d
GCPrepare gc.c /^static void * GCPrepare(GC * gc, char * rawBlock, uint32_t size) {$/;" f typeref:typename:void * file:
GCSweep gc.c /^void GCSweep(GC * gc) {$/;" f typeref:typename:void
GCZalloc gc.c /^void * GCZalloc(GC * gc, uint32_t size) {$/;" f typeref:typename:void *
GC_H_N8L3U4KK gc.h /^#define GC_H_N8L3U4KK$/;" d
GetCompilationInfoForFile .ycm_extra_conf.py /^def GetCompilationInfoForFile( filename ):$/;" f
GetSpecial compile.c /^static SpecialFormHelper GetSpecial(Array * form) {$/;" f typeref:typename:SpecialFormHelper file:
GetUpValue vm.c /^static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) {$/;" f typeref:typename:Value * file:
HEX value.c /^#define HEX(/;" d file:
HEX_CHARACTERS value.c /^static const char * HEX_CHARACTERS = "0123456789ABCDEF";$/;" v typeref:typename:const char * file:
IsHeaderFile .ycm_extra_conf.py /^def IsHeaderFile( filename ):$/;" f
LoadCString value.c /^static uint8_t * LoadCString(GC * gc, const char * string, uint32_t len) {$/;" f typeref:typename:uint8_t * file:
LoadConstant vm.c /^static Value * LoadConstant(VM * vm, Func * fn, uint16_t index) {$/;" f typeref:typename:Value * file:
MakeRelativePathsInFlagsAbsolute .ycm_extra_conf.py /^def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):$/;" f
NO_UPVALUE vm.c /^static const char NO_UPVALUE[] = "Out of memory";$/;" v typeref:typename:const char[] file:
Number datatypes.h /^typedef double Number;$/;" t typeref:typename:double
NumberToString value.c /^static uint8_t * NumberToString(GC * gc, Number x) {$/;" f typeref:typename:uint8_t * file:
OBJECTS Makefile /^OBJECTS=$(patsubst %.c,%.o,$(SOURCES))$/;" m
OOM vm.c /^static const char OOM[] = "Out of memory";$/;" v typeref:typename:const char[] file:
OPCODES_H_EFPEYNZ0 opcodes.h /^#define OPCODES_H_EFPEYNZ0$/;" d
OpCode opcodes.h /^enum OpCode {$/;" g
PARSER_ERROR datatypes.h /^#define PARSER_ERROR /;" d
PARSER_FULL datatypes.h /^#define PARSER_FULL /;" d
PARSER_PENDING datatypes.h /^#define PARSER_PENDING /;" d
PARSE_ERROR parse.h /^#define PARSE_ERROR /;" d
PARSE_H_ONYWMADW parse.h /^#define PARSE_H_ONYWMADW$/;" d
PARSE_VALUE_PENDING parse.h /^#define PARSE_VALUE_PENDING /;" d
PARSE_VALUE_READ parse.h /^#define PARSE_VALUE_READ /;" d
PError parse.c /^#define PError(/;" d file:
PREFIX Makefile /^PREFIX=\/usr\/local$/;" m
PTYPE_ARRAY parse.c /^ PTYPE_ARRAY,$/;" e enum:ParseType file:
PTYPE_DICTIONARY parse.c /^ PTYPE_DICTIONARY,$/;" e enum:ParseType file:
PTYPE_FORM parse.c /^ PTYPE_FORM,$/;" e enum:ParseType file:
PTYPE_ROOT parse.c /^ PTYPE_ROOT,$/;" e enum:ParseType file:
PTYPE_STRING parse.c /^ PTYPE_STRING,$/;" e enum:ParseType file:
PTYPE_TOKEN parse.c /^ PTYPE_TOKEN$/;" e enum:ParseType file:
ParseReadNumber parse.c /^static int ParseReadNumber(const uint8_t * string, const uint8_t * end, double * ret, int forceI/;" f typeref:typename:int file:
ParseState datatypes.h /^typedef struct ParseState ParseState;$/;" t typeref:struct:ParseState
ParseState parse.c /^struct ParseState {$/;" s file:
ParseType parse.c /^typedef enum ParseType {$/;" g file:
ParseType parse.c /^} ParseType;$/;" t typeref:enum:ParseType file:
Parser datatypes.h /^struct Parser {$/;" s
Parser datatypes.h /^typedef struct Parser Parser;$/;" t typeref:struct:Parser
ParserArrayState parse.c /^static int ParserArrayState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserBuildTokenBuffer parse.c /^static Value ParserBuildTokenBuffer(Parser * p, Buffer * buf) {$/;" f typeref:typename:Value file:
ParserDictState parse.c /^static int ParserDictState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserDispatchChar parse.c /^static int ParserDispatchChar(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserFormState parse.c /^static int ParserFormState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserInit parse.c /^void ParserInit(Parser * p, VM * vm) {$/;" f typeref:typename:void
ParserMainState parse.c /^static int ParserMainState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserParseCString parse.c /^int ParserParseCString(Parser * p, const char * string) {$/;" f typeref:typename:int
ParserPeek parse.c /^static ParseState * ParserPeek(Parser * p) {$/;" f typeref:typename:ParseState * file:
ParserPop parse.c /^static ParseState * ParserPop(Parser * p) {$/;" f typeref:typename:ParseState * file:
ParserPush parse.c /^static void ParserPush(Parser *p, ParseType type) {$/;" f typeref:typename:void file:
ParserRootState parse.c /^static int ParserRootState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserStringState parse.c /^static int ParserStringState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserTokenState parse.c /^static int ParserTokenState(Parser * p, uint8_t c) {$/;" f typeref:typename:int file:
ParserTopAppend parse.c /^static void ParserTopAppend(Parser * p, Value x) {$/;" f typeref:typename:void file:
SOURCES Makefile /^SOURCES=main.c parse.c value.c vm.c dict.c array.c buffer.c gc.c compile.c$/;" m
SOURCE_EXTENSIONS .ycm_extra_conf.py /^SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]$/;" v
STRING_STATE_BASE parse.c /^ STRING_STATE_BASE,$/;" e enum:ParseState::__anon9bce43d1010a::__anon9bce43d10308::__anon9bce43d10403 file:
STRING_STATE_ESCAPE parse.c /^ STRING_STATE_ESCAPE,$/;" e enum:ParseState::__anon9bce43d1010a::__anon9bce43d10308::__anon9bce43d10403 file:
STRING_STATE_ESCAPE_HEX parse.c /^ STRING_STATE_ESCAPE_HEX$/;" e enum:ParseState::__anon9bce43d1010a::__anon9bce43d10308::__anon9bce43d10403 file:
STRING_STATE_ESCAPE_UNICODE parse.c /^ STRING_STATE_ESCAPE_UNICODE,$/;" e enum:ParseState::__anon9bce43d1010a::__anon9bce43d10308::__anon9bce43d10403 file:
Scope compile.c /^struct Scope {$/;" s file:
Scope datatypes.h /^typedef struct Scope Scope;$/;" t typeref:struct:Scope
ScopeSymbolResolve compile.c /^static int ScopeSymbolResolve(Scope * scope, Value x,$/;" f typeref:typename:int file:
Slot compile.c /^struct Slot {$/;" s file:
Slot compile.c /^typedef struct Slot Slot;$/;" t typeref:struct:Slot file:
SlotDefault compile.c /^static Slot SlotDefault() {$/;" f typeref:typename:Slot file:
SlotTracker compile.c /^struct SlotTracker {$/;" s file:
SlotTracker compile.c /^typedef struct SlotTracker SlotTracker;$/;" t typeref:struct:SlotTracker file:
SpecialFormHelper compile.c /^typedef Slot (*SpecialFormHelper) (Compiler * c, FormOptions opts, Array * form);$/;" t typeref:typename:Slot (*)(Compiler * c,FormOptions opts,Array * form) file:
StringDescription value.c /^static uint8_t * StringDescription(GC * gc, const char * title, uint32_t titlelen, void * pointe/;" f typeref:typename:uint8_t * file:
TARGET Makefile /^TARGET=interp$/;" m
THREAD_STATUS_ALIVE datatypes.h /^#define THREAD_STATUS_ALIVE /;" d
THREAD_STATUS_DEAD datatypes.h /^#define THREAD_STATUS_DEAD /;" d
THREAD_STATUS_PENDING datatypes.h /^#define THREAD_STATUS_PENDING /;" d
TYPE_ARRAY datatypes.h /^ TYPE_ARRAY,$/;" e enum:Type
TYPE_BOOLEAN datatypes.h /^ TYPE_BOOLEAN,$/;" e enum:Type
TYPE_BYTEBUFFER datatypes.h /^ TYPE_BYTEBUFFER,$/;" e enum:Type
TYPE_CFUNCTION datatypes.h /^ TYPE_CFUNCTION,$/;" e enum:Type
TYPE_DICTIONARY datatypes.h /^ TYPE_DICTIONARY,$/;" e enum:Type
TYPE_FORM datatypes.h /^ TYPE_FORM,$/;" e enum:Type
TYPE_FUNCDEF datatypes.h /^ TYPE_FUNCDEF,$/;" e enum:Type
TYPE_FUNCENV datatypes.h /^ TYPE_FUNCENV$/;" e enum:Type
TYPE_FUNCTION datatypes.h /^ TYPE_FUNCTION,$/;" e enum:Type
TYPE_NIL datatypes.h /^ TYPE_NIL = 0,$/;" e enum:Type
TYPE_NUMBER datatypes.h /^ TYPE_NUMBER,$/;" e enum:Type
TYPE_STRING datatypes.h /^ TYPE_STRING,$/;" e enum:Type
TYPE_SYMBOL datatypes.h /^ TYPE_SYMBOL,$/;" e enum:Type
TYPE_THREAD datatypes.h /^ TYPE_THREAD,$/;" e enum:Type
ThreadStack datatypes.h /^#define ThreadStack(/;" d
Type datatypes.h /^typedef enum Type {$/;" g
Type datatypes.h /^} Type;$/;" t typeref:enum:Type
UNEXPECTED_CLOSING_DELIM parse.c /^static const char UNEXPECTED_CLOSING_DELIM[] = "Unexpected closing delimiter";$/;" v typeref:typename:const char[] file:
VALUE_H_1RJPQKFM value.h /^#define VALUE_H_1RJPQKFM$/;" d
VM datatypes.h /^struct VM {$/;" s
VM datatypes.h /^typedef struct VM VM;$/;" t typeref:struct:VM
VMArg vm.c /^#define VMArg(/;" d file:
VMAssert vm.h /^#define VMAssert(/;" d
VMCallOp vm.c /^static void VMCallOp(VM * vm) {$/;" f typeref:typename:void file:
VMCollect vm.c /^void VMCollect(VM * vm) {$/;" f typeref:typename:void
VMDeinit vm.c /^void VMDeinit(VM * vm) {$/;" f typeref:typename:void
VMError vm.h /^#define VMError(/;" d
VMExit vm.h /^#define VMExit(/;" d
VMHandleOutOfMemory vm.c /^static void VMHandleOutOfMemory(GC * gc) {$/;" f typeref:typename:void file:
VMInit vm.c /^void VMInit(VM * vm) {$/;" f typeref:typename:void
VMLoad vm.c /^void VMLoad(VM * vm, Func * func) {$/;" f typeref:typename:void
VMMakeClosure vm.c /^static void VMMakeClosure(VM * vm, uint16_t ret, uint16_t literal) {$/;" f typeref:typename:void file:
VMMark vm.c /^void VMMark(VM * vm) {$/;" f typeref:typename:void
VMMaybeCollect vm.c /^void VMMaybeCollect(VM * vm) {$/;" f typeref:typename:void
VMOpArg vm.c /^#define VMOpArg(/;" d file:
VMPushCallee vm.c /^static void VMPushCallee(VM * vm, uint32_t ret, uint32_t arity, Value * callee) {$/;" f typeref:typename:void file:
VMResult vm.h /^#define VMResult(/;" d
VMReturn vm.c /^static void VMReturn(VM * vm, Value ret) {$/;" f typeref:typename:void file:
VMS_EXPECTED_NUMBER_LOP vm.c /^static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number";$/;" v typeref:typename:const char[] file:
VMS_EXPECTED_NUMBER_ROP vm.c /^static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number";$/;" v typeref:typename:const char[] file:
VMStart vm.c /^int VMStart(VM * vm) {$/;" f typeref:typename:int
VMTailCallOp vm.c /^static void VMTailCallOp(VM * vm) {$/;" f typeref:typename:void file:
VMThreadPop vm.c /^static void VMThreadPop(VM * vm, Array * thread) {$/;" f typeref:typename:void file:
VMThreadPush vm.c /^static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {$/;" f typeref:typename:void file:
VMThreadSplitStack vm.c /^static void VMThreadSplitStack(VM * vm, Array * thread) {$/;" f typeref:typename:void file:
VM_H_C4OZU8CQ vm.h /^#define VM_H_C4OZU8CQ$/;" d
VM_OP_ADD opcodes.h /^ VM_OP_ADD = 0, \/* 0x0000 *\/$/;" e enum:OpCode
VM_OP_ADM opcodes.h /^ VM_OP_ADM, \/* 0x001c *\/$/;" e enum:OpCode
VM_OP_ARR opcodes.h /^ VM_OP_ARR, \/* 0x0019 *\/$/;" e enum:OpCode
VM_OP_CAL opcodes.h /^ VM_OP_CAL, \/* 0x000e *\/$/;" e enum:OpCode
VM_OP_CLN opcodes.h /^ VM_OP_CLN, \/* 0x0015 *\/$/;" e enum:OpCode
VM_OP_CST opcodes.h /^ VM_OP_CST, \/* 0x0011 *\/$/;" e enum:OpCode
VM_OP_DIC opcodes.h /^ VM_OP_DIC, \/* 0x001a *\/$/;" e enum:OpCode
VM_OP_DIV opcodes.h /^ VM_OP_DIV, \/* 0x0003 *\/$/;" e enum:OpCode
VM_OP_DVM opcodes.h /^ VM_OP_DVM, \/* 0x001f *\/$/;" e enum:OpCode
VM_OP_EQL opcodes.h /^ VM_OP_EQL, \/* 0x0016 *\/$/;" e enum:OpCode
VM_OP_F64 opcodes.h /^ VM_OP_F64, \/* 0x0013 *\/$/;" e enum:OpCode
VM_OP_FLS opcodes.h /^ VM_OP_FLS, \/* 0x0007 *\/$/;" e enum:OpCode
VM_OP_I16 opcodes.h /^ VM_OP_I16, \/* 0x000a *\/$/;" e enum:OpCode
VM_OP_I32 opcodes.h /^ VM_OP_I32, \/* 0x0012 *\/$/;" e enum:OpCode
VM_OP_JIF opcodes.h /^ VM_OP_JIF, \/* 0x000c *\/$/;" e enum:OpCode
VM_OP_JMP opcodes.h /^ VM_OP_JMP, \/* 0x000d *\/$/;" e enum:OpCode
VM_OP_LD0 opcodes.h /^ VM_OP_LD0, \/* 0x0005 *\/$/;" e enum:OpCode
VM_OP_LD1 opcodes.h /^ VM_OP_LD1, \/* 0x0006 *\/$/;" e enum:OpCode
VM_OP_LTE opcodes.h /^ VM_OP_LTE, \/* 0x0018 *\/$/;" e enum:OpCode
VM_OP_LTN opcodes.h /^ VM_OP_LTN, \/* 0x0017 *\/$/;" e enum:OpCode
VM_OP_MOV opcodes.h /^ VM_OP_MOV, \/* 0x0014 *\/$/;" e enum:OpCode
VM_OP_MUL opcodes.h /^ VM_OP_MUL, \/* 0x0002 *\/$/;" e enum:OpCode
VM_OP_MUM opcodes.h /^ VM_OP_MUM, \/* 0x001e *\/$/;" e enum:OpCode
VM_OP_NIL opcodes.h /^ VM_OP_NIL, \/* 0x0009 *\/$/;" e enum:OpCode
VM_OP_NOT opcodes.h /^ VM_OP_NOT, \/* 0x0004 *\/$/;" e enum:OpCode
VM_OP_RET opcodes.h /^ VM_OP_RET, \/* 0x000f *\/$/;" e enum:OpCode
VM_OP_RTN opcodes.h /^ VM_OP_RTN \/* 0x0020 *\/$/;" e enum:OpCode
VM_OP_SBM opcodes.h /^ VM_OP_SBM, \/* 0x001d *\/$/;" e enum:OpCode
VM_OP_SUB opcodes.h /^ VM_OP_SUB, \/* 0x0001 *\/$/;" e enum:OpCode
VM_OP_SUV opcodes.h /^ VM_OP_SUV, \/* 0x0010 *\/$/;" e enum:OpCode
VM_OP_TCL opcodes.h /^ VM_OP_TCL, \/* 0x001b *\/$/;" e enum:OpCode
VM_OP_TRU opcodes.h /^ VM_OP_TRU, \/* 0x0008 *\/$/;" e enum:OpCode
VM_OP_UPV opcodes.h /^ VM_OP_UPV, \/* 0x000b *\/$/;" e enum:OpCode
VSTRING_H_TFCSVBEE vstring.h /^#define VSTRING_H_TFCSVBEE$/;" d
VStringHash vstring.h /^#define VStringHash(/;" d
VStringRaw vstring.h /^#define VStringRaw(/;" d
VStringSize vstring.h /^#define VStringSize(/;" d
Value datatypes.h /^struct Value {$/;" s
Value datatypes.h /^typedef struct Value Value;$/;" t typeref:struct:Value
ValueCompare value.c /^int ValueCompare(Value * x, Value * y) {$/;" f typeref:typename:int
ValueData datatypes.h /^typedef union ValueData ValueData;$/;" t typeref:union:ValueData
ValueData datatypes.h /^union ValueData {$/;" u
ValueEqual value.c /^int ValueEqual(Value * x, Value * y) {$/;" f typeref:typename:int
ValueHash value.c /^uint32_t ValueHash(Value * x) {$/;" f typeref:typename:uint32_t
ValueLoadCString value.c /^Value ValueLoadCString(GC * gc, const char * string) {$/;" f typeref:typename:Value
ValuePrint value.c /^void ValuePrint(Value * x, uint32_t indent) {$/;" f typeref:typename:void
ValueToString value.c /^uint8_t * ValueToString(GC * gc, Value * x) {$/;" f typeref:typename:uint8_t *
__anon69403613010a value.c /^ union {$/;" u function:StringDescription file:
__anon69403613020a value.c /^ union {$/;" u function:ValueHash file:
__anon9bce43d1010a parse.c /^ union {$/;" u struct:ParseState file:
__anon9bce43d10208 parse.c /^ struct {$/;" s union:ParseState::__anon9bce43d1010a file:
__anon9bce43d10308 parse.c /^ struct {$/;" s union:ParseState::__anon9bce43d1010a file:
__anon9bce43d10403 parse.c /^ enum {$/;" g struct:ParseState::__anon9bce43d1010a::__anon9bce43d10308 file:
all Makefile /^all: $(TARGET)$/;" t
arity datatypes.h /^ uint32_t arity;$/;" m struct:FuncDef typeref:typename:uint32_t
array datatypes.h /^ Array * array;$/;" m union:ValueData typeref:typename:Array *
array parse.c /^ Array * array;$/;" m union:ParseState::__anon9bce43d1010a typeref:typename:Array * file:
base datatypes.h /^ Value * base;$/;" m struct:VM typeref:typename:Value *
black datatypes.h /^ uint32_t black : 1;$/;" m struct:GC typeref:typename:uint32_t:1
blocks datatypes.h /^ void * blocks;$/;" m struct:GC typeref:typename:void *
boolean datatypes.h /^ Boolean boolean;$/;" m union:ValueData typeref:typename:Boolean
bucket datatypes.h /^ DictBucket * bucket;$/;" m struct:DictionaryIterator typeref:typename:DictBucket *
buckets datatypes.h /^ DictBucket ** buckets;$/;" m struct:Dictionary typeref:typename:DictBucket **
buf parse.c /^ } buf;$/;" m struct:ParseState typeref:union:ParseState::__anon9bce43d1010a file:
buffer datatypes.h /^ Buffer * buffer;$/;" m union:ValueData typeref:typename:Buffer *
buffer datatypes.h /^ Buffer buffer;$/;" m struct:Compiler typeref:typename:Buffer
buffer parse.c /^ Buffer * buffer;$/;" m struct:ParseState::__anon9bce43d1010a::__anon9bce43d10308 typeref:typename:Buffer * file:
byteCode datatypes.h /^ uint16_t * byteCode;$/;" m struct:FuncDef typeref:typename:uint16_t *
byteCodeLen datatypes.h /^ uint32_t byteCodeLen;$/;" m struct:FuncDef typeref:typename:uint32_t
bytes value.c /^ uint8_t bytes[sizeof(void *)];$/;" m union:StringDescription::__anon69403613010a typeref:typename:uint8_t[] file:
canChoose compile.c /^ uint16_t canChoose : 1;$/;" m struct:FormOptions typeref:typename:uint16_t:1 file:
canDrop compile.c /^ uint16_t canDrop : 1;$/;" m struct:FormOptions typeref:typename:uint16_t:1 file:
cap datatypes.h /^ uint32_t cap;$/;" m struct:Parser typeref:typename:uint32_t
capacity compile.c /^ uint32_t capacity;$/;" m struct:SlotTracker typeref:typename:uint32_t file:
capacity datatypes.h /^ uint32_t capacity;$/;" m struct:Array typeref:typename:uint32_t
capacity datatypes.h /^ uint32_t capacity;$/;" m struct:Buffer typeref:typename:uint32_t
capacity datatypes.h /^ uint32_t capacity;$/;" m struct:Dictionary typeref:typename:uint32_t
cfunction datatypes.h /^ CFunction cfunction;$/;" m union:ValueData typeref:typename:CFunction
checkStrConst parse.c /^static int checkStrConst(const char * ref, const uint8_t * start, const uint8_t * end) {$/;" f typeref:typename:int file:
clean Makefile /^clean:$/;" t
color gc.c /^ uint32_t color;$/;" m struct:GCMemoryHeader typeref:typename:uint32_t file:
compilation_database_folder .ycm_extra_conf.py /^compilation_database_folder = ''$/;" v
count compile.c /^ uint32_t count;$/;" m struct:SlotTracker typeref:typename:uint32_t file:
count datatypes.h /^ uint32_t count;$/;" m struct:Array typeref:typename:uint32_t
count datatypes.h /^ uint32_t count;$/;" m struct:Buffer typeref:typename:uint32_t
count datatypes.h /^ uint32_t count;$/;" m struct:Dictionary typeref:typename:uint32_t
count datatypes.h /^ uint32_t count;$/;" m struct:Parser typeref:typename:uint32_t
data datatypes.h /^ ParseState * data;$/;" m struct:Parser typeref:typename:ParseState *
data datatypes.h /^ Value * data;$/;" m struct:Array typeref:typename:Value *
data datatypes.h /^ ValueData data;$/;" m struct:Value typeref:typename:ValueData
data datatypes.h /^ uint8_t * data;$/;" m struct:Buffer typeref:typename:uint8_t *
data datatypes.h /^} data;$/;" v typeref:union:ValueData
database .ycm_extra_conf.py /^ database = ycm_core.CompilationDatabase( compilation_database_folder )$/;" v
debug Makefile /^debug: $(TARGET)$/;" t
debugRepl main.c /^void debugRepl() {$/;" f typeref:typename:void
def datatypes.h /^ FuncDef * def;$/;" m struct:Func typeref:typename:FuncDef *
dict datatypes.h /^ Dictionary * dict;$/;" m struct:DictionaryIterator typeref:typename:Dictionary *
dict datatypes.h /^ Dictionary * dict;$/;" m union:ValueData typeref:typename:Dictionary *
dict parse.c /^ Dictionary * dict;$/;" m struct:ParseState::__anon9bce43d1010a::__anon9bce43d10208 typeref:typename:Dictionary * file:
dictState parse.c /^ } dictState;$/;" m union:ParseState::__anon9bce43d1010a typeref:struct:ParseState::__anon9bce43d1010a::__anon9bce43d10208 file:
djb2 value.c /^uint32_t djb2(const uint8_t * str) {$/;" f typeref:typename:uint32_t
env datatypes.h /^ Array env;$/;" m struct:Compiler typeref:typename:Array
env datatypes.h /^ FuncEnv * env;$/;" m struct:Func typeref:typename:FuncEnv *
error datatypes.h /^ const char * error;$/;" m struct:Compiler typeref:typename:const char *
error datatypes.h /^ const char * error;$/;" m struct:Parser typeref:typename:const char *
error datatypes.h /^ const char * error;$/;" m struct:VM typeref:typename:const char *
exp10 parse.c /^static double exp10(int power) {$/;" f typeref:typename:double file:
flags .ycm_extra_conf.py /^flags = [$/;" v
freeHeap compile.c /^ uint16_t * freeHeap;$/;" m struct:Scope typeref:typename:uint16_t * file:
func datatypes.h /^ Func * func;$/;" m union:ValueData typeref:typename:Func *
funcdef datatypes.h /^ FuncDef * funcdef;$/;" m union:ValueData typeref:typename:FuncDef *
funcenv datatypes.h /^ FuncEnv * funcenv;$/;" m union:ValueData typeref:typename:FuncEnv *
gc datatypes.h /^ GC gc;$/;" m struct:VM typeref:typename:GC
handleOutOfMemory datatypes.h /^ void (*handleOutOfMemory)(GC * gc);$/;" m struct:GC typeref:typename:void (*)(GC * gc)
hash value.c /^ uint32_t hash;$/;" m union:ValueHash::__anon69403613020a typeref:typename:uint32_t file:
heapCapacity compile.c /^ uint32_t heapCapacity;$/;" m struct:Scope typeref:typename:uint32_t file:
heapSize compile.c /^ uint32_t heapSize;$/;" m struct:Scope typeref:typename:uint32_t file:
index compile.c /^ uint16_t index;$/;" m struct:Slot typeref:typename:uint16_t file:
index datatypes.h /^ uint32_t index;$/;" m struct:DictionaryIterator typeref:typename:uint32_t
index datatypes.h /^ uint32_t index;$/;" m struct:Parser typeref:typename:uint32_t
install Makefile /^install: $(TARGET)$/;" t
isDud compile.c /^ uint16_t isDud : 1;$/;" m struct:Slot typeref:typename:uint16_t:1 file:
isSymbolChar parse.c /^static int isSymbolChar(uint8_t c) {$/;" f typeref:typename:int file:
isTail compile.c /^ uint16_t isTail : 1;$/;" m struct:FormOptions typeref:typename:uint16_t:1 file:
isTemp compile.c /^ uint16_t isTemp : 1;$/;" m struct:Slot typeref:typename:uint16_t:1 file:
isWhitespace parse.c /^static int isWhitespace(uint8_t c) {$/;" f typeref:typename:int file:
jump datatypes.h /^ jmp_buf jump;$/;" m struct:VM typeref:typename:jmp_buf
key datatypes.h /^ Value key;$/;" m struct:DictBucket typeref:typename:Value
key parse.c /^ Value key;$/;" m struct:ParseState::__anon9bce43d1010a::__anon9bce43d10208 typeref:typename:Value file:
keyFound parse.c /^ int keyFound;$/;" m struct:ParseState::__anon9bce43d1010a::__anon9bce43d10208 typeref:typename:int file:
literals compile.c /^ Dictionary * literals;$/;" m struct:Scope typeref:typename:Dictionary * file:
literals datatypes.h /^ Value * literals; \/* Contains strings, FuncDefs, etc. *\/$/;" m struct:FuncDef typeref:typename:Value *
literalsArray compile.c /^ Array * literalsArray;$/;" m struct:Scope typeref:typename:Array * file:
literalsLen datatypes.h /^ uint32_t literalsLen;$/;" m struct:FuncDef typeref:typename:uint32_t
locals compile.c /^ Dictionary locals;$/;" m struct:Scope typeref:typename:Dictionary file:
locals datatypes.h /^ uint32_t locals;$/;" m struct:FuncDef typeref:typename:uint32_t
main main.c /^int main() {$/;" f typeref:typename:int
memoryInterval datatypes.h /^ uint32_t memoryInterval;$/;" m struct:GC typeref:typename:uint32_t
next datatypes.h /^ DictBucket * next;$/;" m struct:DictBucket typeref:typename:DictBucket *
next gc.c /^ GCMemoryHeader * next;$/;" m struct:GCMemoryHeader typeref:typename:GCMemoryHeader * file:
nextCollection datatypes.h /^ uint32_t nextCollection;$/;" m struct:GC typeref:typename:uint32_t
nextLocal compile.c /^ uint16_t nextLocal;$/;" m struct:Scope typeref:typename:uint16_t file:
nextScope compile.c /^ Scope * nextScope;$/;" m struct:Scope typeref:typename:Scope * file:
number datatypes.h /^ Number number;$/;" m union:ValueData typeref:typename:Number
number value.c /^ Number number;$/;" m union:ValueHash::__anon69403613020a typeref:typename:Number file:
onError datatypes.h /^ jmp_buf onError;$/;" m struct:Compiler typeref:typename:jmp_buf
p value.c /^ void * p;$/;" m union:StringDescription::__anon69403613010a typeref:typename:void * file:
parent datatypes.h /^ Func * parent;$/;" m struct:Func typeref:typename:Func *
pc datatypes.h /^ uint16_t * pc;$/;" m struct:VM typeref:typename:uint16_t *
pointer datatypes.h /^ void * pointer;$/;" m union:ValueData typeref:typename:void *
previousScope compile.c /^ Scope * previousScope;$/;" m struct:Scope typeref:typename:Scope * file:
root datatypes.h /^ Scope * root;$/;" m struct:Compiler typeref:typename:Scope *
run Makefile /^run: $(TARGET)$/;" t
slots compile.c /^ Slot * slots;$/;" m struct:SlotTracker typeref:typename:Slot * file:
stackOffset datatypes.h /^ uint32_t stackOffset; \/* Used as environment size when off stack *\/$/;" m struct:FuncEnv typeref:typename:uint32_t
state parse.c /^ } state;$/;" m struct:ParseState::__anon9bce43d1010a::__anon9bce43d10308 typeref:enum:ParseState::__anon9bce43d1010a::__anon9bce43d10308::__anon9bce43d10403 file:
status datatypes.h /^ uint32_t status;$/;" m struct:Parser typeref:typename:uint32_t
string datatypes.h /^ uint8_t * string;$/;" m union:ValueData typeref:typename:uint8_t *
string parse.c /^ } string;$/;" m union:ParseState::__anon9bce43d1010a typeref:struct:ParseState::__anon9bce43d1010a::__anon9bce43d10308 file:
tail datatypes.h /^ Scope * tail;$/;" m struct:Compiler typeref:typename:Scope *
target compile.c /^ uint16_t target;$/;" m struct:FormOptions typeref:typename:uint16_t file:
tempRoot datatypes.h /^ Value tempRoot; \/* Temporary GC root *\/$/;" m struct:VM typeref:typename:Value
thread datatypes.h /^ Array * thread; \/* When nil, index the local values *\/$/;" m struct:FuncEnv typeref:typename:Array *
thread datatypes.h /^ Array * thread;$/;" m struct:VM typeref:typename:Array *
truthy vm.c /^static int truthy(Value * v) {$/;" f typeref:typename:int file:
type datatypes.h /^ Type type;$/;" m struct:Value typeref:typename:Type
type parse.c /^ ParseType type;$/;" m struct:ParseState typeref:typename:ParseType file:
u16 datatypes.h /^ uint16_t u16[4];$/;" m union:ValueData typeref:typename:uint16_t[4]
u8 datatypes.h /^ uint8_t u8[8];$/;" m union:ValueData typeref:typename:uint8_t[8]
user datatypes.h /^ void * user;$/;" m struct:GC typeref:typename:void *
value datatypes.h /^ Value value;$/;" m struct:DictBucket typeref:typename:Value
value datatypes.h /^ Value value;$/;" m struct:Parser typeref:typename:Value
values datatypes.h /^ Value * values;$/;" m struct:FuncEnv typeref:typename:Value *
vm datatypes.h /^ VM * vm;$/;" m struct:Compiler typeref:typename:VM *
vm datatypes.h /^ VM * vm;$/;" m struct:Parser typeref:typename:VM *

343
value.c Normal file
View File

@ -0,0 +1,343 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gc.h"
#include "vstring.h"
#include "value.h"
#include "buffer.h"
/* Print a value recursively. Used for debugging */
void ValuePrint(Value * x, uint32_t indent) {
uint32_t i;
for (i = 0; i < indent; ++i)
fputc(' ', stdout);
switch (x->type) {
case TYPE_NIL:
printf("<nil>");
break;
case TYPE_BOOLEAN:
printf(x->data.boolean ? "<true>" : "<false>");
break;
case TYPE_NUMBER:
printf("%f", x->data.number);
break;
case TYPE_FORM:
case TYPE_ARRAY:
if (x->type == TYPE_ARRAY) printf(" [\n"); else printf(" (\n");
for (i = 0; i < x->data.array->count; ++i) {
ValuePrint(x->data.array->data + i, indent + 4);
printf("\n");
}
for (i = 0; i < indent; ++i) fputc(' ', stdout);
if (x->type == TYPE_ARRAY) printf(" ]\n"); else printf(" )\n");
break;
case TYPE_STRING:
printf("\"%.*s\"", VStringSize(x->data.string), (char *) x->data.string);
break;
case TYPE_SYMBOL:
printf("%.*s", VStringSize(x->data.string), (char *) x->data.string);
break;
case TYPE_CFUNCTION:
printf("<cfunction>");
break;
case TYPE_FUNCTION:
printf("<function>");
break;
case TYPE_DICTIONARY:
printf("<dictionary>");
break;
case TYPE_BYTEBUFFER:
printf("<bytebuffer>");
break;
case TYPE_FUNCDEF:
printf("<funcdef>");
break;
case TYPE_FUNCENV:
printf("<funcenv>");
break;
case TYPE_THREAD:
printf("<thread>");
break;
}
}
static uint8_t * LoadCString(GC * gc, const char * string, uint32_t len) {
uint8_t * data = GCAlloc(gc, len + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
VStringHash(data) = 0;
VStringSize(data) = len;
memcpy(data, string, len);
return data;
}
Value ValueLoadCString(GC * gc, const char * string) {
Value ret;
ret.type = TYPE_STRING;
ret.data.string = LoadCString(gc, string, strlen(string));
return ret;
}
static uint8_t * NumberToString(GC * gc, Number x) {
static const uint32_t SIZE = 20;
uint8_t * data = GCAlloc(gc, SIZE + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
snprintf((char *) data, SIZE, "%.17g", x);
VStringHash(data) = 0;
VStringSize(data) = strlen((char *) data);
return data;
}
static const char * HEX_CHARACTERS = "0123456789ABCDEF";
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
/* Returns a string description for a pointer */
static uint8_t * StringDescription(GC * gc, const char * title, uint32_t titlelen, void * pointer) {
uint32_t len = 3 + titlelen + sizeof(pointer) * 2;
uint32_t i;
uint8_t * data = GCAlloc(gc, len + 2 * sizeof(uint32_t));
uint8_t * c;
union {
uint8_t bytes[sizeof(void *)];
void * p;
} buf;
buf.p = pointer;
data += 2 * sizeof(uint32_t);
c = data;
*c++ = '<';
for (i = 0; i < titlelen; ++i) {
*c++ = ((uint8_t *)title) [i];
}
*c++ = ' ';
for (i = 0; i < sizeof(void *); ++i) {
uint8_t byte = buf.bytes[i];
*c++ = HEX(byte >> 4);
*c++ = HEX(byte & 0xF);
}
*c++ = '>';
return data;
}
/* Returns a string pointer or NULL if could not allocate memory. */
uint8_t * ValueToString(GC * gc, Value * x) {
switch (x->type) {
case TYPE_NIL:
return LoadCString(gc, "nil", 3);
case TYPE_BOOLEAN:
if (x->data.boolean) {
return LoadCString(gc, "true", 4);
} else {
return LoadCString(gc, "false", 5);
}
case TYPE_NUMBER:
return NumberToString(gc, x->data.number);
case TYPE_ARRAY:
return StringDescription(gc, "array", 5, x->data.array);
case TYPE_FORM:
return StringDescription(gc, "form", 4, x->data.array);
case TYPE_STRING:
case TYPE_SYMBOL:
return x->data.string;
case TYPE_BYTEBUFFER:
return StringDescription(gc, "buffer", 6, x->data.buffer);
case TYPE_CFUNCTION:
return StringDescription(gc, "cfunction", 9, x->data.cfunction);
case TYPE_FUNCTION:
return StringDescription(gc, "function", 8, x->data.func);
case TYPE_DICTIONARY:
return StringDescription(gc, "dictionary", 10, x->data.dict);
case TYPE_FUNCDEF:
return StringDescription(gc, "funcdef", 7, x->data.funcdef);
case TYPE_FUNCENV:
return StringDescription(gc, "funcenv", 7, x->data.funcenv);
case TYPE_THREAD:
return StringDescription(gc, "thread", 6, x->data.array);
}
return NULL;
}
/* Simple hash function */
uint32_t djb2(const uint8_t * str) {
const uint8_t * end = str + VStringSize(str);
uint32_t hash = 5381;
while (str < end)
hash = (hash << 5) + hash + *str++;
return hash;
}
/* Check if two values are equal. This is strict equality with no conversion. */
int ValueEqual(Value * x, Value * y) {
int result;
if (x->type != y->type) {
result = 0;
} else {
switch (x->type) {
case TYPE_NIL:
result = 1;
break;
case TYPE_BOOLEAN:
result = x->data.boolean == y->data.boolean;
break;
case TYPE_NUMBER:
result = x->data.number == y->data.number;
break;
/* Assume that when strings are created, equal strings
* are set to the same string */
case TYPE_STRING:
case TYPE_SYMBOL:
if (x->data.string == y->data.string) {
result = 1;
break;
}
if (ValueHash(x) != ValueHash(y) ||
VStringSize(x->data.string) != VStringSize(y->data.string)) {
result = 0;
break;
}
/* If two different strings are equal, merge them to share the same data */
if (!strncmp((char *) x->data.string, (char *) y->data.string, VStringSize(x->data.string))) {
/* Use the lower pointer in memory. This means that in long running
* programs, repeated string compares will eventually all use identical
* pointers for identical strings. */
if (x->data.string < y->data.string) {
y->data.string = x->data.string;
} else {
x->data.string = y->data.string;
}
result = 1;
break;
}
result = 0;
break;
case TYPE_ARRAY:
case TYPE_FORM:
case TYPE_BYTEBUFFER:
case TYPE_CFUNCTION:
case TYPE_DICTIONARY:
case TYPE_FUNCTION:
case TYPE_FUNCDEF:
case TYPE_FUNCENV:
case TYPE_THREAD:
/* compare pointers */
result = x->data.array == y->data.array;
break;
}
}
return result;
}
/* Computes a hash value for a function */
uint32_t ValueHash(Value * x) {
uint32_t hash;
switch (x->type) {
case TYPE_NIL:
hash = 0;
break;
case TYPE_BOOLEAN:
hash = x->data.boolean;
break;
case TYPE_NUMBER:
{
union {
uint32_t hash;
Number number;
} u;
u.number = x->data.number;
hash = u.hash;
}
break;
/* String hashes */
case TYPE_SYMBOL:
case TYPE_STRING:
/* Assume 0 is not hashed. */
if (VStringHash(x->data.string))
hash = VStringHash(x->data.string);
else
hash = VStringHash(x->data.string) = djb2(x->data.string);
break;
case TYPE_ARRAY:
case TYPE_FORM:
case TYPE_BYTEBUFFER:
case TYPE_CFUNCTION:
case TYPE_DICTIONARY:
case TYPE_FUNCTION:
case TYPE_FUNCDEF:
case TYPE_FUNCENV:
case TYPE_THREAD:
/* Cast the pointer */
hash = (uint32_t) x->data.string;
break;
}
return hash;
}
/* Compares x to y. If they are equal retuns 0. If x is less, returns -1.
* If y is less, returns 1. All types are comparable
* and should have strict ordering. */
int ValueCompare(Value * x, Value * y) {
if (x->type == y->type) {
switch (x->type) {
case TYPE_NIL:
return 0;
case TYPE_BOOLEAN:
if (x->data.boolean == y->data.boolean) {
return 0;
} else {
return x->data.boolean ? 1 : -1;
}
case TYPE_NUMBER:
/* TODO: define behavior for NaN and infinties. */
if (x->data.number == y->data.number) {
return 0;
} else {
return x->data.number > y->data.number ? 1 : -1;
}
case TYPE_STRING:
case TYPE_SYMBOL:
if (x->data.string == y->data.string) {
return 0;
} else {
uint32_t xlen = VStringSize(x->data.string);
uint32_t ylen = VStringSize(y->data.string);
uint32_t len = xlen > ylen ? ylen : xlen;
uint32_t i;
for (i = 0; i < len; ++i) {
if (x->data.string[i] == y->data.string[i]) {
continue;
} else if (x->data.string[i] < y->data.string[i]) {
return 1; /* x is less then y */
} else {
return -1; /* y is less than x */
}
}
if (xlen == ylen) {
/* Merge the two strings */
if (x->data.string < y->data.string) {
y->data.string = x->data.string;
} else {
x->data.string = y->data.string;
}
return 0;
} else {
return xlen < ylen ? -1 : 1;
}
}
case TYPE_ARRAY:
case TYPE_FORM:
case TYPE_BYTEBUFFER:
case TYPE_CFUNCTION:
case TYPE_FUNCTION:
case TYPE_DICTIONARY:
case TYPE_FUNCDEF:
case TYPE_FUNCENV:
case TYPE_THREAD:
if (x->data.string == y->data.string) {
return 0;
} else {
return x->data.string > y->data.string ? 1 : -1;
}
}
} else if (x->type < y->type) {
return -1;
}
return 1;
}

18
value.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef VALUE_H_1RJPQKFM
#define VALUE_H_1RJPQKFM
#include "datatypes.h"
void ValuePrint(Value * x, uint32_t indent);
int ValueCompare(Value * x, Value * y);
int ValueEqual(Value * x, Value * y);
Value ValueLoadCString(GC * gc, const char * string);
uint8_t * ValueToString(GC * gc, Value * x);
uint32_t ValueHash(Value * x);
#endif /* end of include guard: VALUE_H_1RJPQKFM */

605
vm.c Normal file
View File

@ -0,0 +1,605 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "vm.h"
#include "value.h"
#include "array.h"
#include "vstring.h"
#include "dict.h"
#include "gc.h"
#include "buffer.h"
#include "opcodes.h"
#define VMArg(i) (vm->base + (i))
#define VMOpArg(i) (VMArg(vm->pc[(i)]))
static const char OOM[] = "Out of memory";
static const char NO_UPVALUE[] = "Out of memory";
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";
/* Mark memory reachable by VM */
void VMMark(VM * vm) {
Value thread;
thread.type = TYPE_THREAD;
thread.data.array = vm->thread;
GCMark(&vm->gc, &thread);
GCMark(&vm->gc, &vm->tempRoot);
}
/* Run garbage collection */
void VMCollect(VM * vm) {
VMMark(vm);
GCSweep(&vm->gc);
}
/* Run garbage collection if needed */
void VMMaybeCollect(VM * vm) {
if (GCNeedsCollect(&vm->gc)) {
VMCollect(vm);
}
}
/* OOM handler for the vm's gc */
static void VMHandleOutOfMemory(GC * gc) {
VM * vm = (VM *) gc->user;
VMError(vm, OOM);
}
/* Push a stack frame onto a thread */
static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
uint16_t oldSize;
uint32_t nextCount, i;
if (thread->count) {
oldSize = FrameSize(thread);
nextCount = thread->count + oldSize + FRAME_SIZE;
} else {
oldSize = 0;
nextCount = FRAME_SIZE;
}
ArrayEnsure(&vm->gc, thread, nextCount + size);
/* 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 = TYPE_NIL;
}
thread->count = nextCount;
FramePrevSize(thread) = oldSize;
FrameSize(thread) = size;
FrameEnvValue(thread).type = TYPE_NIL;
FrameEnv(thread) = NULL;
FrameCallee(thread) = callee;
FrameMeta(thread).type = TYPE_NUMBER;
FramePCValue(thread).type = TYPE_NUMBER;
vm->base = ThreadStack(thread);
}
/* Copy the current function stack to the current closure
environment */
static void VMThreadSplitStack(VM * vm, Array * thread) {
FuncEnv * env = FrameEnv(thread);
/* Check for closures */
if (env) {
uint32_t size = FrameSize(thread);
env->thread = NULL;
env->stackOffset = size;
env->values = GCAlloc(&vm->gc, sizeof(Value) * size);
memcpy(env->values, ThreadStack(thread), size * sizeof(Value));
}
}
/* Pop the top-most stack frame from stack */
static void VMThreadPop(VM * vm, Array * thread) {
if (thread->count) {
VMThreadSplitStack(vm, thread);
thread->count -= FRAME_SIZE + FramePrevSize(thread);
} else {
VMError(vm, "Nothing to pop from stack.");
}
vm->base = ThreadStack(thread);
}
/* Get an upvalue */
static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) {
FuncEnv * env;
Value * stack;
while (fn && level--)
fn = fn->parent;
VMAssert(vm, fn, NO_UPVALUE);
env = fn->env;
if (env->thread)
stack = env->thread->data + env->stackOffset;
else
stack = env->values;
return stack + index;
}
/* Get a constant */
static Value * LoadConstant(VM * vm, Func * fn, uint16_t index) {
if (index > fn->def->literalsLen) {
VMError(vm, NO_UPVALUE);
}
return fn->def->literals + index;
}
/* Truthiness definition in VM */
static int truthy(Value * v) {
return v->type != TYPE_NIL && !(v->type == TYPE_BOOLEAN && !v->data.boolean);
}
/* Pushes a function on the call stack. */
static void VMPushCallee(VM * vm, uint32_t ret, uint32_t arity, Value callee) {
Array * thread = vm->thread;
FrameReturn(thread) = ret;
if (callee.type == TYPE_FUNCTION) {
Func * fn = callee.data.func;
VMThreadPush(vm, thread, callee, fn->def->locals);
} else if (callee.type == TYPE_CFUNCTION) {
VMThreadPush(vm, thread, callee, arity);
} else {
VMError(vm, EXPECTED_FUNCTION);
return;
}
/* Reset the base and frame after changing the stack */
vm->base = ThreadStack(thread);
}
/* Return from the vm */
static void VMReturn(VM * vm, Value ret) {
VMThreadPop(vm, vm->thread);
if (vm->thread->count == 0) {
VMExit(vm, ret);
}
vm->base = ThreadStack(vm->thread);
vm->pc = FramePC(vm->thread);
vm->base[FrameReturn(vm->thread)] = ret;
}
/* Implementation of the opcode for function calls */
static void VMCallOp(VM * vm) {
uint32_t ret = vm->pc[1];
uint32_t arity = vm->pc[2];
Value callee = *VMOpArg(3);
uint32_t i;
Value * argWriter;
FramePC(vm->thread) = vm->pc + 4 + arity;
VMPushCallee(vm, ret, arity, callee);
argWriter = vm->base;
if (callee.type == TYPE_CFUNCTION) {
for (i = 0; i < arity; ++i)
*(argWriter++) = *VMOpArg(4 + i);
VMReturn(vm, callee.data.cfunction(vm));
VMMaybeCollect(vm);
} else if (callee.type == TYPE_FUNCTION) {
Func * f = callee.data.func;
uint32_t extraNils = f->def->locals;
if (arity > f->def->arity) {
arity = f->def->arity;
} else if (arity < f->def->arity) {
extraNils += f->def->arity - arity;
}
for (i = 0; i < arity; ++i)
*(argWriter++) = *VMOpArg(4 + i);
for (i = 0; i < extraNils; ++i)
(argWriter++)->type = TYPE_NIL;
vm->pc = f->def->byteCode;
} else {
VMError(vm, EXPECTED_FUNCTION);
}
}
/* Implementation of the opcode for tail calls */
static void VMTailCallOp(VM * vm) {
GC * gc = &vm->gc;
uint32_t arity = vm->pc[1];
Value callee = *VMOpArg(2);
Value * extra, * argWriter;
Array * thread = vm->thread;
uint16_t newFrameSize;
uint32_t i;
/* Check for closures */
if (FrameEnvValue(thread).type == TYPE_FUNCENV) {
FuncEnv * env = FrameEnv(thread);
uint16_t frameSize = FrameSize(thread);
Value * envValues = GCAlloc(gc, FrameSize(thread) * sizeof(Value));
env->values = envValues;
memcpy(envValues, vm->base, frameSize * sizeof(Value));
env->stackOffset = frameSize;
env->thread = NULL;
}
if (callee.type == TYPE_CFUNCTION) {
newFrameSize = arity;
} else if (callee.type == TYPE_FUNCTION) {
Func * f = callee.data.func;
newFrameSize = f->def->locals;
} else {
VMError(vm, EXPECTED_FUNCTION);
}
/* Ensure that stack is zeroed in this spot */
ArrayEnsure(&vm->gc, thread, thread->count + newFrameSize + arity);
vm->base = ThreadStack(thread);
extra = argWriter = vm->base + FrameSize(thread) + FRAME_SIZE;
for (i = 0; i < arity; ++i) {
*argWriter++ = *VMOpArg(3 + i);
}
/* Copy the end of the stack to the parameter position */
memcpy(vm->base, extra, arity * sizeof(Value));
/* nil the new stack for gc */
argWriter = vm->base + arity;
for (i = arity; i < newFrameSize; ++i) {
(argWriter++)->type = TYPE_NIL;
}
FrameSize(thread) = newFrameSize;
FrameCallee(thread) = callee;
if (callee.type == TYPE_CFUNCTION) {
VMReturn(vm, callee.data.cfunction(vm));
VMMaybeCollect(vm);
} else {
Func * f = callee.data.func;
vm->pc = f->def->byteCode;
}
}
/* Instantiate a closure */
static void VMMakeClosure(VM * vm, uint16_t ret, uint16_t literal) {
Value * vRet = VMArg(ret);
if (FrameCallee(vm->thread).type != TYPE_FUNCTION) {
VMError(vm, EXPECTED_FUNCTION);
} else {
Func * fn, * current;
Value * constant;
Array * thread = vm->thread;
FuncEnv * env = FrameEnv(vm->thread);
if (!env) {
env = GCAlloc(&vm->gc, sizeof(FuncEnv));
env->thread = thread;
env->stackOffset = thread->count;
env->values = NULL;
FrameEnvValue(vm->thread).data.funcenv = env;
FrameEnvValue(vm->thread).type = TYPE_FUNCENV;
}
current = FrameCallee(vm->thread).data.func;
constant = LoadConstant(vm, current, literal);
if (constant->type != TYPE_FUNCDEF) {
VMError(vm, EXPECTED_FUNCTION);
}
fn = GCAlloc(&vm->gc, sizeof(Func));
fn->def = constant->data.funcdef;
fn->parent = current;
fn->env = env;
vRet->type = TYPE_FUNCTION;
vRet->data.func = fn;
VMMaybeCollect(vm);
}
}
/* Start running the VM */
int VMStart(VM * vm) {
/* Set jmp_buf to jump back to for return. */
{
int n;
if ((n = setjmp(vm->jump))) {
/* Good return */
if (n == 1) {
return 0;
} else {
/* Error */
return n;
}
}
}
for (;;) {
uint16_t opcode = *vm->pc;
switch (opcode) {
Value *vRet, *v1, *v2;
case VM_OP_ADD: /* Addition */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
v2 = VMOpArg(3);
VMAssert(vm, v1->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP);
VMAssert(vm, v2->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP);
vRet->type = TYPE_NUMBER;
vRet->data.number = v1->data.number + v2->data.number;
vm->pc += 4;
break;
case VM_OP_SUB: /* Subtraction */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
v2 = VMOpArg(3);
VMAssert(vm, v1->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP);
VMAssert(vm, v2->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP);
vRet->type = TYPE_NUMBER;
vRet->data.number = v1->data.number - v2->data.number;
vm->pc += 4;
break;
case VM_OP_MUL: /* Multiplication */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
v2 = VMOpArg(3);
VMAssert(vm, v1->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP);
VMAssert(vm, v2->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP);
vRet->type = TYPE_NUMBER;
vRet->data.number = v1->data.number * v2->data.number;
vm->pc += 4;
break;
case VM_OP_DIV: /* Division */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
v2 = VMOpArg(3);
VMAssert(vm, v1->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP);
VMAssert(vm, v2->type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP);
vRet->type = TYPE_NUMBER;
vRet->data.number = v1->data.number / v2->data.number;
vm->pc += 4;
break;
case VM_OP_NOT: /* Boolean unary (Boolean not) */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
vm->pc += 3;
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = !truthy(v1);
break;
case VM_OP_LD0: /* Load 0 */
vRet = VMOpArg(1);
vRet->type = TYPE_NUMBER;
vRet->data.number = 0;
vm->pc += 2;
break;
case VM_OP_LD1: /* Load 1 */
vRet = VMOpArg(1);
vRet->type = TYPE_NUMBER;
vRet->data.number = 1;
vm->pc += 2;
break;
case VM_OP_FLS: /* Load False */
vRet = VMOpArg(1);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = 0;
vm->pc += 2;
break;
case VM_OP_TRU: /* Load True */
vRet = VMOpArg(1);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = 1;
vm->pc += 2;
break;
case VM_OP_NIL: /* Load Nil */
vRet = VMOpArg(1);
vRet->type = TYPE_NIL;
vm->pc += 2;
break;
case VM_OP_I16: /* Load Small Integer */
vRet = VMOpArg(1);
vRet->type = TYPE_NUMBER;
vRet->data.number = ((int16_t *)(vm->pc))[2];
vm->pc += 3;
break;
case VM_OP_UPV: /* Load Up Value */
{
Value callee;
callee = FrameCallee(vm->thread);
VMAssert(vm, callee.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
vRet = VMOpArg(1);
*vRet = *GetUpValue(vm, callee.data.func, vm->pc[2], vm->pc[3]);
vm->pc += 4;
}
break;
case VM_OP_JIF: /* Jump If */
if (truthy(VMOpArg(1))) {
vm->pc += 4;
} else {
vm->pc += *((int32_t *)(vm->pc + 2));
}
break;
case VM_OP_JMP: /* Jump */
vm->pc += *((int32_t *)(vm->pc + 1));
break;
case VM_OP_CAL: /* Call */
VMCallOp(vm);
break;
case VM_OP_RET: /* Return */
VMReturn(vm, *VMOpArg(1));
break;
case VM_OP_SUV: /* Set Up Value */
VMAssert(vm, FrameCallee(vm->thread).type == TYPE_FUNCTION, EXPECTED_FUNCTION);
vRet = VMOpArg(1);
*GetUpValue(vm, FrameCallee(vm->thread).data.func, vm->pc[2], vm->pc[3]) = *vRet;
vm->pc += 4;
break;
case VM_OP_CST: /* Load constant value */
VMAssert(vm, FrameCallee(vm->thread).type == TYPE_FUNCTION, EXPECTED_FUNCTION);
vRet = VMOpArg(1);
*vRet = *LoadConstant(vm, FrameCallee(vm->thread).data.func, vm->pc[2]);
vm->pc += 3;
break;
case VM_OP_I32: /* Load 32 bit integer */
vRet = VMOpArg(1);
vRet->type = TYPE_NUMBER;
vRet->data.number = *((int32_t *)(vm->pc + 2));
vm->pc += 4;
break;
case VM_OP_F64: /* Load 64 bit float */
vRet = VMOpArg(1);
vRet->type = TYPE_NUMBER;
vRet->data.number = (Number) *((double *)(vm->pc + 2));
vm->pc += 6;
break;
case VM_OP_MOV: /* Move Values */
vRet = VMOpArg(1);
v1 = vm->base + *((uint32_t *)(vm->pc + 2));
*vRet = *v1;
vm->pc += 4;
break;
case VM_OP_CLN: /* Create closure from constant FuncDef */
VMMakeClosure(vm, vm->pc[1], vm->pc[2]);
vm->pc += 3;
break;
case VM_OP_EQL: /* Equality */
vRet = VMOpArg(1);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = ValueEqual(VMOpArg(2), VMOpArg(3));
vm->pc += 4;
break;
case VM_OP_LTN: /* Less Than */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
v2 = VMOpArg(3);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = (ValueCompare(VMOpArg(2), VMOpArg(3)) == -1);
vm->pc += 4;
break;
case VM_OP_LTE: /* Less Than or Equal to */
vRet = VMOpArg(1);
v1 = VMOpArg(2);
v2 = VMOpArg(3);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = (ValueCompare(VMOpArg(2), VMOpArg(3)) != 1);
vm->pc += 4;
break;
case VM_OP_ARR: /* Array literal */
vRet = VMOpArg(1);
{
uint32_t i;
uint32_t arrayLen = vm->pc[2];
Array * array = ArrayNew(&vm->gc, arrayLen);
array->count = arrayLen;
for (i = 0; i < arrayLen; ++i)
array->data[i] = *VMOpArg(3 + i);
vRet->type = TYPE_ARRAY;
vRet->data.array = array;
vm->pc += 3 + arrayLen;
VMMaybeCollect(vm);
}
break;
case VM_OP_DIC: /* Dictionary literal */
vRet = VMOpArg(1);
{
uint32_t i = 3;
uint32_t kvs = vm->pc[2];
Dictionary * dict = DictNew(&vm->gc, kvs);
kvs = kvs * 2 + 3;
while (i < kvs) {
v1 = VMOpArg(i++);
v2 = VMOpArg(i++);
DictPut(&vm->gc, dict, v1, v2);
}
vRet->type = TYPE_DICTIONARY;
vRet->data.dict = dict;
vm->pc += kvs;
VMMaybeCollect(vm);
}
break;
case VM_OP_TCL: /* Tail call */
VMTailCallOp(vm);
break;
/* Macro for generating some math operators */
#define DO_MULTI_MATH(op, start) { \
uint16_t i; \
uint16_t count = vm->pc[1]; \
Number accum = start; \
vRet = VMOpArg(2); \
for (i = 0; i < count; ++i) { \
Value * x = VMOpArg(3 + i); \
VMAssert(vm, x->type == TYPE_NUMBER, "Expected number"); \
accum = accum op x->data.number; \
} \
vRet->type = TYPE_NUMBER; vRet->data.number = accum; \
vm->pc += 3 + count; \
}
/* Vectorized math */
case VM_OP_ADM:
DO_MULTI_MATH(+, 0)
break;
case VM_OP_SBM:
DO_MULTI_MATH(-, 0)
break;
case VM_OP_MUM:
DO_MULTI_MATH(*, 1)
break;
case VM_OP_DVM:
DO_MULTI_MATH(/, 1)
break;
#undef DO_MULTI_MATH
case VM_OP_RTN: /* Return nil */
{
Value temp;
temp.type = TYPE_NIL;
VMReturn(vm, temp);
}
break;
default:
VMError(vm, "Unknown opcode");
break;
}
}
}
/* Initialize the VM */
void VMInit(VM * vm) {
GCInit(&vm->gc, 0);
vm->gc.handleOutOfMemory = VMHandleOutOfMemory;
vm->tempRoot.type = TYPE_NIL;
vm->base = NULL;
vm->pc = NULL;
vm->error = NULL;
vm->thread = ArrayNew(&vm->gc, 20);
}
/* Load a function into the VM. The function will be called with
* no arguments when run */
void VMLoad(VM * vm, Func * func) {
Value callee;
callee.type = TYPE_FUNCTION;
callee.data.func = func;
vm->thread = ArrayNew(&vm->gc, 20);
VMThreadPush(vm, vm->thread, callee, func->def->locals);
vm->pc = func->def->byteCode;
}
/* Clear all memory associated with the VM */
void VMDeinit(VM * vm) {
GCClear(&vm->gc);
}
#undef VMOpArg
#undef VMArg

41
vm.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef VM_H_C4OZU8CQ
#define VM_H_C4OZU8CQ
#include "datatypes.h"
/* Exit from the VM normally */
#define VMExit(vm, r) ((vm)->tempRoot = (r), longjmp((vm)->jump, 1))
/* Bail from the VM with an error. */
#define VMError(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 2))
/* Error if the condition is false */
#define VMAssert(vm, cond, e) do \
{ if (!(cond)) { VMError((vm), (e)); } } while (0)
/* Initialize the VM */
void VMInit(VM * vm);
/* Deinitialize the VM */
void VMDeinit(VM * vm);
/* Load a function to be run on the VM */
void VMLoad(VM * vm, Func * func);
/* Start running the VM */
int VMStart(VM * vm);
/* Get the result after VMStart returns */
#define VMResult(vm) ((vm)->tempRoot)
/* Mark memory reachable by VM */
void VMMark(VM * vm);
/* Run garbage collection */
void VMCollect(VM * vm);
/* Collect garbage if enough memory has been allocated since
* the previous collection */
void VMMaybeCollect(VM * vm);
#endif /* end of include guard: VM_H_C4OZU8CQ */

10
vstring.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef VSTRING_H_TFCSVBEE
#define VSTRING_H_TFCSVBEE
#include "datatypes.h"
#define VStringRaw(s) ((uint32_t *)(s) - 2)
#define VStringSize(v) (VStringRaw(v)[0])
#define VStringHash(v) (VStringRaw(v)[1])
#endif /* end of include guard: VSTRING_H_TFCSVBEE */