From fa016a15156bf8122b2d3dccaecca53f471d6ecb Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 21 Apr 2015 15:42:16 -0300 Subject: refactored blocks - changed block names: - single_source -> entry - multiple_sources -> listing - multiple_sources_once -> listing_once - added -t cli option, to build listing pages, instead of guess it from the number of source files provided. --- README.md | 40 +++++++++++++++----------------- src/main.c | 26 +++++++++++++++++---- src/renderer.c | 26 ++++++++++----------- src/renderer.h | 3 ++- src/template-parser.c | 54 +++++++++++++++++++++++-------------------- tests/check_template_parser.c | 36 ++++++++++++++--------------- 6 files changed, 101 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 1fe0295..9043f92 100644 --- a/README.md +++ b/README.md @@ -11,26 +11,26 @@ The main idea is simple: a source file is read by the compiler, and a result fil The source file must provide (almost) all the data needed to build the result file, including any variables. The result file is built using a template, that defines how the information provided by the source file should be used to generate a reasonable result file. -The compiler will always generate a single file, no matter how many source files are provided. If more than one source file is provided, the template must know how to convert them to a single result file. +The compiler will always generate a single file, no matter how many source files are provided. If more than one source file is provided, the compiler (and the template) must know how to convert them to a single result file. The templates can define blocks. These are the block rules: -- If something is defined outside of blocks, it should be always used. -- If something is defined inside a block, it should only be used if the block matches the compiler argument expectations, e.g.: - - ``single_source`` should be used if just one source file is provided. - - ``multiple_sources`` should be used if more than one source file is provided, being used once for each source file. - - ``multiple_sources_once`` should be used if more than one source file is provided, but only once. +- If something is defined outside of blocks, its raw content should be always included. +- If something is defined inside a block, it should only be included if the block matches the compiler argument expectations, e.g.: + - ``entry`` should be include if just one source file is provided. + - ``listing`` should be included if more than one source file is provided, being included once for each source file, if the compiler is called with ``-l``. + - ``listing_once`` should be inclueded if more than one source file is provided, but only once, if the compiler is called with ``-l``. - Template blocks can be defined multiple times in the same template, but can't be nested. The variables defined in the source file are only available inside of blocks. If something does not depends on the source files, and is global, it must be hardcoded in the template, for the sake of simplicity. -The templates can use conditional statements: ``{% if variable %}`` and ``{% endif %}``. They check if a variable is defined or not. As variables are not available outside of blocks, these conditional statements can't be defined outside of blocks. +The templates can use conditional statements: ``{% if variable %}`` and ``{% endif %}``. They check if a variable is defined or not. As variables are not available outside of blocks, these conditional statements can't be defined outside of blocks as well. -Variables are not available in ``multiple_sources_once`` blocks, because it is not possible to guess which source file to get the variables from. +Variables are not available in ``listing_once`` blocks, because it is not possible to guess which source file would provide the variable contents. As the compiler is output-agnostic, Atom feeds and sitemaps should be generated using templates as well. -The content won't touch the content. If the user write pre-formatted text it will stay formatted, and the user may want to enclose the ``{{ CONTENT }}`` statement with ``
`` and ``
`` tags in the templates. For more flexibility, the user can even write raw HTML in the source content. +The compiler won't touch the content. If the user write pre-formatted text it will stay pre-formatted, and the user may want to enclose the ``{{ CONTENT }}`` statement with ``
`` and ``
`` tags in the templates. For more flexibility, the user can even write raw HTML in the source content. The compiler is designed to be easily used with any POSIX-compatible implementation of ``make``. @@ -39,20 +39,18 @@ The compiler is designed to be easily used with any POSIX-compatible implementat ``` TITLE: My nice post -DATE: 2007-04-05T12:30-02:00 +DATE: 2007-04-05 12:30:00 ---- test content. more test content. ``` -If more than one source file is provided, they won't be sorted, and will be used by ``multiple_sources`` blocks in the order that they were provided in the command line. - -The ``DATE`` variable is an ISO-8601 date-time, with seconds, and always in UTC. If you want to show the date of your posts in your blog, you can use the ``DATE`` variable, but it won't be nicely formated, it will always be an ISO-8601 date-time. +If more than one source file is provided to the compiler with the ``-t`` argument, they won't be sorted, and will be included by ``listing`` blocks in the order that they were provided in the command line. Variables are single-line, and all the whitespace characters, including tabs, before the leading non-whitespace character and after the trailing non-whitespace character will be removed. -Pre-formatted content is available in template blocks as the ``CONTENT`` variable. +Raw content is available in template blocks as the ``CONTENT`` variable. ### Template file syntax @@ -60,25 +58,25 @@ Pre-formatted content is available in template blocks as the ``CONTENT`` variabl ```html - {% block single_source %} + {% block entry %} My cool blog >> {{ TITLE }} {% endblock %} - {% block multiple_sources_once %} + {% block listing_once %} My cool blog - Main page {% endblock %}

