once again, completely redo the parsing for indentation so that we can have pretty syntax errors

This commit is contained in:
K. Lange 2020-12-29 15:26:45 +09:00
parent 54c3779938
commit c6e5c8c120
5 changed files with 190 additions and 152 deletions

View File

@ -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();

View File

@ -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 */

View File

@ -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);

76
vm.c
View File

@ -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));

2
vm.h
View File

@ -1,5 +1,6 @@
#pragma once
#include <stdarg.h>
#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)