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)
|
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
177
compile.c
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
26
ds.c
@ -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
6
ds.h
@ -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
16
main.c
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
parse.c
2
parse.c
@ -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
132
value.c
@ -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
10
value.h
@ -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
31
vm.c
@ -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
6
vm.h
@ -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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user