mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 02:59:54 +00:00
Add simple disassembler for debugging. Does not use labels.
This commit is contained in:
parent
3794ec3acd
commit
9ffbdcb3e9
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,9 @@
|
|||||||
# Target
|
# Target
|
||||||
interp
|
interp
|
||||||
|
|
||||||
|
# Valgrind files
|
||||||
|
vgcore.*
|
||||||
|
|
||||||
# Created by https://www.gitignore.io/api/c
|
# Created by https://www.gitignore.io/api/c
|
||||||
|
|
||||||
### C ###
|
### C ###
|
||||||
|
4
Makefile
4
Makefile
@ -6,8 +6,8 @@ TARGET=interp
|
|||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
|
|
||||||
# C sources
|
# C sources
|
||||||
HEADERS=vm.h ds.h compile.h parse.h value.h
|
HEADERS=vm.h ds.h compile.h parse.h value.h disasm.h
|
||||||
SOURCES=main.c parse.c value.c vm.c ds.c compile.c
|
SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c
|
||||||
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
153
compile.c
153
compile.c
@ -79,7 +79,7 @@ struct Scope {
|
|||||||
static Slot SlotDefault() {
|
static Slot SlotDefault() {
|
||||||
Slot slot;
|
Slot slot;
|
||||||
slot.index = 0;
|
slot.index = 0;
|
||||||
slot.isDud = 0;
|
slot.isDud = 1;
|
||||||
slot.isTemp = 0;
|
slot.isTemp = 0;
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
@ -103,9 +103,13 @@ BufferDefine(Number, Number);
|
|||||||
BufferDefine(UInt16, uint16_t);
|
BufferDefine(UInt16, uint16_t);
|
||||||
BufferDefine(Int16, int16_t);
|
BufferDefine(Int16, int16_t);
|
||||||
|
|
||||||
|
static void onError(Compiler * c) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/* If there is an error during compilation,
|
/* If there is an error during compilation,
|
||||||
* jump back to start */
|
* jump back to start */
|
||||||
#define CError(c, e) ((c)->error = (e), longjmp((c)->onError, 1))
|
#define CError(c, e) (onError(c), (c)->error = (e), longjmp((c)->onError, 1))
|
||||||
|
|
||||||
/* Push a new scope in the compiler and return
|
/* Push a new scope in the compiler and return
|
||||||
* a pointer to it for configuration. There is
|
* a pointer to it for configuration. There is
|
||||||
@ -168,30 +172,34 @@ static uint16_t CompilerGetLocal(Compiler * c, Scope * scope) {
|
|||||||
return scope->nextLocal++;
|
return scope->nextLocal++;
|
||||||
} else {
|
} else {
|
||||||
uint16_t ret = scope->freeHeap[0];
|
uint16_t ret = scope->freeHeap[0];
|
||||||
uint16_t left, right, current, * heap;
|
uint16_t * heap;
|
||||||
uint32_t currentIndex = 0;
|
uint32_t currentIndex = 0;
|
||||||
|
if (scope->heapSize == 0) {
|
||||||
|
CError(c, "Invalid freeing of slot.");
|
||||||
|
}
|
||||||
heap = scope->freeHeap;
|
heap = scope->freeHeap;
|
||||||
heap[0] = heap[--scope->heapSize];
|
heap[0] = heap[--scope->heapSize];
|
||||||
/* Min Heap Bubble down */
|
/* Min Heap Bubble down */
|
||||||
while (currentIndex < scope->heapSize) {
|
for (;;) {
|
||||||
uint32_t leftIndex = 2 * currentIndex + 1;
|
uint32_t leftIndex = 2 * currentIndex + 1;
|
||||||
uint32_t rightIndex = leftIndex + 1;
|
uint32_t rightIndex = leftIndex + 1;
|
||||||
current = heap[currentIndex];
|
uint32_t minIndex;
|
||||||
left = heap[leftIndex];
|
if (rightIndex >= scope->heapSize) {
|
||||||
right = heap[rightIndex];
|
if (leftIndex >= scope->heapSize) {
|
||||||
if (current == left || current == right) {
|
break;
|
||||||
CError(c, "Double slot allocation. Error in Compiler.");
|
} else {
|
||||||
}
|
minIndex = leftIndex;
|
||||||
if (left < current && left <= right) {
|
}
|
||||||
heap[currentIndex] = left;
|
|
||||||
heap[leftIndex] = current;
|
|
||||||
currentIndex = leftIndex;
|
|
||||||
} else if (right < current && right <= left) {
|
|
||||||
heap[currentIndex] = left;
|
|
||||||
heap[leftIndex] = current;
|
|
||||||
currentIndex = leftIndex;
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
minIndex = heap[leftIndex] < heap[rightIndex] ? leftIndex : rightIndex;
|
||||||
|
}
|
||||||
|
if (heap[minIndex] < heap[currentIndex]) {
|
||||||
|
uint16_t temp = heap[currentIndex];
|
||||||
|
heap[currentIndex] = heap[minIndex];
|
||||||
|
heap[minIndex] = temp;
|
||||||
|
currentIndex = minIndex;
|
||||||
|
} else if (heap[minIndex] == heap[currentIndex]) {
|
||||||
|
CError(c, "Double slot allocation. Error in Compiler.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -202,7 +210,7 @@ static uint16_t CompilerGetLocal(Compiler * c, Scope * scope) {
|
|||||||
/* Free a slot on the stack for other locals and/or
|
/* Free a slot on the stack for other locals and/or
|
||||||
* intermediate values */
|
* intermediate values */
|
||||||
static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) {
|
static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) {
|
||||||
if (slot == scope->nextLocal - 1) {
|
if (slot == scope->nextLocal - 1) {
|
||||||
--scope->nextLocal;
|
--scope->nextLocal;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -249,7 +257,7 @@ static void CompilerTrackerInit(Compiler * c, SlotTracker * tracker) {
|
|||||||
* belong to a named local). If the slot does belong
|
* belong to a named local). If the slot does belong
|
||||||
* to a named variable, does nothing. */
|
* to a named variable, does nothing. */
|
||||||
static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) {
|
static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) {
|
||||||
if (slot.isTemp && !(slot.isTemp)) {
|
if (!slot.isDud && slot.isTemp) {
|
||||||
CompilerFreeLocal(c, scope, slot.index);
|
CompilerFreeLocal(c, scope, slot.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,9 +371,9 @@ static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {
|
|||||||
Slot ret = SlotDefault();
|
Slot ret = SlotDefault();
|
||||||
uint16_t literalIndex;
|
uint16_t literalIndex;
|
||||||
if (opts.canDrop) {
|
if (opts.canDrop) {
|
||||||
ret.isDud = 1;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret.isDud = 0;
|
||||||
if (opts.canChoose) {
|
if (opts.canChoose) {
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
ret.index = CompilerGetLocal(c, scope);
|
ret.index = CompilerGetLocal(c, scope);
|
||||||
@ -387,14 +395,14 @@ static Slot CompileNonReferenceType(Compiler * c, FormOptions opts, Value x) {
|
|||||||
/* If the value is not used, the compiler can just immediately
|
/* If the value is not used, the compiler can just immediately
|
||||||
* ignore it as there are no side effects. */
|
* ignore it as there are no side effects. */
|
||||||
if (opts.canDrop) {
|
if (opts.canDrop) {
|
||||||
ret.isDud = 1;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret.isDud = 0;
|
||||||
if (opts.canChoose) {
|
if (opts.canChoose) {
|
||||||
ret.index = CompilerGetLocal(c, scope);
|
ret.index = CompilerGetLocal(c, scope);
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
} else {
|
} else {
|
||||||
ret.index = (uint16_t) opts.target;
|
ret.index = opts.target;
|
||||||
}
|
}
|
||||||
if (x.type == TYPE_NIL) {
|
if (x.type == TYPE_NIL) {
|
||||||
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
|
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
|
||||||
@ -437,9 +445,9 @@ static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) {
|
|||||||
/* We can just do nothing if we are dropping the
|
/* We can just do nothing if we are dropping the
|
||||||
* results, as dereferencing a symbol has no side effects. */
|
* results, as dereferencing a symbol has no side effects. */
|
||||||
if (opts.canDrop) {
|
if (opts.canDrop) {
|
||||||
ret.isDud = 1;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret.isDud = 0;
|
||||||
if (ScopeSymbolResolve(scope, sym, &level, &index)) {
|
if (ScopeSymbolResolve(scope, sym, &level, &index)) {
|
||||||
if (level > 0) {
|
if (level > 0) {
|
||||||
/* We have an upvalue */
|
/* We have an upvalue */
|
||||||
@ -499,6 +507,7 @@ static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!opts.canDrop) {
|
if (!opts.canDrop) {
|
||||||
|
ret.isDud = 0;
|
||||||
/* Write Dictionary literal opcode */
|
/* Write Dictionary literal opcode */
|
||||||
if (opts.canChoose) {
|
if (opts.canChoose) {
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
@ -508,9 +517,7 @@ static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) {
|
|||||||
}
|
}
|
||||||
BufferPushUInt16(c->vm, buffer, VM_OP_DIC);
|
BufferPushUInt16(c->vm, buffer, VM_OP_DIC);
|
||||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
BufferPushUInt16(c->vm, buffer, ret.index);
|
||||||
BufferPushUInt16(c->vm, buffer, dict->count);
|
BufferPushUInt16(c->vm, buffer, dict->count * 2);
|
||||||
} else {
|
|
||||||
ret.isDud = 1;
|
|
||||||
}
|
}
|
||||||
/* 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, 1);
|
||||||
@ -538,6 +545,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
|
|||||||
CompilerTrackerPush(c, &tracker, slot);
|
CompilerTrackerPush(c, &tracker, slot);
|
||||||
}
|
}
|
||||||
if (!opts.canDrop) {
|
if (!opts.canDrop) {
|
||||||
|
ret.isDud = 0;
|
||||||
/* Write Array literal opcode */
|
/* Write Array literal opcode */
|
||||||
if (opts.canChoose) {
|
if (opts.canChoose) {
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
@ -548,8 +556,6 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
|
|||||||
BufferPushUInt16(c->vm, buffer, VM_OP_ARR);
|
BufferPushUInt16(c->vm, buffer, VM_OP_ARR);
|
||||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
BufferPushUInt16(c->vm, buffer, ret.index);
|
||||||
BufferPushUInt16(c->vm, buffer, array->count);
|
BufferPushUInt16(c->vm, buffer, array->count);
|
||||||
} else {
|
|
||||||
ret.isDud = 1;
|
|
||||||
}
|
}
|
||||||
/* 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, 1);
|
||||||
@ -575,41 +581,35 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
|
|||||||
FormOptions subOpts = FormOptionsDefault();
|
FormOptions subOpts = FormOptionsDefault();
|
||||||
SlotTracker tracker;
|
SlotTracker tracker;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
/* Calculate sub options */
|
|
||||||
subOpts.canDrop = opts.canDrop;
|
|
||||||
/* Compile all of the arguments */
|
/* Compile all of the arguments */
|
||||||
CompilerTrackerInit(c, &tracker);
|
CompilerTrackerInit(c, &tracker);
|
||||||
for (i = 1; i < form->count; ++i) {
|
for (i = 1; i < form->count; ++i) {
|
||||||
Slot slot = CompileValue(c, subOpts, form->data[i]);
|
Slot slot = CompileValue(c, subOpts, form->data[i]);
|
||||||
if (subOpts.canDrop)
|
CompilerTrackerPush(c, &tracker, slot);
|
||||||
CompilerDropSlot(c, scope, slot);
|
|
||||||
else
|
|
||||||
CompilerTrackerPush(c, &tracker, slot);
|
|
||||||
}
|
}
|
||||||
if (!opts.canDrop) {
|
ret.isDud = 0;
|
||||||
/* Write the correct opcode */
|
/* Write the correct opcode */
|
||||||
if (form->count < 2) {
|
if (form->count < 2) {
|
||||||
if (op0 < 0) CError(c, "This operator does not take 0 arguments.");
|
if (op0 < 0) CError(c, "This operator does not take 0 arguments.");
|
||||||
BufferPushUInt16(c->vm, buffer, op0);
|
BufferPushUInt16(c->vm, buffer, op0);
|
||||||
} else if (form->count == 2) {
|
} else if (form->count == 2) {
|
||||||
if (op1 < 0) CError(c, "This operator does not take 1 argument.");
|
if (op1 < 0) CError(c, "This operator does not take 1 argument.");
|
||||||
BufferPushUInt16(c->vm, buffer, op1);
|
BufferPushUInt16(c->vm, buffer, op1);
|
||||||
} else if (form->count == 3) {
|
} else if (form->count == 3) {
|
||||||
if (op2 < 0) CError(c, "This operator does not take 2 arguments.");
|
if (op2 < 0) CError(c, "This operator does not take 2 arguments.");
|
||||||
BufferPushUInt16(c->vm, buffer, op2);
|
BufferPushUInt16(c->vm, buffer, op2);
|
||||||
} else {
|
} else {
|
||||||
if (opn < 0) CError(c, "This operator does not take n arguments.");
|
if (opn < 0) CError(c, "This operator does not take n arguments.");
|
||||||
BufferPushUInt16(c->vm, buffer, opn);
|
BufferPushUInt16(c->vm, buffer, opn);
|
||||||
BufferPushUInt16(c->vm, buffer, form->count - 1);
|
BufferPushUInt16(c->vm, buffer, form->count - 1);
|
||||||
}
|
|
||||||
if (opts.canChoose) {
|
|
||||||
ret.isTemp = 1;
|
|
||||||
ret.index = CompilerGetLocal(c, scope);
|
|
||||||
} else {
|
|
||||||
ret.isDud = opts.target;
|
|
||||||
}
|
|
||||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
|
||||||
}
|
}
|
||||||
|
if (opts.canChoose) {
|
||||||
|
ret.isTemp = 1;
|
||||||
|
ret.index = CompilerGetLocal(c, scope);
|
||||||
|
} else {
|
||||||
|
ret.index = opts.target;
|
||||||
|
}
|
||||||
|
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, reverseOperands ? 2 : 1);
|
CompilerTrackerFree(c, scope, &tracker, reverseOperands ? 2 : 1);
|
||||||
return ret;
|
return ret;
|
||||||
@ -650,10 +650,9 @@ static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
/* Helper function to return nil from a form that doesn't do anything
|
/* Helper function to return nil from a form that doesn't do anything
|
||||||
(set, while, etc.) */
|
(set, while, etc.) */
|
||||||
static Slot CompileReturnNil(Compiler * c, FormOptions opts) {
|
static Slot CompileReturnNil(Compiler * c, FormOptions opts) {
|
||||||
Slot ret;
|
Slot ret = SlotDefault();
|
||||||
/* If we need a return value, we just use nil */
|
/* If we need a return value, we just use nil */
|
||||||
if (opts.isTail) {
|
if (opts.isTail) {
|
||||||
ret.isDud = 1;
|
|
||||||
BufferPushUInt16(c->vm, c->buffer, VM_OP_RTN);
|
BufferPushUInt16(c->vm, c->buffer, VM_OP_RTN);
|
||||||
} else if (!opts.canDrop) {
|
} else if (!opts.canDrop) {
|
||||||
ret.isDud = 0;
|
ret.isDud = 0;
|
||||||
@ -726,16 +725,15 @@ 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) || opts.canDrop;
|
subOpts.canDrop = (current != form->count - 1);
|
||||||
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) {
|
||||||
CompilerDropSlot(c, scope, ret);
|
CompilerDropSlot(c, scope, ret);
|
||||||
ret.isDud = 1;
|
|
||||||
}
|
}
|
||||||
++current;
|
++current;
|
||||||
}
|
}
|
||||||
if (opts.isTail && !ret.isDud) {
|
if (opts.isTail) {
|
||||||
CompilerReturnSlot(c, ret);
|
CompilerReturnSlot(c, ret);
|
||||||
ret.isDud = 1;
|
ret.isDud = 1;
|
||||||
}
|
}
|
||||||
@ -791,9 +789,9 @@ static Slot CompileFunction(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
/* Do nothing if we can drop. This is rather pointless from
|
/* Do nothing if we can drop. This is rather pointless from
|
||||||
* a language point of view, but it doesn't hurt. */
|
* a language point of view, but it doesn't hurt. */
|
||||||
if (opts.canDrop) {
|
if (opts.canDrop) {
|
||||||
ret.isDud = 1;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret.isDud = 0;
|
||||||
subScope = CompilerPushScope(c, 0);
|
subScope = CompilerPushScope(c, 0);
|
||||||
/* Check for function documentation - for now just ignore. */
|
/* Check for function documentation - for now just ignore. */
|
||||||
if (form->data[current].type == TYPE_STRING) {
|
if (form->data[current].type == TYPE_STRING) {
|
||||||
@ -862,11 +860,10 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
/* Configure options for bodies of if */
|
/* Configure options for bodies of if */
|
||||||
if (opts.isTail) {
|
if (opts.isTail) {
|
||||||
ret.isDud = 1;
|
ret.isDud = 1;
|
||||||
resOpts.isTail = 1;
|
|
||||||
} else if (opts.canDrop) {
|
} else if (opts.canDrop) {
|
||||||
ret.isDud = 1;
|
|
||||||
resOpts.canDrop = 1;
|
resOpts.canDrop = 1;
|
||||||
} else if (opts.canChoose) {
|
} else if (opts.canChoose) {
|
||||||
|
ret.isDud = 0;
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
ret.index = CompilerGetLocal(c, scope);
|
ret.index = CompilerGetLocal(c, scope);
|
||||||
resOpts.target = ret.index;
|
resOpts.target = ret.index;
|
||||||
@ -896,10 +893,7 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
BufferPushUInt32(c->vm, buffer, 0);
|
BufferPushUInt32(c->vm, buffer, 0);
|
||||||
/* Compile true path */
|
/* Compile true path */
|
||||||
left = CompileValue(c, resOpts, form->data[2]);
|
left = CompileValue(c, resOpts, form->data[2]);
|
||||||
if (opts.isTail && !left.isDud) {
|
if (opts.isTail) CompilerReturnSlot(c, left);
|
||||||
BufferPushUInt16(c->vm, buffer, VM_OP_RET);
|
|
||||||
BufferPushUInt16(c->vm, buffer, left.index);
|
|
||||||
}
|
|
||||||
CompilerDropSlot(c, scope, left);
|
CompilerDropSlot(c, scope, left);
|
||||||
/* If we need to jump again, do so */
|
/* If we need to jump again, do so */
|
||||||
if (!opts.isTail && form->count == 4) {
|
if (!opts.isTail && form->count == 4) {
|
||||||
@ -917,10 +911,7 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
/* Compile false path */
|
/* Compile false path */
|
||||||
if (form->count == 4) {
|
if (form->count == 4) {
|
||||||
right = CompileValue(c, resOpts, form->data[3]);
|
right = CompileValue(c, resOpts, form->data[3]);
|
||||||
if (opts.isTail && !right.isDud) {
|
if (opts.isTail) CompilerReturnSlot(c, right);
|
||||||
BufferPushUInt16(c->vm, buffer, VM_OP_RET);
|
|
||||||
BufferPushUInt16(c->vm, buffer, right.index);
|
|
||||||
}
|
|
||||||
CompilerDropSlot(c, scope, right);
|
CompilerDropSlot(c, scope, right);
|
||||||
}
|
}
|
||||||
/* Set the jump length */
|
/* Set the jump length */
|
||||||
@ -992,9 +983,9 @@ static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
return CompileNonReferenceType(c, opts, x);
|
return CompileNonReferenceType(c, opts, x);
|
||||||
}
|
}
|
||||||
if (opts.canDrop) {
|
if (opts.canDrop) {
|
||||||
ret.isDud = 1;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret.isDud = 0;
|
||||||
if (opts.canChoose) {
|
if (opts.canChoose) {
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
ret.index = CompilerGetLocal(c, scope);
|
ret.index = CompilerGetLocal(c, scope);
|
||||||
@ -1158,9 +1149,9 @@ static Slot CompileForm(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
}
|
}
|
||||||
/* If this is in tail position do a tail call. */
|
/* If this is in tail position do a tail call. */
|
||||||
if (opts.isTail) {
|
if (opts.isTail) {
|
||||||
ret.isDud = 1;
|
|
||||||
BufferPushUInt16(c->vm, buffer, VM_OP_TCL);
|
BufferPushUInt16(c->vm, buffer, VM_OP_TCL);
|
||||||
} else {
|
} else {
|
||||||
|
ret.isDud = 0;
|
||||||
BufferPushUInt16(c->vm, buffer, VM_OP_CAL);
|
BufferPushUInt16(c->vm, buffer, VM_OP_CAL);
|
||||||
if (opts.canDrop) {
|
if (opts.canDrop) {
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
@ -1216,6 +1207,14 @@ void CompilerAddGlobal(Compiler * c, const char * name, Value x) {
|
|||||||
ArrayPush(c->vm, c->env, x);
|
ArrayPush(c->vm, c->env, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register a global c function for the compilation environment. */
|
||||||
|
void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) {
|
||||||
|
Value func;
|
||||||
|
func.type = TYPE_CFUNCTION;
|
||||||
|
func.data.cfunction = f;
|
||||||
|
return CompilerAddGlobal(c, name, func);
|
||||||
|
}
|
||||||
|
|
||||||
/* Compile interface. Returns a function that evaluates the
|
/* Compile interface. Returns a function that evaluates the
|
||||||
* given AST. Returns NULL if there was an error during compilation. */
|
* given AST. Returns NULL if there was an error during compilation. */
|
||||||
Func * CompilerCompile(Compiler * c, Value form) {
|
Func * CompilerCompile(Compiler * c, Value form) {
|
||||||
|
@ -9,6 +9,9 @@ void CompilerInit(Compiler * c, VM * vm);
|
|||||||
/* Register a global for the compilation environment. */
|
/* Register a global for the compilation environment. */
|
||||||
void CompilerAddGlobal(Compiler * c, const char * name, Value x);
|
void CompilerAddGlobal(Compiler * c, const char * name, Value x);
|
||||||
|
|
||||||
|
/* Register a global c function for the compilation environment. */
|
||||||
|
void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f);
|
||||||
|
|
||||||
/* Compile a function that evaluates the given form. */
|
/* Compile a function that evaluates the given form. */
|
||||||
Func * CompilerCompile(Compiler * c, Value form);
|
Func * CompilerCompile(Compiler * c, Value form);
|
||||||
|
|
||||||
|
200
disasm.c
Normal file
200
disasm.c
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#include "disasm.h"
|
||||||
|
|
||||||
|
/* Width of padded opcode names */
|
||||||
|
#define OP_WIDTH 20
|
||||||
|
|
||||||
|
/* Print various register and arguments to instructions */
|
||||||
|
static void dasmPrintSlot(FILE * out, uint16_t index) { fprintf(out, "%d ", index); }
|
||||||
|
static void dasmPrintI16(FILE * out, int16_t number) { fprintf(out, "#%d ", number); }
|
||||||
|
static void dasmPrintI32(FILE * out, int32_t number) { fprintf(out, "#%d ", number); }
|
||||||
|
static void dasmPrintF64(FILE * out, double number) { fprintf(out, "#%f ", number); }
|
||||||
|
static void dasmPrintLiteral(FILE * out, uint16_t index) { fprintf(out, "(%d) ", index); }
|
||||||
|
static void dasmPrintUpValue(FILE * out, uint16_t level, uint16_t index) {
|
||||||
|
fprintf(out, "<%d, %d> ", level, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print the name of the argument but pad it */
|
||||||
|
static void dasmPrintArg(FILE * out, const char * name) {
|
||||||
|
uint32_t i = 0;
|
||||||
|
char c;
|
||||||
|
while ((c = *name++)) {
|
||||||
|
fputc(c, out);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
for (; i < OP_WIDTH; ++i)
|
||||||
|
fputc(' ', out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print instructions that take a fixed number of arguments */
|
||||||
|
static uint32_t dasmPrintFixedOp(FILE * out, const uint16_t * current,
|
||||||
|
const char * name, uint32_t size) {
|
||||||
|
uint32_t i;
|
||||||
|
dasmPrintArg(out, name);
|
||||||
|
for (i = 1; i <= size; ++i) {
|
||||||
|
dasmPrintSlot(out, current[i]);
|
||||||
|
}
|
||||||
|
return size + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print instructions that take a variable number of arguments */
|
||||||
|
static uint32_t dasmPrintVarArgOp(FILE * out, const uint16_t * current,
|
||||||
|
const char * name, uint32_t extra) {
|
||||||
|
uint32_t i, argCount;
|
||||||
|
dasmPrintArg(out, name);
|
||||||
|
argCount = current[1];
|
||||||
|
for (i = 0; i < extra; ++i) {
|
||||||
|
dasmPrintSlot(out, current[i + 2]);
|
||||||
|
}
|
||||||
|
fprintf(out, "| "); /* Argument separator */
|
||||||
|
for (i = 0; i < argCount; ++i) {
|
||||||
|
dasmPrintSlot(out, current[i + extra + 2]);
|
||||||
|
}
|
||||||
|
return argCount + extra + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print the disassembly for a function definition */
|
||||||
|
void dasmFuncDef(FILE * out, FuncDef * def) {
|
||||||
|
dasm(out, def->byteCode, def->byteCodeLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print the disassembly for a function */
|
||||||
|
void dasmFunc(FILE * out, Func * f) {
|
||||||
|
dasm(out, f->def->byteCode, f->def->byteCodeLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disassemble some bytecode and display it as opcode + arguments assembly */
|
||||||
|
void dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
||||||
|
uint16_t *current = byteCode;
|
||||||
|
uint16_t *end = byteCode + len;
|
||||||
|
|
||||||
|
while (current < end) {
|
||||||
|
switch (*current) {
|
||||||
|
case VM_OP_ADD:
|
||||||
|
current += dasmPrintFixedOp(out, current, "add", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_SUB:
|
||||||
|
current += dasmPrintFixedOp(out, current, "sub", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_MUL:
|
||||||
|
current += dasmPrintFixedOp(out, current, "mul", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_DIV:
|
||||||
|
current += dasmPrintFixedOp(out, current, "div", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_NOT:
|
||||||
|
current += dasmPrintFixedOp(out, current, "not", 2);
|
||||||
|
break;
|
||||||
|
case VM_OP_LD0:
|
||||||
|
current += dasmPrintFixedOp(out, current, "load0", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_LD1:
|
||||||
|
current += dasmPrintFixedOp(out, current, "load1", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_FLS:
|
||||||
|
current += dasmPrintFixedOp(out, current, "loadFalse", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_TRU:
|
||||||
|
current += dasmPrintFixedOp(out, current, "loadTrue", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_NIL:
|
||||||
|
current += dasmPrintFixedOp(out, current, "loadNil", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_I16:
|
||||||
|
dasmPrintArg(out, "loadInt16");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintI16(out, ((int16_t *)current)[2]);
|
||||||
|
current += 3;
|
||||||
|
break;
|
||||||
|
case VM_OP_UPV:
|
||||||
|
dasmPrintArg(out, "loadUpValue");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintUpValue(out, current[2], current[3]);
|
||||||
|
current += 4;
|
||||||
|
break;
|
||||||
|
case VM_OP_JIF:
|
||||||
|
dasmPrintArg(out, "jumpIf");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintI32(out, ((int32_t *)(current + 2))[0]);
|
||||||
|
current += 4;
|
||||||
|
break;
|
||||||
|
case VM_OP_JMP:
|
||||||
|
dasmPrintArg(out, "jump");
|
||||||
|
dasmPrintI32(out, ((int32_t *)(current + 1))[0]);
|
||||||
|
current += 3;
|
||||||
|
break;
|
||||||
|
case VM_OP_CAL:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "call", 2);
|
||||||
|
break;
|
||||||
|
case VM_OP_RET:
|
||||||
|
current += dasmPrintFixedOp(out, current, "return", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_SUV:
|
||||||
|
dasmPrintArg(out, "setUpValue");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintUpValue(out, current[2], current[3]);
|
||||||
|
current += 4;
|
||||||
|
break;
|
||||||
|
case VM_OP_CST:
|
||||||
|
dasmPrintArg(out, "loadLiteral");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintLiteral(out, current[2]);
|
||||||
|
current += 3;
|
||||||
|
break;
|
||||||
|
case VM_OP_I32:
|
||||||
|
dasmPrintArg(out, "loadInt32");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintI32(out, ((int32_t *)(current + 2))[0]);
|
||||||
|
current += 4;
|
||||||
|
break;
|
||||||
|
case VM_OP_F64:
|
||||||
|
dasmPrintArg(out, "loadFloat64");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintF64(out, ((double *)(current + 2))[0]);
|
||||||
|
current += 6;
|
||||||
|
break;
|
||||||
|
case VM_OP_MOV:
|
||||||
|
current += dasmPrintFixedOp(out, current, "move", 2);
|
||||||
|
break;
|
||||||
|
case VM_OP_CLN:
|
||||||
|
dasmPrintArg(out, "makeClosure");
|
||||||
|
dasmPrintSlot(out, current[1]);
|
||||||
|
dasmPrintLiteral(out, current[2]);
|
||||||
|
current += 3;
|
||||||
|
break;
|
||||||
|
case VM_OP_EQL:
|
||||||
|
current += dasmPrintFixedOp(out, current, "equals", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_LTN:
|
||||||
|
current += dasmPrintFixedOp(out, current, "lessThan", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_LTE:
|
||||||
|
current += dasmPrintFixedOp(out, current, "lessThanEquals", 3);
|
||||||
|
break;
|
||||||
|
case VM_OP_ARR:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "array", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_DIC:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "dictionary", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_TCL:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "tailCall", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_ADM:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "addMultiple", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_SBM:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "subMultiple", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_MUM:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "mulMultiple", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_DVM:
|
||||||
|
current += dasmPrintVarArgOp(out, current, "divMultiple", 1);
|
||||||
|
break;
|
||||||
|
case VM_OP_RTN:
|
||||||
|
current += dasmPrintFixedOp(out, current, "returnNil", 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(out, "\n");
|
||||||
|
}
|
||||||
|
}
|
17
disasm.h
Normal file
17
disasm.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef disasm_h_INCLUDED
|
||||||
|
#define disasm_h_INCLUDED
|
||||||
|
|
||||||
|
#include "datatypes.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Print disassembly for a given funciton */
|
||||||
|
void dasm(FILE * out, uint16_t * byteCode, uint32_t len);
|
||||||
|
|
||||||
|
/* Print the disassembly for a function definition */
|
||||||
|
void dasmFuncDef(FILE * out, FuncDef * def);
|
||||||
|
|
||||||
|
/* Print the disassembly for a function */
|
||||||
|
void dasmFunc(FILE * out, Func * f);
|
||||||
|
|
||||||
|
#endif // disasm_h_INCLUDED
|
||||||
|
|
20
main.c
20
main.c
@ -6,15 +6,19 @@
|
|||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include "disasm.h"
|
||||||
|
|
||||||
Value print(VM * vm) {
|
Value print(VM * vm) {
|
||||||
uint32_t i;
|
uint32_t i, j, count;
|
||||||
Value nil;
|
Value nil;
|
||||||
uint8_t * string = ValueToString(vm, VMGetArg(vm, 0));
|
count = VMCountArgs(vm);
|
||||||
uint32_t len = VStringSize(string);
|
for (j = 0; j < count; ++j) {
|
||||||
for (i = 0; i < len; ++i)
|
uint8_t * string = ValueToString(vm, VMGetArg(vm, j));
|
||||||
fputc(string[i], stdout);
|
uint32_t len = VStringSize(string);
|
||||||
fputc('\n', stdout);
|
for (i = 0; i < len; ++i)
|
||||||
|
fputc(string[i], stdout);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
nil.type = TYPE_NIL;
|
nil.type = TYPE_NIL;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@ -69,6 +73,7 @@ void debugRepl() {
|
|||||||
|
|
||||||
/* Try to compile generated AST */
|
/* Try to compile generated AST */
|
||||||
CompilerInit(&c, &vm);
|
CompilerInit(&c, &vm);
|
||||||
|
CompilerAddGlobalCFunc(&c, "print", print);
|
||||||
func = CompilerCompile(&c, p.value);
|
func = CompilerCompile(&c, p.value);
|
||||||
|
|
||||||
/* Check for compilation errors */
|
/* Check for compilation errors */
|
||||||
@ -79,6 +84,9 @@ void debugRepl() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print the function that will be executed */
|
||||||
|
dasmFunc(stdout, func);
|
||||||
|
|
||||||
/* Execute function */
|
/* Execute function */
|
||||||
VMLoad(&vm, func);
|
VMLoad(&vm, func);
|
||||||
if (VMStart(&vm)) {
|
if (VMStart(&vm)) {
|
||||||
|
17
vm.c
17
vm.c
@ -9,7 +9,7 @@
|
|||||||
#define VMOpArg(i) (VMArg(vm->pc[(i)]))
|
#define VMOpArg(i) (VMArg(vm->pc[(i)]))
|
||||||
|
|
||||||
static const char OOM[] = "Out of memory";
|
static const char OOM[] = "Out of memory";
|
||||||
static const char NO_UPVALUE[] = "Out of memory";
|
static const char NO_UPVALUE[] = "No upvalue";
|
||||||
static const char EXPECTED_FUNCTION[] = "Expected function";
|
static const char EXPECTED_FUNCTION[] = "Expected function";
|
||||||
static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number";
|
static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number";
|
||||||
static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number";
|
static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number";
|
||||||
@ -272,7 +272,10 @@ static void VMThreadPop(VM * vm, Array * thread) {
|
|||||||
static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) {
|
static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) {
|
||||||
FuncEnv * env;
|
FuncEnv * env;
|
||||||
Value * stack;
|
Value * stack;
|
||||||
while (fn && level--)
|
if (!level) {
|
||||||
|
return vm->base + index;
|
||||||
|
}
|
||||||
|
while (fn && --level)
|
||||||
fn = fn->parent;
|
fn = fn->parent;
|
||||||
VMAssert(vm, fn, NO_UPVALUE);
|
VMAssert(vm, fn, NO_UPVALUE);
|
||||||
env = fn->env;
|
env = fn->env;
|
||||||
@ -452,6 +455,7 @@ int VMStart(VM * vm) {
|
|||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
if ((n = setjmp(vm->jump))) {
|
if ((n = setjmp(vm->jump))) {
|
||||||
|
vm->lock = 0;
|
||||||
/* Good return */
|
/* Good return */
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -569,7 +573,7 @@ int VMStart(VM * vm) {
|
|||||||
vRet = VMOpArg(1);
|
vRet = VMOpArg(1);
|
||||||
*vRet = *GetUpValue(vm, callee.data.func, vm->pc[2], vm->pc[3]);
|
*vRet = *GetUpValue(vm, callee.data.func, vm->pc[2], vm->pc[3]);
|
||||||
vm->pc += 4;
|
vm->pc += 4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_JIF: /* Jump If */
|
case VM_OP_JIF: /* Jump If */
|
||||||
@ -679,7 +683,7 @@ int VMStart(VM * vm) {
|
|||||||
uint32_t i = 3;
|
uint32_t i = 3;
|
||||||
uint32_t kvs = vm->pc[2];
|
uint32_t kvs = vm->pc[2];
|
||||||
Dictionary * dict = DictNew(vm, kvs);
|
Dictionary * dict = DictNew(vm, kvs);
|
||||||
kvs = kvs * 2 + 3;
|
kvs = kvs + 3;
|
||||||
while (i < kvs) {
|
while (i < kvs) {
|
||||||
v1 = VMOpArg(i++);
|
v1 = VMOpArg(i++);
|
||||||
v2 = VMOpArg(i++);
|
v2 = VMOpArg(i++);
|
||||||
@ -759,6 +763,11 @@ void VMSetArg(VM * vm, uint16_t index, Value x) {
|
|||||||
*VMArg(index) = x;
|
*VMArg(index) = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the size of the VMStack */
|
||||||
|
uint16_t VMCountArgs(VM * vm) {
|
||||||
|
return FrameSize(vm->thread);
|
||||||
|
}
|
||||||
|
|
||||||
#undef VMOpArg
|
#undef VMOpArg
|
||||||
#undef VMArg
|
#undef VMArg
|
||||||
|
|
||||||
|
3
vm.h
3
vm.h
@ -50,4 +50,7 @@ Value VMGetArg(VM * vm, uint16_t index);
|
|||||||
/* Put a value on the stack */
|
/* Put a value on the stack */
|
||||||
void VMSetArg(VM * vm, uint16_t index, Value x);
|
void VMSetArg(VM * vm, uint16_t index, Value x);
|
||||||
|
|
||||||
|
/* Get the number of arguments on the stack */
|
||||||
|
uint16_t VMCountArgs(VM * vm);
|
||||||
|
|
||||||
#endif /* end of include guard: VM_H_C4OZU8CQ */
|
#endif /* end of include guard: VM_H_C4OZU8CQ */
|
||||||
|
Loading…
Reference in New Issue
Block a user