once again, completely redo the parsing for indentation so that we can have pretty syntax errors
This commit is contained in:
parent
54c3779938
commit
c6e5c8c120
235
compiler.c
235
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();
|
||||
|
27
scanner.c
27
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 */
|
||||
|
@ -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
76
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));
|
||||
|
2
vm.h
2
vm.h
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user