diff --git a/compiler.c b/compiler.c index 38a4933..f14eede 100644 --- a/compiler.c +++ b/compiler.c @@ -35,6 +35,7 @@ typedef enum { typedef void (*ParseFn)(int); typedef struct { + const char * name; ParseFn prefix; ParseFn infix; Precedence precedence; @@ -135,14 +136,26 @@ static void errorAt(KrkToken * token, const char * message) { if (parser.panicMode) return; parser.panicMode = 1; - fprintf(stderr, "[line %d] Error", (int)token->line); - if (token->type == TOKEN_EOF) { - fprintf(stderr, " at end"); - } else if (token->type != TOKEN_ERROR) { - fprintf(stderr, " at '%.*s'", (int)token->length, token->start); - } + size_t i = (token->col - 1); + while (token->linePtr[i] && token->linePtr[i] != '\n') i++; - fprintf(stderr, ": %s\n", message); + fprintf(stderr, "Parse error in \"%s\" on line %d col %d (%s): %s\n" + " %.*s\033[31m%.*s\033[0m%.*s\n" + " %-*s\033[31m^\033[0m\n", + currentChunk()->filename->chars, + (int)token->line, + (int)token->col, + getRule(token->type)->name, + message, + (int)(token->col - 1), + token->linePtr, + (int)(token->length), + token->linePtr + (token->col - 1), + (int)(i - token->col - 1 + token->length), + token->linePtr + (token->col - 1 + token->length), + (int)token->col-1, + "" + ); parser.hadError = 1; } @@ -151,8 +164,6 @@ static void error(const char * message) { } static void errorAtCurrent(const char * message) { - errorAt(&parser.previous, "(token before actual error)"); - parser.panicMode = 0; errorAt(&parser.current, message); } @@ -162,8 +173,8 @@ static void advance() { for (;;) { parser.current = krk_scanToken(); -#ifdef ENABLE_SCAN_TRACING - if (vm.flags & KRK_ENABLE_SCAN_TRACING) { +#ifdef ENABLE_DEBUGGING + if (vm.flags & KRK_ENABLE_DEBUGGING) { fprintf(stderr, "Token %d (start=%p, length=%d) '%.*s' on line %d\n", parser.current.type, parser.current.start, (int)parser.current.length, @@ -400,10 +411,15 @@ static void declaration() { defDeclaration(); } else if (match(TOKEN_LET)) { varDeclaration(); + if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) { + error("Expected EOL after variable declaration.\n"); + } } else if (check(TOKEN_CLASS)) { classDeclaration(); } else if (match(TOKEN_EOL) || match(TOKEN_EOF)) { return; + } else if (check(TOKEN_INDENTATION)) { + return; } else { statement(); } @@ -433,33 +449,28 @@ static void endScope() { } } -static void block(size_t indentation) { +static void block(size_t indentation, const char * blockName) { if (match(TOKEN_EOL)) { /* Begin actual blocks */ if (check(TOKEN_INDENTATION)) { size_t currentIndentation = parser.current.length; if (currentIndentation <= indentation) { - errorAtCurrent("Unexpected indentation level for new block"); + return; } do { if (parser.current.length < currentIndentation) break; advance(); /* Pass indentation */ declaration(); - if (check(TOKEN_EOL)) endOfLine(); } while (check(TOKEN_INDENTATION)); #ifdef ENABLE_DEBUGGING if (vm.flags & KRK_ENABLE_DEBUGGING) { - fprintf(stderr, "On line %d, ", (int)parser.current.line); - if (check(TOKEN_INDENTATION)) { - fprintf(stderr, "Exiting block from %d to %d\n", - (int)currentIndentation, (int)parser.current.length); - } else { - fprintf(stderr, "Exiting block from %d to something that isn't indentation.\n", - (int)currentIndentation); - } + fprintf(stderr, "finished with block %s (ind=%d) on line %d, sitting on a %s (len=%d)\n", + blockName, + (int)indentation, + (int)parser.current.line, + getRule(parser.current.type)->name, + (int)parser.current.length); } - } else { - fprintf(stderr, "Block is emtpy.\n"); #endif } } else { @@ -493,7 +504,7 @@ static void function(FunctionType type, size_t blockWidth) { consume(TOKEN_RIGHT_PAREN, "Expected end of parameter list."); consume(TOKEN_COLON, "Expected colon after function signature."); - block(blockWidth); + block(blockWidth,"def"); KrkFunction * function = endCompiler(); size_t ind = krk_addConstant(currentChunk(), OBJECT_VAL(function)); @@ -509,7 +520,9 @@ static void function(FunctionType type, size_t blockWidth) { static void method(size_t blockWidth) { /* This is actually "inside of a class definition", and that might mean * arbitrary blank lines we need to accept... Sorry. */ - if (match(TOKEN_EOL)) return; + if (match(TOKEN_EOL)) { + return; + } /* def method(...): - just like functions; unlike Python, I'm just always * going to assign `self` because Lox always assigns `this`; it should not @@ -579,6 +592,9 @@ static void classDeclaration() { method(currentIndentation); if (check(TOKEN_EOL)) endOfLine(); } while (check(TOKEN_INDENTATION)); +#ifdef ENABLE_SCAN_TRACING + if (vm.flags & KRK_ENABLE_SCAN_TRACING) fprintf(stderr, "Exiting from class definition on %s\n", getRule(parser.current.type)->name); +#endif /* Exit from block */ } } /* else empty class (and at end of file?) we'll allow it for now... */ @@ -645,7 +661,7 @@ static void ifStatement() { /* Start a new scope and enter a block */ beginScope(); - block(blockWidth); + block(blockWidth,"if"); endScope(); int elseJump = emitJump(OP_JUMP); @@ -664,13 +680,15 @@ static void ifStatement() { /* TODO ELIF or ELSE IF */ consume(TOKEN_COLON, "Expect ':' after else."); beginScope(); - block(blockWidth); + block(blockWidth,"else"); endScope(); - } else if (!check(TOKEN_EOL) && !check(TOKEN_EOF)) { - krk_ungetToken(parser.current); - parser.current = parser.previous; - if (blockWidth) { - parser.previous = previous; + } else { + if (!check(TOKEN_EOF) && !check(TOKEN_EOL)) { + krk_ungetToken(parser.current); + parser.current = parser.previous; + if (blockWidth) { + parser.previous = previous; + } } } } @@ -691,7 +709,7 @@ static void whileStatement() { emitByte(OP_POP); beginScope(); - block(blockWidth); + block(blockWidth,"while"); endScope(); emitLoop(loopStart); @@ -776,7 +794,7 @@ static void forStatement() { consume(TOKEN_COLON,"expect :"); beginScope(); - block(blockWidth); + block(blockWidth,"for"); endScope(); emitLoop(loopStart); @@ -809,7 +827,7 @@ static void tryStatement() { defineVariable(0); beginScope(); - block(blockWidth); + block(blockWidth,"try"); endScope(); int successJump = emitJump(OP_JUMP); @@ -824,7 +842,7 @@ static void tryStatement() { if (match(TOKEN_EXCEPT)) { consume(TOKEN_COLON, "Expect ':' after except."); beginScope(); - block(blockWidth); + block(blockWidth,"except"); endScope(); } else if (!check(TOKEN_EOL) && !check(TOKEN_EOF)) { krk_ungetToken(parser.current); @@ -865,19 +883,7 @@ static void statement() { return; /* Meaningless blank line */ } - if (match(TOKEN_PRINT)) { - printStatement(); - } else if (match(TOKEN_EXPORT)) { - exportStatement(); - } else if (check(TOKEN_IF)) { - /* - * We check rather than match because we need to look at the indentation - * token that came before this (if it was one) to figure out what block - * indentation level we're at, so that we can match our companion else - * and make sure it's not the else for a higher if block. - * - * TODO: Are there other things where we want to do this? - */ + if (check(TOKEN_IF)) { ifStatement(); } else if (check(TOKEN_WHILE)) { whileStatement(); @@ -885,14 +891,23 @@ static void statement() { forStatement(); } else if (check(TOKEN_TRY)) { tryStatement(); - } else if (match(TOKEN_RAISE)) { - raiseStatement(); - } else if (match(TOKEN_RETURN)) { - returnStatement(); - } else if (match(TOKEN_IMPORT)) { - importStatement(); } else { - expressionStatement(); + if (match(TOKEN_PRINT)) { + printStatement(); + } else if (match(TOKEN_EXPORT)) { + exportStatement(); + } else if (match(TOKEN_RAISE)) { + raiseStatement(); + } else if (match(TOKEN_RETURN)) { + returnStatement(); + } else if (match(TOKEN_IMPORT)) { + importStatement(); + } else { + expressionStatement(); + } + if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) { + errorAtCurrent("Unexpected token after statement."); + } } } @@ -1012,64 +1027,67 @@ static void super_(int canAssign) { EMIT_CONSTANT_OP(OP_GET_SUPER, ind); } +#define RULE(token, a, b, c) [token] = {# token, a, b, c} + ParseRule rules[] = { - [TOKEN_LEFT_PAREN] = {grouping, call, PREC_CALL}, - [TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE}, - [TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE}, - [TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE}, - [TOKEN_LEFT_SQUARE] = {NULL, get_, PREC_CALL}, - [TOKEN_RIGHT_SQUARE] = {NULL, NULL, PREC_NONE}, - [TOKEN_COLON] = {NULL, NULL, PREC_NONE}, - [TOKEN_COMMA] = {NULL, NULL, PREC_NONE}, - [TOKEN_DOT] = {NULL, dot, PREC_CALL}, - [TOKEN_MINUS] = {unary, binary, PREC_TERM}, - [TOKEN_PLUS] = {NULL, binary, PREC_TERM}, - [TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE}, - [TOKEN_SOLIDUS] = {NULL, binary, PREC_FACTOR}, - [TOKEN_ASTERISK] = {NULL, binary, PREC_FACTOR}, - [TOKEN_MODULO] = {NULL, binary, PREC_FACTOR}, - [TOKEN_BANG] = {unary, NULL, PREC_NONE}, - [TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY}, - [TOKEN_EQUAL] = {NULL, NULL, PREC_NONE}, - [TOKEN_EQUAL_EQUAL] = {NULL, binary, PREC_EQUALITY}, - [TOKEN_GREATER] = {NULL, binary, PREC_COMPARISON}, - [TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON}, - [TOKEN_LESS] = {NULL, binary, PREC_COMPARISON}, - [TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON}, - [TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE}, - [TOKEN_STRING] = {string, NULL, PREC_NONE}, - [TOKEN_NUMBER] = {number, NULL, PREC_NONE}, - [TOKEN_CODEPOINT] = {NULL, NULL, PREC_NONE}, /* TODO */ - [TOKEN_AND] = {NULL, and_, PREC_AND}, - [TOKEN_CLASS] = {NULL, NULL, PREC_NONE}, - [TOKEN_ELSE] = {NULL, NULL, PREC_NONE}, - [TOKEN_FALSE] = {literal, NULL, PREC_NONE}, - [TOKEN_FOR] = {NULL, NULL, PREC_NONE}, - [TOKEN_DEF] = {NULL, NULL, PREC_NONE}, - [TOKEN_IF] = {NULL, NULL, PREC_NONE}, - [TOKEN_IN] = {NULL, NULL, PREC_NONE}, - [TOKEN_LET] = {NULL, NULL, PREC_NONE}, - [TOKEN_NONE] = {literal, NULL, PREC_NONE}, - [TOKEN_NOT] = {unary, NULL, PREC_NONE}, - [TOKEN_OR] = {NULL, or_, PREC_OR}, - [TOKEN_PRINT] = {NULL, NULL, PREC_NONE}, - [TOKEN_RETURN] = {NULL, NULL, PREC_NONE}, - [TOKEN_SELF] = {self, NULL, PREC_NONE}, - [TOKEN_SUPER] = {super_, NULL, PREC_NONE}, - [TOKEN_TRUE] = {literal, NULL, PREC_NONE}, - [TOKEN_WHILE] = {NULL, NULL, PREC_NONE}, + RULE(TOKEN_LEFT_PAREN, grouping, call, PREC_CALL), + RULE(TOKEN_RIGHT_PAREN, NULL, NULL, PREC_NONE), + RULE(TOKEN_LEFT_BRACE, NULL, NULL, PREC_NONE), + RULE(TOKEN_RIGHT_BRACE, NULL, NULL, PREC_NONE), + RULE(TOKEN_LEFT_SQUARE, NULL, get_, PREC_CALL), + RULE(TOKEN_RIGHT_SQUARE, NULL, NULL, PREC_NONE), + RULE(TOKEN_COLON, NULL, NULL, PREC_NONE), + RULE(TOKEN_COMMA, NULL, NULL, PREC_NONE), + RULE(TOKEN_DOT, NULL, dot, PREC_CALL), + RULE(TOKEN_MINUS, unary, binary, PREC_TERM), + RULE(TOKEN_PLUS, NULL, binary, PREC_TERM), + RULE(TOKEN_SEMICOLON, NULL, NULL, PREC_NONE), + RULE(TOKEN_SOLIDUS, NULL, binary, PREC_FACTOR), + RULE(TOKEN_ASTERISK, NULL, binary, PREC_FACTOR), + RULE(TOKEN_MODULO, NULL, binary, PREC_FACTOR), + RULE(TOKEN_BANG, unary, NULL, PREC_NONE), + RULE(TOKEN_BANG_EQUAL, NULL, binary, PREC_EQUALITY), + RULE(TOKEN_EQUAL, NULL, NULL, PREC_NONE), + RULE(TOKEN_EQUAL_EQUAL, NULL, binary, PREC_EQUALITY), + RULE(TOKEN_GREATER, NULL, binary, PREC_COMPARISON), + RULE(TOKEN_GREATER_EQUAL, NULL, binary, PREC_COMPARISON), + RULE(TOKEN_LESS, NULL, binary, PREC_COMPARISON), + RULE(TOKEN_LESS_EQUAL, NULL, binary, PREC_COMPARISON), + RULE(TOKEN_IDENTIFIER, variable, NULL, PREC_NONE), + RULE(TOKEN_STRING, string, NULL, PREC_NONE), + RULE(TOKEN_NUMBER, number, NULL, PREC_NONE), + RULE(TOKEN_CODEPOINT, NULL, NULL, PREC_NONE), /* TODO */ + RULE(TOKEN_AND, NULL, and_, PREC_AND), + RULE(TOKEN_CLASS, NULL, NULL, PREC_NONE), + RULE(TOKEN_ELSE, NULL, NULL, PREC_NONE), + RULE(TOKEN_FALSE, literal, NULL, PREC_NONE), + RULE(TOKEN_FOR, NULL, NULL, PREC_NONE), + RULE(TOKEN_DEF, NULL, NULL, PREC_NONE), + RULE(TOKEN_IF, NULL, NULL, PREC_NONE), + RULE(TOKEN_IN, NULL, NULL, PREC_NONE), + RULE(TOKEN_LET, NULL, NULL, PREC_NONE), + RULE(TOKEN_NONE, literal, NULL, PREC_NONE), + RULE(TOKEN_NOT, unary, NULL, PREC_NONE), + RULE(TOKEN_OR, NULL, or_, PREC_OR), + RULE(TOKEN_PRINT, NULL, NULL, PREC_NONE), + RULE(TOKEN_RETURN, NULL, NULL, PREC_NONE), + RULE(TOKEN_SELF, self, NULL, PREC_NONE), + RULE(TOKEN_SUPER, super_, NULL, PREC_NONE), + RULE(TOKEN_TRUE, literal, NULL, PREC_NONE), + RULE(TOKEN_WHILE, NULL, NULL, PREC_NONE), /* This is going to get interesting */ - [TOKEN_INDENTATION] = {NULL, NULL, PREC_NONE}, - [TOKEN_ERROR] = {NULL, NULL, PREC_NONE}, - [TOKEN_EOF] = {NULL, NULL, PREC_NONE}, + RULE(TOKEN_INDENTATION, NULL, NULL, PREC_NONE), + RULE(TOKEN_ERROR, NULL, NULL, PREC_NONE), + RULE(TOKEN_EOL, NULL, NULL, PREC_NONE), + RULE(TOKEN_EOF, NULL, NULL, PREC_NONE), }; static void parsePrecedence(Precedence precedence) { advance(); ParseFn prefixRule = getRule(parser.previous.type)->prefix; if (prefixRule == NULL) { - error("expect expression"); + errorAtCurrent("expect expression"); return; } int canAssign = precedence <= PREC_ASSIGNMENT; @@ -1189,7 +1207,10 @@ KrkFunction * krk_compile(const char * src, int newScope, char * fileName) { while (!match(TOKEN_EOF)) { declaration(); - if (check(TOKEN_EOL)) advance(); + if (check(TOKEN_EOL) || check(TOKEN_INDENTATION)) { + /* There's probably already and error... */ + advance(); + } } KrkFunction * function = endCompiler(); diff --git a/scanner.c b/scanner.c index 9d936d7..bce5d8b 100644 --- a/scanner.c +++ b/scanner.c @@ -8,6 +8,7 @@ typedef struct { const char * start; const char * cur; + const char * linePtr; size_t line; int startOfLine; int hasUnget; @@ -20,6 +21,7 @@ void krk_initScanner(const char * src) { scanner.start = src; scanner.cur = src; scanner.line = 1; + scanner.linePtr = src; scanner.startOfLine = 1; scanner.hasUnget = 0; /* file, etc. ? */ @@ -29,12 +31,19 @@ static int isAtEnd() { return *scanner.cur == '\0'; } +static void nextLine() { + scanner.line++; + scanner.linePtr = scanner.cur; +} + static KrkToken makeToken(KrkTokenType type) { return (KrkToken){ .type = type, .start = scanner.start, - .length = (size_t)(scanner.cur - scanner.start), - .line = scanner.line + .length = (type == TOKEN_EOL) ? 0 : (size_t)(scanner.cur - scanner.start), + .line = scanner.line, + .linePtr = scanner.linePtr, + .col = (scanner.start - scanner.linePtr) + 1, }; } @@ -43,7 +52,9 @@ static KrkToken errorToken(const char * errorStr) { .type = TOKEN_ERROR, .start = errorStr, .length = strlen(errorStr), - .line = scanner.line + .line = scanner.line, + .linePtr = scanner.linePtr, + .col = (scanner.start - scanner.linePtr) + 1, }; } @@ -99,7 +110,7 @@ static KrkToken makeIndentation() { static KrkToken string() { while (peek() != '"' && !isAtEnd()) { if (peek() == '\\') advance(); /* Advance twice */ - if (peek() == '\n') scanner.line++; /* Not start of line because string */ + if (peek() == '\n') nextLine(); advance(); } @@ -259,14 +270,16 @@ KrkToken krk_scanToken() { if (isAtEnd()) return makeToken(TOKEN_EOF); if (c == '\n') { - scanner.line++; + KrkToken out; if (scanner.startOfLine) { /* Ignore completely blank lines */ - return makeToken(TOKEN_RETRY); + out = makeToken(TOKEN_RETRY); } else { scanner.startOfLine = 1; - return makeToken(TOKEN_EOL); + out = makeToken(TOKEN_EOL); } + nextLine(); + return out; } /* Not indentation, not a linefeed on an empty line, must be not be start of line any more */ diff --git a/scanner.h b/scanner.h index 275107d..f3bf54b 100644 --- a/scanner.h +++ b/scanner.h @@ -56,6 +56,8 @@ typedef struct { const char * start; size_t length; size_t line; + const char * linePtr; + size_t col; } KrkToken; extern void krk_initScanner(const char * src); diff --git a/vm.c b/vm.c index d080d53..dd18f30 100644 --- a/vm.c +++ b/vm.c @@ -43,7 +43,7 @@ static void dumpTraceback() { } } -static void runtimeError(const char * fmt, ...) { +void krk_runtimeError(const char * fmt, ...) { char buf[1024] = {0}; va_list args; va_start(args, fmt); @@ -96,7 +96,7 @@ void krk_defineNative(KrkTable * table, const char * name, NativeFn function) { static KrkValue krk_sleep(int argc, KrkValue argv[]) { if (argc < 1) { - runtimeError("sleep: expect at least one argument."); + krk_runtimeError("sleep: expect at least one argument."); return BOOLEAN_VAL(0); } @@ -120,20 +120,20 @@ static KrkValue krk_expose_hash_new(int argc, KrkValue argv[]) { static KrkValue krk_expose_hash_get(int argc, KrkValue argv[]) { if (argc < 2 || !IS_CLASS(argv[0])) { - runtimeError("wrong number of arguments"); + krk_runtimeError("wrong number of arguments"); return NONE_VAL(); } KrkClass * map = AS_CLASS(argv[0]); KrkValue out = NONE_VAL(); if (!krk_tableGet(&map->methods, argv[1], &out)) { - runtimeError("key error"); + krk_runtimeError("key error"); } return out; } static KrkValue krk_expose_hash_set(int argc, KrkValue argv[]) { if (argc < 3 || !IS_CLASS(argv[0])) { - runtimeError("wrong number of arguments"); + krk_runtimeError("wrong number of arguments"); return NONE_VAL(); } KrkClass * map = AS_CLASS(argv[0]); @@ -143,7 +143,7 @@ static KrkValue krk_expose_hash_set(int argc, KrkValue argv[]) { static KrkValue krk_expose_hash_capacity(int argc, KrkValue argv[]) { if (argc < 1 || !IS_CLASS(argv[0])) { - runtimeError("wrong number of arguments"); + krk_runtimeError("wrong number of arguments"); return NONE_VAL(); } KrkClass * map = AS_CLASS(argv[0]); @@ -152,7 +152,7 @@ static KrkValue krk_expose_hash_capacity(int argc, KrkValue argv[]) { static KrkValue krk_expose_hash_count(int argc, KrkValue argv[]) { if (argc < 1 || !IS_CLASS(argv[0])) { - runtimeError("wrong number of arguments"); + krk_runtimeError("wrong number of arguments"); return NONE_VAL(); } KrkClass * map = AS_CLASS(argv[0]); @@ -161,17 +161,17 @@ static KrkValue krk_expose_hash_count(int argc, KrkValue argv[]) { static KrkValue krk_expose_hash_key_at_index(int argc, KrkValue argv[]) { if (argc < 2 || !IS_CLASS(argv[0])) { - runtimeError("wrong number of arguments"); + krk_runtimeError("wrong number of arguments"); return NONE_VAL(); } if (!IS_INTEGER(argv[1])) { - runtimeError("expected integer index but got %s", krk_typeName(argv[1])); + krk_runtimeError("expected integer index but got %s", krk_typeName(argv[1])); return NONE_VAL(); } int i = AS_INTEGER(argv[1]); KrkClass * map = AS_CLASS(argv[0]); if (i < 0 || i > (int)map->methods.capacity) { - runtimeError("hash table index is out of range: %d", i); + krk_runtimeError("hash table index is out of range: %d", i); return NONE_VAL(); } KrkTableEntry entry = map->methods.entries[i]; @@ -185,13 +185,13 @@ static KrkValue krk_expose_list_new(int argc, KrkValue argv[]) { static KrkValue krk_expose_list_get(int argc, KrkValue argv[]) { if (argc < 2 || !IS_FUNCTION(argv[0]) || !IS_INTEGER(argv[1])) { - runtimeError("wrong number or type of arguments"); + krk_runtimeError("wrong number or type of arguments"); return NONE_VAL(); } KrkFunction * list = AS_FUNCTION(argv[0]); int index = AS_INTEGER(argv[1]); if (index < 0 || index >= (int)list->chunk.constants.count) { - runtimeError("index is out of range: %d", index); + krk_runtimeError("index is out of range: %d", index); return NONE_VAL(); } return list->chunk.constants.values[index]; @@ -199,13 +199,13 @@ static KrkValue krk_expose_list_get(int argc, KrkValue argv[]) { static KrkValue krk_expose_list_set(int argc, KrkValue argv[]) { if (argc < 3 || !IS_FUNCTION(argv[0]) || !IS_INTEGER(argv[1])) { - runtimeError("wrong number or type of arguments"); + krk_runtimeError("wrong number or type of arguments"); return NONE_VAL(); } KrkFunction * list = AS_FUNCTION(argv[0]); int index = AS_INTEGER(argv[1]); if (index < 0 || index >= (int)list->chunk.constants.count) { - runtimeError("index is out of range: %d", index); + krk_runtimeError("index is out of range: %d", index); return NONE_VAL(); } list->chunk.constants.values[index] = argv[2]; @@ -214,7 +214,7 @@ static KrkValue krk_expose_list_set(int argc, KrkValue argv[]) { static KrkValue krk_expose_list_append(int argc, KrkValue argv[]) { if (argc < 2 || !IS_FUNCTION(argv[0])) { - runtimeError("wrong number or type of arguments"); + krk_runtimeError("wrong number or type of arguments"); return NONE_VAL(); } KrkFunction * list = AS_FUNCTION(argv[0]); @@ -224,7 +224,7 @@ static KrkValue krk_expose_list_append(int argc, KrkValue argv[]) { static KrkValue krk_expose_list_length(int argc, KrkValue argv[]) { if (argc < 1 || !IS_FUNCTION(argv[0])) { - runtimeError("wrong number or type of arguments"); + krk_runtimeError("wrong number or type of arguments"); return NONE_VAL(); } KrkFunction * list = AS_FUNCTION(argv[0]); @@ -297,11 +297,11 @@ static KrkValue krk_set_tracing(int argc, KrkValue argv[]) { static int call(KrkClosure * closure, int argCount) { if (argCount != closure->function->arity) { - runtimeError("Wrong number of arguments (%d expected, got %d)", closure->function->arity, argCount); + krk_runtimeError("Wrong number of arguments (%d expected, got %d)", closure->function->arity, argCount); return 0; } if (vm.frameCount == FRAMES_MAX) { - runtimeError("Too many call frames."); + krk_runtimeError("Too many call frames."); return 0; } CallFrame * frame = &vm.frames[vm.frameCount++]; @@ -334,7 +334,7 @@ static int callValue(KrkValue callee, int argCount) { if (krk_tableGet(&_class->methods, vm.specialMethodNames[METHOD_INIT], &initializer)) { return call(AS_CLOSURE(initializer), argCount); } else if (argCount != 0) { - runtimeError("Class does not have an __init__ but arguments were passed to initializer: %d\n", argCount); + krk_runtimeError("Class does not have an __init__ but arguments were passed to initializer: %d\n", argCount); return 0; } return 1; @@ -348,7 +348,7 @@ static int callValue(KrkValue callee, int argCount) { break; } } - runtimeError("Attempted to call non-callable type: %s", krk_typeName(callee)); + krk_runtimeError("Attempted to call non-callable type: %s", krk_typeName(callee)); return 0; } @@ -509,7 +509,7 @@ const char * krk_typeName(KrkValue value) { } else if (IS_FLOATING(b)) { \ if (IS_INTEGER(a)) return FLOATING_VAL((double)AS_INTEGER(a) operator AS_FLOATING(b)); \ } \ - runtimeError("Incompatible types for binary operand %s: %s and %s", #operator, krk_typeName(a), krk_typeName(b)); \ + krk_runtimeError("Incompatible types for binary operand %s: %s and %s", #operator, krk_typeName(a), krk_typeName(b)); \ return NONE_VAL(); \ } @@ -532,7 +532,7 @@ static KrkValue modulo(KrkValue a, KrkValue b) { } else if (IS_FLOATING(b)) { \ if (IS_INTEGER(a)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ } \ - runtimeError("Can not compare types %s and %s", krk_typeName(a), krk_typeName(b)); \ + krk_runtimeError("Can not compare types %s and %s", krk_typeName(a), krk_typeName(b)); \ return NONE_VAL(); \ } @@ -585,7 +585,7 @@ static void addObjects() { KrkValue result = run(); vm.exitOnFrame = previousExitFrame; if (!IS_STRING(result)) { - runtimeError("__str__ produced something that wasn't a string: %s", krk_typeName(result)); + krk_runtimeError("__str__ produced something that wasn't a string: %s", krk_typeName(result)); return; } sprintf(tmp, "%s", AS_CSTRING(result)); @@ -595,7 +595,7 @@ static void addObjects() { } concatenate(a->chars,tmp,a->length,strlen(tmp)); } else { - runtimeError("Can not concatenate types %s and %s", krk_typeName(_a), krk_typeName(_b)); \ + krk_runtimeError("Can not concatenate types %s and %s", krk_typeName(_a), krk_typeName(_b)); \ } } @@ -617,7 +617,7 @@ static size_t readBytes(CallFrame * frame, int num) { return (top << 16) | (mid << 8) | (bot); } - runtimeError("Invalid byte read?"); + krk_runtimeError("Invalid byte read?"); return (size_t)-1; } @@ -659,20 +659,20 @@ static KrkValue _string_length(int argc, KrkValue argv[]) { static KrkValue _string_get(int argc, KrkValue argv[]) { if (argc != 2) { - runtimeError("Wrong number of arguments to String.__get__"); + krk_runtimeError("Wrong number of arguments to String.__get__"); return NONE_VAL(); } if (!IS_STRING(argv[0])) { - runtimeError("First argument to __get__ must be String"); + krk_runtimeError("First argument to __get__ must be String"); return NONE_VAL(); } if (!IS_INTEGER(argv[1])) { - runtimeError("Strings can not index by %s", krk_typeName(argv[1])); + krk_runtimeError("Strings can not index by %s", krk_typeName(argv[1])); return NONE_VAL(); } int asInt = AS_INTEGER(argv[1]); if (asInt < 0 || asInt >= (int)AS_STRING(argv[0])->length) { - runtimeError("String index out of range: %d", asInt); + krk_runtimeError("String index out of range: %d", asInt); return NONE_VAL(); } return INTEGER_VAL(AS_CSTRING(argv[0])[asInt]); @@ -771,7 +771,7 @@ static KrkValue run() { KrkValue value = krk_pop(); if (IS_INTEGER(value)) krk_push(INTEGER_VAL(-AS_INTEGER(value))); else if (IS_FLOATING(value)) krk_push(FLOATING_VAL(-AS_FLOATING(value))); - else { runtimeError("Incompatible operand type for prefix negation."); goto _finishException; } + else { krk_runtimeError("Incompatible operand type for prefix negation."); goto _finishException; } break; } case OP_CONSTANT_LONG: @@ -798,7 +798,7 @@ static KrkValue run() { KrkString * name = READ_STRING((opcode == OP_GET_GLOBAL ? 1 : 3)); KrkValue value; if (!krk_tableGet(&vm.globals, OBJECT_VAL(name), &value)) { - runtimeError("Undefined variable '%s'.", name->chars); + krk_runtimeError("Undefined variable '%s'.", name->chars); goto _finishException; } krk_push(value); @@ -810,7 +810,7 @@ static KrkValue run() { if (krk_tableSet(&vm.globals, OBJECT_VAL(name), krk_peek(0))) { krk_tableDelete(&vm.globals, OBJECT_VAL(name)); /* TODO: This should probably just work as an assignment? */ - runtimeError("Undefined variable '%s'.", name->chars); + krk_runtimeError("Undefined variable '%s'.", name->chars); goto _finishException; } break; @@ -828,7 +828,7 @@ static KrkValue run() { module = krk_runfile(tmp,1,name->chars,tmp); vm.exitOnFrame = previousExitFrame; if (!IS_OBJECT(module)) { - runtimeError("Failed to import module - expected to receive an object, but got a %s instead.", krk_typeName(module)); + krk_runtimeError("Failed to import module - expected to receive an object, but got a %s instead.", krk_typeName(module)); goto _finishException; } krk_push(module); @@ -980,7 +980,7 @@ static KrkValue run() { krk_push(OBJECT_VAL(bound)); KRK_RESUME_GC(); } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_SET])) { - runtimeError("Strings are not mutable."); + krk_runtimeError("Strings are not mutable."); goto _finishException; } else { goto _undefined; @@ -1004,18 +1004,18 @@ static KrkValue run() { break; } default: - runtimeError("Don't know how to retreive properties for %s yet", krk_typeName(krk_peek(0))); + krk_runtimeError("Don't know how to retreive properties for %s yet", krk_typeName(krk_peek(0))); goto _finishException; } break; _undefined: - runtimeError("Field '%s' of %s is not defined.", name->chars, krk_typeName(krk_peek(0))); + krk_runtimeError("Field '%s' of %s is not defined.", name->chars, krk_typeName(krk_peek(0))); goto _finishException; } case OP_SET_PROPERTY_LONG: case OP_SET_PROPERTY: { if (!IS_INSTANCE(krk_peek(1))) { - runtimeError("Don't know how to set properties for %s yet", krk_typeName(krk_peek(1))); + krk_runtimeError("Don't know how to set properties for %s yet", krk_typeName(krk_peek(1))); goto _finishException; } KrkInstance * instance = AS_INSTANCE(krk_peek(1)); @@ -1034,7 +1034,7 @@ _undefined: case OP_INHERIT: { KrkValue superclass = krk_peek(1); if (!IS_CLASS(superclass)) { - runtimeError("Superclass must be a class."); + krk_runtimeError("Superclass must be a class."); return NONE_VAL(); } KrkClass * subclass = AS_CLASS(krk_peek(0)); diff --git a/vm.h b/vm.h index a38aecf..1a0e71c 100644 --- a/vm.h +++ b/vm.h @@ -1,5 +1,6 @@ #pragma once +#include #include "kuroko.h" #include "value.h" #include "table.h" @@ -70,6 +71,7 @@ extern KrkValue krk_pop(void); extern const char * krk_typeName(KrkValue value); extern void krk_defineNative(KrkTable * table, const char * name, NativeFn function); extern void krk_attachNamedObject(KrkTable * table, const char name[], KrkObj * obj); +extern void krk_runtimeError(const char * fmt, ...); #define KRK_PAUSE_GC() do { vm.flags |= KRK_GC_PAUSED; } while (0) #define KRK_RESUME_GC() do { vm.flags &= ~(KRK_GC_PAUSED); } while (0)