Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close #38: Improve error reporting #45

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion src/error_codes.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ enum ErrorCodes {
ERROR_CODE_INVALID_STATE,
ERROR_CODE_INVALID_ARGUMENT,

ERROR_CODE_UNEXPECTED_EOF,
ERROR_CODE_UNEXPECTED_CHARACTER,

ERROR_CODE_LEXER_ERROR,

ERROR_CODE_INSTRUMENT_BUFFER_OVERFLOW,

ERROR_CODE_BAR_TOO_SHORT,
Expand Down
39 changes: 22 additions & 17 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ static int play_bar_token(Player* player, Instrument* instrument, Token* bar);

// --------

const char* InterpreterErrors[NUM_INTERPRETER_ERROR_STATES] = {
"unknown error",
"unexpected token",
"internal error",
};

// --------

static bool destroy_token(Token* token) {
Token_destroy_at(token);
return true;
Expand Down Expand Up @@ -80,20 +88,23 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) {
if(status != 0) {
interpreter->finished = true;
interpreter->error = true;
interpreter->error_state = INTERPRETER_ERROR_STATE_INTERNAL_ERROR;
return status;
}

status = Player_init_at(&player);
if(status != 0) {
interpreter->finished = true;
interpreter->error = true;
interpreter->error_state = INTERPRETER_ERROR_STATE_INTERNAL_ERROR;
return status;
}

status = Instrument_init_at(&instrument, NULL);
if(status != 0) {
interpreter->finished = true;
interpreter->error = true;
interpreter->error_state = INTERPRETER_ERROR_STATE_INTERNAL_ERROR;
Player_destroy_at(&player);
return status;
}
Expand All @@ -117,6 +128,7 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) {
default:
interpreter->finished = true;
interpreter->error = true;
interpreter->error_state = INTERPRETER_ERROR_STATE_UNEXPECTED_TOKEN;
continue;
}

Expand All @@ -128,7 +140,6 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) {
if((status = play_bar_token(&player, &instrument, &token)) != 0) {
interpreter->finished = true;
interpreter->error = true;
interpreter->error_state = INTERPRETER_ERROR_STATE_INTERNAL_ERROR;
continue;
}
continue;
Expand All @@ -140,6 +151,7 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) {
default:
interpreter->finished = true;
interpreter->error = true;
interpreter->error_state = INTERPRETER_ERROR_STATE_UNEXPECTED_TOKEN;
continue;
}

Expand All @@ -155,35 +167,28 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) {
}

if(lexer.error) {
fprintf(stderr, "Error at %s:%u:%u\n", interpreter->filename, lexer.line, lexer.col);
status = status != 0? status: ERROR_CODE_UNKNOWN_ERROR;
fputs(interpreter->filename, stderr);
fputs(":", stderr);
Lexer_print_error(&lexer, stderr);
status = status != 0? status: ERROR_CODE_LEXER_ERROR;
}

