feat: add compile error detection and line tracking to test runner and compiler
Add support for testing compile errors by introducing `// expect error` annotations in test files. The test runner now validates that expected errors occur on specific lines and that the compiler exits with code 1 when errors are present. The compiler's error output is enhanced to include line numbers in the format `[Line N] Error`, and the `Token` struct gains a `line` field to track token positions. The main entry point returns exit code 1 when compilation fails, enabling the test runner to verify error exit codes.
This commit is contained in:
+24
-8
@@ -51,8 +51,15 @@ typedef enum
|
||||
typedef struct Token_s
|
||||
{
|
||||
TokenType type;
|
||||
|
||||
// The beginning of the token as an offset of characters in the source.
|
||||
int start;
|
||||
|
||||
// The offset of the character immediately following the end of the token.
|
||||
int end;
|
||||
|
||||
// The 1-based line where the token appears.
|
||||
int line;
|
||||
} Token;
|
||||
|
||||
typedef struct
|
||||
@@ -68,6 +75,9 @@ typedef struct
|
||||
// The position of the current character being lexed.
|
||||
int currentChar;
|
||||
|
||||
// The 1-based line number of [currentChar].
|
||||
int currentLine;
|
||||
|
||||
// The most recently lexed token.
|
||||
Token current;
|
||||
|
||||
@@ -191,12 +201,14 @@ ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
||||
|
||||
parser.tokenStart = 0;
|
||||
parser.currentChar = 0;
|
||||
parser.currentLine = 1;
|
||||
|
||||
// Zero-init the current token. This will get copied to previous when
|
||||
// advance() is called below.
|
||||
parser.current.type = TOKEN_EOF;
|
||||
parser.current.start = 0;
|
||||
parser.current.end = 0;
|
||||
parser.current.line = 0;
|
||||
|
||||
// Read the first token.
|
||||
nextToken(&parser);
|
||||
@@ -300,22 +312,22 @@ void emit(Compiler* compiler, Code code)
|
||||
void error(Compiler* compiler, const char* format, ...)
|
||||
{
|
||||
compiler->parser->hasError = 1;
|
||||
printf("Compile error on '");
|
||||
fprintf(stderr, "[Line %d] Error on '", compiler->parser->previous.line);
|
||||
|
||||
for (int i = compiler->parser->previous.start;
|
||||
i < compiler->parser->previous.end; i++)
|
||||
{
|
||||
putchar(compiler->parser->source[i]);
|
||||
putc(compiler->parser->source[i], stderr);
|
||||
}
|
||||
|
||||
printf("': ");
|
||||
fprintf(stderr, "': ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void statement(Compiler* compiler)
|
||||
@@ -499,8 +511,8 @@ void primary(Compiler* compiler)
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(bob): Look for globals or names in outer scopes.
|
||||
error(compiler, "Unknown variable.");
|
||||
// TODO(bob): Look for names in outer scopes.
|
||||
error(compiler, "Undefined variable.");
|
||||
}
|
||||
|
||||
// Number.
|
||||
@@ -734,7 +746,10 @@ void readRawToken(Parser* parser)
|
||||
}
|
||||
return;
|
||||
|
||||
case '\n': makeToken(parser, TOKEN_LINE); return;
|
||||
case '\n':
|
||||
parser->currentLine++;
|
||||
makeToken(parser, TOKEN_LINE);
|
||||
return;
|
||||
|
||||
case ' ': skipWhitespace(parser); break;
|
||||
case '"': readString(parser); return;
|
||||
@@ -842,5 +857,6 @@ void makeToken(Parser* parser, TokenType type)
|
||||
parser->current.type = type;
|
||||
parser->current.start = parser->tokenStart;
|
||||
parser->current.end = parser->currentChar;
|
||||
parser->current.line = parser->currentLine;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -28,15 +28,20 @@ int main(int argc, const char * argv[])
|
||||
VM* vm = newVM();
|
||||
ObjBlock* block = compile(vm, source, length);
|
||||
|
||||
int exitCode = 0;
|
||||
if (block)
|
||||
{
|
||||
interpret(vm, block);
|
||||
}
|
||||
else
|
||||
{
|
||||
exitCode = 1;
|
||||
}
|
||||
|
||||
freeVM(vm);
|
||||
free(source);
|
||||
|
||||
return 0;
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user