From 5ec6e46f1a44e86331c5aca2700de93bd67f82e8 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 22 Feb 2017 18:19:46 -0500 Subject: [PATCH] Add error handling and try catch expression. --- compile.c | 130 ++++++++++++++++++++++++++++++++++++++++++++-------- datatypes.h | 10 ++-- disasm.c | 14 +++++- main.c | 12 +++-- vm.c | 30 ++++++++++-- vm.h | 5 +- 6 files changed, 167 insertions(+), 34 deletions(-) diff --git a/compile.c b/compile.c index bebf46ad..723d5755 100644 --- a/compile.c +++ b/compile.c @@ -86,7 +86,6 @@ static FormOptions form_options_default() { /* 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 * functions. */ -BUFFER_DEFINE(u32, uint32_t) BUFFER_DEFINE(i32, int32_t) BUFFER_DEFINE(number, GstNumber) BUFFER_DEFINE(u16, uint16_t) @@ -254,7 +253,7 @@ static Slot compiler_realize_slot(GstCompiler *c, Slot 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 */ 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) { GstValue x; uint16_t target; - if (sym.type != GST_STRING) { + if (sym.type != GST_STRING) c_error(c, "Expected symbol"); - } target = compiler_get_local(c, scope); x.type = GST_NUMBER; 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 * 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; GstBuffer *buffer = c->buffer; 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 */ static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) { - GstScope * scope = c->tail; - GstBuffer * buffer = c->buffer; + GstScope *scope = c->tail; + GstBuffer *buffer = c->buffer; uint32_t current = 1; uint32_t i; uint32_t sizeBefore; /* Size of buffer before compiling function */ - GstScope * subGstScope; - GstArray * params; + GstScope *subGstScope; + GstArray *params; FormOptions subOpts = form_options_default(); Slot ret; if (opts.resultUnused) return nil_slot(); @@ -763,8 +761,8 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) { /* Branching special */ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) { - GstScope * scope = c->tail; - GstBuffer * buffer = c->buffer; + GstScope *scope = c->tail; + GstBuffer *buffer = c->buffer; FormOptions condOpts = opts; FormOptions branchOpts = opts; 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 * length later */ countAtJumpIf = buffer->count; - /* Write jump instruction. Will later be replaced with correct index. */ - 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); + buffer->count += sizeof(int32_t) + 2 * sizeof(uint16_t); /* Configure branch form options */ branchOpts.canChoose = 0; 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 (form->count == 4) { countAtJump = buffer->count; - gst_buffer_push_u16(c->vm, buffer, GST_OP_JMP); - gst_buffer_push_u32(c->vm, buffer, 0); + buffer->count += sizeof(int32_t) + sizeof(uint16_t); } } compiler_drop_slot(c, scope, left); @@ -813,7 +807,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) { buffer->count = countAtJumpIf; 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, (countAfterFirstBranch - countAtJumpIf) / 2); + gst_buffer_push_i32(c->vm, buffer, (countAfterFirstBranch - countAtJumpIf) / 2); buffer->count = countAfterFirstBranch; /* Compile false path */ if (form->count == 4) { @@ -828,7 +822,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) { countAfterFirstBranch = buffer->count; buffer->count = countAtJump; 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; } if (opts.isTail) @@ -836,6 +830,84 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstArray *form) { 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 */ static Slot compile_while(GstCompiler *c, FormOptions opts, GstArray *form) { Slot cond; @@ -891,7 +963,7 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, GstArray *form) { Slot ret; uint16_t literalIndex; 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]; if (x.type == GST_NIL || x.type == GST_BOOLEAN || @@ -969,6 +1041,16 @@ static SpecialFormHelper get_special(GstArray *form) { 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': { if (gst_string_length(name) == 3 && @@ -1038,6 +1120,14 @@ static SpecialFormHelper get_special(GstArray *form) { } } break; + case 't': + { + if (gst_string_length(name) == 3 && + name[1] == 'r' && + name[2] == 'y') { + return compile_try; + } + } case 'w': { if (gst_string_length(name) == 5 && diff --git a/datatypes.h b/datatypes.h index 4be1f744..ff6f7543 100644 --- a/datatypes.h +++ b/datatypes.h @@ -160,9 +160,10 @@ struct Gst { GstValue *base; GstStackFrame *frame; /* Return state */ - const char *error; + const char *crash; jmp_buf jump; - GstValue ret; /* Returned value from VMStart */ + GstValue error; + GstValue ret; /* Returned value from VMStart. Also holds errors. */ /* Object definitions */ GstValue metas[GST_OBJECT]; }; @@ -233,7 +234,10 @@ enum GstOpCode { GST_OP_DVM, /* 0x001f */ GST_OP_RTN, /* 0x0020 */ 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 diff --git a/disasm.c b/disasm.c index e4eedf7e..898d6b38 100644 --- a/disasm.c +++ b/disasm.c @@ -194,13 +194,25 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) { current += dasm_varg_op(out, current, "divMultiple", 1); break; case GST_OP_RTN: - current += dasm_fixed_op(out, current, "returnNil", 0); + current += dasm_fixed_op(out, current, "returnNil", 0); break; case GST_OP_GET: current += dasm_fixed_op(out, current, "get", 3); break; case GST_OP_SET: 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; } fprintf(out, "\n"); diff --git a/main.c b/main.c index 2adba306..0f1028c8 100644 --- a/main.c +++ b/main.c @@ -87,14 +87,20 @@ void debugRepl() { } /* Print asm */ - printf("\n"); +/* printf("\n"); gst_dasm_function(stdout, func.data.function); - printf("\n"); + printf("\n");*/ /* Execute function */ gst_load(&vm, func); 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; buffer[0] = 0; continue; diff --git a/vm.c b/vm.c index 092acfce..f78683cd 100644 --- a/vm.c +++ b/vm.c @@ -298,6 +298,7 @@ void gst_collect(Gst *vm) { gst_mark(vm, &thread); } gst_mark(vm, &vm->ret); + gst_mark(vm, &vm->error); gst_sweep(vm); vm->nextCollection = 0; } @@ -471,13 +472,15 @@ int gst_start(Gst *vm) { return 0; } else if (n == 2) { /* Error. Handling TODO. */ - do { - if (vm->thread->count == 0) - return n; + while (vm->thread->count && !vm->frame->errorJump) { thread_pop(vm); - } while (!vm->frame->errorJump); + } + if (vm->thread->count == 0) + return n; /* Jump to the error location */ vm->pc = vm->frame->errorJump; + /* Set error */ + vm->base[vm->frame->errorSlot] = vm->error; vm->lock = 0; } else { /* Crash. just return */ @@ -737,6 +740,22 @@ int gst_start(Gst *vm) { vm->pc += 4; 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: gst_error(vm, "unknown opcode"); break; @@ -770,10 +789,11 @@ uint16_t gst_count_args(Gst *vm) { /* Initialize the VM */ void gst_init(Gst *vm) { vm->ret.type = GST_NIL; + vm->error.type = GST_NIL; vm->base = NULL; vm->frame = NULL; vm->pc = NULL; - vm->error = NULL; + vm->crash = NULL; /* Garbage collection */ vm->blocks = NULL; vm->nextCollection = 0; diff --git a/vm.h b/vm.h index ad8b8b5c..1eba976b 100644 --- a/vm.h +++ b/vm.h @@ -2,15 +2,16 @@ #define VM_H_C4OZU8CQ #include "datatypes.h" +#include "value.h" /* Exit from the VM normally */ #define gst_exit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1)) /* 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. */ -#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 */ #define gst_assert(vm, cond, e) do \