mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 00:00:10 +00:00
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`,`<error>` is returned. Simply print the `<error>` to retrieve the error string.
This commit is contained in:
parent
417a1f15f1
commit
eb2f50e456
@ -21,7 +21,7 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
if (!IS_BOOLEAN(args[0])) {
|
||||
cosmoV_error(state, "assert() expected (<boolean>), got (%s!)", cosmoV_typeStr(args[0]));
|
||||
cosmoV_typeError(state, "assert()", "<boolean>", "%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 (<string>, <number>), got (%s, %s)!", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%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 (<string>, <number>, <number>), got (%s, %s, %s)!", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%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] ================================================================
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
14
src/cmem.c
14
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);
|
||||
}
|
||||
|
||||
|
31
src/cobj.c
31
src/cobj.c
@ -3,6 +3,7 @@
|
||||
#include "cobj.h"
|
||||
#include "cmem.h"
|
||||
#include "cvm.h"
|
||||
#include "clex.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -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, "<dict> %p", (void*)obj) + 1; // +1 for the null character
|
||||
|
13
src/cobj.h
13
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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
30
src/cparse.c
30
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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
122
src/cvm.c
122
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user