1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-25 07:50:27 +00:00

More work on compiler.

* Fix up while special form
* Change Value functions to pass-by-value
This commit is contained in:
Calvin Rose 2017-02-09 23:28:11 -05:00
parent 0557c8b2a6
commit 3794ec3acd
11 changed files with 274 additions and 135 deletions

View File

@ -32,6 +32,6 @@ debug: $(TARGET)
gdb $(TARGET)
valgrind: $(TARGET)
valgrind ./$(TARGET)
valgrind ./$(TARGET) --leak-check=full
.PHONY: clean install run debug valgrind

177
compile.c
View File

@ -63,6 +63,7 @@ struct SlotTracker {
* points to the parent Scope, and its current child
* Scope. */
struct Scope {
uint32_t level;
uint16_t nextLocal;
uint32_t heapCapacity;
uint32_t heapSize;
@ -118,8 +119,12 @@ static Scope * CompilerPushScope(Compiler * c, int sameFunction) {
scope->heapCapacity = 10;
scope->nextScope = NULL;
scope->previousScope = c->tail;
if (c->tail)
if (c->tail) {
c->tail->nextScope = scope;
scope->level = c->tail->level + (sameFunction ? 0 : 1);
} else {
scope->level = 0;
}
if (sameFunction) {
if (!c->tail) {
CError(c, "Cannot inherit scope when root scope");
@ -256,12 +261,17 @@ static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) {
* Useful for dictionary literals, array literals, function calls, etc. */
static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToBuffer) {
uint32_t i;
if (writeToBuffer) {
Buffer * buffer = c->buffer;
Buffer * buffer = c->buffer;
if (writeToBuffer == 1) {
for (i = 0; i < tracker->count; ++i) {
Slot * s = tracker->slots + i;
BufferPushUInt16(c->vm, buffer, s->index);
}
} else if (writeToBuffer == 2) { /* Write in reverse */
for (i = 0; i < tracker->count; ++i) {
Slot * s = tracker->slots + tracker->count - 1 - i;
BufferPushUInt16(c->vm, buffer, s->index);
}
}
/* Free in reverse order */
for (i = tracker->count - 1; i < tracker->count; --i) {
@ -286,7 +296,7 @@ static void CompilerTrackerPush(Compiler * c, SlotTracker * tracker, Slot slot)
* that one instead of creating a new literal. This allows for some reuse
* of things like string constants.*/
static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) {
Value checkDup = DictGet(scope->literals, &x);
Value checkDup = DictGet(scope->literals, x);
uint16_t literalIndex = 0;
if (checkDup.type != TYPE_NIL) {
/* An equal literal is already registered in the current scope */
@ -297,7 +307,7 @@ static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) {
valIndex.type = TYPE_NUMBER;
literalIndex = scope->literalsArray->count;
valIndex.data.number = literalIndex;
DictPut(c->vm, scope->literals, &x, &valIndex);
DictPut(c->vm, scope->literals, x, valIndex);
ArrayPush(c->vm, scope->literalsArray, x);
}
return literalIndex;
@ -312,7 +322,7 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {
uint16_t target = CompilerGetLocal(c, scope);
x.type = TYPE_NUMBER;
x.data.number = target;
DictPut(c->vm, scope->locals, &sym, &x);
DictPut(c->vm, scope->locals, sym, x);
return target;
}
@ -320,15 +330,14 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {
* pass back the level and index by reference. */
static int ScopeSymbolResolve(Scope * scope, Value x,
uint16_t * level, uint16_t * index) {
uint16_t levelTest = 0;
uint32_t currentLevel = scope->level;
while (scope) {
Value check = DictGet(scope->locals, &x);
Value check = DictGet(scope->locals, x);
if (check.type != TYPE_NIL) {
*level = levelTest;
*level = currentLevel - scope->level;
*index = (uint16_t) check.data.number;
return 1;
}
++levelTest;
scope = scope->previousScope;
}
return 0;
@ -559,7 +568,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
* its argument is still carried out, but their results can
* also be ignored). */
static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
int16_t op0, int16_t op1, int16_t op2, int16_t opn) {
int16_t op0, int16_t op1, int16_t op2, int16_t opn, int reverseOperands) {
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
Slot ret = SlotDefault();
@ -602,22 +611,63 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
BufferPushUInt16(c->vm, buffer, ret.index);
}
/* Write the location of all of the arguments */
CompilerTrackerFree(c, scope, &tracker, 1);
CompilerTrackerFree(c, scope, &tracker, reverseOperands ? 2 : 1);
return ret;
}
/* Math specials */
static Slot CompileAddition(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_ADM, VM_OP_ADD, VM_OP_ADM);
return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_ADM, VM_OP_ADD, VM_OP_ADM, 0);
}
static Slot CompileSubtraction(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_SBM, VM_OP_SUB, VM_OP_SBM);
return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_SBM, VM_OP_SUB, VM_OP_SBM, 0);
}
static Slot CompileMultiplication(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_MUM, VM_OP_MUL, VM_OP_MUM);
return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_MUM, VM_OP_MUL, VM_OP_MUM, 0);
}
static Slot CompileDivision(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_DVM, VM_OP_DIV, VM_OP_DVM);
return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_DVM, VM_OP_DIV, VM_OP_DVM, 0);
}
static Slot CompileEquals(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_EQL, -1, 0);
}
static Slot CompileLessThan(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTN, -1, 0);
}
static Slot CompileLessThanOrEqual(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTE, -1, 0);
}
static Slot CompileGreaterThan(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTN, -1, 1);
}
static Slot CompileGreaterThanOrEqual(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTE, -1, 1);
}
static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) {
return CompileOperator(c, opts, form, VM_OP_FLS, VM_OP_NOT, -1, -1, 0);
}
/* Helper function to return nil from a form that doesn't do anything
(set, while, etc.) */
static Slot CompileReturnNil(Compiler * c, FormOptions opts) {
Slot ret;
/* If we need a return value, we just use nil */
if (opts.isTail) {
ret.isDud = 1;
BufferPushUInt16(c->vm, c->buffer, VM_OP_RTN);
} else if (!opts.canDrop) {
ret.isDud = 0;
if (opts.canChoose) {
ret.isTemp = 1;
ret.index = CompilerGetLocal(c, c->tail);
} else {
ret.isTemp = 0;
ret.index = opts.target;
}
BufferPushUInt16(c->vm, c->buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, c->buffer, ret.index);
}
return ret;
}
/* Compile an assignment operation */
@ -625,7 +675,6 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
FormOptions subOpts = FormOptionsDefault();
Slot ret = SlotDefault();
uint16_t target = 0;
uint16_t level = 0;
if (ScopeSymbolResolve(scope, left, &level, &target)) {
@ -641,7 +690,12 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
BufferPushUInt16(c->vm, buffer, target);
/* Drop the possibly temporary slot if it is indeed temporary */
CompilerDropSlot(c, scope, slot);
return ret;
} else {
Slot slot;
subOpts.canChoose = 0;
subOpts.target = target;
slot = CompileValue(c, subOpts, right);
CompilerDropSlot(c, scope, slot);
}
} else {
/* We need to declare a new symbol */
@ -649,18 +703,7 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
subOpts.canChoose = 0;
CompileValue(c, subOpts, right);
}
/* If we need a return value, we just use nil */
if (!opts.canDrop) {
if (opts.canChoose) {
ret.isTemp = 1;
ret.index = CompilerGetLocal(c, scope);
} else {
ret.index = opts.target;
}
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, buffer, ret.index);
}
return ret;
return CompileReturnNil(c, opts);
}
/* Writes bytecode to return a slot */
@ -683,7 +726,7 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t
uint32_t current = startIndex;
/* Compile the body */
while (current < form->count) {
subOpts.canDrop = current != form->count - 1;
subOpts.canDrop = (current != form->count - 1) || opts.canDrop;
subOpts.isTail = opts.isTail && !subOpts.canDrop;
ret = CompileValue(c, subOpts, form->data[current]);
if (subOpts.canDrop) {
@ -692,7 +735,7 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t
}
++current;
}
if (opts.isTail && !ret.isDud) {
if (opts.isTail && !ret.isDud) {
CompilerReturnSlot(c, ret);
ret.isDud = 1;
}
@ -891,6 +934,39 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
return ret;
}
/* While special */
static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) {
Slot cond;
uint32_t countAtStart = c->buffer->count;
uint32_t countAtJumpDelta;
uint32_t countAtFinish;
FormOptions subOpts = FormOptionsDefault();
subOpts.canDrop = 1;
CompilerPushScope(c, 1);
/* Compile condition */
cond = CompileValue(c, FormOptionsDefault(), form->data[1]);
/* Leave space for jump later */
countAtJumpDelta = c->buffer->count;
c->buffer->count += sizeof(uint16_t) * 2 + sizeof(int32_t);
/* Compile loop body */
CompilerDropSlot(c, c->tail, CompileBlock(c, subOpts, form, 2));
/* Jump back to the loop start */
countAtFinish = c->buffer->count;
BufferPushUInt16(c->vm, c->buffer, VM_OP_JMP);
BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtStart) / -2);
countAtFinish = c->buffer->count;
/* Set the jump to the correct length */
c->buffer->count = countAtJumpDelta;
BufferPushUInt16(c->vm, c->buffer, VM_OP_JIF);
BufferPushUInt16(c->vm, c->buffer, cond.index);
BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtJumpDelta) / 2);
/* Pop scope */
c->buffer->count = countAtFinish;
CompilerPopScope(c);
/* Return dud */
return CompileReturnNil(c, opts);
}
/* Do special */
static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) {
Slot ret;
@ -960,6 +1036,9 @@ static SpecialFormHelper GetSpecial(Array * form) {
case '-': return CompileSubtraction;
case '*': return CompileMultiplication;
case '/': return CompileDivision;
case '>': return CompileGreaterThan;
case '<': return CompileLessThan;
case '=': return CompileEquals;
case '\'': return CompileQuote;
default:
break;
@ -967,6 +1046,22 @@ static SpecialFormHelper GetSpecial(Array * form) {
}
/* Multi character specials. Mostly control flow. */
switch (name[0]) {
case '>':
{
if (VStringSize(name) == 2 &&
name[1] == '=') {
return CompileGreaterThanOrEqual;
}
}
break;
case '<':
{
if (VStringSize(name) == 2 &&
name[1] == '=') {
return CompileLessThanOrEqual;
}
}
break;
case 'd':
{
if (VStringSize(name) == 2 &&
@ -991,6 +1086,14 @@ static SpecialFormHelper GetSpecial(Array * form) {
}
}
break;
case 'n':
{
if (VStringSize(name) == 3 &&
name[1] == 'o' &&
name[2] == 't') {
return CompileNot;
}
}
case 'q':
{
if (VStringSize(name) == 5 &&
@ -1011,6 +1114,16 @@ static SpecialFormHelper GetSpecial(Array * form) {
}
}
break;
case 'w':
{
if (VStringSize(name) == 5 &&
name[1] == 'h' &&
name[2] == 'i' &&
name[3] == 'l' &&
name[4] == 'e') {
return CompileWhile;
}
}
default:
break;
}

View File

@ -139,6 +139,7 @@ struct VM {
uint32_t memoryInterval;
uint32_t nextCollection;
uint32_t black : 1;
uint32_t lock : 31;
/* Thread */
uint16_t * pc;
Array * thread;

26
ds.c
View File

@ -166,7 +166,7 @@ static void DictReHash(VM * vm, Dictionary * dict, uint32_t size) {
while (bucket) {
uint32_t index;
DictBucket * next = bucket->next;
index = ValueHash(&bucket->key) % size;
index = ValueHash(bucket->key) % size;
bucket->next = newBuckets[index];
newBuckets[index] = bucket;
bucket = next;
@ -177,11 +177,11 @@ static void DictReHash(VM * vm, Dictionary * dict, uint32_t size) {
}
/* Find the bucket that contains the given key */
static DictBucket * DictFind(Dictionary * dict, Value * 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))
if (ValueEqual(bucket->key, key))
return bucket;
bucket = bucket->next;
}
@ -189,7 +189,7 @@ static DictBucket * DictFind(Dictionary * dict, Value * key) {
}
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key) {
Value DictGet(Dictionary * dict, Value key) {
DictBucket * bucket = DictFind(dict, key);
if (bucket) {
return bucket->value;
@ -201,13 +201,13 @@ Value DictGet(Dictionary * dict, Value * key) {
}
/* Remove an entry from the dictionary */
Value DictRemove(VM * vm, Dictionary * dict, Value * key) {
Value DictRemove(VM * vm, 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 (ValueEqual(bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
@ -232,16 +232,16 @@ Value DictRemove(VM * vm, 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(VM * vm, Dictionary * dict, Value * key, Value * value) {
void DictPut(VM * vm, Dictionary * dict, Value key, Value value) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
if (key->type == TYPE_NIL) return;
if (key.type == TYPE_NIL) return;
/* Do a removal if value is nil */
if (value->type == TYPE_NIL) {
if (value.type == TYPE_NIL) {
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (ValueEqual(bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
@ -259,15 +259,15 @@ void DictPut(VM * vm, Dictionary * dict, Value * key, Value * value) {
} else {
bucket = DictFind(dict, key);
if (bucket) {
bucket->value = *value;
bucket->value = value;
} else {
if (dict->count >= 2 * dict->capacity) {
DictReHash(vm, dict, 2 * dict->capacity);
}
bucket = VMAlloc(vm, sizeof(DictBucket));
bucket->next = dict->buckets[index];
bucket->value = *value;
bucket->key = *key;
bucket->value = value;
bucket->key = key;
dict->buckets[index] = bucket;
++dict->count;
}

6
ds.h
View File

@ -68,15 +68,15 @@ Value ArrayPeek(Array * array);
Dictionary * DictNew(VM * vm, uint32_t capacity);
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key);
Value DictGet(Dictionary * dict, Value key);
/* Get a Value from the dictionary, but remove it at the same
* time. */
Value DictRemove(VM * vm, Dictionary * dict, Value * key);
Value DictRemove(VM * vm, 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(VM * vm, Dictionary * dict, Value * key, Value * value);
void DictPut(VM * vm, Dictionary * dict, Value key, Value value);
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator);

16
main.c
View File

@ -7,6 +7,18 @@
#include "compile.h"
#include "value.h"
Value print(VM * vm) {
uint32_t i;
Value nil;
uint8_t * string = ValueToString(vm, VMGetArg(vm, 0));
uint32_t len = VStringSize(string);
for (i = 0; i < len; ++i)
fputc(string[i], stdout);
fputc('\n', stdout);
nil.type = TYPE_NIL;
return nil;
}
/* A simple repl for debugging */
void debugRepl() {
char buffer[128] = {0};
@ -30,7 +42,7 @@ void debugRepl() {
while (p.status == PARSER_PENDING) {
/* Get some input if we are done */
if (*reader == '\0') {
printf(">> ");
printf("> ");
if (!fgets(buffer, sizeof(buffer), stdin)) {
return;
}
@ -75,7 +87,7 @@ void debugRepl() {
buffer[0] = 0;
continue;
} else {
ValuePrint(&vm.tempRoot, 0);
ValuePrint(vm.tempRoot, 0);
printf("\n");
}
}

View File

@ -109,7 +109,7 @@ static void ParserTopAppend(Parser * p, Value x) {
break;
case PTYPE_DICTIONARY:
if (top->buf.dictState.keyFound) {
DictPut(p->vm, top->buf.dictState.dict, &top->buf.dictState.key, &x);
DictPut(p->vm, top->buf.dictState.dict, top->buf.dictState.key, x);
} else {
top->buf.dictState.key = x;
}

132
value.c
View File

@ -20,42 +20,42 @@ static void FuncDefBytecodePrint(FuncDef * def) {
}
/* Print a value recursively. Used for debugging */
void ValuePrint(Value * x, uint32_t indent) {
void ValuePrint(Value x, uint32_t indent) {
uint32_t i;
for (i = 0; i < indent; ++i)
fputc(' ', stdout);
switch (x->type) {
switch (x.type) {
case TYPE_NIL:
printf("<nil>");
break;
case TYPE_BOOLEAN:
printf(x->data.boolean ? "<true>" : "<false>");
printf(x.data.boolean ? "<true>" : "<false>");
break;
case TYPE_NUMBER:
printf("%f", x->data.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);
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");
if (x.type == TYPE_ARRAY) printf(" ]\n"); else printf(" )\n");
break;
case TYPE_STRING:
printf("\"%.*s\"", VStringSize(x->data.string), (char *) x->data.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);
printf("%.*s", VStringSize(x.data.string), (char *) x.data.string);
break;
case TYPE_CFUNCTION:
printf("<cfunction>");
break;
case TYPE_FUNCTION:
printf("<function ");
FuncDefBytecodePrint(x->data.func->def);
FuncDefBytecodePrint(x.data.func->def);
printf(">");
break;
case TYPE_DICTIONARY:
@ -66,7 +66,7 @@ void ValuePrint(Value * x, uint32_t indent) {
break;
case TYPE_FUNCDEF:
printf("<funcdef ");
FuncDefBytecodePrint(x->data.funcdef);
FuncDefBytecodePrint(x.data.funcdef);
printf(">");
break;
case TYPE_FUNCENV:
@ -135,39 +135,39 @@ static uint8_t * StringDescription(VM * vm, const char * title, uint32_t titlele
}
/* Returns a string pointer or NULL if could not allocate memory. */
uint8_t * ValueToString(VM * vm, Value * x) {
switch (x->type) {
uint8_t * ValueToString(VM * vm, Value x) {
switch (x.type) {
case TYPE_NIL:
return LoadCString(vm, "nil", 3);
case TYPE_BOOLEAN:
if (x->data.boolean) {
if (x.data.boolean) {
return LoadCString(vm, "true", 4);
} else {
return LoadCString(vm, "false", 5);
}
case TYPE_NUMBER:
return NumberToString(vm, x->data.number);
return NumberToString(vm, x.data.number);
case TYPE_ARRAY:
return StringDescription(vm, "array", 5, x->data.array);
return StringDescription(vm, "array", 5, x.data.array);
case TYPE_FORM:
return StringDescription(vm, "form", 4, x->data.array);
return StringDescription(vm, "form", 4, x.data.array);
case TYPE_STRING:
case TYPE_SYMBOL:
return x->data.string;
return x.data.string;
case TYPE_BYTEBUFFER:
return StringDescription(vm, "buffer", 6, x->data.buffer);
return StringDescription(vm, "buffer", 6, x.data.buffer);
case TYPE_CFUNCTION:
return StringDescription(vm, "cfunction", 9, x->data.cfunction);
return StringDescription(vm, "cfunction", 9, x.data.cfunction);
case TYPE_FUNCTION:
return StringDescription(vm, "function", 8, x->data.func);
return StringDescription(vm, "function", 8, x.data.func);
case TYPE_DICTIONARY:
return StringDescription(vm, "dictionary", 10, x->data.dict);
return StringDescription(vm, "dictionary", 10, x.data.dict);
case TYPE_FUNCDEF:
return StringDescription(vm, "funcdef", 7, x->data.funcdef);
return StringDescription(vm, "funcdef", 7, x.data.funcdef);
case TYPE_FUNCENV:
return StringDescription(vm, "funcenv", 7, x->data.funcenv);
return StringDescription(vm, "funcenv", 7, x.data.funcenv);
case TYPE_THREAD:
return StringDescription(vm, "thread", 6, x->data.array);
return StringDescription(vm, "thread", 6, x.data.array);
}
return NULL;
}
@ -182,44 +182,36 @@ uint32_t djb2(const uint8_t * str) {
}
/* Check if two values are equal. This is strict equality with no conversion. */
int ValueEqual(Value * x, Value * y) {
int ValueEqual(Value x, Value y) {
int result = 0;
if (x->type != y->type) {
if (x.type != y.type) {
result = 0;
} else {
switch (x->type) {
switch (x.type) {
case TYPE_NIL:
result = 1;
break;
case TYPE_BOOLEAN:
result = x->data.boolean == y->data.boolean;
result = x.data.boolean == y.data.boolean;
break;
case TYPE_NUMBER:
result = x->data.number == y->data.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:
/* TODO: keep cache to for symbols to get quick equality. */
case TYPE_SYMBOL:
if (x->data.string == y->data.string) {
if (x.data.string == y.data.string) {
result = 1;
break;
}
if (ValueHash(x) != ValueHash(y) ||
VStringSize(x->data.string) != VStringSize(y->data.string)) {
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;
}
if (!strncmp((char *) x.data.string, (char *) y.data.string, VStringSize(x.data.string))) {
result = 1;
break;
}
@ -235,7 +227,7 @@ int ValueEqual(Value * x, Value * y) {
case TYPE_FUNCENV:
case TYPE_THREAD:
/* compare pointers */
result = x->data.array == y->data.array;
result = x.data.array == y.data.array;
break;
}
}
@ -243,14 +235,14 @@ int ValueEqual(Value * x, Value * y) {
}
/* Computes a hash value for a function */
uint32_t ValueHash(Value * x) {
uint32_t ValueHash(Value x) {
uint32_t hash = 0;
switch (x->type) {
switch (x.type) {
case TYPE_NIL:
hash = 0;
break;
case TYPE_BOOLEAN:
hash = x->data.boolean;
hash = x.data.boolean;
break;
case TYPE_NUMBER:
{
@ -258,7 +250,7 @@ uint32_t ValueHash(Value * x) {
uint32_t hash;
Number number;
} u;
u.number = x->data.number;
u.number = x.data.number;
hash = u.hash;
}
break;
@ -266,10 +258,10 @@ uint32_t ValueHash(Value * x) {
case TYPE_SYMBOL:
case TYPE_STRING:
/* Assume 0 is not hashed. */
if (VStringHash(x->data.string))
hash = VStringHash(x->data.string);
if (VStringHash(x.data.string))
hash = VStringHash(x.data.string);
else
hash = VStringHash(x->data.string) = djb2(x->data.string);
hash = VStringHash(x.data.string) = djb2(x.data.string);
break;
case TYPE_ARRAY:
case TYPE_FORM:
@ -286,7 +278,7 @@ uint32_t ValueHash(Value * x) {
void * pointer;
uint32_t hash;
} u;
u.pointer = x->data.pointer;
u.pointer = x.data.pointer;
hash = u.hash;
}
break;
@ -297,49 +289,43 @@ uint32_t ValueHash(Value * x) {
/* 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) {
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) {
if (x.data.boolean == y.data.boolean) {
return 0;
} else {
return x->data.boolean ? 1 : -1;
return x.data.boolean ? 1 : -1;
}
case TYPE_NUMBER:
/* TODO: define behavior for NaN and infinties. */
if (x->data.number == y->data.number) {
if (x.data.number == y.data.number) {
return 0;
} else {
return x->data.number > y->data.number ? 1 : -1;
return x.data.number > y.data.number ? 1 : -1;
}
case TYPE_STRING:
case TYPE_SYMBOL:
if (x->data.string == y->data.string) {
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 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]) {
if (x.data.string[i] == y.data.string[i]) {
continue;
} else if (x->data.string[i] < y->data.string[i]) {
} 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;
@ -354,13 +340,13 @@ int ValueCompare(Value * x, Value * y) {
case TYPE_FUNCDEF:
case TYPE_FUNCENV:
case TYPE_THREAD:
if (x->data.string == y->data.string) {
if (x.data.string == y.data.string) {
return 0;
} else {
return x->data.string > y->data.string ? 1 : -1;
return x.data.string > y.data.string ? 1 : -1;
}
}
} else if (x->type < y->type) {
} else if (x.type < y.type) {
return -1;
}
return 1;

10
value.h
View File

@ -3,16 +3,16 @@
#include "datatypes.h"
void ValuePrint(Value * x, uint32_t indent);
void ValuePrint(Value x, uint32_t indent);
int ValueCompare(Value * x, Value * y);
int ValueCompare(Value x, Value y);
int ValueEqual(Value * x, Value * y);
int ValueEqual(Value x, Value y);
Value ValueLoadCString(VM * vm, const char * string);
uint8_t * ValueToString(VM * vm, Value * x);
uint8_t * ValueToString(VM * vm, Value x);
uint32_t ValueHash(Value * x);
uint32_t ValueHash(Value x);
#endif /* end of include guard: VALUE_H_1RJPQKFM */

31
vm.c
View File

@ -198,6 +198,7 @@ void * VMZalloc(VM * vm, uint32_t size) {
/* Run garbage collection */
void VMCollect(VM * vm) {
if (vm->lock > 0) return;
Value thread;
thread.type = TYPE_THREAD;
thread.data.array = vm->thread;
@ -336,7 +337,9 @@ static void VMCallOp(VM * vm) {
if (callee.type == TYPE_CFUNCTION) {
for (i = 0; i < arity; ++i)
*(argWriter++) = *VMOpArg(4 + i);
++vm->lock;
VMReturn(vm, callee.data.cfunction(vm));
--vm->lock;
VMMaybeCollect(vm);
} else if (callee.type == TYPE_FUNCTION) {
Func * f = callee.data.func;
@ -399,7 +402,9 @@ static void VMTailCallOp(VM * vm) {
FrameSize(thread) = newFrameSize;
FrameCallee(thread) = callee;
if (callee.type == TYPE_CFUNCTION) {
++vm->lock;
VMReturn(vm, callee.data.cfunction(vm));
--vm->lock;
VMMaybeCollect(vm);
} else {
Func * f = callee.data.func;
@ -630,7 +635,7 @@ int VMStart(VM * vm) {
case VM_OP_EQL: /* Equality */
vRet = VMOpArg(1);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = ValueEqual(VMOpArg(2), VMOpArg(3));
vRet->data.boolean = ValueEqual(*VMOpArg(2), *VMOpArg(3));
vm->pc += 4;
break;
@ -639,7 +644,7 @@ int VMStart(VM * vm) {
v1 = VMOpArg(2);
v2 = VMOpArg(3);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = (ValueCompare(VMOpArg(2), VMOpArg(3)) == -1);
vRet->data.boolean = (ValueCompare(*VMOpArg(2), *VMOpArg(3)) == -1);
vm->pc += 4;
break;
@ -648,7 +653,7 @@ int VMStart(VM * vm) {
v1 = VMOpArg(2);
v2 = VMOpArg(3);
vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = (ValueCompare(VMOpArg(2), VMOpArg(3)) != 1);
vRet->data.boolean = (ValueCompare(*VMOpArg(2), *VMOpArg(3)) != 1);
vm->pc += 4;
break;
@ -678,7 +683,7 @@ int VMStart(VM * vm) {
while (i < kvs) {
v1 = VMOpArg(i++);
v2 = VMOpArg(i++);
DictPut(vm, dict, v1, v2);
DictPut(vm, dict, *v1, *v2);
}
vRet->type = TYPE_DICTIONARY;
vRet->data.dict = dict;
@ -740,6 +745,20 @@ int VMStart(VM * vm) {
}
}
/* Get an argument from the stack */
Value VMGetArg(VM * vm, uint16_t index) {
uint16_t frameSize = FrameSize(vm->thread);
VMAssert(vm, frameSize > index, "Cannot get arg out of stack bounds");
return *VMArg(index);
}
/* Put a value on the stack */
void VMSetArg(VM * vm, uint16_t index, Value x) {
uint16_t frameSize = FrameSize(vm->thread);
VMAssert(vm, frameSize > index, "Cannot set arg out of stack bounds");
*VMArg(index) = x;
}
#undef VMOpArg
#undef VMArg
@ -749,12 +768,14 @@ void VMInit(VM * vm) {
vm->base = NULL;
vm->pc = NULL;
vm->error = NULL;
vm->thread = ArrayNew(vm, 20);
/* Garbage collection */
vm->blocks = NULL;
vm->nextCollection = 0;
vm->memoryInterval = 1024 * 256;
vm->black = 0;
vm->lock = 0;
/* Create new thread */
vm->thread = ArrayNew(vm, 20);
}
/* Load a function into the VM. The function will be called with

6
vm.h
View File

@ -44,4 +44,10 @@ void * VMAlloc(VM * vm, uint32_t amount);
/* Allocate zeroed memory */
void * VMZalloc(VM * vm, uint32_t amount);
/* Get an argument from the stack */
Value VMGetArg(VM * vm, uint16_t index);
/* Put a value on the stack */
void VMSetArg(VM * vm, uint16_t index, Value x);
#endif /* end of include guard: VM_H_C4OZU8CQ */