if(interpreter->error) {
else if(interpreter->error) {

switch(interpreter->error_state) {

case INTERPRETER_ERROR_STATE_UNEXPECTED_TOKEN:

fprintf(stderr, "%s:%u:%u: Unexpected token\n", interpreter->filename, lexer.line, lexer.col);
if(token.content != NULL) {
fputs((char*) (token.content), stderr);
}
break;
fprintf(stderr, "%s:%u:%u: %s\n", interpreter->filename, lexer.line, lexer.col, InterpreterErrors[interpreter->error_state]);

case INTERPRETER_ERROR_STATE_INTERNAL_ERROR:

if(token.content == NULL) {
fprintf(stderr, "Internal interpreter error while processing %s:%u:%u\n", interpreter->filename, lexer.line, lexer.col);
}
else {
fprintf(stderr, "Internal interpreter error while processing %s:%u:%u: %s\n", interpreter->filename, lexer.line, lexer.col, (char*) (token.content));
if(Token_print(&token, stderr)) {
fputs("\n", stderr);
}

break;

default:
fprintf(stderr, "Unknown interpreter error\n");
fprintf(stderr, "%s: %s while parsing\n", interpreter->filename, InterpreterErrors[interpreter->error_state]);
break;
}

Expand Down
6 changes: 5 additions & 1 deletion src/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ typedef enum InterpreterState {

typedef enum InterpreterErrorState {
INTERPRETER_ERROR_STATE_UNKNOWN_ERROR = 0,
INTERPRETER_ERROR_STATE_UNEXPECTED_TOKEN,
INTERPRETER_ERROR_STATE_INTERNAL_ERROR,
INTERPRETER_ERROR_STATE_UNEXPECTED_TOKEN,
NUM_INTERPRETER_ERROR_STATES,
} InterpreterErrorState;

Expand All @@ -39,6 +39,10 @@ struct Interpreter {

// --------

extern const char* InterpreterErrors[NUM_INTERPRETER_ERROR_STATES];

// --------

void Interpreter_init_at(Interpreter* interpreter);

int Interpreter_interpret(Interpreter* interpreter, FILE* stream);
Expand Down
86 changes: 81 additions & 5 deletions src/lexer.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
Expand All @@ -12,6 +11,16 @@ static int advance(Lexer* lexer);

static int fill_buffer_until(Lexer* lexer, char** buffer, size_t initial_capacity, size_t* length, char endchar, bool skip_first);

static int print_error_details(Lexer* lexer, FILE* stream);

// --------

const char* LexerErrors[NUM_LEXER_ERROR_STATES] = {
"unknown error",
"unexpected end of file",
"unexpected character",
};

// --------

static int advance(Lexer* lexer) {
Expand Down Expand Up @@ -82,8 +91,58 @@ static int fill_buffer_until(Lexer* lexer, char** buffer, size_t initial_capacit

lexer->finished = true;
lexer->error = true;
lexer->error_state = LEXER_ERROR_STATE_UNEXPECTED_EOF;

return ERROR_CODE_UNEXPECTED_EOF;
return ERROR_CODE_LEXER_ERROR;
}

static int print_error_details(Lexer* lexer, FILE* stream) {

char* buf = NULL;
long offset = 0;
size_t err_pos = 0;

err_pos = lexer->col - 1;

switch(lexer->error_state) {

case LEXER_ERROR_STATE_UNEXPECTED_EOF:
case LEXER_ERROR_STATE_UNEXPECTED_CHARACTER:

if((offset = ftell(lexer->stream)) < 0) {
perror("ftell");
return ERROR_CODE_UNKNOWN_SYSTEM_ERROR;
}

if(fseek(lexer->stream, offset - err_pos, SEEK_SET) < 0) {
perror("fseek");
return ERROR_CODE_UNKNOWN_SYSTEM_ERROR;
}

if((buf = reallocarray(NULL, err_pos, sizeof(char))) == NULL) {
perror("malloc");
return ERROR_CODE_MALLOC_FAILURE;
}
memset(buf, 0, err_pos);

fread(buf, sizeof(char), err_pos, lexer->stream);
fputs(buf, stream);

free(buf);

fputs("\n", stream);

if(lexer->error_state == LEXER_ERROR_STATE_UNEXPECTED_CHARACTER) {
if(fprintf(stream, "%.*s^\n", (int) err_pos - 1, " ") < 0) {
return ERROR_CODE_UNKNOWN_SYSTEM_ERROR;
}
}

return 0;

default:
return 0;
}
}

// --------
Expand All @@ -92,6 +151,7 @@ int Lexer_init_at(Lexer* lexer, FILE* stream) {

lexer->stream = stream;
lexer->state = LEXER_STATE_EXPECTING_NEW_TOKEN;
lexer->error_state = LEXER_ERROR_STATE_UNKNOWN_ERROR;
lexer->symbol = '\0';

if(lexer->stream == NULL) {
Expand Down Expand Up @@ -157,15 +217,17 @@ int Lexer_get_next_token(Lexer* lexer, Token* token) {
default:
lexer->error = true;
lexer->finished = true;
return ERROR_CODE_UNEXPECTED_CHARACTER;
lexer->error_state = LEXER_ERROR_STATE_UNEXPECTED_CHARACTER;
return ERROR_CODE_LEXER_ERROR;
}

case LEXER_STATE_EXPECTING_TRACK_START:

if(lexer->symbol != '|') {
lexer->error = true;
lexer->finished = true;
return ERROR_CODE_UNEXPECTED_CHARACTER;
lexer->error_state = LEXER_ERROR_STATE_UNEXPECTED_CHARACTER;
return ERROR_CODE_LEXER_ERROR;
}

lexer->state = LEXER_STATE_EXPECTING_BAR;
Expand Down Expand Up @@ -212,7 +274,10 @@ int Lexer_get_next_token(Lexer* lexer, Token* token) {
case LEXER_STATE_EXPECTING_BAR_COMMENT:

if(!(lexer->symbol == '/')) {
return ERROR_CODE_UNEXPECTED_CHARACTER;
lexer->error = true;
lexer->finished = true;
lexer->error_state = LEXER_ERROR_STATE_UNEXPECTED_CHARACTER;
return ERROR_CODE_LEXER_ERROR;
}

status = fill_buffer_until(lexer, &buffer, 16, &length, lexer->state == LEXER_STATE_EXPECTING_BAR_COMMENT? '|': '\n', true);
Expand Down Expand Up @@ -241,3 +306,14 @@ int Lexer_get_next_token(Lexer* lexer, Token* token) {
return 0;
}

int Lexer_print_error(Lexer* lexer, FILE* stream) {

if(lexer == NULL || stream == NULL) {
return ERROR_CODE_INVALID_ARGUMENT;
}

fprintf(stream ,"%u:%u: %s\n", lexer->line, lexer->col - 1, LexerErrors[lexer->error_state]);

return print_error_details(lexer, stream);
}

14 changes: 14 additions & 0 deletions src/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@ typedef enum LexerState {
NUM_LEXER_STATES,
} LexerState;

typedef enum LexerErrorState {
LEXER_ERROR_STATE_UNKNOWN_ERROR = 0,
LEXER_ERROR_STATE_UNEXPECTED_EOF,
LEXER_ERROR_STATE_UNEXPECTED_CHARACTER,
NUM_LEXER_ERROR_STATES,
} LexerErrorState;

struct Lexer {
FILE* stream;
LexerState state;
LexerErrorState error_state;
unsigned int line;
unsigned int col;
char symbol;
Expand All @@ -38,10 +46,16 @@ struct Lexer {

// --------

extern const char* LexerErrors[NUM_LEXER_ERROR_STATES];

// --------

int Lexer_init_at(Lexer* lexer, FILE* stream);

int Lexer_get_next_token(Lexer* lexer, Token* token);

int Lexer_print_error(Lexer* lexer, FILE* stream);

// --------
#endif

22 changes: 22 additions & 0 deletions src/token.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <string.h>

#include "error_codes.h"
#include "token.h"

// --------
Expand Down Expand Up @@ -35,3 +36,24 @@ void Token_destroy_at(Token* token) {
return;
}

int Token_print(Token* token, FILE* stream) {

if(token == NULL || stream == NULL) {
return ERROR_CODE_INVALID_ARGUMENT;
}

switch(token->type) {

case TOKEN_BAR:
if(token->content != NULL) {
fputs("\"", stream);
fputs((char*) (token->content), stream);
fputs("\"", stream);
}
return 0;

default:
return 0;
}
}

3 changes: 3 additions & 0 deletions src/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define TOKEN_H
// --------

#include <stdio.h>
#include <stddef.h>

// --------
Expand Down Expand Up @@ -39,6 +40,8 @@ void Token_set_content(Token* token, void* content, size_t content_length);

void Token_destroy_at(Token* token);

int Token_print(Token* token, FILE* stream);

// --------
#endif