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:
parent
0557c8b2a6
commit
3794ec3acd
2
Makefile
2
Makefile
@ -32,6 +32,6 @@ debug: $(TARGET)
|
||||
gdb $(TARGET)
|
||||
|
||||
valgrind: $(TARGET)
|
||||
valgrind ./$(TARGET)
|
||||
valgrind ./$(TARGET) --leak-check=full
|
||||
|
||||
.PHONY: clean install run debug valgrind
|
||||
|
173
compile.c
173
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;
|
||||
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) {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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
26
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;
|
||||
}
|
||||
|
6
ds.h
6
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);
|
||||
|
16
main.c
16
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");
|
||||
}
|
||||
}
|
||||
|
2
parse.c
2
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;
|
||||
}
|
||||
|
132
value.c
132
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("<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
10
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 */
|
||||
|
31
vm.c
31
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
|
||||
|
6
vm.h
6
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user