Improvements to exceptions
- Exceptions get repr'd to print for better flexibility and no more weird if/else tree in dumpTraceback to handle other cases. - Parser / compiler errors are now SyntaxError's. - Try to read filenames when printing tracebacks. - Fixup formatting to look more like CPython.
This commit is contained in:
parent
f97d8cd562
commit
97922d3922
52
compiler.c
52
compiler.c
@ -189,44 +189,30 @@ static void string(int canAssign);
|
|||||||
static KrkToken decorator(size_t level, FunctionType type);
|
static KrkToken decorator(size_t level, FunctionType type);
|
||||||
static void call(int canAssign);
|
static void call(int canAssign);
|
||||||
|
|
||||||
static void errorAt(KrkToken * token, const char * message) {
|
static void finishError(KrkToken * token) {
|
||||||
if (parser.panicMode) return;
|
|
||||||
parser.panicMode = 1;
|
|
||||||
|
|
||||||
size_t i = (token->col - 1);
|
size_t i = (token->col - 1);
|
||||||
while (token->linePtr[i] && token->linePtr[i] != '\n') i++;
|
while (token->linePtr[i] && token->linePtr[i] != '\n') i++;
|
||||||
|
|
||||||
const char fancyError[] = "Parse error in \"%s\" on line %d col %d (%s): %s\n"
|
krk_attachNamedObject(&AS_INSTANCE(vm.currentException)->fields, "line", (KrkObj*)krk_copyString(token->linePtr, i));
|
||||||
" %.*s\033[31m%.*s\033[39m%.*s\n"
|
krk_attachNamedObject(&AS_INSTANCE(vm.currentException)->fields, "file", (KrkObj*)currentChunk()->filename);
|
||||||
" %-*s\033[31m^\033[39m\n";
|
krk_attachNamedValue (&AS_INSTANCE(vm.currentException)->fields, "lineno", INTEGER_VAL(token->line));
|
||||||
const char plainError[] = "Parse error in \"%s\" on line %d col %d (%s): %s\n"
|
krk_attachNamedValue (&AS_INSTANCE(vm.currentException)->fields, "colno", INTEGER_VAL(token->col));
|
||||||
" %.*s%.*s%.*s\n"
|
krk_attachNamedValue (&AS_INSTANCE(vm.currentException)->fields, "width", INTEGER_VAL(token->literalWidth));
|
||||||
" %-*s^\n";
|
|
||||||
fprintf(stderr, (vm.flags & KRK_NO_ESCAPE) ? plainError: fancyError,
|
if (current->function->name) {
|
||||||
currentChunk()->filename->chars,
|
krk_attachNamedObject(&AS_INSTANCE(vm.currentException)->fields, "func", (KrkObj*)current->function->name);
|
||||||
(int)token->line,
|
} else {
|
||||||
(int)token->col,
|
KrkValue name = NONE_VAL();
|
||||||
getRule(token->type)->name,
|
krk_tableGet(&vm.module->fields, vm.specialMethodNames[METHOD_NAME], &name);
|
||||||
message,
|
krk_attachNamedValue(&AS_INSTANCE(vm.currentException)->fields, "func", name);
|
||||||
(int)(token->col - 1),
|
}
|
||||||
token->linePtr,
|
|
||||||
(int)(token->literalWidth),
|
parser.panicMode = 1;
|
||||||
token->linePtr + (token->col - 1),
|
|
||||||
(int)(i - (token->col - 1 + token->literalWidth)),
|
|
||||||
token->linePtr + (token->col - 1 + token->literalWidth),
|
|
||||||
(int)token->col-1,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
parser.hadError = 1;
|
parser.hadError = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void error(const char * message) {
|
#define error(...) do { if (parser.panicMode) break; krk_runtimeError(vm.exceptions.syntaxError, __VA_ARGS__); finishError(&parser.previous); } while (0)
|
||||||
errorAt(&parser.previous, message);
|
#define errorAtCurrent(...) do { if (parser.panicMode) break; krk_runtimeError(vm.exceptions.syntaxError, __VA_ARGS__); finishError(&parser.current); } while (0)
|
||||||
}
|
|
||||||
|
|
||||||
static void errorAtCurrent(const char * message) {
|
|
||||||
errorAt(&parser.current, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void advance() {
|
static void advance() {
|
||||||
parser.previous = parser.current;
|
parser.previous = parser.current;
|
||||||
@ -2243,7 +2229,7 @@ static void declareVariable() {
|
|||||||
Local * local = ¤t->locals[i];
|
Local * local = ¤t->locals[i];
|
||||||
if (local->depth != -1 && local->depth < (ssize_t)current->scopeDepth) break;
|
if (local->depth != -1 && local->depth < (ssize_t)current->scopeDepth) break;
|
||||||
if (identifiersEqual(name, &local->name)) {
|
if (identifiersEqual(name, &local->name)) {
|
||||||
error("Duplicate definition");
|
error("Duplicate definition for local '%.*s' in this scope.", (int)name->literalWidth, name->start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addLocal(*name);
|
addLocal(*name);
|
||||||
|
141
vm.c
141
vm.c
@ -49,7 +49,9 @@ KrkVM vm;
|
|||||||
static KrkValue run();
|
static KrkValue run();
|
||||||
static KrkValue krk_isinstance(int argc, KrkValue argv[]);
|
static KrkValue krk_isinstance(int argc, KrkValue argv[]);
|
||||||
static void addObjects();
|
static void addObjects();
|
||||||
|
/* We use these directly sometimes */
|
||||||
static KrkValue _string_get(int argc, KrkValue argv[]);
|
static KrkValue _string_get(int argc, KrkValue argv[]);
|
||||||
|
static KrkValue _string_format(int argc, KrkValue argv[], int hasKw);
|
||||||
|
|
||||||
/* Embedded script for extensions to builtin-ins; see builtins.c/builtins.krk */
|
/* Embedded script for extensions to builtin-ins; see builtins.c/builtins.krk */
|
||||||
extern const char krk_builtinsSrc[];
|
extern const char krk_builtinsSrc[];
|
||||||
@ -123,46 +125,56 @@ static void dumpStack(CallFrame * frame) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a traceback by working through call frames.
|
* Display a traceback by scanning up the stack / call frames.
|
||||||
* Called when no exception handler was available and
|
* The format of the output here is modeled after the output
|
||||||
* an exception was thrown. If there the exception value
|
* given by CPython, so we display the outermost call first
|
||||||
* is not None, it will also be printed using safe methods.
|
* and then move inwards; on each call frame we try to open
|
||||||
|
* the source file and print the corresponding line.
|
||||||
*/
|
*/
|
||||||
void krk_dumpTraceback() {
|
void krk_dumpTraceback() {
|
||||||
if (vm.frameCount) {
|
if (vm.frameCount) {
|
||||||
fprintf(stderr, "Traceback, most recent last:\n");
|
fprintf(stderr, "Traceback (most recent call last):\n");
|
||||||
for (size_t i = 0; i <= vm.frameCount - 1; i++) {
|
for (size_t i = 0; i <= vm.frameCount - 1; i++) {
|
||||||
CallFrame * frame = &vm.frames[i];
|
CallFrame * frame = &vm.frames[i];
|
||||||
KrkFunction * function = frame->closure->function;
|
KrkFunction * function = frame->closure->function;
|
||||||
size_t instruction = frame->ip - function->chunk.code - 1;
|
size_t instruction = frame->ip - function->chunk.code - 1;
|
||||||
|
int lineNo = (int)krk_lineNumber(&function->chunk, instruction);
|
||||||
fprintf(stderr, " File \"%s\", line %d, in %s\n",
|
fprintf(stderr, " File \"%s\", line %d, in %s\n",
|
||||||
(function->chunk.filename ? function->chunk.filename->chars : "?"),
|
(function->chunk.filename ? function->chunk.filename->chars : "?"),
|
||||||
(int)krk_lineNumber(&function->chunk, instruction),
|
lineNo,
|
||||||
(function->name ? function->name->chars : "(unnamed)"));
|
(function->name ? function->name->chars : "(unnamed)"));
|
||||||
|
if (function->chunk.filename) {
|
||||||
|
FILE * f = fopen(function->chunk.filename->chars, "r");
|
||||||
|
if (f) {
|
||||||
|
int line = 1;
|
||||||
|
do {
|
||||||
|
char c = fgetc(f);
|
||||||
|
if (c < -1) break;
|
||||||
|
if (c == '\n') {
|
||||||
|
line++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line == lineNo) {
|
||||||
|
fprintf(stderr," ");
|
||||||
|
while (c == ' ') c = fgetc(f);
|
||||||
|
do {
|
||||||
|
fputc(c, stderr);
|
||||||
|
c = fgetc(f);
|
||||||
|
} while (!feof(f) && c > 0 && c != '\n');
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!feof(f));
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!krk_valuesEqual(vm.currentException,NONE_VAL())) {
|
if (!krk_valuesEqual(vm.currentException,NONE_VAL())) {
|
||||||
if (IS_STRING(vm.currentException)) {
|
krk_push(vm.currentException);
|
||||||
/* Make sure strings are printed without quotes */
|
KrkValue result = krk_callSimple(OBJECT_VAL(AS_CLASS(krk_typeOf(1,&vm.currentException))->_reprer), 1, 0);
|
||||||
fprintf(stderr, "%s", AS_CSTRING(vm.currentException));
|
fprintf(stderr, "%s\n", AS_CSTRING(result));
|
||||||
} else if (AS_BOOLEAN(krk_isinstance(2, (KrkValue[]){vm.currentException, OBJECT_VAL(vm.exceptions.baseException)}))) {
|
|
||||||
/* ErrorClass: arg... */
|
|
||||||
fprintf(stderr, "%s: ", AS_INSTANCE(vm.currentException)->_class->name->chars);
|
|
||||||
KrkValue exceptionArg;
|
|
||||||
krk_tableGet(&AS_INSTANCE(vm.currentException)->fields, OBJECT_VAL(S("arg")), &exceptionArg);
|
|
||||||
if (IS_STRING(exceptionArg)) {
|
|
||||||
/* Make sure strings are printed without quotes */
|
|
||||||
fprintf(stderr, "%s", AS_CSTRING(exceptionArg));
|
|
||||||
} else {
|
|
||||||
krk_printValueSafe(stderr, exceptionArg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Whatever, just print it. */
|
|
||||||
krk_printValueSafe(stderr, vm.currentException);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,11 +194,9 @@ void krk_runtimeError(KrkClass * type, const char * fmt, ...) {
|
|||||||
/* Try to allocate an instance of __builtins__. */
|
/* Try to allocate an instance of __builtins__. */
|
||||||
KrkInstance * exceptionObject = krk_newInstance(type);
|
KrkInstance * exceptionObject = krk_newInstance(type);
|
||||||
krk_push(OBJECT_VAL(exceptionObject));
|
krk_push(OBJECT_VAL(exceptionObject));
|
||||||
KrkString * strArg = S("arg");
|
krk_push(OBJECT_VAL(S("arg")));
|
||||||
krk_push(OBJECT_VAL(strArg));
|
krk_push(OBJECT_VAL(krk_copyString(buf, len)));
|
||||||
KrkString * strVal = krk_copyString(buf, len);
|
krk_tableSet(&exceptionObject->fields, krk_peek(1), krk_peek(0));
|
||||||
krk_push(OBJECT_VAL(strVal));
|
|
||||||
krk_tableSet(&exceptionObject->fields, OBJECT_VAL(strArg), OBJECT_VAL(strVal));
|
|
||||||
krk_pop();
|
krk_pop();
|
||||||
krk_pop();
|
krk_pop();
|
||||||
krk_pop();
|
krk_pop();
|
||||||
@ -1411,12 +1421,66 @@ static KrkValue krk_initException(int argc, KrkValue argv[]) {
|
|||||||
if (argc > 0) {
|
if (argc > 0) {
|
||||||
krk_attachNamedValue(&self->fields, "arg", argv[1]);
|
krk_attachNamedValue(&self->fields, "arg", argv[1]);
|
||||||
} else {
|
} else {
|
||||||
krk_attachNamedValue(&self->fields, "arg", OBJECT_VAL(S("")));
|
krk_attachNamedValue(&self->fields, "arg", NONE_VAL());
|
||||||
}
|
}
|
||||||
|
|
||||||
return argv[0];
|
return argv[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static KrkValue _exception_repr(int argc, KrkValue argv[]) {
|
||||||
|
KrkInstance * self = AS_INSTANCE(argv[0]);
|
||||||
|
/* .arg */
|
||||||
|
KrkValue arg;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("arg")), &arg) || IS_NONE(arg)) {
|
||||||
|
return OBJECT_VAL(self->_class->name);
|
||||||
|
} else {
|
||||||
|
krk_push(OBJECT_VAL(self->_class->name));
|
||||||
|
krk_push(OBJECT_VAL(S(": ")));
|
||||||
|
addObjects();
|
||||||
|
krk_push(arg);
|
||||||
|
addObjects();
|
||||||
|
return krk_pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static KrkValue _syntaxerror_repr(int argc, KrkValue argv[]) {
|
||||||
|
KrkInstance * self = AS_INSTANCE(argv[0]);
|
||||||
|
/* .arg */
|
||||||
|
KrkValue file, line, lineno, colno, width, arg, func;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("file")), &file) || !IS_STRING(file)) goto _badSyntaxError;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("line")), &line) || !IS_STRING(line)) goto _badSyntaxError;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("lineno")), &lineno) || !IS_INTEGER(lineno)) goto _badSyntaxError;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("colno")), &colno) || !IS_INTEGER(colno)) goto _badSyntaxError;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("width")), &width) || !IS_INTEGER(width)) goto _badSyntaxError;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("arg")), &arg) || !IS_STRING(arg)) goto _badSyntaxError;
|
||||||
|
if (!krk_tableGet(&self->fields, OBJECT_VAL(S("func")), &func)) goto _badSyntaxError;
|
||||||
|
|
||||||
|
krk_push(OBJECT_VAL(S(" File \"{}\", line {}{}\n {}\n {}^\n{}: {}")));
|
||||||
|
char * tmp = malloc(AS_INTEGER(colno));
|
||||||
|
memset(tmp,' ',AS_INTEGER(colno));
|
||||||
|
tmp[AS_INTEGER(colno)-1] = '\0';
|
||||||
|
krk_push(OBJECT_VAL(krk_takeString(tmp,AS_INTEGER(colno)-1)));
|
||||||
|
krk_push(OBJECT_VAL(self->_class->name));
|
||||||
|
if (IS_STRING(func)) {
|
||||||
|
krk_push(OBJECT_VAL(S(" in ")));
|
||||||
|
krk_push(func);
|
||||||
|
addObjects();
|
||||||
|
} else {
|
||||||
|
krk_push(OBJECT_VAL(S("")));
|
||||||
|
}
|
||||||
|
KrkValue formattedString = _string_format(8,
|
||||||
|
(KrkValue[]){krk_peek(3), file, lineno, krk_peek(0), line, krk_peek(2), krk_peek(1), arg}, 0);
|
||||||
|
krk_pop(); /* instr */
|
||||||
|
krk_pop(); /* class */
|
||||||
|
krk_pop(); /* spaces */
|
||||||
|
krk_pop(); /* format string */
|
||||||
|
|
||||||
|
return formattedString;
|
||||||
|
|
||||||
|
_badSyntaxError:
|
||||||
|
return OBJECT_VAL(S("SyntaxError: invalid syntax"));
|
||||||
|
}
|
||||||
|
|
||||||
static KrkValue _string_init(int argc, KrkValue argv[]) {
|
static KrkValue _string_init(int argc, KrkValue argv[]) {
|
||||||
/* Ignore argument which would have been an instance */
|
/* Ignore argument which would have been an instance */
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
@ -1491,6 +1555,7 @@ static KrkValue _string_add(int argc, KrkValue argv[]) {
|
|||||||
obj->base = baseClass; \
|
obj->base = baseClass; \
|
||||||
krk_tableAddAll(&baseClass->methods, &obj->methods); \
|
krk_tableAddAll(&baseClass->methods, &obj->methods); \
|
||||||
krk_tableAddAll(&baseClass->fields, &obj->fields); \
|
krk_tableAddAll(&baseClass->fields, &obj->fields); \
|
||||||
|
krk_finalizeClass(obj); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define BUILTIN_FUNCTION(name, func) do { \
|
#define BUILTIN_FUNCTION(name, func) do { \
|
||||||
@ -3193,6 +3258,8 @@ void krk_initVM(int flags) {
|
|||||||
ADD_EXCEPTION_CLASS(vm.exceptions.baseException, "Exception", vm.objectClass);
|
ADD_EXCEPTION_CLASS(vm.exceptions.baseException, "Exception", vm.objectClass);
|
||||||
/* base exception class gets an init that takes an optional string */
|
/* base exception class gets an init that takes an optional string */
|
||||||
krk_defineNative(&vm.exceptions.baseException->methods, ".__init__", krk_initException);
|
krk_defineNative(&vm.exceptions.baseException->methods, ".__init__", krk_initException);
|
||||||
|
krk_defineNative(&vm.exceptions.baseException->methods, ".__repr__", _exception_repr);
|
||||||
|
krk_finalizeClass(vm.exceptions.baseException);
|
||||||
ADD_EXCEPTION_CLASS(vm.exceptions.typeError, "TypeError", vm.exceptions.baseException);
|
ADD_EXCEPTION_CLASS(vm.exceptions.typeError, "TypeError", vm.exceptions.baseException);
|
||||||
ADD_EXCEPTION_CLASS(vm.exceptions.argumentError, "ArgumentError", vm.exceptions.baseException);
|
ADD_EXCEPTION_CLASS(vm.exceptions.argumentError, "ArgumentError", vm.exceptions.baseException);
|
||||||
ADD_EXCEPTION_CLASS(vm.exceptions.indexError, "IndexError", vm.exceptions.baseException);
|
ADD_EXCEPTION_CLASS(vm.exceptions.indexError, "IndexError", vm.exceptions.baseException);
|
||||||
@ -3205,6 +3272,9 @@ void krk_initVM(int flags) {
|
|||||||
ADD_EXCEPTION_CLASS(vm.exceptions.keyboardInterrupt, "KeyboardInterrupt", vm.exceptions.baseException);
|
ADD_EXCEPTION_CLASS(vm.exceptions.keyboardInterrupt, "KeyboardInterrupt", vm.exceptions.baseException);
|
||||||
ADD_EXCEPTION_CLASS(vm.exceptions.zeroDivisionError, "ZeroDivisionError", vm.exceptions.baseException);
|
ADD_EXCEPTION_CLASS(vm.exceptions.zeroDivisionError, "ZeroDivisionError", vm.exceptions.baseException);
|
||||||
ADD_EXCEPTION_CLASS(vm.exceptions.notImplementedError, "NotImplementedError", vm.exceptions.baseException);
|
ADD_EXCEPTION_CLASS(vm.exceptions.notImplementedError, "NotImplementedError", vm.exceptions.baseException);
|
||||||
|
ADD_EXCEPTION_CLASS(vm.exceptions.syntaxError, "SyntaxError", vm.exceptions.baseException);
|
||||||
|
krk_defineNative(&vm.exceptions.syntaxError->methods, ".__repr__", _syntaxerror_repr);
|
||||||
|
krk_finalizeClass(vm.exceptions.syntaxError);
|
||||||
|
|
||||||
/* Build classes for basic types */
|
/* Build classes for basic types */
|
||||||
ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass);
|
ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass);
|
||||||
@ -3630,8 +3700,10 @@ int krk_loadModule(KrkString * name, KrkValue * moduleOut, KrkString * runAs) {
|
|||||||
*moduleOut = krk_runfile(fileName,1,runAs->chars,fileName);
|
*moduleOut = krk_runfile(fileName,1,runAs->chars,fileName);
|
||||||
vm.exitOnFrame = previousExitFrame;
|
vm.exitOnFrame = previousExitFrame;
|
||||||
if (!IS_OBJECT(*moduleOut)) {
|
if (!IS_OBJECT(*moduleOut)) {
|
||||||
|
if (!(vm.flags & KRK_HAS_EXCEPTION)) {
|
||||||
krk_runtimeError(vm.exceptions.importError,
|
krk_runtimeError(vm.exceptions.importError,
|
||||||
"Failed to load module '%s' from '%s'", name->chars, fileName);
|
"Failed to load module '%s' from '%s'", name->chars, fileName);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4322,7 +4394,10 @@ KrkValue krk_interpret(const char * src, int newScope, char * fromName, char * f
|
|||||||
if (newScope) krk_startModule(fromName);
|
if (newScope) krk_startModule(fromName);
|
||||||
|
|
||||||
KrkFunction * function = krk_compile(src, 0, fromFile);
|
KrkFunction * function = krk_compile(src, 0, fromFile);
|
||||||
if (!function) return NONE_VAL();
|
if (!function) {
|
||||||
|
if (!vm.frameCount) handleException();
|
||||||
|
return NONE_VAL();
|
||||||
|
}
|
||||||
|
|
||||||
krk_attachNamedObject(&vm.module->fields, "__file__", (KrkObj*)function->chunk.filename);
|
krk_attachNamedObject(&vm.module->fields, "__file__", (KrkObj*)function->chunk.filename);
|
||||||
|
|
||||||
|
1
vm.h
1
vm.h
@ -87,6 +87,7 @@ typedef struct {
|
|||||||
KrkClass * keyboardInterrupt;
|
KrkClass * keyboardInterrupt;
|
||||||
KrkClass * zeroDivisionError;
|
KrkClass * zeroDivisionError;
|
||||||
KrkClass * notImplementedError;
|
KrkClass * notImplementedError;
|
||||||
|
KrkClass * syntaxError;
|
||||||
} exceptions;
|
} exceptions;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user