mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10:05 +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])) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +43,28 @@ int cosmoB_type(CState *state, int nargs, CValue *args) {
|
|||||||
return 1; // 1 return value, the type string :D
|
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.*] ================================================================
|
||||||
|
|
||||||
// string.sub
|
// string.sub
|
||||||
int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||||
if (nargs == 2) {
|
if (nargs == 2) {
|
||||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,14 +73,14 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
// make sure we stay within memory
|
// make sure we stay within memory
|
||||||
if (indx < 0 || indx >= str->length) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
||||||
} else if (nargs == 3) {
|
} else if (nargs == 3) {
|
||||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +90,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
// make sure we stay within memory
|
// make sure we stay within memory
|
||||||
if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +116,10 @@ void cosmoB_loadLibrary(CState *state) {
|
|||||||
cosmoV_pushString(state, "type");
|
cosmoV_pushString(state, "type");
|
||||||
cosmoV_pushCFunction(state, cosmoB_type);
|
cosmoV_pushCFunction(state, cosmoB_type);
|
||||||
|
|
||||||
|
// pcall
|
||||||
|
cosmoV_pushString(state, "pcall");
|
||||||
|
cosmoV_pushCFunction(state, cosmoB_pcall);
|
||||||
|
|
||||||
// string.
|
// string.
|
||||||
cosmoV_pushString(state, "string");
|
cosmoV_pushString(state, "string");
|
||||||
|
|
||||||
@ -112,7 +131,7 @@ void cosmoB_loadLibrary(CState *state) {
|
|||||||
// string.
|
// string.
|
||||||
|
|
||||||
// register these all to the global table
|
// register these all to the global table
|
||||||
cosmoV_register(state, 4);
|
cosmoV_register(state, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [DEBUG] ================================================================
|
// ================================================================ [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_print(CState *state, int nargs, CValue *args);
|
||||||
COSMO_API int cosmoB_assert(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_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
|
#endif
|
||||||
|
@ -205,6 +205,7 @@ CToken parseString(CLexState *state) {
|
|||||||
case 'r': case 'n': appendBuffer(state, '\n'); break;
|
case 'r': case 'n': appendBuffer(state, '\n'); break;
|
||||||
case 't': appendBuffer(state, '\t'); break;
|
case 't': appendBuffer(state, '\t'); break;
|
||||||
case '\\': appendBuffer(state, '\\'); break;
|
case '\\': appendBuffer(state, '\\'); break;
|
||||||
|
case '"': appendBuffer(state, '"'); break;
|
||||||
default: {
|
default: {
|
||||||
if (isNumerical(peek(state))) {
|
if (isNumerical(peek(state))) {
|
||||||
char *numStart = state->currentChar;
|
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);
|
markObject(state, (CObj*)method->obj);
|
||||||
break;
|
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: {
|
case COBJ_CLOSURE: {
|
||||||
CObjClosure *closure = (CObjClosure*)obj;
|
CObjClosure *closure = (CObjClosure*)obj;
|
||||||
markObject(state, (CObj*)closure->function);
|
markObject(state, (CObj*)closure->function);
|
||||||
@ -232,8 +242,10 @@ void markRoots(CState *state) {
|
|||||||
// mark the user defined roots
|
// mark the user defined roots
|
||||||
markUserRoots(state);
|
markUserRoots(state);
|
||||||
|
|
||||||
// mark our proto object
|
// mark other misc. internally reserved objects
|
||||||
markObject(state, (CObj*)state->protoObj);
|
markObject(state, (CObj*)state->protoObj);
|
||||||
|
markObject(state, (CObj*)state->error);
|
||||||
|
|
||||||
traceGrays(state);
|
traceGrays(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
31
src/cobj.c
31
src/cobj.c
@ -3,6 +3,7 @@
|
|||||||
#include "cobj.h"
|
#include "cobj.h"
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
#include "cvm.h"
|
#include "cvm.h"
|
||||||
|
#include "clex.h"
|
||||||
|
|
||||||
#include <string.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
|
cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug
|
||||||
break;
|
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: {
|
case COBJ_CLOSURE: {
|
||||||
CObjClosure* closure = (CObjClosure*)obj;
|
CObjClosure* closure = (CObjClosure*)obj;
|
||||||
cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount);
|
cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount);
|
||||||
@ -142,6 +149,21 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
|||||||
return cfunc;
|
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 *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj) {
|
||||||
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
||||||
method->func = cosmoV_newObj(func);
|
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 *));
|
cosmoV_pushString(state, va_arg(args, char *));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 't': { // CToken *
|
||||||
|
CToken *token = va_arg(args, CToken *);
|
||||||
|
cosmoV_pushLString(state, token->start, token->length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
char temp[2];
|
char temp[2];
|
||||||
temp[0] = '%';
|
temp[0] = '%';
|
||||||
@ -420,6 +447,10 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
|||||||
return cosmoO_copyString(state, buf, sz);
|
return cosmoO_copyString(state, buf, sz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case COBJ_ERROR: {
|
||||||
|
CObjError *err = (CObjError*)obj;
|
||||||
|
return cosmoV_toString(state, err->err);
|
||||||
|
}
|
||||||
case COBJ_DICT: {
|
case COBJ_DICT: {
|
||||||
char buf[64];
|
char buf[64];
|
||||||
int sz = sprintf(buf, "<dict> %p", (void*)obj) + 1; // +1 for the null character
|
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"
|
#include "ctable.h"
|
||||||
|
|
||||||
typedef struct CState CState;
|
typedef struct CState CState;
|
||||||
|
typedef struct CCallFrame CCallFrame;
|
||||||
typedef uint32_t cosmo_Flag;
|
typedef uint32_t cosmo_Flag;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -16,6 +17,7 @@ typedef enum {
|
|||||||
COBJ_DICT, // dictionary
|
COBJ_DICT, // dictionary
|
||||||
COBJ_FUNCTION,
|
COBJ_FUNCTION,
|
||||||
COBJ_CFUNCTION,
|
COBJ_CFUNCTION,
|
||||||
|
COBJ_ERROR,
|
||||||
// internal use
|
// internal use
|
||||||
COBJ_METHOD,
|
COBJ_METHOD,
|
||||||
COBJ_CLOSURE,
|
COBJ_CLOSURE,
|
||||||
@ -43,6 +45,15 @@ typedef struct CObjString {
|
|||||||
uint32_t hash; // for hashtable lookup
|
uint32_t hash; // for hashtable lookup
|
||||||
} CObjString;
|
} 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 {
|
typedef struct CObjObject {
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.)
|
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);
|
CObjDict *cosmoO_newDictionary(CState *state);
|
||||||
CObjFunction *cosmoO_newFunction(CState *state);
|
CObjFunction *cosmoO_newFunction(CState *state);
|
||||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
|
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_newMethod(CState *state, CObjClosure *func, CObjObject *obj);
|
||||||
CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj);
|
CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj);
|
||||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
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]
|
'%d' - decimal numbers [int]
|
||||||
'%f' - floating point [double]
|
'%f' - floating point [double]
|
||||||
'%s' - strings [const char*]
|
'%s' - strings [const char*]
|
||||||
|
'%t' - cosmo tokens [CToken *]
|
||||||
*/
|
*/
|
||||||
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args);
|
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 CObjFunction CObjFunction;
|
||||||
typedef struct CObjCFunction CObjCFunction;
|
typedef struct CObjCFunction CObjCFunction;
|
||||||
typedef struct CObjMethod CObjMethod;
|
typedef struct CObjMethod CObjMethod;
|
||||||
|
typedef struct CObjError CObjError;
|
||||||
typedef struct CObjObject CObjObject;
|
typedef struct CObjObject CObjObject;
|
||||||
typedef struct CObjClosure CObjClosure;
|
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);
|
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)
|
if (pstate->hadError)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fprintf(stderr, "[line %d] Objection", token->line);
|
|
||||||
|
|
||||||
if (token->type == TOKEN_EOF) {
|
if (token->type == TOKEN_EOF) {
|
||||||
fprintf(stderr, " at end");
|
cosmoV_pushString(pstate->state, "At end: ");
|
||||||
} else if (!(token->type == TOKEN_ERROR)) {
|
} 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->hadError = true;
|
||||||
pstate->panic = true;
|
pstate->panic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void errorAtCurrent(CParseState *pstate, const char *msg) {
|
static void errorAtCurrent(CParseState *pstate, const char *format, ...) {
|
||||||
errorAt(pstate, &pstate->current, msg);
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
errorAt(pstate, &pstate->current, format, args);
|
||||||
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void error(CParseState *pstate, const char *msg) {
|
static void error(CParseState *pstate, const char *format, ...) {
|
||||||
errorAt(pstate, &pstate->previous, msg);
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
errorAt(pstate, &pstate->previous, format, args);
|
||||||
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void advance(CParseState *pstate) {
|
static void advance(CParseState *pstate) {
|
||||||
|
@ -31,7 +31,9 @@ CState *cosmoV_newState() {
|
|||||||
state->top = state->stack;
|
state->top = state->stack;
|
||||||
state->frameCount = 0;
|
state->frameCount = 0;
|
||||||
state->openUpvalues = NULL;
|
state->openUpvalues = NULL;
|
||||||
|
|
||||||
state->protoObj = NULL;
|
state->protoObj = NULL;
|
||||||
|
state->error = NULL;
|
||||||
|
|
||||||
cosmoT_initTable(state, &state->strings, 8); // init string table
|
cosmoT_initTable(state, &state->strings, 8); // init string table
|
||||||
cosmoT_initTable(state, &state->globals, 8); // init global table
|
cosmoT_initTable(state, &state->globals, 8); // init global table
|
||||||
|
@ -37,6 +37,7 @@ typedef struct CState {
|
|||||||
int frameCount;
|
int frameCount;
|
||||||
|
|
||||||
CObjObject *protoObj; // start met obj for all objects (NULL by default)
|
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 *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
|
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
|
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found
|
||||||
|
112
src/cvm.c
112
src/cvm.c
@ -15,43 +15,78 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoV_error(CState *state, const char *format, ...) {
|
// inserts val at state->top - indx - 1, moving everything else up
|
||||||
if (state->panic)
|
COSMO_API void cosmo_insert(CState *state, int indx, CValue val) {
|
||||||
return;
|
StkPtr tmp = cosmoV_getTop(state, indx);
|
||||||
state->panic = true;
|
|
||||||
|
|
||||||
|
// 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
|
// print stack trace
|
||||||
for (int i = 0; i < state->frameCount; i++) {
|
for (int i = 0; i < err->frameCount; i++) {
|
||||||
CCallFrame *frame = &state->callFrame[i];
|
CCallFrame *frame = &err->frames[i];
|
||||||
CObjFunction *function = frame->closure->function;
|
CObjFunction *function = frame->closure->function;
|
||||||
CChunk *chunk = &function->chunk;
|
CChunk *chunk = &function->chunk;
|
||||||
|
|
||||||
int line = chunk->lineInfo[frame->pc - chunk->buf - 1];
|
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);
|
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line);
|
||||||
if (function->name == NULL) { // unnamed chunk
|
else
|
||||||
fprintf(stderr, "%s\n\t", UNNAMEDCHUNK);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "%.*s()\n\t", function->name->length, function->name->str);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "[line %d] in ", line);
|
fprintf(stderr, "[line %d] in ", line);
|
||||||
|
|
||||||
if (function->name == NULL) { // unnamed chunk
|
if (function->name == NULL) { // unnamed chunk
|
||||||
fprintf(stderr, "%s\n", UNNAMEDCHUNK);
|
fprintf(stderr, "%s\n", UNNAMEDCHUNK);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "%.*s()\n", function->name->length, function->name->str);
|
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_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
CObjString *errString = cosmoO_pushVFString(state, format, args);
|
cosmoO_pushVFString(state, format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
printf("%.*s\n", errString->length, errString->str);
|
// throw the error onto the state
|
||||||
//cosmoV_printStack(state);
|
cosmoV_throw(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjUpval *captureUpvalue(CState *state, CValue *local) {
|
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.
|
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return.
|
||||||
|
|
||||||
returns:
|
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
|
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) {
|
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);
|
int nres = cfunc(state, args, savedBase + 1);
|
||||||
cosmoM_unfreezeGC(state);
|
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
|
// caller function wasn't expecting this many return values, cap it
|
||||||
if (nres > nresults)
|
if (nres > nresults)
|
||||||
@ -168,6 +200,10 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
|||||||
|
|
||||||
state->top = savedBase + offset; // set stack
|
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
|
// 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
|
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
|
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.
|
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return.
|
||||||
|
|
||||||
returns:
|
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
|
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) {
|
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
|
// execute
|
||||||
int nres = cosmoV_execute(state);
|
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
|
if (nres > nresults) // caller function wasn't expecting this many return values, cap it
|
||||||
nres = nresults;
|
nres = nresults;
|
||||||
@ -227,6 +261,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
|||||||
// pop the callframe and return results :)
|
// pop the callframe and return results :)
|
||||||
popCallFrame(state, offset);
|
popCallFrame(state, offset);
|
||||||
|
|
||||||
|
if (state->panic) // panic state
|
||||||
|
return false;
|
||||||
|
|
||||||
// push the return values back onto the stack
|
// 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
|
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
|
state->top += nres; // and make sure to move state->top to match
|
||||||
@ -247,6 +284,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
|||||||
switch (cosmoV_readObj(func)->type) {
|
switch (cosmoV_readObj(func)->type) {
|
||||||
case COBJ_CLOSURE:
|
case COBJ_CLOSURE:
|
||||||
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
||||||
|
case COBJ_CFUNCTION:
|
||||||
|
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
||||||
case COBJ_METHOD: {
|
case COBJ_METHOD: {
|
||||||
CObjMethod *method = (CObjMethod*)cosmoV_readObj(func);
|
CObjMethod *method = (CObjMethod*)cosmoV_readObj(func);
|
||||||
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_CFUNCTION:
|
|
||||||
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
|
||||||
default:
|
default:
|
||||||
cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(func));
|
cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(func));
|
||||||
return false;
|
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);
|
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) {
|
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
||||||
StkPtr base = cosmoV_getTop(state, args);
|
StkPtr base = cosmoV_getTop(state, args);
|
||||||
|
|
||||||
COSMOVMRESULT res = cosmoV_call(state, args, nresults);
|
if (!callCValue(state, *base, args, nresults, 0)) {
|
||||||
|
|
||||||
if (res != COSMOVM_OK) {
|
|
||||||
// restore panic state
|
// restore panic state
|
||||||
state->panic = false;
|
state->panic = false;
|
||||||
|
state->error = NULL;
|
||||||
|
|
||||||
// error is on the stack, grab it
|
cosmoV_pushValue(state, cosmoV_newObj(state->error));
|
||||||
StkPtr err = cosmoV_getTop(state, 0);
|
return COSMOVM_RUNTIME_ERR;
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return COSMOVM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -327,7 +353,7 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
|||||||
|
|
||||||
returns:
|
returns:
|
||||||
COSMOVM_OK: callable object exited normally
|
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) {
|
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
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
|
// args = # of pass parameters, nresults = # of expected results
|
||||||
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
|
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_makeObject(CState *state, int pairs);
|
||||||
COSMO_API void cosmoV_makeDictionary(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 bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
||||||
COSMO_API void cosmoV_concat(CState *state, int vals);
|
COSMO_API void cosmoV_concat(CState *state, int vals);
|
||||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
|
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 cosmoV_error(CState *state, const char *format, ...);
|
||||||
|
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||||
|
|
||||||
// nice to have wrappers
|
// 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
|
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||||
|
|
||||||
if (res == COSMOVM_RUNTIME_ERR)
|
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() {
|
static void repl() {
|
||||||
|
Loading…
Reference in New Issue
Block a user