diff --git a/runtests b/runtests index f9570aa7..c72e9b62 100755 --- a/runtests +++ b/runtests @@ -35,7 +35,7 @@ else: EXPECT_PATTERN = re.compile(r'// expect: (.*)') EXPECT_ERROR_PATTERN = re.compile(r'// expect error') EXPECT_ERROR_LINE_PATTERN = re.compile(r'// expect error line (\d+)') -ERROR_PATTERN = re.compile(r'\[Line (\d+)\] Error ') +ERROR_PATTERN = re.compile(r'\[Line (\d+)\] Error') SKIP_PATTERN = re.compile(r'// skip: (.*)') NONTEST_PATTERN = re.compile(r'// nontest') diff --git a/src/wren_compiler.c b/src/wren_compiler.c index 95c866dd..bac8463f 100644 --- a/src/wren_compiler.c +++ b/src/wren_compiler.c @@ -279,6 +279,24 @@ static int initCompiler(Compiler* compiler, Parser* parser, Compiler* parent, return addConstant(parent, OBJ_VAL(compiler->fn)); } +// Outputs a compile or syntax error. This also marks the compilation as having +// an error, which ensures that the resulting code will be discarded and never +// run. This means that after calling lexError(), it's fine to generate whatever +// invalid bytecode you want since it won't be used. +static void lexError(Parser* parser, const char* format, ...) +{ + parser->hasError = true; + + fprintf(stderr, "[Line %d] Error: ", parser->currentLine); + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + fprintf(stderr, "\n"); +} + // Outputs a compile or syntax error. This also marks the compilation as having // an error, which ensures that the resulting code will be discarded and never // run. This means that after calling error(), it's fine to generate whatever @@ -344,6 +362,7 @@ static char nextChar(Parser* parser) { char c = peekChar(parser); parser->currentChar++; + if (c == '\n') parser->currentLine++; return c; } @@ -388,8 +407,11 @@ static void skipBlockComment(Parser* parser) int nesting = 1; while (nesting > 0) { - // TODO: Unterminated comment. Should return error. - if (peekChar(parser) == '\0') return; + if (peekChar(parser) == '\0') + { + lexError(parser, "Unterminated block comment."); + return; + } if (peekChar(parser) == '/' && peekNextChar(parser) == '*') { @@ -581,7 +603,6 @@ static void readRawToken(Parser* parser) return; case '\n': - parser->currentLine++; makeToken(parser, TOKEN_LINE); return; @@ -604,8 +625,7 @@ static void readRawToken(Parser* parser) } else { - // TODO: Handle error. - makeToken(parser, TOKEN_ERROR); + lexError(parser, "Invalid character '%c'.", c); } return; } diff --git a/test/comments/unterminated_block.wren b/test/comments/unterminated_block.wren new file mode 100644 index 00000000..18d6c474 --- /dev/null +++ b/test/comments/unterminated_block.wren @@ -0,0 +1,3 @@ +// expect error line 3 +IO.write("nope") /* +oops \ No newline at end of file diff --git a/test/comments/unterminated_nested_block.wren b/test/comments/unterminated_nested_block.wren new file mode 100644 index 00000000..2c196fa1 --- /dev/null +++ b/test/comments/unterminated_nested_block.wren @@ -0,0 +1,4 @@ +// expect error line 4 +IO.write("nope") /* /* /* +*/ +oops \ No newline at end of file diff --git a/test/unexpected_character.wren b/test/unexpected_character.wren new file mode 100644 index 00000000..9d6f57f6 --- /dev/null +++ b/test/unexpected_character.wren @@ -0,0 +1,3 @@ +IO.write("no") +// Something that's not a valid character: +^ // expect error