For two reasons: - We'll later want apfl code to load other apfl code. If an IO error happens in that case, we don't want that to be a fatal error, only a regular error that can be catched in apfl (once we have something like `try`). - I want to get rid of fatal errors as a generic category completely. Instead the error reporting functions themselves should tell you the type of error directly, if it was something fatal.
225 lines
8.9 KiB
C
225 lines
8.9 KiB
C
#include <stdio.h>
|
|
|
|
#include "apfl.h"
|
|
|
|
#include "format.h"
|
|
|
|
#define POSFMT "%d:%d"
|
|
#define POSARGS error.position.line, error.position.col
|
|
#define POS2ARGS error.position2.line, error.position2.col
|
|
|
|
#define TRY(x) do { if (!(x)) return false; } while (0)
|
|
|
|
const char *
|
|
apfl_error_type_name(enum apfl_error_type type)
|
|
{
|
|
switch (type) {
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
return "APFL_ERR_MALLOC_FAILED";
|
|
case APFL_ERR_INPUT_ERROR:
|
|
return "APFL_ERR_INPUT_ERROR";
|
|
case APFL_ERR_UNEXPECTED_EOF:
|
|
return "APFL_ERR_UNEXPECTED_EOF";
|
|
case APFL_ERR_EXPECTED_EQ_AFTER_COLON:
|
|
return "APFL_ERR_EXPECTED_EQ_AFTER_COLON";
|
|
case APFL_ERR_UNEXPECTED_BYTE:
|
|
return "APFL_ERR_UNEXPECTED_BYTE";
|
|
case APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER:
|
|
return "APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER";
|
|
case APFL_ERR_EXPECTED_DIGIT:
|
|
return "APFL_ERR_EXPECTED_DIGIT";
|
|
case APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE:
|
|
return "APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE";
|
|
case APFL_ERR_INVALID_ESCAPE_SEQUENCE:
|
|
return "APFL_ERR_INVALID_ESCAPE_SEQUENCE";
|
|
case APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE:
|
|
return "APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE";
|
|
case APFL_ERR_UNEXPECTED_TOKEN:
|
|
return "APFL_ERR_UNEXPECTED_TOKEN";
|
|
case APFL_ERR_MISMATCHING_CLOSING_BRACKET:
|
|
return "APFL_ERR_MISMATCHING_CLOSING_BRACKET";
|
|
case APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN:
|
|
return "APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN";
|
|
case APFL_ERR_STATEMENTS_BEFORE_PARAMETERS:
|
|
return "APFL_ERR_STATEMENTS_BEFORE_PARAMETERS";
|
|
case APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS:
|
|
return "APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS";
|
|
case APFL_ERR_UNEXPECTED_EXPRESSION:
|
|
return "APFL_ERR_UNEXPECTED_EXPRESSION";
|
|
case APFL_ERR_INVALID_ASSIGNMENT_LHS:
|
|
return "APFL_ERR_INVALID_ASSIGNMENT_LHS";
|
|
case APFL_ERR_EMPTY_ASSIGNMENT:
|
|
return "APFL_ERR_EMPTY_ASSIGNMENT";
|
|
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
|
|
return "APFL_ERR_ONLY_ONE_EXPAND_ALLOWED";
|
|
case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS:
|
|
return "APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS";
|
|
case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS:
|
|
return "APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS";
|
|
case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS:
|
|
return "APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS";
|
|
}
|
|
|
|
return "<unknown error>";
|
|
}
|
|
|
|
const char *
|
|
apfl_error_as_const_string(struct apfl_error error)
|
|
{
|
|
switch (error.type) {
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
return apfl_messages.could_not_alloc_mem;
|
|
case APFL_ERR_INPUT_ERROR:
|
|
return apfl_messages.input_error_while_parsing;
|
|
case APFL_ERR_UNEXPECTED_EOF:
|
|
return apfl_messages.unexpected_end_of_file;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
format_error(struct apfl_format_writer w, struct apfl_error error)
|
|
{
|
|
switch (error.type) {
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
return apfl_format_put_string(w, apfl_messages.could_not_alloc_mem);
|
|
case APFL_ERR_INPUT_ERROR:
|
|
return apfl_format_put_string(w, apfl_messages.input_error_while_parsing);
|
|
case APFL_ERR_UNEXPECTED_EOF:
|
|
return apfl_format_put_string(w, apfl_messages.unexpected_end_of_file);
|
|
case APFL_ERR_EXPECTED_EQ_AFTER_COLON:
|
|
TRY(apfl_format_put_string(w, "Expected '=' after ':' at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_BYTE:
|
|
TRY(apfl_format_put_string(w, "Unexpected byte '"));
|
|
TRY(apfl_format_put_char(w, error.byte));
|
|
TRY(apfl_format_put_string(w, "' (0x"));
|
|
TRY(apfl_format_put_hexbyte(w, (unsigned char)error.byte));
|
|
TRY(apfl_format_put_string(w, ") at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER:
|
|
TRY(apfl_format_put_string(w, "Unexpected byte '"));
|
|
TRY(apfl_format_put_char(w, error.byte));
|
|
TRY(apfl_format_put_string(w, "' while parsing number at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_EXPECTED_DIGIT:
|
|
TRY(apfl_format_put_string(w, "Expected a digit at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE:
|
|
TRY(apfl_format_put_string(w, "Expected a hex-digit in hex escape at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_INVALID_ESCAPE_SEQUENCE:
|
|
TRY(apfl_format_put_string(w, "Invalid escape sequence \\"));
|
|
TRY(apfl_format_put_char(w, error.byte));
|
|
TRY(apfl_format_put_string(w, " at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE:
|
|
TRY(apfl_format_put_string(w, "No line break (after optional comments) after \\ at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_TOKEN:
|
|
TRY(apfl_format_put_string(w, "Unexpected `"));
|
|
TRY(apfl_format_put_string(w, apfl_token_type_name(error.token_type)));
|
|
TRY(apfl_format_put_string(w, "` token at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_MISMATCHING_CLOSING_BRACKET:
|
|
TRY(apfl_format_put_string(w, "Closing `"));
|
|
TRY(apfl_format_put_string(w, apfl_token_type_name(error.token_type)));
|
|
TRY(apfl_format_put_string(w, "` token at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
TRY(apfl_format_put_string(w, "Does not match opening `"));
|
|
TRY(apfl_format_put_string(w, apfl_token_type_name(error.token_type2)));
|
|
TRY(apfl_format_put_string(w, "` at "));
|
|
TRY(apfl_format_put_pos(w, error.position2));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN:
|
|
TRY(apfl_format_put_string(w, "Unexpected end of file after `"));
|
|
TRY(apfl_format_put_string(w, apfl_token_type_name(error.token_type)));
|
|
TRY(apfl_format_put_string(w, "` token at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_STATEMENTS_BEFORE_PARAMETERS:
|
|
TRY(apfl_format_put_string(w, "Unexpected statements before parameters near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS:
|
|
TRY(apfl_format_put_string(w, "Unexpected empty assignment before parameters near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_EXPRESSION:
|
|
TRY(apfl_format_put_string(w, "Unexpected expression near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_INVALID_ASSIGNMENT_LHS:
|
|
TRY(apfl_format_put_string(w, "Invalid left hand side of assignment near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_EMPTY_ASSIGNMENT:
|
|
TRY(apfl_format_put_string(w, "Empty assignment at "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
|
|
TRY(apfl_format_put_string(w, "Only one expansion (~) is allowed per level, near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS:
|
|
TRY(apfl_format_put_string(w, "Unexpected constant in member access near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS:
|
|
TRY(apfl_format_put_string(w, "Unexpected expression in member access near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS:
|
|
TRY(apfl_format_put_string(w, "Unexpected blank (\"_\") in member access near "));
|
|
TRY(apfl_format_put_pos(w, error.position));
|
|
return true;
|
|
}
|
|
|
|
TRY(apfl_format_put_string(w, "Unknown error "));
|
|
TRY(apfl_format_put_int(w, (int)error.type));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
apfl_error_print(struct apfl_error error, FILE *file)
|
|
{
|
|
struct apfl_format_writer w = apfl_format_file_writer(file);
|
|
TRY(format_error(w, error));
|
|
TRY(apfl_format_put_char(w, '\n'));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
apfl_error_as_string(struct apfl_error error, struct apfl_allocator allocator, struct apfl_string *out)
|
|
{
|
|
struct apfl_string_builder builder = apfl_string_builder_init(allocator);
|
|
TRY(format_error(apfl_format_string_writer(&builder), error));
|
|
*out = apfl_string_builder_move_string(&builder);
|
|
return true;
|
|
}
|
|
|
|
struct apfl_error
|
|
apfl_error_simple(enum apfl_error_type type)
|
|
{
|
|
return (struct apfl_error) { .type = type };
|
|
}
|
|
|
|
bool
|
|
apfl_error_is_fatal_type(enum apfl_error_type type)
|
|
{
|
|
switch (type) {
|
|
case APFL_ERR_MALLOC_FAILED:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|