My cool blog

- {% block single_source %} + {% block entry %}

{{ TITLE }}

{% if DATE %}

Published in: {{ DATE }}

{% endif %}
{{ CONTENT }}
{% endblock %} - {% block multiple_sources_once %}{% endblock %} + {% block listing_once %}{% endblock %} ``` -This template would generate a list of posts, if multiple source files were provided, and single pages for each post, if only one source file was provided. The ``FILENAME`` variable is generated by the compiler, and is the source file name without its last extension. +This template would generate a list of posts, if multiple source files were provided with ``-t``, and single pages for each post, if only one source file was provided. The ``FILENAME`` variable is generated by the compiler, and is the source file base name without its last extension. diff --git a/src/main.c b/src/main.c index 552c322..f30a92e 100644 --- a/src/main.c +++ b/src/main.c @@ -36,13 +36,14 @@ blogc_print_help(void) { printf( "usage:\n" - " blogc [-h] -t TEMPLATE [-o OUTPUT] SOURCE [SOURCE ...] - A blog compiler.\n" + " blogc [-h] [-l] -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" + " -h show this help message and exit\n" + " -l build listing page, from multiple source files\n" " -t TEMPLATE template file\n" " -o OUTPUT output file\n"); } @@ -51,7 +52,7 @@ blogc_print_help(void) static void blogc_print_usage(void) { - printf("usage: blogc [-h] -t TEMPLATE [-o OUTPUT] SOURCE [SOURCE ...]\n"); + printf("usage: blogc [-h] [-l] -t TEMPLATE [-o OUTPUT] SOURCE [SOURCE ...]\n"); } @@ -95,6 +96,7 @@ main(int argc, char **argv) { int rv = 0; + bool listing = false; char *template = NULL; char *output = NULL; b_slist_t *sources = NULL; @@ -105,6 +107,9 @@ main(int argc, char **argv) case 'h': blogc_print_help(); goto cleanup; + case 'l': + listing = true; + break; case 't': if (i + 1 < argc) template = b_strdup(argv[++i]); @@ -128,7 +133,18 @@ main(int argc, char **argv) if (b_slist_length(sources) == 0) { blogc_print_usage(); - fprintf(stderr, "blogc: error: at least one source file is required\n"); + if (listing) + fprintf(stderr, "blogc: error: at least one source file is required\n"); + else + fprintf(stderr, "blogc: error: one source file is required\n"); + rv = 2; + goto cleanup; + } + + if (!listing && b_slist_length(sources) > 1) { + blogc_print_usage(); + fprintf(stderr, "blogc: error: only one source file should be provided, " + "if running without '-l'\n"); rv = 2; goto cleanup; } @@ -147,7 +163,7 @@ main(int argc, char **argv) goto cleanup3; } - char *out = blogc_render(l, s); + char *out = blogc_render(l, s, listing); bool write_to_stdout = (output == NULL || (0 == strcmp(output, "-"))); diff --git a/src/renderer.c b/src/renderer.c index ffd46c9..69206c3 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -18,15 +18,13 @@ char* -blogc_render(b_slist_t *tmpl, b_slist_t *sources) +blogc_render(b_slist_t *tmpl, b_slist_t *sources, bool listing) { if (tmpl == NULL || sources == NULL) return NULL; - bool single_source = sources->next == NULL; - b_slist_t *current_source = NULL; - b_slist_t *multiple_sources_start = NULL; + b_slist_t *listing_start = NULL; b_string_t *str = b_string_new(); @@ -49,8 +47,8 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources) case BLOGC_TEMPLATE_BLOCK_STMT: if_count = 0; - if (0 == strcmp("single_source", stmt->value)) { - if (!single_source) { + if (0 == strcmp("entry", stmt->value)) { + if (listing) { // we can just skip anything and walk until the next // 'endblock' @@ -63,9 +61,9 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources) current_source = sources; tmp_source = current_source->data; } - else if ((0 == strcmp("multiple_sources", stmt->value)) || - (0 == strcmp("multiple_sources_once", stmt->value))) { - if (single_source) { + else if ((0 == strcmp("listing", stmt->value)) || + (0 == strcmp("listing_once", stmt->value))) { + if (!listing) { // we can just skip anything and walk until the next // 'endblock' @@ -76,9 +74,9 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources) break; } } - if (0 == strcmp("multiple_sources", stmt->value)) { + if (0 == strcmp("listing", stmt->value)) { if (current_source == NULL) { - multiple_sources_start = tmp; + listing_start = tmp; current_source = sources; } tmp_source = current_source->data; @@ -95,14 +93,14 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources) break; case BLOGC_TEMPLATE_ENDBLOCK_STMT: - if (multiple_sources_start != NULL && current_source != NULL) { + if (listing_start != NULL && current_source != NULL) { current_source = current_source->next; if (current_source != NULL) { - tmp = multiple_sources_start; + tmp = listing_start; continue; } else - multiple_sources_start = NULL; + listing_start = NULL; } break; diff --git a/src/renderer.h b/src/renderer.h index 9baf2eb..a6b7a2c 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -9,8 +9,9 @@ #ifndef _RENDERER_H #define _RENDERER_H +#include #include "utils/utils.h" -char* blogc_render(b_slist_t *tmpl, b_slist_t *sources); +char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, bool listing); #endif /* _RENDERER_H */ diff --git a/src/template-parser.c b/src/template-parser.c index ce7dc32..319ae4b 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -37,9 +37,9 @@ typedef enum { typedef enum { BLOCK_CLOSED = 1, - BLOCK_SINGLE_SOURCE, - BLOCK_MULTIPLE_SOURCES, - BLOCK_MULTIPLE_SOURCES_ONCE, + BLOCK_ENTRY, + BLOCK_LISTING, + BLOCK_LISTING_ONCE, } blogc_template_parser_block_state_t; @@ -110,7 +110,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "Invalid statement syntax. Must begin lowercase letter."); + "Invalid statement syntax. Must begin with lowercase letter."); break; case TEMPLATE_BLOCK_TYPE: @@ -141,7 +141,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) break; } else if (0 == strncmp("if", src + start, current - start)) { - if (block_state == BLOCK_SINGLE_SOURCE || block_state == BLOCK_MULTIPLE_SOURCES) { + if (block_state == BLOCK_ENTRY || block_state == BLOCK_LISTING) { state = TEMPLATE_BLOCK_IF_VARIABLE_START; type = BLOGC_TEMPLATE_IF_STMT; start = current; @@ -150,12 +150,12 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "'if' statements only allowed inside 'single_source' " - "and 'multiple_sources' blocks."); + "'if' statements only allowed inside 'entry' and " + "'listing' blocks."); break; } else if (0 == strncmp("endif", src + start, current - start)) { - if (block_state == BLOCK_SINGLE_SOURCE || block_state == BLOCK_MULTIPLE_SOURCES) { + if (block_state == BLOCK_ENTRY || block_state == BLOCK_LISTING) { if (if_count > 0) { state = TEMPLATE_BLOCK_END; type = BLOGC_TEMPLATE_ENDIF_STMT; @@ -169,14 +169,15 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "'endif' statements only allowed inside 'single_source' " - "and 'multiple_sources' blocks."); + "'endif' statements only allowed inside 'entry' " + "and 'listing' blocks."); break; } } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "Invalid statement type: Allowed types are: block, endblock, if, endif."); + "Invalid statement type: Allowed types are: 'block', " + "'endblock', 'if' and 'endif'."); break; case TEMPLATE_BLOCK_BLOCK_TYPE_START: @@ -196,20 +197,20 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) if ((c >= 'a' && c <= 'z') || c == '_') break; if (c == ' ') { - if (0 == strncmp("single_source", src + start, current - start)) { - block_state = BLOCK_SINGLE_SOURCE; + if (0 == strncmp("entry", src + start, current - start)) { + block_state = BLOCK_ENTRY; end = current; state = TEMPLATE_BLOCK_END; break; } - else if (0 == strncmp("multiple_sources", src + start, current - start)) { - block_state = BLOCK_MULTIPLE_SOURCES; + else if (0 == strncmp("listing", src + start, current - start)) { + block_state = BLOCK_LISTING; end = current; state = TEMPLATE_BLOCK_END; break; } - else if (0 == strncmp("multiple_sources_once", src + start, current - start)) { - block_state = BLOCK_MULTIPLE_SOURCES_ONCE; + else if (0 == strncmp("listing_once", src + start, current - start)) { + block_state = BLOCK_LISTING_ONCE; end = current; state = TEMPLATE_BLOCK_END; break; @@ -217,7 +218,8 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *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."); + "Invalid block type. Allowed types are: 'entry', 'listing' " + "and 'listing_once'."); break; case TEMPLATE_BLOCK_IF_VARIABLE_START: @@ -243,7 +245,8 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "Invalid variable name. Must be uppercase letter, number or '_'."); + "Invalid variable name. Must be uppercase letter, number " + "or '_'."); break; case TEMPLATE_BLOCK_END: @@ -262,7 +265,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) if (c == ' ') break; if (c >= 'A' && c <= 'Z') { - if (block_state == BLOCK_SINGLE_SOURCE || block_state == BLOCK_MULTIPLE_SOURCES) { + if (block_state == BLOCK_ENTRY || block_state == BLOCK_LISTING) { state = TEMPLATE_VARIABLE; type = BLOGC_TEMPLATE_VARIABLE_STMT; start = current; @@ -270,8 +273,8 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "variable statements only allowed inside 'single_source' " - "and 'multiple_sources' blocks."); + "variable statements only allowed inside 'entry' and " + "'listing' blocks."); break; } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, @@ -294,7 +297,8 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, - "Invalid variable name. Must be uppercase letter, number or '_'."); + "Invalid variable name. Must be uppercase letter, number " + "or '_'."); break; case TEMPLATE_VARIABLE_END: @@ -339,10 +343,10 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) if (*err == NULL) { if (if_count != 0) *err = blogc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER, - "%d 'if' statements were not closed!", if_count); + "%d open 'if' statements were not closed!", if_count); else if (block_state != BLOCK_CLOSED) *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER, - "A block was not closed!"); + "An open block was not closed!"); } if (*err != NULL) { diff --git a/tests/check_template_parser.c b/tests/check_template_parser.c index 9d67fe3..f4c0825 100644 --- a/tests/check_template_parser.c +++ b/tests/check_template_parser.c @@ -39,20 +39,20 @@ test_template_parse(void **state) const char *a = "Test\n" "\n" - " {% block single_source %}\n" + " {% block entry %}\n" "{% if CHUNDA %}\n" "bola\n" "{% endif %}\n" "{% endblock %}\n" - "{% block multiple_sources %}{{ BOLA }}{% endblock %}\n" - "{% block multiple_sources_once %}asd{% endblock %}\n"; + "{% block listing %}{{ BOLA }}{% endblock %}\n" + "{% block listing_once %}asd{% endblock %}\n"; blogc_error_t *err = NULL; b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); assert_null(err); assert_non_null(stmts); blogc_assert_template_stmt(stmts, "Test\n\n ", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next, "single_source", + blogc_assert_template_stmt(stmts->next, "entry", BLOGC_TEMPLATE_BLOCK_STMT); blogc_assert_template_stmt(stmts->next->next, "\n", BLOGC_TEMPLATE_CONTENT_STMT); @@ -67,7 +67,7 @@ test_template_parse(void **state) b_slist_t *tmp = stmts->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); blogc_assert_template_stmt(tmp->next, "\n", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(tmp->next->next, "multiple_sources", + blogc_assert_template_stmt(tmp->next->next, "listing", BLOGC_TEMPLATE_BLOCK_STMT); blogc_assert_template_stmt(tmp->next->next->next, "BOLA", BLOGC_TEMPLATE_VARIABLE_STMT); @@ -76,7 +76,7 @@ test_template_parse(void **state) blogc_assert_template_stmt(tmp->next->next->next->next->next, "\n", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(tmp->next->next->next->next->next->next, - "multiple_sources_once", BLOGC_TEMPLATE_BLOCK_STMT); + "listing_once", BLOGC_TEMPLATE_BLOCK_STMT); blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next, "asd", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next, @@ -94,24 +94,24 @@ test_template_parse_html(void **state) const char *a = "\n" " \n" - " {% block single_source %}\n" + " {% block entry %}\n" " My cool blog >> {{ TITLE }}\n" " {% endblock %}\n" - " {% block multiple_sources_once %}\n" + " {% block listing_once %}\n" " My cool blog - Main page\n" " {% endblock %}\n" " \n" " \n" "

My cool blog

\n" - " {% block single_source %}\n" + " {% block entry %}\n" "

{{ TITLE }}

\n" " {% if DATE %}

Published in: {{ DATE }}

{% endif %}\n" "
{{ CONTENT }}
\n" " {% endblock %}\n" - " {% block multiple_sources_once %}{% endblock %}\n" " \n" "\n"; blogc_error_t *err = NULL; @@ -120,7 +120,7 @@ test_template_parse_html(void **state) assert_non_null(stmts); blogc_assert_template_stmt(stmts, "\n \n ", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next, "single_source", + blogc_assert_template_stmt(stmts->next, "entry", BLOGC_TEMPLATE_BLOCK_STMT); blogc_assert_template_stmt(stmts->next->next, "\n My cool blog >> ", BLOGC_TEMPLATE_CONTENT_STMT); @@ -133,7 +133,7 @@ test_template_parse_html(void **state) blogc_assert_template_stmt(stmts->next->next->next->next->next->next, "\n ", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(stmts->next->next->next->next->next->next->next, - "multiple_sources_once", BLOGC_TEMPLATE_BLOCK_STMT); + "listing_once", BLOGC_TEMPLATE_BLOCK_STMT); b_slist_t *tmp = stmts->next->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, "\n <title>My cool blog - Main page\n ", @@ -142,7 +142,7 @@ test_template_parse_html(void **state) blogc_assert_template_stmt(tmp->next->next, "\n \n \n

My cool blog

\n ", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(tmp->next->next->next, "single_source", + blogc_assert_template_stmt(tmp->next->next->next, "entry", BLOGC_TEMPLATE_BLOCK_STMT); blogc_assert_template_stmt(tmp->next->next->next->next, "\n

", BLOGC_TEMPLATE_CONTENT_STMT); @@ -170,7 +170,7 @@ test_template_parse_html(void **state) NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); tmp = tmp->next->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, "\n ", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(tmp->next, "multiple_sources_once", + blogc_assert_template_stmt(tmp->next, "listing_once", BLOGC_TEMPLATE_BLOCK_STMT); blogc_assert_template_stmt(tmp->next->next, "", BLOGC_TEMPLATE_CONTENT_STMT); -- cgit v1.2.3-18-g5258