From de39a41da62c4b3820b4805ddb7c4970c36bc257 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sat, 18 Apr 2015 17:17:37 -0300 Subject: added loader, error handling and cli. tests needed --- src/error.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/error.h | 34 +++++++++++++++ src/loader.c | 104 ++++++++++++++++++++++++++++++++++++++++++++ src/loader.h | 22 ++++++++++ src/main.c | 98 ++++++++++++++++++++++++++++++++++++++++-- src/output.c | 39 ----------------- src/output.h | 17 -------- src/source-parser.c | 37 ++++++++++------ src/source-parser.h | 6 ++- src/template-parser.c | 87 +++++++++++++++++++++++++++++-------- src/template-parser.h | 4 +- 11 files changed, 471 insertions(+), 93 deletions(-) create mode 100644 src/error.c create mode 100644 src/error.h create mode 100644 src/loader.c create mode 100644 src/loader.h delete mode 100644 src/output.c delete mode 100644 src/output.h (limited to 'src') diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..8475d66 --- /dev/null +++ b/src/error.c @@ -0,0 +1,116 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file COPYING. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include "utils/utils.h" +#include "error.h" + + +blogc_error_t* +blogc_error_new(blogc_error_type_t type, const char *msg) +{ + blogc_error_t *err = malloc(sizeof(blogc_error_t)); + err->type = type; + err->msg = b_strdup(msg); + return err; +} + + +blogc_error_t* +blogc_error_new_printf(blogc_error_type_t type, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *tmp = b_strdup_vprintf(format, ap); + va_end(ap); + blogc_error_t *rv = blogc_error_new(type, tmp); + free(tmp); + return rv; +} + + +blogc_error_t* +blogc_error_parser(blogc_error_type_t type, const char *src, size_t src_len, + size_t current, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *msg = b_strdup_vprintf(format, ap); + va_end(ap); + + b_string_t *str = b_string_new(); + while (current < src_len) { + char c = src[current]; + + if (c == '\r' || c == '\n') + break; + + b_string_append_c(str, c); + + current++; + } + char *line = b_string_free(str, false); + + blogc_error_t *rv = NULL; + + if (strlen(line) == 0) // "near to" message isn't useful if line is empty + rv = blogc_error_new(type, msg); + else + rv = blogc_error_new_printf(type, + "%s\nError occurred near to \"%s\".", msg, line); + + free(msg); + free(line); + + return rv; +} + + +void +blogc_error_print(blogc_error_t *err) +{ + if (err == NULL) + return; + + char *tmp = NULL; + + switch(err->type) { + case BLOGC_ERROR_SOURCE_PARSER: + tmp = b_strdup("Source parser error"); + break; + case BLOGC_ERROR_TEMPLATE_PARSER: + tmp = b_strdup("Template parser error"); + break; + case BLOGC_ERROR_LOADER: + tmp = b_strdup("Loader error"); + break; + default: + tmp = b_strdup("Unknown error"); + } + + fprintf(stderr, "%s: %s\n", tmp, err->msg); + + free(tmp); +} + + +void +blogc_error_free(blogc_error_t *err) +{ + if (err == NULL) + return; + free(err->msg); + free(err); +} diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000..98aeeb9 --- /dev/null +++ b/src/error.h @@ -0,0 +1,34 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file COPYING. + */ + +#ifndef _ERROR_H +#define _ERROR_H + +#include +#include +#include "utils/utils.h" + +typedef enum { + BLOGC_ERROR_SOURCE_PARSER = 1, + BLOGC_ERROR_TEMPLATE_PARSER, + BLOGC_ERROR_LOADER, +} blogc_error_type_t; + +typedef struct { + char *msg; + blogc_error_type_t type; +} blogc_error_t; + +blogc_error_t* blogc_error_new(blogc_error_type_t type, const char *msg); +blogc_error_t* blogc_error_new_printf(blogc_error_type_t type, const char *format, ...); +blogc_error_t* blogc_error_parser(blogc_error_type_t type, const char *src, + size_t src_len, size_t current, const char *format, ...); +void blogc_error_print(blogc_error_t *err); +void blogc_error_free(blogc_error_t *err); + +#endif /* _ERROR_H */ diff --git a/src/loader.c b/src/loader.c new file mode 100644 index 0000000..35ecdcf --- /dev/null +++ b/src/loader.c @@ -0,0 +1,104 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file COPYING. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include "utils/utils.h" +#include "source-parser.h" +#include "template-parser.h" +#include "loader.h" +#include "error.h" + + +char* +blogc_file_get_contents(const char *path, size_t *len, blogc_error_t **err) +{ + if (err == NULL || *err != NULL) + return NULL; + + *len = 0; + FILE *fp = fopen(path, "r"); + + if (fp == NULL) { + int tmp_errno = errno; + *err = blogc_error_new_printf(BLOGC_ERROR_LOADER, + "Failed to open file (%s): %s", path, strerror(tmp_errno)); + return NULL; + } + + b_string_t *str = b_string_new(); + char buffer[BLOGC_FILE_CHUNK_SIZE]; + + while (!feof(fp)) { + size_t read_len = fread(buffer, sizeof(char), BLOGC_FILE_CHUNK_SIZE, fp); + *len += read_len; + b_string_append_len(str, buffer, read_len); + } + fclose(fp); + return b_string_free(str, false); +} + + +b_slist_t* +blogc_template_parse_from_file(const char *f, blogc_error_t **err) +{ + if (err == NULL || *err != NULL) + return NULL; + size_t len; + char *s = blogc_file_get_contents(f, &len, err); + if (s == NULL) + return NULL; + b_slist_t *rv = blogc_template_parse(s, len, err); + free(s); + return rv; +} + + +blogc_source_t* +blogc_source_parse_from_file(const char *f, blogc_error_t **err) +{ + if (err == NULL || *err != NULL) + return NULL; + size_t len; + char *s = blogc_file_get_contents(f, &len, err); + if (s == NULL) + return NULL; + blogc_source_t *rv = blogc_source_parse(s, len, err); + free(s); + return rv; +} + + +b_slist_t* +blogc_source_parse_from_files(b_slist_t *l, blogc_error_t **err) +{ + blogc_error_t *tmp_err = NULL; + b_slist_t *rv = NULL; + + for (b_slist_t *tmp = l; tmp != NULL; tmp = tmp->next) { + char *f = tmp->data; + blogc_source_t *s = blogc_source_parse_from_file(f, &tmp_err); + if (s == NULL) { + *err = blogc_error_new_printf(BLOGC_ERROR_LOADER, + "An error occurred while parsing source file: %s\n\n%s", + f, tmp_err->msg); + blogc_error_free(tmp_err); + tmp_err = NULL; + b_slist_free_full(rv, blogc_source_free); + rv = NULL; + break; + } + rv = b_slist_append(rv, s); + } + return rv; +} diff --git a/src/loader.h b/src/loader.h new file mode 100644 index 0000000..42a0abf --- /dev/null +++ b/src/loader.h @@ -0,0 +1,22 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins + * + * This program can be distributed under the terms of the BSD License. + * See the file COPYING. + */ + +#ifndef _LOADER_H +#define _LOADER_H + +#include "utils/utils.h" +#include "error.h" + +#define BLOGC_FILE_CHUNK_SIZE 1024 + +char* blogc_file_get_contents(const char *path, size_t *len, blogc_error_t **err); +b_slist_t* blogc_template_parse_from_file(const char *f, blogc_error_t **err); +blogc_source_t* blogc_source_parse_from_file(const char *f, blogc_error_t **err); +b_slist_t* blogc_source_parse_from_files(b_slist_t *l, blogc_error_t **err); + +#endif /* _LOADER_H */ diff --git a/src/main.c b/src/main.c index 4e72b6e..90e374c 100644 --- a/src/main.c +++ b/src/main.c @@ -10,16 +10,108 @@ #include #endif /* HAVE_CONFIG_H */ +#include #include +#include +#include "utils/utils.h" #include "source-parser.h" #include "template-parser.h" -#include +#include "loader.h" +#include "error.h" + + +static void +blogc_print_help(void) +{ + printf( + "usage:\n" + " blogc [-h] -t TEMPLATE [-o OUTPUT] SOURCE [SOURCE ...] - A blog compiler.\n" + "\n" + "positional arguments:\n" + " SOURCE source file(s)\n" + "\n" + "optional arguments:\n" + " -h, --help show this help message and exit\n" + " -t TEMPLATE template file\n" + " -o OUTPUT output file\n"); +} + + +static void +blogc_print_usage(void) +{ + printf("usage: blogc [-h] -t TEMPLATE [-o OUTPUT] SOURCE [SOURCE ...]\n"); +} int main(int argc, char **argv) { - printf("Hello, World!\n"); - return 0; + int rv = 0; + + char *template = NULL; + char *output = NULL; + b_slist_t *sources = NULL; + + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'h': + blogc_print_help(); + goto cleanup; + case 't': + if (i + 1 < argc) + template = b_strdup(argv[++i]); + break; + case 'o': + if (i + 1 < argc) + output = b_strdup(argv[++i]); + break; + } + } + else + sources = b_slist_append(sources, b_strdup(argv[i])); + } + + if (template == NULL) { + blogc_print_usage(); + fprintf(stderr, "blogc: error: argument -t is required\n"); + rv = 2; + goto cleanup; + } + + if (b_slist_length(sources) == 0) { + blogc_print_usage(); + fprintf(stderr, "blogc: error: at least one source file is required\n"); + rv = 2; + goto cleanup; + } + + blogc_error_t *err = NULL; + + b_slist_t* l = blogc_template_parse_from_file(template, &err); + if (err != NULL) { + blogc_error_print(err); + goto cleanup2; + } + + b_slist_t *s = blogc_source_parse_from_files(sources, &err); + if (err != NULL) { + blogc_error_print(err); + goto cleanup3; + } + + printf("%d\n", s == NULL); + +cleanup3: + b_slist_free_full(s, blogc_source_free); +cleanup2: + blogc_template_free_stmts(l); + blogc_error_free(err); +cleanup: + free(template); + free(output); + b_slist_free_full(sources, free); + return rv; } diff --git a/src/output.c b/src/output.c deleted file mode 100644 index 1664afe..0000000 --- a/src/output.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins - * - * This program can be distributed under the terms of the BSD License. - * See the file COPYING. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "utils/utils.h" -#include "output.h" - - -void -blogc_parser_syntax_error(const char *name, const char *src, size_t src_len, - size_t current) -{ - b_string_t *msg = b_string_new(); - - while (current < src_len) { - char c = src[current]; - - if (c == '\r' || c == '\n') - break; - - b_string_append_c(msg, c); - - current++; - } - - fprintf(stderr, "%s parser error: failed to parse input near \"%s\".\n", name, - msg->str); - - b_string_free(msg, true); -} diff --git a/src/output.h b/src/output.h deleted file mode 100644 index 6995324..0000000 --- a/src/output.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins - * - * This program can be distributed under the terms of the BSD License. - * See the file COPYING. - */ - -#ifndef _OUTPUT_H -#define _OUTPUT_H - -#include - -void blogc_parser_syntax_error(const char *name, const char *src, - size_t src_len, size_t current); - -#endif /* _OUTPUT_H */ diff --git a/src/source-parser.c b/src/source-parser.c index ad7e77d..7cace0c 100644 --- a/src/source-parser.c +++ b/src/source-parser.c @@ -14,7 +14,7 @@ #include "utils/utils.h" #include "source-parser.h" -#include "output.h" +#include "error.h" typedef enum { @@ -29,12 +29,14 @@ typedef enum { blogc_source_t* -blogc_source_parse(const char *src, size_t src_len) +blogc_source_parse(const char *src, size_t src_len, blogc_error_t **err) { + if (err == NULL || *err != NULL) + return NULL; + size_t current = 0; size_t start = 0; - bool error = false; char *key = NULL; char *tmp = NULL; b_trie_t *config = b_trie_new(free); @@ -59,7 +61,9 @@ blogc_source_parse(const char *src, size_t src_len) state = SOURCE_SEPARATOR; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len, + current, + "Can't find a configuration key or the content separator."); break; case SOURCE_CONFIG_KEY: @@ -70,7 +74,8 @@ blogc_source_parse(const char *src, size_t src_len) state = SOURCE_CONFIG_VALUE_START; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len, + current, "Invalid configuration key."); break; case SOURCE_CONFIG_VALUE_START: @@ -79,7 +84,9 @@ blogc_source_parse(const char *src, size_t src_len) start = current; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len, + current, "Configuration value not provided for '%s'.", + key); break; case SOURCE_CONFIG_VALUE: @@ -100,7 +107,9 @@ blogc_source_parse(const char *src, size_t src_len) state = SOURCE_CONTENT_START; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len, + current, + "Invalid content separator. Must be one or more '-' characters."); break; case SOURCE_CONTENT_START: @@ -114,17 +123,16 @@ blogc_source_parse(const char *src, size_t src_len) break; } - if (error) + if (*err != NULL) break; current++; } - if (error) { + if (*err != NULL) { free(key); free(content); b_trie_free(config); - blogc_parser_syntax_error("source", src, src_len, current); return NULL; } @@ -137,11 +145,12 @@ blogc_source_parse(const char *src, size_t src_len) void -blogc_source_free(blogc_source_t *source) +blogc_source_free(void *source) { if (source == NULL) return; - free(source->content); - b_trie_free(source->config); - free(source); + blogc_source_t *s = source; + free(s->content); + b_trie_free(s->config); + free(s); } diff --git a/src/source-parser.h b/src/source-parser.h index e33a7d4..c816e5b 100644 --- a/src/source-parser.h +++ b/src/source-parser.h @@ -11,13 +11,15 @@ #include #include "utils/utils.h" +#include "error.h" typedef struct { b_trie_t *config; char *content; } blogc_source_t; -blogc_source_t* blogc_source_parse(const char *src, size_t src_len); -void blogc_source_free(blogc_source_t *source); +blogc_source_t* blogc_source_parse(const char *src, size_t src_len, + blogc_error_t **err); +void blogc_source_free(void *source); #endif /* _SOURCE_PARSER_H */ diff --git a/src/template-parser.c b/src/template-parser.c index 54dc642..1038190 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -15,7 +15,7 @@ #include "utils/utils.h" #include "template-parser.h" -#include "output.h" +#include "error.h" typedef enum { @@ -44,13 +44,15 @@ typedef enum { b_slist_t* -blogc_template_parse(const char *src, size_t src_len) +blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) { + if (err == NULL || *err != NULL) + return NULL; + size_t current = 0; size_t start = 0; size_t end = 0; - bool error = false; char *tmp = NULL; unsigned int if_count = 0; @@ -95,7 +97,11 @@ blogc_template_parse(const char *src, size_t src_len) stmts = b_slist_append(stmts, stmt); stmt = NULL; } + break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Must be '%%' or '{'."); break; case TEMPLATE_BLOCK_START: @@ -106,7 +112,9 @@ blogc_template_parse(const char *src, size_t src_len) start = current; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Must begin lowercase letter."); break; case TEMPLATE_BLOCK_TYPE: @@ -120,6 +128,9 @@ blogc_template_parse(const char *src, size_t src_len) start = current; break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, "Blocks can't be nested."); + break; } else if (0 == strncmp("endblock", src + start, current - start)) { if (block_state != BLOCK_CLOSED) { @@ -128,6 +139,10 @@ blogc_template_parse(const char *src, size_t src_len) block_state = BLOCK_CLOSED; break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "'endblock' statement without an open 'block' statement."); + break; } else if (0 == strncmp("if", src + start, current - start)) { if (block_state == BLOCK_SINGLE_SOURCE || block_state == BLOCK_MULTIPLE_SOURCES) { @@ -137,6 +152,11 @@ blogc_template_parse(const char *src, size_t src_len) if_count++; break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "'if' statements only allowed inside 'single_source' " + "and 'multiple_sources' blocks."); + break; } else if (0 == strncmp("endif", src + start, current - start)) { if (block_state == BLOCK_SINGLE_SOURCE || block_state == BLOCK_MULTIPLE_SOURCES) { @@ -146,10 +166,21 @@ blogc_template_parse(const char *src, size_t src_len) if_count--; break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "'endif' statement without an open 'if' statement."); + break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "'endif' statements only allowed inside 'single_source' " + "and 'multiple_sources' blocks."); + break; } } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement type: Allowed types are: block, endblock, if, endif."); break; case TEMPLATE_BLOCK_BLOCK_TYPE_START: @@ -160,7 +191,9 @@ blogc_template_parse(const char *src, size_t src_len) start = current; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid block syntax. Must begin with lowercase letter."); break; case TEMPLATE_BLOCK_BLOCK_TYPE: @@ -186,7 +219,9 @@ blogc_template_parse(const char *src, size_t src_len) break; } } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid block type. Allowed types are: single_source, multiple_sources, multiple_sources_once."); break; case TEMPLATE_BLOCK_IF_VARIABLE_START: @@ -197,7 +232,9 @@ blogc_template_parse(const char *src, size_t src_len) start = current; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid variable name. Must begin with uppercase letter."); break; case TEMPLATE_BLOCK_IF_VARIABLE: @@ -208,7 +245,9 @@ blogc_template_parse(const char *src, size_t src_len) state = TEMPLATE_BLOCK_END; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid variable name. Must be uppercase letter, number or '_'."); break; case TEMPLATE_BLOCK_END: @@ -218,7 +257,9 @@ blogc_template_parse(const char *src, size_t src_len) state = TEMPLATE_CLOSE_BRACKET; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Must end with '%}'."); break; case TEMPLATE_VARIABLE_START: @@ -231,8 +272,15 @@ blogc_template_parse(const char *src, size_t src_len) start = current; break; } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "variable statements only allowed inside 'single_source' " + "and 'multiple_sources' blocks."); + break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid variable name. Must begin with uppercase letter."); break; case TEMPLATE_VARIABLE: @@ -248,7 +296,9 @@ blogc_template_parse(const char *src, size_t src_len) state = TEMPLATE_CLOSE_BRACKET; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid variable name. Must be uppercase letter, number or '_'."); break; case TEMPLATE_VARIABLE_END: @@ -258,7 +308,9 @@ blogc_template_parse(const char *src, size_t src_len) state = TEMPLATE_CLOSE_BRACKET; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Must end with '}}'."); break; case TEMPLATE_CLOSE_BRACKET: @@ -275,24 +327,25 @@ blogc_template_parse(const char *src, size_t src_len) start = current + 1; break; } - error = true; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Must end with '}'."); break; } - if (error) + if (*err != NULL) break; current++; } - if (error) { + if (*err != NULL) { if (stmt != NULL) { free(stmt->value); free(stmt); } blogc_template_free_stmts(stmts); - blogc_parser_syntax_error("template", src, src_len, current); return NULL; } diff --git a/src/template-parser.h b/src/template-parser.h index 1f43fe8..d95c87a 100644 --- a/src/template-parser.h +++ b/src/template-parser.h @@ -10,6 +10,7 @@ #define _TEMPLATE_PARSER_H #include "utils/utils.h" +#include "error.h" typedef enum { BLOGC_TEMPLATE_IF_STMT = 1, @@ -25,7 +26,8 @@ typedef struct { char *value; } blogc_template_stmt_t; -b_slist_t* blogc_template_parse(const char *src, size_t src_len); +b_slist_t* blogc_template_parse(const char *src, size_t src_len, + blogc_error_t **err); void blogc_template_free_stmts(b_slist_t *stmts); #endif /* _TEMPLATE_GRAMMAR_H */ -- cgit v1.2.3-18-g5258