mirror of
https://github.com/janet-lang/janet
synced 2024-12-24 23:40:27 +00:00
First commit.
This commit is contained in:
commit
a80dd4bff3
57
.gitignore
vendored
Normal file
57
.gitignore
vendored
Normal 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
141
.ycm_extra_conf.py
Normal 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
33
Makefile
Normal 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
|
78
array.c
Normal file
78
array.c
Normal 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
30
array.h
Normal 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
62
buffer.c
Normal 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
26
buffer.h
Normal 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 */
|
15
compile.h
Normal file
15
compile.h
Normal 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
185
datatypes.h
Normal 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
152
dict.c
Normal 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
26
dict.h
Normal 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
209
gc.c
Normal 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
41
gc.h
Normal 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
90
main.c
Normal 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
40
opcodes.h
Normal 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
456
parse.c
Normal 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
14
parse.h
Normal 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
425
tags
Normal 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
343
value.c
Normal 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
18
value.h
Normal 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
605
vm.c
Normal 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
41
vm.h
Normal 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
10
vstring.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user