1
0
mirror of https://github.com/janet-lang/janet synced 2025-01-12 16:40:27 +00:00

Add error handling and try catch expression.

This commit is contained in:
Calvin Rose 2017-02-22 18:19:46 -05:00
parent 6521ee69bd
commit 5ec6e46f1a
6 changed files with 167 additions and 34 deletions

130
compile.c
View File

@ -86,7 +86,6 @@ static FormOptions form_options_default() {
/* Create some helpers that allows us to push more than just raw bytes /* Create some helpers that allows us to push more than just raw bytes
* to the byte buffer. This helps us create the byte code for the compiled * to the byte buffer. This helps us create the byte code for the compiled
* functions. */ * functions. */
BUFFER_DEFINE(u32, uint32_t)
BUFFER_DEFINE(i32, int32_t) BUFFER_DEFINE(i32, int32_t)
BUFFER_DEFINE(number, GstNumber) BUFFER_DEFINE(number, GstNumber)
BUFFER_DEFINE(u16, uint16_t) BUFFER_DEFINE(u16, uint16_t)
@ -254,7 +253,7 @@ static Slot compiler_realize_slot(GstCompiler *c, Slot slot) {
} }
/* Helper to get a nil slot */ /* Helper to get a nil slot */
static Slot nil_slot() { Slot ret; ret.isNil = 1; return ret; } static Slot nil_slot() { Slot ret; ret.isNil = 1; ret.hasReturned = 0; return ret; }
/* Writes all of the slots in the tracker to the compiler */ /* Writes all of the slots in the tracker to the compiler */
static void compiler_tracker_write(GstCompiler *c, SlotTracker *tracker, int reverse) { static void compiler_tracker_write(GstCompiler *c, SlotTracker *tracker, int reverse) {
@ -320,9 +319,8 @@ static uint16_t compiler_add_literal(GstCompiler *c, GstScope *scope, GstValue x
static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValue sym) { static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValue sym) {
GstValue x; GstValue x;
uint16_t target; uint16_t target;
if (sym.type != GST_STRING) { if (sym.type != GST_STRING)
c_error(c, "Expected symbol"); c_error(c, "Expected symbol");
}
target = compiler_get_local(c, scope); target = compiler_get_local(c, scope);
x.type = GST_NUMBER; x.type = GST_NUMBER;
x.data.number = target; x.data.number = target;
@ -676,7 +674,7 @@ static Slot compile_block(GstCompiler *c, FormOptions opts, GstArray *form, uint
/* Extract the last n bytes from the buffer and use them to construct /* Extract the last n bytes from the buffer and use them to construct
* a function definition. */ * a function definition. */
static GstFuncDef * compiler_gen_funcdef(GstCompiler *c, uint32_t lastNBytes, uint32_t arity) { static GstFuncDef *compiler_gen_funcdef(GstCompiler *c, uint32_t lastNBytes, uint32_t arity) {
GstScope *scope = c->tail; GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer; GstBuffer *buffer = c->buffer;
GstFuncDef *def = gst_alloc(c->vm, sizeof(GstFuncDef)); GstFuncDef *def = gst_alloc(c->vm, sizeof(GstFuncDef));
@ -710,13 +708,13 @@ static GstFuncDef * compiler_gen_funcdef(GstCompiler *c, uint32_t lastNBytes, ui
/* Compile a function from a function literal */ /* Compile a function from a function literal */
static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) {
GstScope * scope = c->tail; GstScope *scope = c->tail;
GstBuffer * buffer = c->buffer; GstBuffer *buffer = c->buffer;
uint32_t current = 1; uint32_t current = 1;
uint32_t i; uint32_t i;
uint32_t sizeBefore; /* Size of buffer before compiling function */ uint32_t sizeBefore; /* Size of buffer before compiling function */
GstScope * subGstScope; GstScope *subGstScope;
GstArray * params; GstArray *params;
FormOptions subOpts = form_options_default(); FormOptions subOpts = form_options_default();
Slot ret; Slot ret;
if (opts.resultUnused) return nil_slot(); if (opts.resultUnused) return nil_slot();
@ -763,8 +761,8 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) {
/* Branching special */ /* Branching special */
static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) {
GstScope * scope = c->tail; GstScope *scope = c->tail;
GstBuffer * buffer = c->buffer; GstBuffer *buffer = c->buffer;
FormOptions condOpts = opts; FormOptions condOpts = opts;
FormOptions branchOpts = opts; FormOptions branchOpts = opts;
Slot left, right, condition; Slot left, right, condition;
@ -788,10 +786,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) {
/* Mark where the buffer is now so we can write the jump /* Mark where the buffer is now so we can write the jump
* length later */ * length later */
countAtJumpIf = buffer->count; countAtJumpIf = buffer->count;
/* Write jump instruction. Will later be replaced with correct index. */ buffer->count += sizeof(int32_t) + 2 * sizeof(uint16_t);
gst_buffer_push_u16(c->vm, buffer, GST_OP_JIF);
gst_buffer_push_u16(c->vm, buffer, condition.index);
gst_buffer_push_u32(c->vm, buffer, 0);
/* Configure branch form options */ /* Configure branch form options */
branchOpts.canChoose = 0; branchOpts.canChoose = 0;
branchOpts.target = condition.index; branchOpts.target = condition.index;
@ -803,8 +798,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) {
/* If we need to jump again, do so */ /* If we need to jump again, do so */
if (form->count == 4) { if (form->count == 4) {
countAtJump = buffer->count; countAtJump = buffer->count;
gst_buffer_push_u16(c->vm, buffer, GST_OP_JMP); buffer->count += sizeof(int32_t) + sizeof(uint16_t);
gst_buffer_push_u32(c->vm, buffer, 0);
} }
} }
compiler_drop_slot(c, scope, left); compiler_drop_slot(c, scope, left);
@ -813,7 +807,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) {
buffer->count = countAtJumpIf; buffer->count = countAtJumpIf;
gst_buffer_push_u16(c->vm, buffer, GST_OP_JIF); gst_buffer_push_u16(c->vm, buffer, GST_OP_JIF);
gst_buffer_push_u16(c->vm, buffer, condition.index); gst_buffer_push_u16(c->vm, buffer, condition.index);
gst_buffer_push_u32(c->vm, buffer, (countAfterFirstBranch - countAtJumpIf) / 2); gst_buffer_push_i32(c->vm, buffer, (countAfterFirstBranch - countAtJumpIf) / 2);
buffer->count = countAfterFirstBranch; buffer->count = countAfterFirstBranch;
/* Compile false path */ /* Compile false path */
if (form->count == 4) { if (form->count == 4) {
@ -828,7 +822,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) {
countAfterFirstBranch = buffer->count; countAfterFirstBranch = buffer->count;
buffer->count = countAtJump; buffer->count = countAtJump;
gst_buffer_push_u16(c->vm, buffer, GST_OP_JMP); gst_buffer_push_u16(c->vm, buffer, GST_OP_JMP);
gst_buffer_push_u32(c->vm, buffer, (countAfterFirstBranch - countAtJump) / 2); gst_buffer_push_i32(c->vm, buffer, (countAfterFirstBranch - countAtJump) / 2);
buffer->count = countAfterFirstBranch; buffer->count = countAfterFirstBranch;
} }
if (opts.isTail) if (opts.isTail)
@ -836,6 +830,84 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) {
return condition; return condition;
} }
/* Special to throw an error */
static Slot compile_error(GstCompiler *c, FormOptions opts, GstArray *form) {
GstBuffer *buffer = c->buffer;
Slot ret;
GstValue x;
if (form->count != 2)
c_error(c, "error takes exactly 1 argument");
x = form->data[1];
ret = compiler_realize_slot(c, compile_value(c, opts, x));
gst_buffer_push_u16(c->vm, buffer, GST_OP_ERR);
gst_buffer_push_u16(c->vm, buffer, ret.index);
return nil_slot();
}
/* Try catch special */
static Slot compile_try(GstCompiler *c, FormOptions opts, GstArray *form) {
GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer;
Slot body;
uint16_t errorIndex;
uint32_t countAtTry, countTemp, countAtJump;
/* Check argument count */
if (form->count < 3 || form->count > 4)
c_error(c, "try takes either 2 or 3 arguments");
/* Check for symbol to bind error to */
if (form->data[1].type != GST_STRING)
c_error(c, "expected symbol at start of try");
/* Add subscope for error variable */
GstScope *subScope = compiler_push_scope(c, 1);
errorIndex = compiler_declare_symbol(c, subScope, form->data[1]);
/* Leave space for try instruction */
countAtTry = buffer->count;
buffer->count += sizeof(uint32_t) + 2 * sizeof(uint16_t);
/* Compile the body */
body = compile_value(c, opts, form->data[2]);
if (opts.isTail) {
compiler_return(c, body);
} else {
/* If we need to jump over the catch, do so */
if (form->count == 4) {
countAtJump = buffer->count;
buffer->count += sizeof(int32_t) + sizeof(uint16_t);
}
}
/* Reinsert try jump with correct index */
countTemp = buffer->count;
buffer->count = countAtTry;
gst_buffer_push_u16(c->vm, buffer, GST_OP_TRY);
gst_buffer_push_u16(c->vm, buffer, errorIndex);
gst_buffer_push_i32(c->vm, buffer, (countTemp - countAtTry) / 2);
buffer->count = countTemp;
/* Compile catch path */
if (form->count == 4) {
Slot catch;
countAtJump = buffer->count;
catch = compile_value(c, opts, form->data[3]);
if (opts.isTail) compiler_return(c, catch);
compiler_drop_slot(c, scope, catch);
} else if (opts.isTail) {
compiler_return(c, nil_slot());
}
/* Reset the second jump length */
if (!opts.isTail && form->count == 4) {
countTemp = buffer->count;
buffer->count = countAtJump;
gst_buffer_push_u16(c->vm, buffer, GST_OP_JMP);
gst_buffer_push_i32(c->vm, buffer, (countTemp - countAtJump) / 2);
buffer->count = countTemp;
}
/* Untry */
gst_buffer_push_u16(c->vm, buffer, GST_OP_UTY);
/* Pop the error scope */
compiler_pop_scope(c);
if (opts.isTail)
body.hasReturned = 1;
return body;
}
/* While special */ /* While special */
static Slot compile_while(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_while(GstCompiler *c, FormOptions opts, GstArray *form) {
Slot cond; Slot cond;
@ -891,7 +963,7 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, GstArray *form) {
Slot ret; Slot ret;
uint16_t literalIndex; uint16_t literalIndex;
if (form->count != 2) if (form->count != 2)
c_error(c, "Quote takes exactly 1 argument."); c_error(c, "quote takes exactly 1 argument");
GstValue x = form->data[1]; GstValue x = form->data[1];
if (x.type == GST_NIL || if (x.type == GST_NIL ||
x.type == GST_BOOLEAN || x.type == GST_BOOLEAN ||
@ -969,6 +1041,16 @@ static SpecialFormHelper get_special(GstArray *form) {
return compile_array; return compile_array;
} }
} }
case 'e':
{
if (gst_string_length(name) == 5 &&
name[1] == 'r' &&
name[2] == 'r' &&
name[3] == 'o' &&
name[4] == 'r') {
return compile_error;
}
}
case 'g': case 'g':
{ {
if (gst_string_length(name) == 3 && if (gst_string_length(name) == 3 &&
@ -1038,6 +1120,14 @@ static SpecialFormHelper get_special(GstArray *form) {
} }
} }
break; break;
case 't':
{
if (gst_string_length(name) == 3 &&
name[1] == 'r' &&
name[2] == 'y') {
return compile_try;
}
}
case 'w': case 'w':
{ {
if (gst_string_length(name) == 5 && if (gst_string_length(name) == 5 &&

View File

@ -160,9 +160,10 @@ struct Gst {
GstValue *base; GstValue *base;
GstStackFrame *frame; GstStackFrame *frame;
/* Return state */ /* Return state */
const char *error; const char *crash;
jmp_buf jump; jmp_buf jump;
GstValue ret; /* Returned value from VMStart */ GstValue error;
GstValue ret; /* Returned value from VMStart. Also holds errors. */
/* Object definitions */ /* Object definitions */
GstValue metas[GST_OBJECT]; GstValue metas[GST_OBJECT];
}; };
@ -233,7 +234,10 @@ enum GstOpCode {
GST_OP_DVM, /* 0x001f */ GST_OP_DVM, /* 0x001f */
GST_OP_RTN, /* 0x0020 */ GST_OP_RTN, /* 0x0020 */
GST_OP_SET, /* 0x0021 */ GST_OP_SET, /* 0x0021 */
GST_OP_GET /* 0x0022 */ GST_OP_GET, /* 0x0022 */
GST_OP_ERR, /* 0x0023 */
GST_OP_TRY, /* 0x0024 */
GST_OP_UTY /* 0x0025 */
}; };
#endif #endif

View File

@ -201,6 +201,18 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
break; break;
case GST_OP_SET: case GST_OP_SET:
current += dasm_fixed_op(out, current, "set", 3); current += dasm_fixed_op(out, current, "set", 3);
break;
case GST_OP_ERR:
current += dasm_fixed_op(out, current, "error", 1);
break;
case GST_OP_TRY:
dasm_print_arg(out, "try");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, *(int32_t *)(current + 2));
current += 4;
break;
case GST_OP_UTY:
current += dasm_fixed_op(out, current, "untry", 0);
break; break;
} }
fprintf(out, "\n"); fprintf(out, "\n");

12
main.c
View File

@ -87,14 +87,20 @@ void debugRepl() {
} }
/* Print asm */ /* Print asm */
printf("\n"); /* printf("\n");
gst_dasm_function(stdout, func.data.function); gst_dasm_function(stdout, func.data.function);
printf("\n"); printf("\n");*/
/* Execute function */ /* Execute function */
gst_load(&vm, func); gst_load(&vm, func);
if (gst_start(&vm)) { if (gst_start(&vm)) {
printf("VM error: %s\n", vm.error); if (vm.crash) {
printf("VM crash: %s\n", vm.crash);
} else {
printf("VM error: ");
string_put(gst_to_string(&vm, vm.error));
printf("\n");
}
reader = buffer; reader = buffer;
buffer[0] = 0; buffer[0] = 0;
continue; continue;

28
vm.c
View File

@ -298,6 +298,7 @@ void gst_collect(Gst *vm) {
gst_mark(vm, &thread); gst_mark(vm, &thread);
} }
gst_mark(vm, &vm->ret); gst_mark(vm, &vm->ret);
gst_mark(vm, &vm->error);
gst_sweep(vm); gst_sweep(vm);
vm->nextCollection = 0; vm->nextCollection = 0;
} }
@ -471,13 +472,15 @@ int gst_start(Gst *vm) {
return 0; return 0;
} else if (n == 2) { } else if (n == 2) {
/* Error. Handling TODO. */ /* Error. Handling TODO. */
do { while (vm->thread->count && !vm->frame->errorJump) {
thread_pop(vm);
}
if (vm->thread->count == 0) if (vm->thread->count == 0)
return n; return n;
thread_pop(vm);
} while (!vm->frame->errorJump);
/* Jump to the error location */ /* Jump to the error location */
vm->pc = vm->frame->errorJump; vm->pc = vm->frame->errorJump;
/* Set error */
vm->base[vm->frame->errorSlot] = vm->error;
vm->lock = 0; vm->lock = 0;
} else { } else {
/* Crash. just return */ /* Crash. just return */
@ -737,6 +740,22 @@ int gst_start(Gst *vm) {
vm->pc += 4; vm->pc += 4;
break; break;
case GST_OP_ERR:
vm->error = vm->base[vm->pc[1]];
longjmp(vm->jump, 2);
break;
case GST_OP_TRY:
vm->frame->errorSlot = vm->pc[1];
vm->frame->errorJump = vm->pc + *(uint32_t *)(vm->pc + 2);
vm->pc += 4;
break;
case GST_OP_UTY:
vm->frame->errorJump = NULL;
vm->pc++;
break;
default: default:
gst_error(vm, "unknown opcode"); gst_error(vm, "unknown opcode");
break; break;
@ -770,10 +789,11 @@ uint16_t gst_count_args(Gst *vm) {
/* Initialize the VM */ /* Initialize the VM */
void gst_init(Gst *vm) { void gst_init(Gst *vm) {
vm->ret.type = GST_NIL; vm->ret.type = GST_NIL;
vm->error.type = GST_NIL;
vm->base = NULL; vm->base = NULL;
vm->frame = NULL; vm->frame = NULL;
vm->pc = NULL; vm->pc = NULL;
vm->error = NULL; vm->crash = NULL;
/* Garbage collection */ /* Garbage collection */
vm->blocks = NULL; vm->blocks = NULL;
vm->nextCollection = 0; vm->nextCollection = 0;

5
vm.h
View File

@ -2,15 +2,16 @@
#define VM_H_C4OZU8CQ #define VM_H_C4OZU8CQ
#include "datatypes.h" #include "datatypes.h"
#include "value.h"
/* Exit from the VM normally */ /* Exit from the VM normally */
#define gst_exit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1)) #define gst_exit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1))
/* Bail from the VM with an error. */ /* Bail from the VM with an error. */
#define gst_error(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 2)) #define gst_error(vm, e) ((vm)->error = gst_load_cstring((vm), (e)), longjmp((vm)->jump, 2))
/* Crash. Not catchable, unlike error. */ /* Crash. Not catchable, unlike error. */
#define gst_crash(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 3)) #define gst_crash(vm, e) ((vm)->crash = (e), longjmp((vm)->jump, 3))
/* Error if the condition is false */ /* Error if the condition is false */
#define gst_assert(vm, cond, e) do \ #define gst_assert(vm, cond, e) do \