From eb2f50e4569f4afcb5f88f8ce3baa38e9e0f19ec Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 5 Jan 2021 22:27:59 -0600 Subject: [PATCH] Added CObjError, cosmoV_throw(), pcall(), and cosmoV_printError() Errors are now handled very differently, parser errors and VM errors are now treated the same. When cosmoV_error is called, cosmoV_throw is also called, which formats the error object and sets the panic state. state->error now points to the latest CObjError when state->panic is true. To get a nice formatted Objection message, use cosmoV_printError() and pass the state->error. pcall() was added to the standard base library. When called, the first argument passed is called with the subsequent arguments given. If the call completed successfully, `true`,`nil` is returned. However when an error occurs during the call, `false`,`` is returned. Simply print the `` to retrieve the error string. --- src/cbaselib.c | 31 ++++++++++--- src/cbaselib.h | 4 ++ src/clex.c | 1 + src/cmem.c | 14 +++++- src/cobj.c | 31 +++++++++++++ src/cobj.h | 13 ++++++ src/cosmo.h | 1 + src/cparse.c | 30 ++++++++---- src/cstate.c | 2 + src/cstate.h | 1 + src/cvm.c | 122 ++++++++++++++++++++++++++++++------------------- src/cvm.h | 4 ++ src/main.c | 6 ++- 13 files changed, 195 insertions(+), 65 deletions(-) diff --git a/src/cbaselib.c b/src/cbaselib.c index baf6e66..b24f953 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -21,7 +21,7 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) { } if (!IS_BOOLEAN(args[0])) { - cosmoV_error(state, "assert() expected (), got (%s!)", cosmoV_typeStr(args[0])); + cosmoV_typeError(state, "assert()", "", "%s", cosmoV_typeStr(args[0])); return 0; } @@ -43,13 +43,28 @@ int cosmoB_type(CState *state, int nargs, CValue *args) { return 1; // 1 return value, the type string :D } +int cosmoB_pcall(CState *state, int nargs, CValue *args) { + if (nargs < 1) { + cosmoV_error(state, "pcall() expected at least 1 argument!"); + return 0; + } + + // call the passed callable + COSMOVMRESULT res = cosmoV_pcall(state, nargs-1, 1); + + // insert false before the result + cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK)); + cosmoV_printStack(state); + return 2; +} + // ================================================================ [STRING.*] ================================================================ // string.sub int cosmoB_sSub(CState *state, int nargs, CValue *args) { if (nargs == 2) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { - cosmoV_error(state, "string.sub() expected (, ), got (%s, %s)!", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "string.sub()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -58,14 +73,14 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { // make sure we stay within memory if (indx < 0 || indx >= str->length) { - cosmoV_error(state, "string.sub() Expected index to be 0-%d, got %d!", str->length, indx); + cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length, indx); return 0; } cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); } else if (nargs == 3) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) { - cosmoV_error(state, "string.sub() expected (, , ), got (%s, %s, %s)!", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); + cosmoV_typeError(state, "string.sub()", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); return 0; } @@ -75,7 +90,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { // make sure we stay within memory if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) { - cosmoV_error(state, "string.sub() Expected subbed string goes out of bounds, max length is %d!", str->length); + cosmoV_error(state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length); return 0; } @@ -101,6 +116,10 @@ void cosmoB_loadLibrary(CState *state) { cosmoV_pushString(state, "type"); cosmoV_pushCFunction(state, cosmoB_type); + // pcall + cosmoV_pushString(state, "pcall"); + cosmoV_pushCFunction(state, cosmoB_pcall); + // string. cosmoV_pushString(state, "string"); @@ -112,7 +131,7 @@ void cosmoB_loadLibrary(CState *state) { // string. // register these all to the global table - cosmoV_register(state, 4); + cosmoV_register(state, 5); } // ================================================================ [DEBUG] ================================================================ diff --git a/src/cbaselib.h b/src/cbaselib.h index e7ca5a6..1f13229 100644 --- a/src/cbaselib.h +++ b/src/cbaselib.h @@ -8,5 +8,9 @@ COSMO_API void cosmoB_loadDebug(CState *state); COSMO_API int cosmoB_print(CState *state, int nargs, CValue *args); COSMO_API int cosmoB_assert(CState *state, int nargs, CValue *args); COSMO_API int cosmoB_type(CState *state, int nargs, CValue *args); +COSMO_API int cosmoB_pcall(CState *state, int nargs, CValue *args); + +#define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \ + cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__); #endif diff --git a/src/clex.c b/src/clex.c index 0cd8c5e..230d177 100644 --- a/src/clex.c +++ b/src/clex.c @@ -205,6 +205,7 @@ CToken parseString(CLexState *state) { case 'r': case 'n': appendBuffer(state, '\n'); break; case 't': appendBuffer(state, '\t'); break; case '\\': appendBuffer(state, '\\'); break; + case '"': appendBuffer(state, '"'); break; default: { if (isNumerical(peek(state))) { char *numStart = state->currentChar; diff --git a/src/cmem.c b/src/cmem.c index 9ca929e..92df1ba 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -120,6 +120,16 @@ void blackenObject(CState *state, CObj *obj) { markObject(state, (CObj*)method->obj); break; } + case COBJ_ERROR: { + CObjError *err = (CObjError*)obj; + markValue(state, err->err); + + // mark callframes + for (int i = 0; i < err->frameCount; i++) + markObject(state, (CObj*)err->frames[i].closure); + + break; + } case COBJ_CLOSURE: { CObjClosure *closure = (CObjClosure*)obj; markObject(state, (CObj*)closure->function); @@ -232,8 +242,10 @@ void markRoots(CState *state) { // mark the user defined roots markUserRoots(state); - // mark our proto object + // mark other misc. internally reserved objects markObject(state, (CObj*)state->protoObj); + markObject(state, (CObj*)state->error); + traceGrays(state); } diff --git a/src/cobj.c b/src/cobj.c index 0001cec..9f6dcc3 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -3,6 +3,7 @@ #include "cobj.h" #include "cmem.h" #include "cvm.h" +#include "clex.h" #include @@ -75,6 +76,12 @@ void cosmoO_free(CState *state, CObj* obj) { cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug break; } + case COBJ_ERROR: { + CObjError *err = (CObjError*)obj; + cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount); + cosmoM_free(state, CObjError, obj); + break; + } case COBJ_CLOSURE: { CObjClosure* closure = (CObjClosure*)obj; cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount); @@ -142,6 +149,21 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) { return cfunc; } +CObjError *cosmoO_newError(CState *state, CValue err) { + CObjError *cerror = (CObjError*)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); + cerror->err = err; + cerror->frameCount = state->frameCount; + + // allocate the callframe + cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount); + + // clone the call frame + for (int i = 0; i < state->frameCount; i++) + cerror->frames[i] = state->callFrame[i]; + + return cerror; +} + CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj) { CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); method->func = cosmoV_newObj(func); @@ -251,6 +273,11 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) cosmoV_pushString(state, va_arg(args, char *)); break; } + case 't': { // CToken * + CToken *token = va_arg(args, CToken *); + cosmoV_pushLString(state, token->start, token->length); + break; + } default: { char temp[2]; temp[0] = '%'; @@ -420,6 +447,10 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) { return cosmoO_copyString(state, buf, sz); } } + case COBJ_ERROR: { + CObjError *err = (CObjError*)obj; + return cosmoV_toString(state, err->err); + } case COBJ_DICT: { char buf[64]; int sz = sprintf(buf, " %p", (void*)obj) + 1; // +1 for the null character diff --git a/src/cobj.h b/src/cobj.h index ccdd21a..3348f32 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -8,6 +8,7 @@ #include "ctable.h" typedef struct CState CState; +typedef struct CCallFrame CCallFrame; typedef uint32_t cosmo_Flag; typedef enum { @@ -16,6 +17,7 @@ typedef enum { COBJ_DICT, // dictionary COBJ_FUNCTION, COBJ_CFUNCTION, + COBJ_ERROR, // internal use COBJ_METHOD, COBJ_CLOSURE, @@ -43,6 +45,15 @@ typedef struct CObjString { uint32_t hash; // for hashtable lookup } CObjString; +typedef struct CObjError { + CommonHeader; // "is a" CObj + bool parserError; // if true, cosmoV_printError will format the error to the lexer + int frameCount; + int line; // reserved for parser errors + CValue err; // error string + CCallFrame *frames; +} CObjError; + typedef struct CObjObject { CommonHeader; // "is a" CObj cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.) @@ -129,6 +140,7 @@ CObjObject *cosmoO_newObject(CState *state); CObjDict *cosmoO_newDictionary(CState *state); CObjFunction *cosmoO_newFunction(CState *state); CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func); +CObjError *cosmoO_newError(CState *state, CValue err); CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj); CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj); CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func); @@ -161,6 +173,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin '%d' - decimal numbers [int] '%f' - floating point [double] '%s' - strings [const char*] + '%t' - cosmo tokens [CToken *] */ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args); diff --git a/src/cosmo.h b/src/cosmo.h index 4b7db1e..664cc60 100644 --- a/src/cosmo.h +++ b/src/cosmo.h @@ -36,6 +36,7 @@ typedef struct CObjUpval CObjUpval; typedef struct CObjFunction CObjFunction; typedef struct CObjCFunction CObjCFunction; typedef struct CObjMethod CObjMethod; +typedef struct CObjError CObjError; typedef struct CObjObject CObjObject; typedef struct CObjClosure CObjClosure; diff --git a/src/cparse.c b/src/cparse.c index 972cb0c..0ba4b77 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -140,29 +140,41 @@ static void freeParseState(CParseState *pstate) { cosmoL_freeLexState(pstate->state, pstate->lex); } -static void errorAt(CParseState *pstate, CToken *token, const char * msg) { +static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args) { if (pstate->hadError) return; - fprintf(stderr, "[line %d] Objection", token->line); if (token->type == TOKEN_EOF) { - fprintf(stderr, " at end"); + cosmoV_pushString(pstate->state, "At end: "); } else if (!(token->type == TOKEN_ERROR)) { - fprintf(stderr, " at '%.*s'", token->length, token->start); + cosmoV_pushFString(pstate->state, "At '%t'", token); // this is why the '%t' exist in cosmoO_pushFString lol } - printf(": \n\t%s\n", msg); + cosmoO_pushVFString(pstate->state, format, args); + + cosmoV_concat(pstate->state, 2); // concats the two strings together + + CObjError *err = cosmoV_throw(pstate->state); + err->line = token->line; + err->parserError = true; + pstate->hadError = true; pstate->panic = true; } -static void errorAtCurrent(CParseState *pstate, const char *msg) { - errorAt(pstate, &pstate->current, msg); +static void errorAtCurrent(CParseState *pstate, const char *format, ...) { + va_list args; + va_start(args, format); + errorAt(pstate, &pstate->current, format, args); + va_end(args); } -static void error(CParseState *pstate, const char *msg) { - errorAt(pstate, &pstate->previous, msg); +static void error(CParseState *pstate, const char *format, ...) { + va_list args; + va_start(args, format); + errorAt(pstate, &pstate->previous, format, args); + va_end(args); } static void advance(CParseState *pstate) { diff --git a/src/cstate.c b/src/cstate.c index a3de789..8be1f51 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -31,7 +31,9 @@ CState *cosmoV_newState() { state->top = state->stack; state->frameCount = 0; state->openUpvalues = NULL; + state->protoObj = NULL; + state->error = NULL; cosmoT_initTable(state, &state->strings, 8); // init string table cosmoT_initTable(state, &state->globals, 8); // init global table diff --git a/src/cstate.h b/src/cstate.h index 9b540fc..1791734 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -37,6 +37,7 @@ typedef struct CState { int frameCount; CObjObject *protoObj; // start met obj for all objects (NULL by default) + CObjError *error; // NULL, unless panic is true CObj *objects; // tracks all of our allocated objects CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found diff --git a/src/cvm.c b/src/cvm.c index 376a154..4b174d3 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -15,43 +15,78 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) { va_end(args); } -void cosmoV_error(CState *state, const char *format, ...) { - if (state->panic) - return; - state->panic = true; +// inserts val at state->top - indx - 1, moving everything else up +COSMO_API void cosmo_insert(CState *state, int indx, CValue val) { + StkPtr tmp = cosmoV_getTop(state, indx); + // moves everything up + for (StkPtr i = state->top; i > tmp; i--) + *i = *(i-1); + + *tmp = val; + state->top++; +} + +void cosmoV_printError(CState *state, CObjError *err) { // print stack trace - for (int i = 0; i < state->frameCount; i++) { - CCallFrame *frame = &state->callFrame[i]; + for (int i = 0; i < err->frameCount; i++) { + CCallFrame *frame = &err->frames[i]; CObjFunction *function = frame->closure->function; CChunk *chunk = &function->chunk; int line = chunk->lineInfo[frame->pc - chunk->buf - 1]; - if (i == state->frameCount - 1) { // it's the last call frame, prepare for the objection to be printed + if (i == err->frameCount - 1 && !err->parserError) // it's the last call frame (and not a parser error), prepare for the objection to be printed fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line); - if (function->name == NULL) { // unnamed chunk - fprintf(stderr, "%s\n\t", UNNAMEDCHUNK); - } else { - fprintf(stderr, "%.*s()\n\t", function->name->length, function->name->str); - } - } else { + else fprintf(stderr, "[line %d] in ", line); - if (function->name == NULL) { // unnamed chunk - fprintf(stderr, "%s\n", UNNAMEDCHUNK); - } else { - fprintf(stderr, "%.*s()\n", function->name->length, function->name->str); - } + + if (function->name == NULL) { // unnamed chunk + fprintf(stderr, "%s\n", UNNAMEDCHUNK); + } else { + fprintf(stderr, "%.*s()\n", function->name->length, function->name->str); } } + if (err->parserError) + fprintf(stderr, "Objection while parsing on [line %d]\n", err->line); + + // finally, print the error message + CObjString *errString = cosmoV_toString(state, err->err); + printf("\t%.*s\n", errString->length, errString->str); +} + +/* + takes value on top of the stack and wraps an CObjError around it, state->error is set to that value + the value on the stack is *expected* to be a string, but not required, so + yes, this means you could throw a nil value if you really wanted too.. +*/ +CObjError* cosmoV_throw(CState *state) { + StkPtr temp = cosmoV_getTop(state, 0); + + CObjError *error = cosmoO_newError(state, *temp); + state->error = error; + state->panic = true; + + cosmoV_pop(state); // pops thrown value off the stack + return error; +} + +void cosmoV_error(CState *state, const char *format, ...) { + if (state->panic) + return; + + // i set panic before calling cosmoO_pushVFString, since that can also call cosmoV_error + state->panic = true; + + // format the error string and push it onto the stack va_list args; va_start(args, format); - CObjString *errString = cosmoO_pushVFString(state, format, args); + cosmoO_pushVFString(state, format, args); va_end(args); - printf("%.*s\n", errString->length, errString->str); - //cosmoV_printStack(state); + // throw the error onto the state + cosmoV_throw(state); } CObjUpval *captureUpvalue(CState *state, CValue *local) { @@ -144,7 +179,7 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nre calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return. returns: - false: state paniced during C Function, stack is preserved so that the error is left on the stack + false: state paniced during C Function, error is at state->error true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset */ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) { @@ -155,9 +190,6 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre int nres = cfunc(state, args, savedBase + 1); cosmoM_unfreezeGC(state); - // if the state paniced during the c function, return false and leave the stack preserved (so the error is left on the stack) - if (state->panic) - return false; // caller function wasn't expecting this many return values, cap it if (nres > nresults) @@ -168,6 +200,10 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre state->top = savedBase + offset; // set stack + // if the state paniced during the c function, return false + if (state->panic) + return false; + // push the return value back onto the stack memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack state->top += nres; // and make sure to move state->top to match @@ -183,7 +219,7 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return. returns: - false: state paniced, stack is preserved so that the error is left on the stack + false: state paniced, error is at state->error true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset */ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) { @@ -215,8 +251,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, // execute int nres = cosmoV_execute(state); - if (nres == -1) // panic state - return false; if (nres > nresults) // caller function wasn't expecting this many return values, cap it nres = nresults; @@ -226,6 +260,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, // pop the callframe and return results :) popCallFrame(state, offset); + + if (state->panic) // panic state + return false; // push the return values back onto the stack memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack @@ -247,6 +284,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) switch (cosmoV_readObj(func)->type) { case COBJ_CLOSURE: return rawCall(state, cosmoV_readClosure(func), args, nresults, offset); + case COBJ_CFUNCTION: + return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); case COBJ_METHOD: { CObjMethod *method = (CObjMethod*)cosmoV_readObj(func); return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1); @@ -277,8 +316,6 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) } break; } - case COBJ_CFUNCTION: - return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); default: cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(func)); return false; @@ -295,31 +332,20 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nre return callCValue(state, func, args+1, nresults, offset); } -// wraps cosmoV_call in a protected state, error string will be pushed onto the stack if function call failed, else return values are passed +// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call failed, else return values are passed COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { StkPtr base = cosmoV_getTop(state, args); - COSMOVMRESULT res = cosmoV_call(state, args, nresults); - - if (res != COSMOVM_OK) { + if (!callCValue(state, *base, args, nresults, 0)) { // restore panic state state->panic = false; + state->error = NULL; - // error is on the stack, grab it - StkPtr err = cosmoV_getTop(state, 0); - state->top = base; - - if (nresults > 0) { - cosmoV_pushValue(state, *err); - - // push the nils to fill up the expected return values - for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value - cosmoV_pushValue(state, cosmoV_newNil()); - } - } + cosmoV_pushValue(state, cosmoV_newObj(state->error)); + return COSMOVM_RUNTIME_ERR; } - return res; + return COSMOVM_OK; } /* @@ -327,7 +353,7 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { returns: COSMOVM_OK: callable object exited normally - COSMOVM_RUNTIME_ERR: an error occurred, the error will be at the top of the stack (the rest of the stack is junk) + COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error */ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args diff --git a/src/cvm.h b/src/cvm.h index 2720922..898aade 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -14,12 +14,16 @@ typedef enum { // args = # of pass parameters, nresults = # of expected results COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults); +COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults); COSMO_API void cosmoV_makeObject(CState *state, int pairs); COSMO_API void cosmoV_makeDictionary(CState *state, int pairs); COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val); COSMO_API void cosmoV_concat(CState *state, int vals); COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); +COSMO_API void cosmoV_printError(CState *state, CObjError *err); +COSMO_API CObjError* cosmoV_throw(CState *state); COSMO_API void cosmoV_error(CState *state, const char *format, ...); +COSMO_API void cosmo_insert(CState *state, int indx, CValue val); // nice to have wrappers diff --git a/src/main.c b/src/main.c index f2ceced..1f650e9 100644 --- a/src/main.c +++ b/src/main.c @@ -40,8 +40,12 @@ static void interpret(CState *state, const char *script, const char *mod) { COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected if (res == COSMOVM_RUNTIME_ERR) - state->panic = false; // so our repl isn't broken + cosmoV_printError(state, state->error); + } else { + cosmoV_printError(state, state->error); } + + state->panic = false; // so our repl isn't broken } static void repl() {