1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-26 00:10: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) gdb $(TARGET)
valgrind: $(TARGET) valgrind: $(TARGET)
valgrind ./$(TARGET) valgrind ./$(TARGET) --leak-check=full
.PHONY: clean install run debug valgrind .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 * points to the parent Scope, and its current child
* Scope. */ * Scope. */
struct Scope { struct Scope {
uint32_t level;
uint16_t nextLocal; uint16_t nextLocal;
uint32_t heapCapacity; uint32_t heapCapacity;
uint32_t heapSize; uint32_t heapSize;
@ -118,8 +119,12 @@ static Scope * CompilerPushScope(Compiler * c, int sameFunction) {
scope->heapCapacity = 10; scope->heapCapacity = 10;
scope->nextScope = NULL; scope->nextScope = NULL;
scope->previousScope = c->tail; scope->previousScope = c->tail;
if (c->tail) if (c->tail) {
c->tail->nextScope = scope; c->tail->nextScope = scope;
scope->level = c->tail->level + (sameFunction ? 0 : 1);
} else {
scope->level = 0;
}
if (sameFunction) { if (sameFunction) {
if (!c->tail) { if (!c->tail) {
CError(c, "Cannot inherit scope when root scope"); 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. */ * Useful for dictionary literals, array literals, function calls, etc. */
static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToBuffer) { static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToBuffer) {
uint32_t i; uint32_t i;
if (writeToBuffer) { Buffer * buffer = c->buffer;
Buffer * buffer = c->buffer; if (writeToBuffer == 1) {
for (i = 0; i < tracker->count; ++i) { for (i = 0; i < tracker->count; ++i) {
Slot * s = tracker->slots + i; Slot * s = tracker->slots + i;
BufferPushUInt16(c->vm, buffer, s->index); 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 */ /* Free in reverse order */
for (i = tracker->count - 1; i < tracker->count; --i) { 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 * that one instead of creating a new literal. This allows for some reuse
* of things like string constants.*/ * of things like string constants.*/
static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) { 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; uint16_t literalIndex = 0;
if (checkDup.type != TYPE_NIL) { if (checkDup.type != TYPE_NIL) {
/* An equal literal is already registered in the current scope */ /* 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; valIndex.type = TYPE_NUMBER;
literalIndex = scope->literalsArray->count; literalIndex = scope->literalsArray->count;
valIndex.data.number = literalIndex; valIndex.data.number = literalIndex;
DictPut(c->vm, scope->literals, &x, &valIndex); DictPut(c->vm, scope->literals, x, valIndex);
ArrayPush(c->vm, scope->literalsArray, x); ArrayPush(c->vm, scope->literalsArray, x);
} }
return literalIndex; return literalIndex;
@ -312,7 +322,7 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {
uint16_t target = CompilerGetLocal(c, scope); uint16_t target = CompilerGetLocal(c, scope);
x.type = TYPE_NUMBER; x.type = TYPE_NUMBER;
x.data.number = target; x.data.number = target;
DictPut(c->vm, scope->locals, &sym, &x); DictPut(c->vm, scope->locals, sym, x);
return target; return target;
} }
@ -320,15 +330,14 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {
* pass back the level and index by reference. */ * pass back the level and index by reference. */
static int ScopeSymbolResolve(Scope * scope, Value x, static int ScopeSymbolResolve(Scope * scope, Value x,
uint16_t * level, uint16_t * index) { uint16_t * level, uint16_t * index) {
uint16_t levelTest = 0; uint32_t currentLevel = scope->level;
while (scope) { while (scope) {
Value check = DictGet(scope->locals, &x); Value check = DictGet(scope->locals, x);
if (check.type != TYPE_NIL) { if (check.type != TYPE_NIL) {
*level = levelTest; *level = currentLevel - scope->level;
*index = (uint16_t) check.data.number; *index = (uint16_t) check.data.number;
return 1; return 1;
} }
++levelTest;
scope = scope->previousScope; scope = scope->previousScope;
} }
return 0; 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 * its argument is still carried out, but their results can
* also be ignored). */ * also be ignored). */
static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, 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; Scope * scope = c->tail;
Buffer * buffer = c->buffer; Buffer * buffer = c->buffer;
Slot ret = SlotDefault(); Slot ret = SlotDefault();
@ -602,22 +611,63 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
BufferPushUInt16(c->vm, buffer, ret.index); BufferPushUInt16(c->vm, buffer, ret.index);
} }
/* Write the location of all of the arguments */ /* Write the location of all of the arguments */
CompilerTrackerFree(c, scope, &tracker, 1); CompilerTrackerFree(c, scope, &tracker, reverseOperands ? 2 : 1);
return ret; return ret;
} }
/* Math specials */ /* Math specials */
static Slot CompileAddition(Compiler * c, FormOptions opts, Array * form) { 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) { 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) { 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) { 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 */ /* Compile an assignment operation */
@ -625,7 +675,6 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
Scope * scope = c->tail; Scope * scope = c->tail;
Buffer * buffer = c->buffer; Buffer * buffer = c->buffer;
FormOptions subOpts = FormOptionsDefault(); FormOptions subOpts = FormOptionsDefault();
Slot ret = SlotDefault();
uint16_t target = 0; uint16_t target = 0;
uint16_t level = 0; uint16_t level = 0;
if (ScopeSymbolResolve(scope, left, &level, &target)) { 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); BufferPushUInt16(c->vm, buffer, target);
/* Drop the possibly temporary slot if it is indeed temporary */ /* Drop the possibly temporary slot if it is indeed temporary */
CompilerDropSlot(c, scope, slot); 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 { } else {
/* We need to declare a new symbol */ /* 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; subOpts.canChoose = 0;
CompileValue(c, subOpts, right); CompileValue(c, subOpts, right);
} }
/* If we need a return value, we just use nil */ return CompileReturnNil(c, opts);
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;
} }
/* Writes bytecode to return a slot */ /* 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; uint32_t current = startIndex;
/* Compile the body */ /* Compile the body */
while (current < form->count) { while (current < form->count) {
subOpts.canDrop = current != form->count - 1; subOpts.canDrop = (current != form->count - 1) || opts.canDrop;
subOpts.isTail = opts.isTail && !subOpts.canDrop; subOpts.isTail = opts.isTail && !subOpts.canDrop;
ret = CompileValue(c, subOpts, form->data[current]); ret = CompileValue(c, subOpts, form->data[current]);
if (subOpts.canDrop) { if (subOpts.canDrop) {
@ -692,7 +735,7 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t
} }
++current; ++current;
} }
if (opts.isTail && !ret.isDud) { if (opts.isTail && !ret.isDud) {
CompilerReturnSlot(c, ret); CompilerReturnSlot(c, ret);
ret.isDud = 1; ret.isDud = 1;
} }
@ -891,6 +934,39 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
return ret; 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 */ /* Do special */
static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) { static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) {
Slot ret; Slot ret;
@ -960,6 +1036,9 @@ static SpecialFormHelper GetSpecial(Array * form) {
case '-': return CompileSubtraction; case '-': return CompileSubtraction;
case '*': return CompileMultiplication; case '*': return CompileMultiplication;
case '/': return CompileDivision; case '/': return CompileDivision;
case '>': return CompileGreaterThan;
case '<': return CompileLessThan;
case '=': return CompileEquals;
case '\'': return CompileQuote; case '\'': return CompileQuote;
default: default:
break; break;
@ -967,6 +1046,22 @@ static SpecialFormHelper GetSpecial(Array * form) {
} }
/* Multi character specials. Mostly control flow. */ /* Multi character specials. Mostly control flow. */
switch (name[0]) { 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': case 'd':
{ {
if (VStringSize(name) == 2 && if (VStringSize(name) == 2 &&
@ -991,6 +1086,14 @@ static SpecialFormHelper GetSpecial(Array * form) {
} }
} }
break; break;
case 'n':
{
if (VStringSize(name) == 3 &&
name[1] == 'o' &&
name[2] == 't') {
return CompileNot;
}
}
case 'q': case 'q':
{ {
if (VStringSize(name) == 5 && if (VStringSize(name) == 5 &&
@ -1011,6 +1114,16 @@ static SpecialFormHelper GetSpecial(Array * form) {
} }
} }
break; break;
case 'w':
{
if (VStringSize(name) == 5 &&
name[1] == 'h' &&
name[2] == 'i' &&
name[3] == 'l' &&
name[4] == 'e') {
return CompileWhile;
}
}
default: default:
break; break;
} }

View File

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

26
ds.c
View File

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

6
ds.h
View File

@ -68,15 +68,15 @@ Value ArrayPeek(Array * array);
Dictionary * DictNew(VM * vm, uint32_t capacity); Dictionary * DictNew(VM * vm, uint32_t capacity);
/* Get a value out of the dictionary */ /* 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 /* Get a Value from the dictionary, but remove it at the same
* time. */ * 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. /* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */ * 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 */ /* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator); void DictIterate(Dictionary * dict, DictionaryIterator * iterator);

16
main.c
View File

@ -7,6 +7,18 @@
#include "compile.h" #include "compile.h"
#include "value.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 */ /* A simple repl for debugging */
void debugRepl() { void debugRepl() {
char buffer[128] = {0}; char buffer[128] = {0};
@ -30,7 +42,7 @@ void debugRepl() {
while (p.status == PARSER_PENDING) { while (p.status == PARSER_PENDING) {
/* Get some input if we are done */ /* Get some input if we are done */
if (*reader == '\0') { if (*reader == '\0') {
printf(">> "); printf("> ");
if (!fgets(buffer, sizeof(buffer), stdin)) { if (!fgets(buffer, sizeof(buffer), stdin)) {
return; return;
} }
@ -75,7 +87,7 @@ void debugRepl() {
buffer[0] = 0; buffer[0] = 0;
continue; continue;
} else { } else {
ValuePrint(&vm.tempRoot, 0); ValuePrint(vm.tempRoot, 0);
printf("\n"); printf("\n");
} }
} }

View File

@ -109,7 +109,7 @@ static void ParserTopAppend(Parser * p, Value x) {
break; break;
case PTYPE_DICTIONARY: case PTYPE_DICTIONARY:
if (top->buf.dictState.keyFound) { 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 { } else {
top->buf.dictState.key = x; 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 */ /* Print a value recursively. Used for debugging */
void ValuePrint(Value * x, uint32_t indent) { void ValuePrint(Value x, uint32_t indent) {
uint32_t i; uint32_t i;
for (i = 0; i < indent; ++i) for (i = 0; i < indent; ++i)
fputc(' ', stdout); fputc(' ', stdout);
switch (x->type) { switch (x.type) {
case TYPE_NIL: case TYPE_NIL:
printf("<nil>"); printf("<nil>");
break; break;
case TYPE_BOOLEAN: case TYPE_BOOLEAN:
printf(x->data.boolean ? "<true>" : "<false>"); printf(x.data.boolean ? "<true>" : "<false>");
break; break;
case TYPE_NUMBER: case TYPE_NUMBER:
printf("%f", x->data.number); printf("%f", x.data.number);
break; break;
case TYPE_FORM: case TYPE_FORM:
case TYPE_ARRAY: case TYPE_ARRAY:
if (x->type == TYPE_ARRAY) printf(" [\n"); else printf(" (\n"); if (x.type == TYPE_ARRAY) printf(" [\n"); else printf(" (\n");
for (i = 0; i < x->data.array->count; ++i) { for (i = 0; i < x.data.array->count; ++i) {
ValuePrint(x->data.array->data + i, indent + 4); ValuePrint(x.data.array->data[i], indent + 4);
printf("\n"); printf("\n");
} }
for (i = 0; i < indent; ++i) fputc(' ', stdout); 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; break;
case TYPE_STRING: case TYPE_STRING:
printf("\"%.*s\"", VStringSize(x->data.string), (char *) x->data.string); printf("\"%.*s\"", VStringSize(x.data.string), (char *) x.data.string);
break; break;
case TYPE_SYMBOL: case TYPE_SYMBOL:
printf("%.*s", VStringSize(x->data.string), (char *) x->data.string); printf("%.*s", VStringSize(x.data.string), (char *) x.data.string);
break; break;
case TYPE_CFUNCTION: case TYPE_CFUNCTION:
printf("<cfunction>"); printf("<cfunction>");
break; break;
case TYPE_FUNCTION: case TYPE_FUNCTION:
printf("<function "); printf("<function ");
FuncDefBytecodePrint(x->data.func->def); FuncDefBytecodePrint(x.data.func->def);
printf(">"); printf(">");
break; break;
case TYPE_DICTIONARY: case TYPE_DICTIONARY:
@ -66,7 +66,7 @@ void ValuePrint(Value * x, uint32_t indent) {
break; break;
case TYPE_FUNCDEF: case TYPE_FUNCDEF:
printf("<funcdef "); printf("<funcdef ");
FuncDefBytecodePrint(x->data.funcdef); FuncDefBytecodePrint(x.data.funcdef);
printf(">"); printf(">");
break; break;
case TYPE_FUNCENV: 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. */ /* Returns a string pointer or NULL if could not allocate memory. */
uint8_t * ValueToString(VM * vm, Value * x) { uint8_t * ValueToString(VM * vm, Value x) {
switch (x->type) { switch (x.type) {
case TYPE_NIL: case TYPE_NIL:
return LoadCString(vm, "nil", 3); return LoadCString(vm, "nil", 3);
case TYPE_BOOLEAN: case TYPE_BOOLEAN:
if (x->data.boolean) { if (x.data.boolean) {
return LoadCString(vm, "true", 4); return LoadCString(vm, "true", 4);
} else { } else {
return LoadCString(vm, "false", 5); return LoadCString(vm, "false", 5);
} }
case TYPE_NUMBER: case TYPE_NUMBER:
return NumberToString(vm, x->data.number); return NumberToString(vm, x.data.number);
case TYPE_ARRAY: case TYPE_ARRAY:
return StringDescription(vm, "array", 5, x->data.array); return StringDescription(vm, "array", 5, x.data.array);
case TYPE_FORM: case TYPE_FORM:
return StringDescription(vm, "form", 4, x->data.array); return StringDescription(vm, "form", 4, x.data.array);
case TYPE_STRING: case TYPE_STRING:
case TYPE_SYMBOL: case TYPE_SYMBOL:
return x->data.string; return x.data.string;
case TYPE_BYTEBUFFER: case TYPE_BYTEBUFFER:
return StringDescription(vm, "buffer", 6, x->data.buffer); return StringDescription(vm, "buffer", 6, x.data.buffer);
case TYPE_CFUNCTION: case TYPE_CFUNCTION:
return StringDescription(vm, "cfunction", 9, x->data.cfunction); return StringDescription(vm, "cfunction", 9, x.data.cfunction);
case TYPE_FUNCTION: case TYPE_FUNCTION:
return StringDescription(vm, "function", 8, x->data.func); return StringDescription(vm, "function", 8, x.data.func);
case TYPE_DICTIONARY: case TYPE_DICTIONARY:
return StringDescription(vm, "dictionary", 10, x->data.dict); return StringDescription(vm, "dictionary", 10, x.data.dict);
case TYPE_FUNCDEF: case TYPE_FUNCDEF:
return StringDescription(vm, "funcdef", 7, x->data.funcdef); return StringDescription(vm, "funcdef", 7, x.data.funcdef);
case TYPE_FUNCENV: case TYPE_FUNCENV:
return StringDescription(vm, "funcenv", 7, x->data.funcenv); return StringDescription(vm, "funcenv", 7, x.data.funcenv);
case TYPE_THREAD: case TYPE_THREAD:
return StringDescription(vm, "thread", 6, x->data.array); return StringDescription(vm, "thread", 6, x.data.array);
} }
return NULL; 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. */ /* 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; int result = 0;
if (x->type != y->type) { if (x.type != y.type) {
result = 0; result = 0;
} else { } else {
switch (x->type) { switch (x.type) {
case TYPE_NIL: case TYPE_NIL:
result = 1; result = 1;
break; break;
case TYPE_BOOLEAN: case TYPE_BOOLEAN:
result = x->data.boolean == y->data.boolean; result = x.data.boolean == y.data.boolean;
break; break;
case TYPE_NUMBER: case TYPE_NUMBER:
result = x->data.number == y->data.number; result = x.data.number == y.data.number;
break; break;
/* Assume that when strings are created, equal strings /* Assume that when strings are created, equal strings
* are set to the same string */ * are set to the same string */
case TYPE_STRING: case TYPE_STRING:
/* TODO: keep cache to for symbols to get quick equality. */
case TYPE_SYMBOL: case TYPE_SYMBOL:
if (x->data.string == y->data.string) { if (x.data.string == y.data.string) {
result = 1; result = 1;
break; break;
} }
if (ValueHash(x) != ValueHash(y) || if (ValueHash(x) != ValueHash(y) ||
VStringSize(x->data.string) != VStringSize(y->data.string)) { VStringSize(x.data.string) != VStringSize(y.data.string)) {
result = 0; result = 0;
break; 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))) {
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; result = 1;
break; break;
} }
@ -235,7 +227,7 @@ int ValueEqual(Value * x, Value * y) {
case TYPE_FUNCENV: case TYPE_FUNCENV:
case TYPE_THREAD: case TYPE_THREAD:
/* compare pointers */ /* compare pointers */
result = x->data.array == y->data.array; result = x.data.array == y.data.array;
break; break;
} }
} }
@ -243,14 +235,14 @@ int ValueEqual(Value * x, Value * y) {
} }
/* Computes a hash value for a function */ /* Computes a hash value for a function */
uint32_t ValueHash(Value * x) { uint32_t ValueHash(Value x) {
uint32_t hash = 0; uint32_t hash = 0;
switch (x->type) { switch (x.type) {
case TYPE_NIL: case TYPE_NIL:
hash = 0; hash = 0;
break; break;
case TYPE_BOOLEAN: case TYPE_BOOLEAN:
hash = x->data.boolean; hash = x.data.boolean;
break; break;
case TYPE_NUMBER: case TYPE_NUMBER:
{ {
@ -258,7 +250,7 @@ uint32_t ValueHash(Value * x) {
uint32_t hash; uint32_t hash;
Number number; Number number;
} u; } u;
u.number = x->data.number; u.number = x.data.number;
hash = u.hash; hash = u.hash;
} }
break; break;
@ -266,10 +258,10 @@ uint32_t ValueHash(Value * x) {
case TYPE_SYMBOL: case TYPE_SYMBOL:
case TYPE_STRING: case TYPE_STRING:
/* Assume 0 is not hashed. */ /* Assume 0 is not hashed. */
if (VStringHash(x->data.string)) if (VStringHash(x.data.string))
hash = VStringHash(x->data.string); hash = VStringHash(x.data.string);
else else
hash = VStringHash(x->data.string) = djb2(x->data.string); hash = VStringHash(x.data.string) = djb2(x.data.string);
break; break;
case TYPE_ARRAY: case TYPE_ARRAY:
case TYPE_FORM: case TYPE_FORM:
@ -286,7 +278,7 @@ uint32_t ValueHash(Value * x) {
void * pointer; void * pointer;
uint32_t hash; uint32_t hash;
} u; } u;
u.pointer = x->data.pointer; u.pointer = x.data.pointer;
hash = u.hash; hash = u.hash;
} }
break; 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. /* 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 * If y is less, returns 1. All types are comparable
* and should have strict ordering. */ * and should have strict ordering. */
int ValueCompare(Value * x, Value * y) { int ValueCompare(Value x, Value y) {
if (x->type == y->type) { if (x.type == y.type) {
switch (x->type) { switch (x.type) {
case TYPE_NIL: case TYPE_NIL:
return 0; return 0;
case TYPE_BOOLEAN: case TYPE_BOOLEAN:
if (x->data.boolean == y->data.boolean) { if (x.data.boolean == y.data.boolean) {
return 0; return 0;
} else { } else {
return x->data.boolean ? 1 : -1; return x.data.boolean ? 1 : -1;
} }
case TYPE_NUMBER: case TYPE_NUMBER:
/* TODO: define behavior for NaN and infinties. */ /* TODO: define behavior for NaN and infinties. */
if (x->data.number == y->data.number) { if (x.data.number == y.data.number) {
return 0; return 0;
} else { } else {
return x->data.number > y->data.number ? 1 : -1; return x.data.number > y.data.number ? 1 : -1;
} }
case TYPE_STRING: case TYPE_STRING:
case TYPE_SYMBOL: case TYPE_SYMBOL:
if (x->data.string == y->data.string) { if (x.data.string == y.data.string) {
return 0; return 0;
} else { } else {
uint32_t xlen = VStringSize(x->data.string); uint32_t xlen = VStringSize(x.data.string);
uint32_t ylen = VStringSize(y->data.string); uint32_t ylen = VStringSize(y.data.string);
uint32_t len = xlen > ylen ? ylen : xlen; uint32_t len = xlen > ylen ? ylen : xlen;
uint32_t i; uint32_t i;
for (i = 0; i < len; ++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; 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 */ return 1; /* x is less then y */
} else { } else {
return -1; /* y is less than x */ return -1; /* y is less than x */
} }
} }
if (xlen == ylen) { 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; return 0;
} else { } else {
return xlen < ylen ? -1 : 1; return xlen < ylen ? -1 : 1;
@ -354,13 +340,13 @@ int ValueCompare(Value * x, Value * y) {
case TYPE_FUNCDEF: case TYPE_FUNCDEF:
case TYPE_FUNCENV: case TYPE_FUNCENV:
case TYPE_THREAD: case TYPE_THREAD:
if (x->data.string == y->data.string) { if (x.data.string == y.data.string) {
return 0; return 0;
} else { } 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;
} }
return 1; return 1;

10
value.h
View File

@ -3,16 +3,16 @@
#include "datatypes.h" #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); 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 */ #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 */ /* Run garbage collection */
void VMCollect(VM * vm) { void VMCollect(VM * vm) {
if (vm->lock > 0) return;
Value thread; Value thread;
thread.type = TYPE_THREAD; thread.type = TYPE_THREAD;
thread.data.array = vm->thread; thread.data.array = vm->thread;
@ -336,7 +337,9 @@ static void VMCallOp(VM * vm) {
if (callee.type == TYPE_CFUNCTION) { if (callee.type == TYPE_CFUNCTION) {
for (i = 0; i < arity; ++i) for (i = 0; i < arity; ++i)
*(argWriter++) = *VMOpArg(4 + i); *(argWriter++) = *VMOpArg(4 + i);
++vm->lock;
VMReturn(vm, callee.data.cfunction(vm)); VMReturn(vm, callee.data.cfunction(vm));
--vm->lock;
VMMaybeCollect(vm); VMMaybeCollect(vm);
} else if (callee.type == TYPE_FUNCTION) { } else if (callee.type == TYPE_FUNCTION) {
Func * f = callee.data.func; Func * f = callee.data.func;
@ -399,7 +402,9 @@ static void VMTailCallOp(VM * vm) {
FrameSize(thread) = newFrameSize; FrameSize(thread) = newFrameSize;
FrameCallee(thread) = callee; FrameCallee(thread) = callee;
if (callee.type == TYPE_CFUNCTION) { if (callee.type == TYPE_CFUNCTION) {
++vm->lock;
VMReturn(vm, callee.data.cfunction(vm)); VMReturn(vm, callee.data.cfunction(vm));
--vm->lock;
VMMaybeCollect(vm); VMMaybeCollect(vm);
} else { } else {
Func * f = callee.data.func; Func * f = callee.data.func;
@ -630,7 +635,7 @@ int VMStart(VM * vm) {
case VM_OP_EQL: /* Equality */ case VM_OP_EQL: /* Equality */
vRet = VMOpArg(1); vRet = VMOpArg(1);
vRet->type = TYPE_BOOLEAN; vRet->type = TYPE_BOOLEAN;
vRet->data.boolean = ValueEqual(VMOpArg(2), VMOpArg(3)); vRet->data.boolean = ValueEqual(*VMOpArg(2), *VMOpArg(3));
vm->pc += 4; vm->pc += 4;
break; break;
@ -639,7 +644,7 @@ int VMStart(VM * vm) {
v1 = VMOpArg(2); v1 = VMOpArg(2);
v2 = VMOpArg(3); v2 = VMOpArg(3);
vRet->type = TYPE_BOOLEAN; 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; vm->pc += 4;
break; break;
@ -648,7 +653,7 @@ int VMStart(VM * vm) {
v1 = VMOpArg(2); v1 = VMOpArg(2);
v2 = VMOpArg(3); v2 = VMOpArg(3);
vRet->type = TYPE_BOOLEAN; 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; vm->pc += 4;
break; break;
@ -678,7 +683,7 @@ int VMStart(VM * vm) {
while (i < kvs) { while (i < kvs) {
v1 = VMOpArg(i++); v1 = VMOpArg(i++);
v2 = VMOpArg(i++); v2 = VMOpArg(i++);
DictPut(vm, dict, v1, v2); DictPut(vm, dict, *v1, *v2);
} }
vRet->type = TYPE_DICTIONARY; vRet->type = TYPE_DICTIONARY;
vRet->data.dict = dict; 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 VMOpArg
#undef VMArg #undef VMArg
@ -749,12 +768,14 @@ void VMInit(VM * vm) {
vm->base = NULL; vm->base = NULL;
vm->pc = NULL; vm->pc = NULL;
vm->error = NULL; vm->error = NULL;
vm->thread = ArrayNew(vm, 20);
/* Garbage collection */ /* Garbage collection */
vm->blocks = NULL; vm->blocks = NULL;
vm->nextCollection = 0; vm->nextCollection = 0;
vm->memoryInterval = 1024 * 256; vm->memoryInterval = 1024 * 256;
vm->black = 0; 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 /* 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 */ /* Allocate zeroed memory */
void * VMZalloc(VM * vm, uint32_t amount); 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 */ #endif /* end of include guard: VM_H_C4OZU8CQ */