mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-21 15:00:06 +00:00
WIP: major error handling refactoring
switching to setjmp instead of the really bad global 'panic' flag
This commit is contained in:
parent
3f39211081
commit
75d27afe2c
@ -389,9 +389,8 @@ static CToken parseIdentifier(CLexState *state)
|
||||
return makeToken(state, identifierType(state)); // is it a reserved word?
|
||||
}
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source)
|
||||
{
|
||||
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
||||
state->startChar = (char *)source;
|
||||
state->currentChar = (char *)source;
|
||||
state->line = 1;
|
||||
@ -400,13 +399,11 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||
state->cstate = cstate;
|
||||
|
||||
resetBuffer(state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate)
|
||||
void cosmoL_cleanupLexState(CState *state, CLexState *lstate)
|
||||
{
|
||||
cosmoM_free(state, CLexState, lstate);
|
||||
// stubbed
|
||||
}
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state)
|
||||
|
@ -103,8 +103,8 @@ typedef struct
|
||||
CState *cstate;
|
||||
} CLexState;
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *state, const char *source);
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate);
|
||||
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source);
|
||||
void cosmoL_cleanupLexState(CState *state, CLexState *lstate);
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state);
|
||||
|
||||
|
73
src/cparse.c
73
src/cparse.c
@ -59,14 +59,12 @@ typedef struct CCompilerState
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CLexState lex;
|
||||
CState *state;
|
||||
CLexState *lex;
|
||||
CCompilerState *compiler;
|
||||
CObjString *module; // name of the module
|
||||
CToken current;
|
||||
CToken previous; // token right after the current token
|
||||
bool hadError;
|
||||
bool panic;
|
||||
} CParseState;
|
||||
|
||||
typedef enum
|
||||
@ -124,6 +122,8 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||
ccstate->function->module = pstate->module;
|
||||
|
||||
cosmoV_pushRef(pstate->state, (CObj *)ccstate->function);
|
||||
|
||||
ccstate->loop.scope = -1; // there is no loop yet
|
||||
|
||||
if (type != FTYPE_SCRIPT) {
|
||||
@ -146,11 +146,9 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
||||
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s,
|
||||
const char *source, const char *module)
|
||||
{
|
||||
pstate->lex = cosmoL_newLexState(s, source);
|
||||
cosmoL_initLexState(s, &pstate->lex, source);
|
||||
|
||||
pstate->state = s;
|
||||
pstate->hadError = false;
|
||||
pstate->panic = false;
|
||||
pstate->compiler = ccstate;
|
||||
pstate->module = cosmoO_copyString(s, module, strlen(module));
|
||||
|
||||
@ -159,14 +157,11 @@ static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState
|
||||
|
||||
static void freeParseState(CParseState *pstate)
|
||||
{
|
||||
cosmoL_freeLexState(pstate->state, pstate->lex);
|
||||
cosmoL_cleanupLexState(pstate->state, &pstate->lex);
|
||||
}
|
||||
|
||||
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
if (token->type == TOKEN_EOF) {
|
||||
cosmoV_pushString(pstate->state, "At end: ");
|
||||
} else if (!(token->type == TOKEN_ERROR)) {
|
||||
@ -177,14 +172,9 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l
|
||||
|
||||
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;
|
||||
// throw complete error string
|
||||
cosmoV_concat(pstate->state, 2);
|
||||
cosmoV_throw(pstate->state);
|
||||
}
|
||||
|
||||
static void errorAtCurrent(CParseState *pstate, const char *format, ...)
|
||||
@ -206,7 +196,7 @@ static void error(CParseState *pstate, const char *format, ...)
|
||||
static void advance(CParseState *pstate)
|
||||
{
|
||||
pstate->previous = pstate->current;
|
||||
pstate->current = cosmoL_scanToken(pstate->lex);
|
||||
pstate->current = cosmoL_scanToken(&pstate->lex);
|
||||
|
||||
if (pstate->current.type == TOKEN_ERROR) {
|
||||
errorAtCurrent(pstate, pstate->current.start);
|
||||
@ -280,7 +270,6 @@ uint16_t makeConstant(CParseState *pstate, CValue val)
|
||||
int indx = addConstant(pstate->state, getChunk(pstate), val);
|
||||
if (indx > UINT16_MAX) {
|
||||
error(pstate, "UInt overflow! Too many constants in one chunk!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint16_t)indx;
|
||||
@ -350,7 +339,6 @@ static void addLocal(CParseState *pstate, CToken name)
|
||||
{
|
||||
if (pstate->compiler->localCount > UINT8_MAX) {
|
||||
error(pstate, "UInt overflow! Too many locals in scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
Local *local = &pstate->compiler->locals[pstate->compiler->localCount++];
|
||||
@ -365,7 +353,6 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx
|
||||
|
||||
if (upvals > UINT8_MAX) {
|
||||
error(pstate, "UInt overflow! Too many upvalues in scope!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check and make sure we haven't already captured it
|
||||
@ -768,7 +755,6 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
tblType = 1; // array-like
|
||||
} else {
|
||||
error(pstate, "Can't change table description type mid-definition!");
|
||||
return;
|
||||
}
|
||||
|
||||
entries++;
|
||||
@ -818,7 +804,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
// "pop" the 1 value
|
||||
valuePopped(pstate, 1);
|
||||
entries++;
|
||||
} while (match(pstate, TOKEN_COMMA) && !pstate->hadError);
|
||||
} while (match(pstate, TOKEN_COMMA));
|
||||
|
||||
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition.");
|
||||
}
|
||||
@ -1158,9 +1144,6 @@ static uint16_t parseVariable(CParseState *pstate, const char *errorMessage, boo
|
||||
|
||||
static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
if (pstate->compiler->scopeDepth > 0 || forceLocal) {
|
||||
markInitialized(pstate, global);
|
||||
valuePopped(pstate, 1); // the local stays on the stack!
|
||||
@ -1177,7 +1160,7 @@ static void _proto(CParseState *pstate)
|
||||
{
|
||||
int entries = 0;
|
||||
|
||||
while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF) && !pstate->hadError) {
|
||||
while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF)) {
|
||||
if (match(pstate, TOKEN_FUNC)) {
|
||||
// define method
|
||||
consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!");
|
||||
@ -1224,9 +1207,6 @@ static void localProto(CParseState *pstate)
|
||||
|
||||
static void popLocals(CParseState *pstate, int toScope)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
// count the locals in scope to pop
|
||||
int localsToPop = 0;
|
||||
|
||||
@ -1686,18 +1666,6 @@ static void continueStatement(CParseState *pstate)
|
||||
writeJmpBack(pstate, pstate->compiler->loop.startBytecode);
|
||||
}
|
||||
|
||||
static void synchronize(CParseState *pstate)
|
||||
{
|
||||
pstate->panic = false;
|
||||
|
||||
while (pstate->current.type != TOKEN_EOF) {
|
||||
if (pstate->previous.type == TOKEN_EOS)
|
||||
return;
|
||||
|
||||
advance(pstate);
|
||||
}
|
||||
}
|
||||
|
||||
static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded)
|
||||
{
|
||||
int lastExpected = pstate->compiler->expectedValues;
|
||||
@ -1787,10 +1755,6 @@ static void statement(CParseState *pstate)
|
||||
static void declaration(CParseState *pstate)
|
||||
{
|
||||
statement(pstate);
|
||||
|
||||
// if we paniced, skip the whole statement!
|
||||
if (pstate->panic)
|
||||
synchronize(pstate);
|
||||
}
|
||||
|
||||
static CObjFunction *endCompiler(CParseState *pstate)
|
||||
@ -1812,7 +1776,6 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
|
||||
{
|
||||
CParseState parser;
|
||||
CCompilerState compiler;
|
||||
cosmoM_freezeGC(state); // ignore all GC events while compiling
|
||||
initParseState(&parser, &compiler, state, source, module);
|
||||
|
||||
advance(&parser);
|
||||
@ -1825,24 +1788,10 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
|
||||
|
||||
popLocals(&parser, 0);
|
||||
|
||||
if (parser.hadError) { // we don't free the function, the state already has a reference to it in
|
||||
// it's linked list of objects!
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
cosmoM_unfreezeGC(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CObjFunction *resFunc = compiler.function;
|
||||
|
||||
// finally free out parser states
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
// push the funciton onto the stack so if we cause an GC event, it won't be free'd
|
||||
cosmoV_pushRef(state, (CObj *)resFunc);
|
||||
cosmoM_unfreezeGC(state);
|
||||
cosmoV_pop(state);
|
||||
return resFunc;
|
||||
}
|
||||
|
16
src/cstate.c
16
src/cstate.c
@ -7,6 +7,21 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
CPanic *cosmoV_newPanic(CState *state)
|
||||
{
|
||||
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
|
||||
panic->prev = state->panic;
|
||||
state->panic = panic;
|
||||
return panic;
|
||||
}
|
||||
|
||||
void cosmoV_freePanic(CState *state)
|
||||
{
|
||||
CPanic *panic = state->panic;
|
||||
state->panic = panic->prev;
|
||||
cosmoM_free(state, CPanic, panic);
|
||||
}
|
||||
|
||||
CState *cosmoV_newState()
|
||||
{
|
||||
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
||||
@ -19,6 +34,7 @@ CState *cosmoV_newState()
|
||||
|
||||
state->panic = false;
|
||||
state->freezeGC = 1; // we start frozen
|
||||
state->panic = NULL;
|
||||
|
||||
// GC
|
||||
state->objects = NULL;
|
||||
|
17
src/cstate.h
17
src/cstate.h
@ -6,6 +6,8 @@
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
struct CCallFrame
|
||||
{
|
||||
CObjClosure *closure;
|
||||
@ -38,11 +40,17 @@ typedef struct ArrayCObj
|
||||
int capacity;
|
||||
} ArrayCObj;
|
||||
|
||||
typedef struct CPanic
|
||||
{
|
||||
jmp_buf jmp;
|
||||
struct CPanic *prev;
|
||||
} CPanic;
|
||||
|
||||
struct CState
|
||||
{
|
||||
bool panic;
|
||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||
int frameCount;
|
||||
CPanic *panic;
|
||||
|
||||
CObjError *error; // NULL, unless panic is true
|
||||
CObj *objects; // tracks all of our allocated objects
|
||||
@ -64,10 +72,15 @@ struct CState
|
||||
CValue stack[STACK_MAX]; // stack
|
||||
};
|
||||
|
||||
CPanic *cosmoV_newPanic(CState *state);
|
||||
void cosmoV_freePanic(CState *state);
|
||||
|
||||
COSMO_API CState *cosmoV_newState();
|
||||
COSMO_API void cosmoV_freeState(CState *state);
|
||||
|
||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||
COSMO_API void cosmoV_register(CState *state, int pairs);
|
||||
COSMO_API void cosmoV_freeState(CState *state);
|
||||
|
||||
COSMO_API void cosmoV_printStack(CState *state);
|
||||
|
||||
#endif
|
||||
|
69
src/cvm.c
69
src/cvm.c
@ -10,6 +10,18 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
bool cosmoV_protect(CState *state)
|
||||
{
|
||||
CPanic *panic = cosmoV_newPanic(state);
|
||||
|
||||
if (setjmp(panic->jmp) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cosmoV_freePanic(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
@ -57,21 +69,20 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
||||
{
|
||||
CObjFunction *func;
|
||||
|
||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
||||
// success
|
||||
if (cosmoV_protect(state)) {
|
||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
||||
// success
|
||||
#ifdef VM_DEBUG
|
||||
disasmChunk(&func->chunk, func->module->str, 0);
|
||||
disasmChunk(&func->chunk, func->module->str, 0);
|
||||
#endif
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||
// location put our closure
|
||||
cosmoV_pushRef(state, (CObj *)func);
|
||||
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||
return true;
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||
// location put our closure
|
||||
cosmoV_pushRef(state, (CObj *)func);
|
||||
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// fail recovery
|
||||
state->panic = false;
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -109,30 +120,26 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err)
|
||||
}
|
||||
|
||||
/*
|
||||
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..
|
||||
takes value on top of the stack and wraps an CObjError around it, then throws it
|
||||
*/
|
||||
CObjError *cosmoV_throw(CState *state)
|
||||
void 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;
|
||||
// replace the value on the stack with the error
|
||||
*temp = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp));
|
||||
if (state->panic) {
|
||||
longjmp(state->panic->jmp, 1);
|
||||
} else {
|
||||
fprintf(stderr, "Unhandled panic! ");
|
||||
cosmoV_printError(state, error);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -422,9 +429,11 @@ bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults,
|
||||
// returns false if function call failed, true if function call succeeded
|
||||
bool cosmoV_pcall(CState *state, int args, int nresults)
|
||||
{
|
||||
StkPtr base = cosmoV_getTop(state, args);
|
||||
|
||||
if (!callCValue(state, *base, args, nresults, 0)) {
|
||||
if (cosmoV_protect(state)) {
|
||||
cosmoV_call(state, args, nresults);
|
||||
return true;
|
||||
} else {
|
||||
printf("caught panic!\n");
|
||||
// restore panic state
|
||||
state->panic = false;
|
||||
|
||||
@ -438,8 +447,6 @@ bool cosmoV_pcall(CState *state, int args, int nresults)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
||||
|
@ -31,7 +31,7 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs);
|
||||
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_throw(CState *state);
|
||||
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user