diff --git a/src/Makefile.am b/src/Makefile.am index 4e43da2..7901177 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ libapfl_a_SOURCES += context.c libapfl_a_SOURCES += error.c libapfl_a_SOURCES += eval.c libapfl_a_SOURCES += expr.c +libapfl_a_SOURCES += format.c libapfl_a_SOURCES += gc.c libapfl_a_SOURCES += hashmap.c libapfl_a_SOURCES += internal.c @@ -28,6 +29,7 @@ apfl_internal_headers += alloc.h apfl_internal_headers += bytecode.h apfl_internal_headers += compile.h apfl_internal_headers += context.h +apfl_internal_headers += format.h apfl_internal_headers += gc.h apfl_internal_headers += hashmap.h apfl_internal_headers += internal.h diff --git a/src/apfl.h b/src/apfl.h index 1366bc2..d5b0691 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -191,7 +191,7 @@ struct apfl_error { char byte; }; -void apfl_error_print(struct apfl_error, FILE *); +bool apfl_error_print(struct apfl_error, FILE *); struct apfl_error apfl_error_simple(enum apfl_error_type); bool apfl_error_is_fatal_type(enum apfl_error_type); diff --git a/src/error.c b/src/error.c index 8621aa4..52b1165 100644 --- a/src/error.c +++ b/src/error.c @@ -1,11 +1,15 @@ - #include +#include #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) { @@ -61,132 +65,136 @@ apfl_error_type_name(enum apfl_error_type type) return ""; } -void -apfl_error_print(struct apfl_error error, FILE *file) +static bool +format_pos(struct apfl_format_writer w, struct apfl_position pos) +{ + TRY(apfl_format_put_int(w, pos.line)); + TRY(apfl_format_put_string(w, ":")); + TRY(apfl_format_put_int(w, pos.col)); + return true; +} + +static bool +format_error(struct apfl_format_writer w, struct apfl_error error) { switch (error.type) { case APFL_ERR_MALLOC_FAILED: - fprintf(file, "Could not allocate memory\n"); - return; + return apfl_format_put_string(w, "Could not allocate memory"); case APFL_ERR_INPUT_ERROR: - fprintf(file, "Input error while parsing\n"); - return; + return apfl_format_put_string(w, "Input error while parsing"); case APFL_ERR_UNEXPECTED_EOF: - fprintf(file, "Unexpected end of file\n"); - return; + return apfl_format_put_string(w, "Unexpected end of file"); case APFL_ERR_EXPECTED_EQ_AFTER_COLON: - fprintf(file, "Expected '=' after ':' at " POSFMT "\n", POSARGS); - return; + TRY(apfl_format_put_string(w, "Expected '=' after ':' at ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_BYTE: - fprintf(file, "Unexpected byte '%c' (0x%X) at " POSFMT "\n", error.byte, (unsigned)error.byte, POSARGS); - return; + 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(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_BYTE_IN_NUMBER: - fprintf(file, "Unexpected byte '%c' while parsing number at " POSFMT "\n", error.byte, POSARGS); - return; + 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(format_pos(w, error.position)); + return true; case APFL_ERR_EXPECTED_DIGIT: - fprintf(file, "Expected a digit at " POSFMT "\n", POSARGS); - return; + TRY(apfl_format_put_string(w, "Expected a digit at ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_EXPECTED_HEX_IN_HEX_ESCAPE: - fprintf(file, "Expected a hex-digit in hex escape at " POSFMT "\n", POSARGS); - return; + TRY(apfl_format_put_string(w, "Expected a hex-digit in hex escape at ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_INVALID_ESCAPE_SEQUENCE: - fprintf(file, "Invalid escape sequence \\%c at " POSFMT "\n", error.byte, POSARGS); - return; + 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(format_pos(w, error.position)); + return true; case APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE: - fprintf(file, "No line break (after optional comments) after \\ at " POSFMT "\n", POSARGS); - return; + TRY(apfl_format_put_string(w, "No line break (after optional comments) after \\ at ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_TOKEN: - fprintf(file, "Unexpected `%s` token at " POSFMT "\n", apfl_token_type_name(error.token_type), POSARGS); - return; + 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(format_pos(w, error.position)); + return true; case APFL_ERR_MISMATCHING_CLOSING_BRACKET: - fprintf( - file, - "Closing `%s` token at " POSFMT " does not match opening `%s` at " POSFMT "\n", - apfl_token_type_name(error.token_type), - POSARGS, - apfl_token_type_name(error.token_type2), - POS2ARGS - ); - return; + 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(format_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(format_pos(w, error.position2)); + return true; case APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN: - fprintf( - file, - "Unexpected end of file after `%s` token at " POSFMT "\n", - apfl_token_type_name(error.token_type), - POSARGS - ); - return; + 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(format_pos(w, error.position)); + return true; case APFL_ERR_STATEMENTS_BEFORE_PARAMETERS: - fprintf( - file, - "Unexpected statements before parameters near " POSFMT "\n", - POSARGS - ); - return; + TRY(apfl_format_put_string(w, "Unexpected statements before parameters near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_EMPTY_ASSIGNMENT_BEFORE_PARAMETERS: - fprintf( - file, - "Unexpected empty assignment before parameters near " POSFMT "\n", - POSARGS - ); - return; + TRY(apfl_format_put_string(w, "Unexpected empty assignment before parameters near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_EXPRESSION: - fprintf( - file, - "Unexpected expression near " POSFMT "\n", - POSARGS - ); - return; + TRY(apfl_format_put_string(w, "Unexpected expression near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_INVALID_ASSIGNMENT_LHS: - fprintf( - file, - "Invalid left hand side of assignment near " POSFMT "\n", - POSARGS - ); - return; + TRY(apfl_format_put_string(w, "Invalid left hand side of assignment near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_EMPTY_ASSIGNMENT: - fprintf( - file, - "Empty assignment at " POSFMT "\n", - POSARGS - ); - return; + TRY(apfl_format_put_string(w, "Empty assignment at ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED: - fprintf( - file, - "Only one expansion (~) is allowed per level, near " POSFMT "\n", - POSARGS - ); - break; + TRY(apfl_format_put_string(w, "Only one expansion (~) is allowed per level, near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS: - fprintf( - file, - "Unexpected constant in member access near " POSFMT "\n", - POSARGS - ); - break; + TRY(apfl_format_put_string(w, "Unexpected constant in member access near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS: - fprintf( - file, - "Unexpected expression in member access near " POSFMT "\n", - POSARGS - ); - break; + TRY(apfl_format_put_string(w, "Unexpected expression in member access near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS: - fprintf( - file, - "Unexpected blank (\"_\") in member access near " POSFMT "\n", - POSARGS - ); - break; + TRY(apfl_format_put_string(w, "Unexpected blank (\"_\") in member access near ")); + TRY(format_pos(w, error.position)); + return true; case APFL_ERR_NOT_IMPLEMENTED: - fprintf( - file, - "Feature not implemented\n" - ); + TRY(apfl_format_put_string(w, "Feature not implemented")); + return true; } - fprintf(file, "Unknown error %d\n", (int)error.type); + 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; } struct apfl_error diff --git a/src/format.c b/src/format.c new file mode 100644 index 0000000..ecf1c7a --- /dev/null +++ b/src/format.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +#include "apfl.h" + +#include "format.h" + +#define WRITE(w, buf, len) w.write(w.opaque, buf, len) +#define TRY(x) if (!(x)) return false; + +static bool +write_file(void *opaque, const char *buf, size_t len) +{ + FILE *f = opaque; + return fwrite(buf, len, 1, f) == 1; +} + +struct apfl_format_writer +apfl_format_file_writer(FILE *f) +{ + return (struct apfl_format_writer) { + .write = write_file, + .opaque = f, + }; +} + +bool +apfl_format_put_string_view(struct apfl_format_writer w, struct apfl_string_view sv) +{ + return WRITE(w, sv.bytes, sv.len); +} + +#define PUT_INT_BUFSIZE 21 // ceil(log10(2**64)) + 1 + +bool +apfl_format_put_int(struct apfl_format_writer w, int i) +{ + char buf[PUT_INT_BUFSIZE]; + size_t len = snprintf(buf, PUT_INT_BUFSIZE, "%d", i); + assert(len < PUT_INT_BUFSIZE); + return WRITE(w, buf, len); +} + +bool +apfl_format_put_char(struct apfl_format_writer w, char c) +{ + return WRITE(w, &c, 1); +} + +#define PUT_HEXBYTE_BUFSIZE 3 + +bool +apfl_format_put_hexbyte(struct apfl_format_writer w, unsigned char c) +{ + char buf[PUT_HEXBYTE_BUFSIZE]; + size_t len = snprintf(buf, PUT_HEXBYTE_BUFSIZE, "%x", (unsigned) c); + assert(len < PUT_HEXBYTE_BUFSIZE); + return WRITE(w, buf, len); +} diff --git a/src/format.h b/src/format.h new file mode 100644 index 0000000..2157def --- /dev/null +++ b/src/format.h @@ -0,0 +1,31 @@ +#ifndef APFL_FORMAT_H +#define APFL_FORMAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "apfl.h" + +struct apfl_format_writer { + bool (*write)(void *, const char *buf, size_t len); + void *opaque; +}; + +struct apfl_format_writer apfl_format_file_writer(FILE *f); + +bool apfl_format_put_string_view(struct apfl_format_writer, struct apfl_string_view); +#define apfl_format_put_string(w, s) apfl_format_put_string_view((w), apfl_string_view_from(s)) +bool apfl_format_put_int(struct apfl_format_writer, int); +bool apfl_format_put_char(struct apfl_format_writer, char); +bool apfl_format_put_hexbyte(struct apfl_format_writer, unsigned char); + +#ifdef __cplusplus +} +#endif + +#endif