From 3794ec3acd3259a9cc9a40d30b844cec6ffe5f49 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 9 Feb 2017 23:28:11 -0500 Subject: [PATCH] More work on compiler. * Fix up while special form * Change Value functions to pass-by-value --- Makefile | 2 +- compile.c | 177 ++++++++++++++++++++++++++++++++++++++++++---------- datatypes.h | 1 + ds.c | 26 ++++---- ds.h | 6 +- main.c | 16 ++++- parse.c | 2 +- value.c | 132 ++++++++++++++++++--------------------- value.h | 10 +-- vm.c | 31 +++++++-- vm.h | 6 ++ 11 files changed, 274 insertions(+), 135 deletions(-) diff --git a/Makefile b/Makefile index d4878e4d..aef5e261 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,6 @@ debug: $(TARGET) gdb $(TARGET) valgrind: $(TARGET) - valgrind ./$(TARGET) + valgrind ./$(TARGET) --leak-check=full .PHONY: clean install run debug valgrind diff --git a/compile.c b/compile.c index f279eb0b..1e2a287f 100644 --- a/compile.c +++ b/compile.c @@ -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; } diff --git a/datatypes.h b/datatypes.h index a6a65547..98c8b915 100644 --- a/datatypes.h +++ b/datatypes.h @@ -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; diff --git a/ds.c b/ds.c index 68d94aa9..6b078c62 100644 --- a/ds.c +++ b/ds.c @@ -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; } diff --git a/ds.h b/ds.h index ab313aa1..4c5a2e04 100644 --- a/ds.h +++ b/ds.h @@ -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); diff --git a/main.c b/main.c index 020749bf..68a77903 100644 --- a/main.c +++ b/main.c @@ -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"); } } diff --git a/parse.c b/parse.c index 0302aca9..7a3326f4 100644 --- a/parse.c +++ b/parse.c @@ -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; } diff --git a/value.c b/value.c index 76fa57f5..50a8e28d 100644 --- a/value.c +++ b/value.c @@ -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(""); break; case TYPE_BOOLEAN: - printf(x->data.boolean ? "" : ""); + printf(x.data.boolean ? "" : ""); 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(""); break; case TYPE_FUNCTION: printf("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("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; diff --git a/value.h b/value.h index ff6d9737..6a587b9d 100644 --- a/value.h +++ b/value.h @@ -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 */ diff --git a/vm.c b/vm.c index e07c4b49..a615875a 100644 --- a/vm.c +++ b/vm.c @@ -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 diff --git a/vm.h b/vm.h index cec3e412..0aa8ebaf 100644 --- a/vm.h +++ b/vm.h @@ -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 */