diff --git a/src/bar_token.c b/src/bar_token.c index ec47441..e9e1881 100644 --- a/src/bar_token.c +++ b/src/bar_token.c @@ -35,7 +35,7 @@ void BarToken_destroy_at(BarToken* token) { return; } -int BarToken_print(BarToken* token, FILE* stream) { +int BarToken_print(const BarToken* token, FILE* stream) { if(token == NULL || token->content == NULL || stream == NULL) { return ERROR_CODE_INVALID_ARGUMENT; diff --git a/src/bar_token.h b/src/bar_token.h index 95d2d83..04a11f5 100644 --- a/src/bar_token.h +++ b/src/bar_token.h @@ -30,7 +30,7 @@ void BarToken_set_content(BarToken* token, char* content, size_t content_length) void BarToken_destroy_at(BarToken* token); -int BarToken_print(BarToken* token, FILE* stream); +int BarToken_print(const BarToken* token, FILE* stream); // -------- #endif diff --git a/src/error_codes.h b/src/error_codes.h index 46c22b4..c7707dd 100644 --- a/src/error_codes.h +++ b/src/error_codes.h @@ -35,6 +35,8 @@ enum ErrorCodes { ERROR_CODE_CONFIG_WRITE_AFTER_READ, + ERROR_CODE_NOTE_COMPILER_ERROR, + }; // -------- diff --git a/src/error_messages.c b/src/error_messages.c new file mode 100644 index 0000000..114a41b --- /dev/null +++ b/src/error_messages.c @@ -0,0 +1,56 @@ +#include +#include + +#include "error_codes.h" +#include "error_messages.h" + +// -------- + +const char* get_error_message(int error_code) { + + switch(error_code) { + + case ERROR_CODE_UNKNOWN_SYSTEM_ERROR: + return "Unknown system error"; + + case ERROR_CODE_INTEGER_OVERFLOW: + return strerror(ERANGE); + + case ERROR_CODE_MALLOC_FAILURE: + return strerror(ENOMEM); + + case ERROR_CODE_OPEN_FAILURE: + return "Cannot open"; + + case ERROR_CODE_SDL_ERROR: + return "SDL error"; + + case ERROR_CODE_SUCCESS: + return "Success"; + + case ERROR_CODE_INVALID_STATE: + return "Encountered invalid state"; + + case ERROR_CODE_INVALID_ARGUMENT: + return strerror(EINVAL); + + case ERROR_CODE_UNEXPECTED_EOF: + return "Unexpected end of file"; + + case ERROR_CODE_UNEXPECTED_CHARACTER: + return "Unexpected character"; + + case ERROR_CODE_UNEXPECTED_FOLLOW_UP_CHARACTER: + return "Unexpected follow-up character"; + + case ERROR_CODE_BAR_TOO_SHORT: + return "Bar too short"; + + case ERROR_CODE_BAR_TOO_LONG: + return "Bar too long"; + + default: + return "Unknown error"; + } +} + diff --git a/src/error_messages.h b/src/error_messages.h new file mode 100644 index 0000000..24ef63e --- /dev/null +++ b/src/error_messages.h @@ -0,0 +1,9 @@ +#ifndef ERROR_MESSAGES_H +#define ERROR_MESSAGES_H +// -------- + +const char* get_error_message(int error_code); + +// -------- +#endif + diff --git a/src/instrument.c b/src/instrument.c index 850a066..e783125 100644 --- a/src/instrument.c +++ b/src/instrument.c @@ -130,7 +130,7 @@ int Instrument_add_notes_for_bar(Instrument* instrument, NoteProvider note_provi } if(status != 0) { - return status; + return ERROR_CODE_NOTE_COMPILER_ERROR; } if(!instrument_done) { diff --git a/src/interpreter.c b/src/interpreter.c index 9686994..bdc2115 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -3,6 +3,7 @@ #include #include "error_codes.h" +#include "error_messages.h" #include "config.h" #include "instrument.h" #include "interpreter.h" @@ -17,9 +18,11 @@ static bool destroy_token(Token* token); static bool destroy_bar_token(BarToken* bar); -static int play_track_token(Token* track); +static int play_track_token(Interpreter* interpreter, Token* track); -static int play_bar_token(Player* player, Instrument* instrument, BarToken* bar); +static int play_bar_token(Interpreter* interpreter, Player* player, Instrument* instrument, BarToken* bar); + +static int print_bar_length_error(const Interpreter* interpreter, FILE* stream, const BarToken* bar, int error_code); // -------- @@ -32,7 +35,7 @@ static bool destroy_bar_token(BarToken* bar) { BarToken_destroy_at(bar); return true; } -static int play_track_token(Token* track) { +static int play_track_token(Interpreter* interpreter, Token* track) { if(track == NULL || track->type != TOKEN_TRACK) { return ERROR_CODE_INVALID_ARGUMENT; @@ -69,7 +72,7 @@ static int play_track_token(Token* track) { } while(status == 0 && destroy_bar_token(&bar) && !lexer.super.finished && (status = TrackLexer_get_next_bar(&lexer, &bar)) == 0) { - status = play_bar_token(&player, &instrument, &bar); + status = play_bar_token(interpreter, &player, &instrument, &bar); } if(status == 0) { @@ -84,7 +87,7 @@ static int play_track_token(Token* track) { return status; } -static int play_bar_token(Player* player, Instrument* instrument, BarToken* bar) { +static int play_bar_token(Interpreter* interpreter, Player* player, Instrument* instrument, BarToken* bar) { NoteCompiler compiler; int status; @@ -95,10 +98,35 @@ static int play_bar_token(Player* player, Instrument* instrument, BarToken* bar) return ERROR_CODE_INVALID_ARGUMENT; } - NoteCompiler_init_at(&compiler, (char*) (bar->content), bar->content_length); + status = NoteCompiler_init_at(&compiler, bar, interpreter->filename); + if(status != 0) { + return status; + } status = Instrument_add_notes_for_bar(instrument, &NoteCompiler_get_next_note, &compiler); + if(status != 0) { + + switch(status) { + + case ERROR_CODE_NOTE_COMPILER_ERROR: + + if(NoteCompiler_print_error(&compiler, stderr) == 0) { + interpreter->printed_err_msg = true; + } + break; + + case ERROR_CODE_BAR_TOO_SHORT: + case ERROR_CODE_BAR_TOO_LONG: + + if(print_bar_length_error(interpreter, stderr, bar, status) == 0) { + interpreter->printed_err_msg = true; + } + break; + + default: + break; + } return status; } @@ -110,6 +138,29 @@ static int play_bar_token(Player* player, Instrument* instrument, BarToken* bar) return Player_play_bar(player); } +static int print_bar_length_error(const Interpreter* interpreter, FILE* stream, const BarToken* bar, int error_code) { + + int status = 0; + + if(interpreter == NULL || stream == NULL || bar == NULL) { + return ERROR_CODE_INVALID_ARGUMENT; + } + + if(bar->content == NULL) { + return ERROR_CODE_INVALID_STATE; + } + + fprintf(stream, "%s:%u:%u: ", interpreter->filename, bar->line, bar->col); + + fputs(get_error_message(error_code), stream); + fputs(":\n", stream); + + status = BarToken_print(bar, stream); + fputs("\n", stream); + + return status; +} + // -------- void Interpreter_init_at(Interpreter* interpreter) { @@ -119,6 +170,7 @@ void Interpreter_init_at(Interpreter* interpreter) { interpreter->error_state = INTERPRETER_ERROR_STATE_UNKNOWN_ERROR; interpreter->finished = false; interpreter->error = false; + interpreter->printed_err_msg = false; return; } @@ -194,7 +246,7 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) { case TOKEN_TRACK: - if((status = play_track_token(&token)) != 0) { + if((status = play_track_token(interpreter, &token)) != 0) { interpreter->finished = true; interpreter->error = true; interpreter->error_state = INTERPRETER_ERROR_STATE_INTERNAL_ERROR; @@ -223,7 +275,7 @@ int Interpreter_interpret(Interpreter* interpreter, FILE* stream) { status = status != 0? status: ERROR_CODE_UNKNOWN_ERROR; } - if(interpreter->error) { + if(interpreter->error && !interpreter->printed_err_msg) { switch(interpreter->error_state) { diff --git a/src/interpreter.h b/src/interpreter.h index a817d3a..0695550 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -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; @@ -35,6 +35,7 @@ struct Interpreter { InterpreterErrorState error_state; bool finished; bool error; + bool printed_err_msg; }; // -------- diff --git a/src/note_compiler.c b/src/note_compiler.c index e8613b9..4163bae 100644 --- a/src/note_compiler.c +++ b/src/note_compiler.c @@ -1,9 +1,9 @@ -#include #include #include #include "error_codes.h" +#include "error_messages.h" #include "note_conversion.h" #include "note_compiler.h" @@ -23,12 +23,12 @@ static int finish_note(NoteCompiler* compiler, Note* note); static int advance(NoteCompiler* compiler) { - if(compiler->position >= compiler->bar_length) { + if(compiler->position >= compiler->bar->content_length) { compiler->finished = true; return END_OF_BAR; } - compiler->symbol = (compiler->bar)[compiler->position]; + compiler->symbol = (compiler->bar->content)[compiler->position]; (compiler->position)++; return (char) (compiler->symbol); @@ -36,11 +36,11 @@ static int advance(NoteCompiler* compiler) { static int peek(NoteCompiler* compiler) { - if(compiler->position >= compiler->bar_length) { + if(compiler->position >= compiler->bar->content_length) { return END_OF_BAR; } - return (char) ((compiler->bar)[compiler->position]); + return (char) ((compiler->bar->content)[compiler->position]); } static int finish_note(NoteCompiler* compiler, Note* note) { @@ -145,18 +145,21 @@ static int finish_note(NoteCompiler* compiler, Note* note) { // -------- -int NoteCompiler_init_at(NoteCompiler* compiler, char* bar, size_t length) { +int NoteCompiler_init_at(NoteCompiler* compiler, BarToken* bar, char* filename) { + + if(compiler == NULL || bar == NULL || bar->content == NULL || bar->content_length == 0) { + return ERROR_CODE_INVALID_ARGUMENT; + } compiler->bar = bar; - compiler->state = NOTE_COMPILER_STATE_EXPECTING_NOTE; - compiler->bar_length = 0; compiler->position = 0; + compiler->state = NOTE_COMPILER_STATE_EXPECTING_NOTE; + compiler->error_state = NOTE_COMPILER_ERROR_STATE_UNKNOWN_ERROR; compiler->symbol = '\0'; compiler->finished = false; compiler->error = false; - compiler->bar = bar; - compiler->bar_length = length; + compiler->filename = filename == NULL? "": filename; return 0; } @@ -210,6 +213,7 @@ int NoteCompiler_get_next_note(void* compiler, Note* note, bool* finished) { default: comp->error = true; comp->finished = true; + comp->error_state = NOTE_COMPILER_ERROR_STATE_UNEXPECTED_CHARACTER; return ERROR_CODE_UNEXPECTED_CHARACTER; } default: @@ -226,3 +230,45 @@ int NoteCompiler_get_next_note(void* compiler, Note* note, bool* finished) { return 0; } +int NoteCompiler_print_error(const NoteCompiler* compiler, FILE* stream) { + + int status = 0; + + if(compiler == NULL || !(compiler->error)) { + return ERROR_CODE_INVALID_ARGUMENT; + } + + if(compiler->bar == NULL || compiler->bar->content == NULL) { + return ERROR_CODE_INVALID_STATE; + } + + fprintf(stream, "%s:%u:%u: ", compiler->filename, compiler->bar->line, compiler->bar->col); + + switch(compiler->error_state) { + + case NOTE_COMPILER_ERROR_STATE_UNEXPECTED_CHARACTER: + + fputs(get_error_message(ERROR_CODE_UNEXPECTED_CHARACTER), stream); + fputs(":\n", stream); + + BarToken_print(compiler->bar, stream); + + if(fprintf(stream, "\n%*s^", (int) (compiler->position - 1), " ") < 0) { + return ERROR_CODE_UNKNOWN_SYSTEM_ERROR; + } + + break; + + default: + + fputs("Unknown error when reading bar:\n", stream); + BarToken_print(compiler->bar, stream); + + break; + } + + fputs("\n", stream); + + return status; +} + diff --git a/src/note_compiler.h b/src/note_compiler.h index ac44409..5b0c91d 100644 --- a/src/note_compiler.h +++ b/src/note_compiler.h @@ -3,8 +3,8 @@ // -------- #include -#include +#include "bar_token.h" #include "note.h" // -------- @@ -22,11 +22,17 @@ typedef enum NoteCompilerState { NUM_NOTE_COMPILER_STATES, } NoteCompilerState; +typedef enum NoteCompilerErrorState { + NOTE_COMPILER_ERROR_STATE_UNKNOWN_ERROR = 0, + NOTE_COMPILER_ERROR_STATE_UNEXPECTED_CHARACTER, +} NoteCompilerErrorState; + struct NoteCompiler { - char* bar; - NoteCompilerState state; - size_t bar_length; + char* filename; + BarToken* bar; size_t position; + NoteCompilerState state; + NoteCompilerErrorState error_state; char symbol; bool finished; bool error; @@ -34,10 +40,12 @@ struct NoteCompiler { // -------- -int NoteCompiler_init_at(NoteCompiler* compiler, char* bar, size_t length); +int NoteCompiler_init_at(NoteCompiler* compiler, BarToken* bar, char* filename); int NoteCompiler_get_next_note(void* compiler, Note* note, bool* finished); +int NoteCompiler_print_error(const NoteCompiler* compiler, FILE* stream); + // -------- #endif diff --git a/src/track_lexer.c b/src/track_lexer.c index 49366bc..2d94078 100644 --- a/src/track_lexer.c +++ b/src/track_lexer.c @@ -164,14 +164,16 @@ static int TrackLexer_get_next_bar_internal(AbstractLexer* lexer, BarToken* toke int TrackLexer_init_at(TrackLexer* lexer, Token* track) { - int status = AbstractLexer_init_at((AbstractLexer*) lexer, 16); + AbstractLexer* super = (AbstractLexer*) lexer; + + int status = AbstractLexer_init_at((AbstractLexer*) super, 16); if(status != 0) { return status; } if(track == NULL || track->type != TOKEN_TRACK || track->content.buffer == NULL) { - ((AbstractLexer*) lexer)->error = true; - ((AbstractLexer*) lexer)->finished = true; + super->error = true; + super->finished = true; return ERROR_CODE_INVALID_ARGUMENT; } @@ -179,6 +181,11 @@ int TrackLexer_init_at(TrackLexer* lexer, Token* track) { lexer->track = track; lexer->track_position = 0; + super->line = track->line; + super->col = track->col; + + update_lineinfo(super); + return 0; }