mirror of
https://github.com/janet-lang/janet
synced 2024-12-25 07:50: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