diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | Makefile.am | 31 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | configure.ac | 31 | ||||
-rw-r--r-- | src/main.c | 8 | ||||
-rw-r--r-- | src/output.c | 2 | ||||
-rw-r--r-- | src/template-grammar.leg | 200 | ||||
-rw-r--r-- | src/template-parser.c | 296 | ||||
-rw-r--r-- | src/template-parser.h (renamed from src/template-grammar.h) | 9 | ||||
-rw-r--r-- | tests/check_template_grammar.c | 145 | ||||
-rw-r--r-- | tests/check_template_parser.c | 222 |
11 files changed, 534 insertions, 417 deletions
@@ -41,11 +41,8 @@ Makefile.in # tests /tests/check_source_parser -/tests/check_template_grammar +/tests/check_template_parser /tests/check_utils -# leg generated source -/src/template-grammar.c - # tarballs blogc-*.tar.* diff --git a/Makefile.am b/Makefile.am index 3138d25..e639307 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,7 +14,6 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ EXTRA_DIST = \ autogen.sh \ README.md \ - src/template-grammar.leg \ $(NULL) CLEANFILES = \ @@ -23,7 +22,7 @@ CLEANFILES = \ noinst_HEADERS = \ src/output.h \ src/source-parser.h \ - src/template-grammar.h \ + src/template-parser.h \ src/utils/utils.h \ $(NULL) @@ -45,7 +44,7 @@ check_PROGRAMS = \ libblogc_la_SOURCES = \ src/output.c \ src/source-parser.c \ - src/template-grammar.c \ + src/template-parser.c \ src/utils/slist.c \ src/utils/strings.c \ src/utils/trie.c \ @@ -59,10 +58,6 @@ libblogc_la_CFLAGS = \ libblogc_la_LIBADD = \ $(NULL) -if USE_LEG -src/%-grammar.c: src/%-grammar.leg - $(AM_V_GEN)$(LEG) -o $@ $< -endif blogc_SOURCES = \ src/main.c \ @@ -78,23 +73,13 @@ blogc_LDADD = \ $(NULL) -## Build rules: examples - -if BUILD_EXAMPLES - -noinst_PROGRAMS += \ - $(NULL) - -endif - - ## Build rules: tests if USE_CMOCKA check_PROGRAMS += \ tests/check_source_parser \ - tests/check_template_grammar \ + tests/check_template_parser \ tests/check_utils \ $(NULL) @@ -115,19 +100,19 @@ tests_check_source_parser_LDADD = \ libblogc.la \ $(NULL) -tests_check_template_grammar_SOURCES = \ - tests/check_template_grammar.c \ +tests_check_template_parser_SOURCES = \ + tests/check_template_parser.c \ $(NULL) -tests_check_template_grammar_CFLAGS = \ +tests_check_template_parser_CFLAGS = \ $(CMOCKA_CFLAGS) \ $(NULL) -tests_check_template_grammar_LDFLAGS = \ +tests_check_template_parser_LDFLAGS = \ -no-install \ $(NULL) -tests_check_template_grammar_LDADD = \ +tests_check_template_parser_LDADD = \ $(CMOCKA_LIBS) \ libblogc.la \ $(NULL) @@ -22,7 +22,7 @@ The templates can define blocks. These are the block rules: 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 %}``, ``{% else %}`` 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 the compiler is output-agnostic, Atom feeds and sitemaps should be generated using templates as well. diff --git a/configure.ac b/configure.ac index f6b73db..5736a33 100644 --- a/configure.ac +++ b/configure.ac @@ -16,16 +16,6 @@ AS_IF([test "x$ac_cv_prog_cc_c99" = "xno"], [ AC_MSG_ERROR([no C99 compiler found, blogc requires a C99 compiler.]) ]) -AC_ARG_ENABLE([examples], AS_HELP_STRING([--enable-examples], [build examples])) -AS_IF([test "x$enable_examples" = "xyes"], [ - build_examples=yes - EXAMPLES="enabled" -], [ - build_examples=no - EXAMPLES="disabled" -]) -AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" = "xyes"]) - AC_ARG_ENABLE([valgrind], AS_HELP_STRING([--disable-valgrind], [ignore presence of valgrind])) AS_IF([test "x$enable_valgrind" != "xno"], [ @@ -45,25 +35,6 @@ AM_CONDITIONAL([USE_VALGRIND], [test "x$have_valgrind" = "xyes"]) VALGRIND="$ac_cv_path_valgrind" AC_SUBST(VALGRIND) -AC_ARG_ENABLE([leg], AS_HELP_STRING([--disable-leg], - [ignore presence of peg/leg and disable parser grammar regeneration])) -AS_IF([test "x$enable_leg" != "xno"], [ - AC_PATH_PROG([leg], [leg]) - AS_IF([test "x$ac_cv_path_leg" = "x"], [ - have_leg=no - ], [ - have_leg=yes - ]) -]) -AS_IF([test "x$have_leg" = "xyes"], , [ - AS_IF([test "x$enable_leg" = "xyes"], [ - AC_MSG_ERROR([peg/leg requested but not found]) - ]) -]) -AM_CONDITIONAL([USE_LEG], [test "x$have_leg" = "xyes"]) -LEG="$ac_cv_path_leg" -AC_SUBST(LEG) - AC_ARG_ENABLE([cmocka], AS_HELP_STRING([--disable-cmocka], [ignore presence of cmocka. this will disable unit tests])) AS_IF([test "x$enable_cmocka" != "xno"], [ @@ -99,9 +70,7 @@ AS_ECHO(" cflags: ${CFLAGS} ldflags: ${LDFLAGS} - examples: ${EXAMPLES} cmocka: ${CMOCKA} valgrind: ${VALGRIND} - leg: ${LEG} ") @@ -13,19 +13,13 @@ #include <stdio.h> #include "source-parser.h" +#include "template-parser.h" #include <string.h> int main(int argc, char **argv) { - const char *a = - "\n \nBOLA : guda\n\t\n\n\n\n" - "CHUNDA: asd\n" - "----\n" - "{% block single_source %}\nbola\n\nzas\n"; - blogc_source_t *t = blogc_source_parse(a, strlen(a)); - printf("%s\n", t->content); printf("Hello, World!\n"); return 0; } diff --git a/src/output.c b/src/output.c index bd96b8e..1664afe 100644 --- a/src/output.c +++ b/src/output.c @@ -32,7 +32,7 @@ blogc_parser_syntax_error(const char *name, const char *src, size_t src_len, current++; } - fprintf(stderr, "%s parser error: syntax error near \"%s\"\n", name, + fprintf(stderr, "%s parser error: failed to parse input near \"%s\".\n", name, msg->str); b_string_free(msg, true); diff --git a/src/template-grammar.leg b/src/template-grammar.leg deleted file mode 100644 index 2425582..0000000 --- a/src/template-grammar.leg +++ /dev/null @@ -1,200 +0,0 @@ -# -# blogc: A blog compiler. -# Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> -# -# This program can be distributed under the terms of the BSD License. -# See the file COPYING. -# - -%{ - -#include <stdio.h> -#include "utils/utils.h" -#include "template-grammar.h" - -#define YY_INPUT(buf, result, max_size) \ -{ \ - int yyc = (charbuf && *charbuf != '\0') ? *charbuf++ : EOF; \ - result = (EOF == yyc) ? 0 : (*buf = yyc, 1); \ -} - - -static b_slist_t *stmts = NULL; -static int if_count = 0; -static int block_count = 0; -static const char *charbuf = NULL; - - -static void -blogc_template_if_stmt(const char *value) -{ - if (block_count <= 0) { - fprintf(stderr, "Syntax error: {" "%% if ... %%" "} statement before " - "any {" "%% block ... %%" "} statement\n"); - exit(1); - } - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = b_strdup(value); - stmt->type = BLOGC_TEMPLATE_IF_STMT; - stmts = b_slist_append(stmts, stmt); - if_count++; -} - - -static void -blogc_template_else_stmt(void) -{ - if (if_count <= 0) { - fprintf(stderr, "Syntax error: {" "%% else %%" "} statement without " - "any open {" "%% if ... %%" "} statement\n"); - exit(1); - } - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = NULL; - stmt->type = BLOGC_TEMPLATE_ELSE_STMT; - stmts = b_slist_append(stmts, stmt); -} - - -static void -blogc_template_endif_stmt(void) -{ - if (if_count-- <= 0) { - fprintf(stderr, "Syntax error: {" "%% endif %%" "} statement before " - "any {" "%% if ... %%" "} statement\n"); - exit(1); - } - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = NULL; - stmt->type = BLOGC_TEMPLATE_ENDIF_STMT; - stmts = b_slist_append(stmts, stmt); -} - - -static void -blogc_template_block_stmt(const char *value) -{ - if (block_count > 0) { - fprintf(stderr, "Syntax error: {" "%% block %%" "} statements " - "can't be nested\n"); - exit(1); - } - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = b_strdup(value); - stmt->type = BLOGC_TEMPLATE_BLOCK_STMT; - stmts = b_slist_append(stmts, stmt); - block_count++; -} - - -static void -blogc_template_endblock_stmt(void) -{ - if (block_count-- <= 0) { - fprintf(stderr, "Syntax error: {" "%% endblock %%" "} statement before " - "any {" "%% block ... %%" "} statement\n"); - exit(1); - } - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = NULL; - stmt->type = BLOGC_TEMPLATE_ENDBLOCK_STMT; - stmts = b_slist_append(stmts, stmt); -} - - -static void -blogc_template_variable_stmt(const char *value) -{ - if (block_count <= 0) { - fprintf(stderr, "Syntax error: {{ ... }} statement before " - "any {" "%% block ... %%" "} statement\n"); - exit(1); - } - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = b_strdup(value); - stmt->type = BLOGC_TEMPLATE_VARIABLE_STMT; - stmts = b_slist_append(stmts, stmt); -} - - -static void -blogc_template_content_stmt(const char *value) -{ - blogc_template_stmt_t *stmt = malloc(sizeof(blogc_template_stmt_t)); - stmt->value = b_strdup(value); - stmt->type = BLOGC_TEMPLATE_CONTENT_STMT; - stmts = b_slist_append(stmts, stmt); -} - -%} - -page = if | else | endif | block | endblock | print | content | anything - { fprintf(stderr, "Syntax error near: %s\n", yytext); exit(1); } - - -# Useful rules -eol = '\n' | '\r\n' | '\r' -eof = !. -- = [\t ]* -id = [A-Z][A-Z0-9_]* -anything = < ( !eol . )* > eol - -# Conditionals -if_open = '{%' - -if_close = - '%}' -if = if_open 'if' ' '+ < id > if_close { blogc_template_if_stmt(yytext); } -else = if_open 'else' if_close { blogc_template_else_stmt(); } -endif = if_open 'endif' if_close { blogc_template_endif_stmt(); } - -# Blocks -block_open = '{%' - -block_close = - '%}' -block_name = ( 'single_source' | 'multiple_sources_once' | 'multiple_sources' ) -block = block_open 'block' ' '+ < block_name > block_close { blogc_template_block_stmt(yytext); } -endblock = block_open 'endblock' block_close { blogc_template_endblock_stmt(); } - -# Print calls -print_open = '{{' - -print_close = - '}}' -print_var = < id > { blogc_template_variable_stmt(yytext); } -print = print_open print_var print_close - -# Generic content -content = < ( !eof !if_open !if_close !block_open !block_close !print_open !print_close . )+ > - { blogc_template_content_stmt(yytext); } - -%% - - -void -blogc_template_free_stmts(b_slist_t *stmts) -{ - for (b_slist_t *tmp = stmts; tmp != NULL; tmp = tmp->next) { - blogc_template_stmt_t *data = tmp->data; - free(data->value); - free(data); - } - b_slist_free(stmts); -} - - -b_slist_t* -blogc_template_parse(const char *tmpl) -{ - if_count = 0; - block_count = 0; - charbuf = tmpl; - while(yyparse()); - if (if_count != 0) { - fprintf(stderr, "Syntax error: You left %d open {" "%% if ... %%" "} statements.\n", if_count); - exit(1); - } - if (block_count != 0) { - fprintf(stderr, "Syntax error: You left %d open {" "%% block ... %%" "} statements.\n", block_count); - exit(1); - } - b_slist_t *rv = stmts; - charbuf = NULL; - stmts = NULL; - return rv; -} diff --git a/src/template-parser.c b/src/template-parser.c new file mode 100644 index 0000000..c5286ea --- /dev/null +++ b/src/template-parser.c @@ -0,0 +1,296 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file COPYING. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> + +#include "utils/utils.h" +#include "template-parser.h" +#include "output.h" + + +typedef enum { + TEMPLATE_START = 1, + TEMPLATE_OPEN_BRACKET, + TEMPLATE_BLOCK_START, + TEMPLATE_BLOCK_TYPE, + TEMPLATE_BLOCK_BLOCK_TYPE_START, + TEMPLATE_BLOCK_BLOCK_TYPE, + TEMPLATE_BLOCK_IF_VARIABLE_START, + TEMPLATE_BLOCK_IF_VARIABLE, + TEMPLATE_BLOCK_END, + TEMPLATE_VARIABLE_START, + TEMPLATE_VARIABLE, + TEMPLATE_VARIABLE_END, + TEMPLATE_CLOSE_BRACKET, +} blogc_template_parser_state_t; + + +b_slist_t* +blogc_template_parse(const char *src, size_t src_len) +{ + size_t current = 0; + size_t start = 0; + size_t end = 0; + size_t remaining = 0; + + bool error = false; + char *tmp = NULL; + + bool open_block = false; + unsigned int if_count = 0; + + b_slist_t *stmts = NULL; + blogc_template_stmt_t *stmt = NULL; + + blogc_template_parser_state_t state = TEMPLATE_START; + blogc_template_stmt_type_t type = BLOGC_TEMPLATE_CONTENT_STMT; + + while (current < src_len) { + char c = src[current]; + bool last = current == src_len - 1; + + switch (state) { + + case TEMPLATE_START: + if (last) { + stmt = malloc(sizeof(blogc_template_stmt_t)); + stmt->type = type; + stmt->value = b_strndup(src + start, src_len - start); + stmts = b_slist_append(stmts, stmt); + stmt = NULL; + } + if (c == '{') { + end = current; + state = TEMPLATE_OPEN_BRACKET; + } + break; + + case TEMPLATE_OPEN_BRACKET: + if (c == '%' || c == '{') { + if (c == '%') + state = TEMPLATE_BLOCK_START; + else + state = TEMPLATE_VARIABLE_START; + if (end > start) { + stmt = malloc(sizeof(blogc_template_stmt_t)); + stmt->type = type; + stmt->value = b_strndup(src + start, end - start); + stmts = b_slist_append(stmts, stmt); + stmt = NULL; + } + } + break; + + case TEMPLATE_BLOCK_START: + if (c == ' ') + break; + if (c >= 'a' && c <= 'z') { + state = TEMPLATE_BLOCK_TYPE; + start = current; + break; + } + error = true; + break; + + case TEMPLATE_BLOCK_TYPE: + if (c >= 'a' && c <= 'z') + break; + if (c == ' ') { + if (0 == strncmp("block", src + start, current - start)) { + if (!open_block) { + state = TEMPLATE_BLOCK_BLOCK_TYPE_START; + type = BLOGC_TEMPLATE_BLOCK_STMT; + start = current; + open_block = true; + break; + } + } + else if (0 == strncmp("endblock", src + start, current - start)) { + if (open_block) { + state = TEMPLATE_BLOCK_END; + type = BLOGC_TEMPLATE_ENDBLOCK_STMT; + open_block = false; + break; + } + } + else if (0 == strncmp("if", src + start, current - start)) { + if (open_block) { + state = TEMPLATE_BLOCK_IF_VARIABLE_START; + type = BLOGC_TEMPLATE_IF_STMT; + start = current; + if_count++; + break; + } + } + else if (0 == strncmp("endif", src + start, current - start)) { + if (open_block) { + if (if_count > 0) { + state = TEMPLATE_BLOCK_END; + type = BLOGC_TEMPLATE_ENDIF_STMT; + if_count--; + break; + } + } + } + } + error = true; + break; + + case TEMPLATE_BLOCK_BLOCK_TYPE_START: + if (c == ' ') + break; + if (c >= 'a' && c <= 'z') { + state = TEMPLATE_BLOCK_BLOCK_TYPE; + start = current; + break; + } + error = true; + break; + + case TEMPLATE_BLOCK_BLOCK_TYPE: + if ((c >= 'a' && c <= 'z') || c == '_') + break; + if (c == ' ') { + if ((0 == strncmp("single_source", src + start, current - start)) || + (0 == strncmp("multiple_sources", src + start, current - start)) || + (0 == strncmp("multiple_sources_once", src + start, current - start))) + { + end = current; + state = TEMPLATE_BLOCK_END; + break; + } + } + error = true; + break; + + case TEMPLATE_BLOCK_IF_VARIABLE_START: + if (c == ' ') + break; + if (c >= 'A' && c <= 'Z') { + state = TEMPLATE_BLOCK_IF_VARIABLE; + start = current; + break; + } + error = true; + break; + + case TEMPLATE_BLOCK_IF_VARIABLE: + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') + break; + if (c == ' ') { + end = current; + state = TEMPLATE_BLOCK_END; + break; + } + error = true; + break; + + case TEMPLATE_BLOCK_END: + if (c == ' ') + break; + if (c == '%') { + state = TEMPLATE_CLOSE_BRACKET; + break; + } + error = true; + break; + + case TEMPLATE_VARIABLE_START: + if (c == ' ') + break; + if (c >= 'A' && c <= 'Z') { + if (open_block) { + state = TEMPLATE_VARIABLE; + type = BLOGC_TEMPLATE_VARIABLE_STMT; + start = current; + break; + } + } + error = true; + break; + + case TEMPLATE_VARIABLE: + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') + break; + if (c == ' ') { + end = current; + state = TEMPLATE_VARIABLE_END; + break; + } + if (c == '}') { + end = current; + state = TEMPLATE_CLOSE_BRACKET; + break; + } + error = true; + break; + + case TEMPLATE_VARIABLE_END: + if (c == ' ') + break; + if (c == '}') { + state = TEMPLATE_CLOSE_BRACKET; + break; + } + error = true; + break; + + case TEMPLATE_CLOSE_BRACKET: + if (c == '}') { + stmt = malloc(sizeof(blogc_template_stmt_t)); + stmt->type = type; + stmt->value = NULL; + if (end > start) + stmt->value = b_strndup(src + start, end - start); + stmts = b_slist_append(stmts, stmt); + stmt = NULL; + state = TEMPLATE_START; + type = BLOGC_TEMPLATE_CONTENT_STMT; + start = current + 1; + break; + } + error = true; + break; + + } + + if (error) + break; + + current++; + } + + if (error) { + if (stmt != NULL) { + free(stmt->value); + free(stmt); + } + blogc_template_free_stmts(stmts); + blogc_parser_syntax_error("template", src, src_len, current); + return NULL; + } + + return stmts; +} + + +void +blogc_template_free_stmts(b_slist_t *stmts) +{ + for (b_slist_t *tmp = stmts; tmp != NULL; tmp = tmp->next) { + blogc_template_stmt_t *data = tmp->data; + free(data->value); + free(data); + } + b_slist_free(stmts); +} diff --git a/src/template-grammar.h b/src/template-parser.h index 310acb5..1f43fe8 100644 --- a/src/template-grammar.h +++ b/src/template-parser.h @@ -6,14 +6,13 @@ * See the file COPYING. */ -#ifndef _TEMPLATE_GRAMMAR_H -#define _TEMPLATE_GRAMMAR_H +#ifndef _TEMPLATE_PARSER_H +#define _TEMPLATE_PARSER_H #include "utils/utils.h" typedef enum { - BLOGC_TEMPLATE_IF_STMT, - BLOGC_TEMPLATE_ELSE_STMT, + BLOGC_TEMPLATE_IF_STMT = 1, BLOGC_TEMPLATE_ENDIF_STMT, BLOGC_TEMPLATE_BLOCK_STMT, BLOGC_TEMPLATE_ENDBLOCK_STMT, @@ -26,7 +25,7 @@ typedef struct { char *value; } blogc_template_stmt_t; -b_slist_t* blogc_template_parse(const char *tmpl); +b_slist_t* blogc_template_parse(const char *src, size_t src_len); void blogc_template_free_stmts(b_slist_t *stmts); #endif /* _TEMPLATE_GRAMMAR_H */ diff --git a/tests/check_template_grammar.c b/tests/check_template_grammar.c deleted file mode 100644 index 3b4dcca..0000000 --- a/tests/check_template_grammar.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> - * - * This program can be distributed under the terms of the BSD License. - * See the file COPYING. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include <cmocka.h> -#include "../src/template-grammar.h" - - -static void -blogc_assert_template_stmt(b_slist_t *l, const char *value, - const blogc_template_stmt_type_t type) -{ - blogc_template_stmt_t *stmt = l->data; - if (value == NULL) - assert_null(stmt->value); - else - assert_string_equal(stmt->value, value); - assert_int_equal(stmt->type, type); -} - - -static void -test_template_parse(void **state) -{ - b_slist_t *stmts = blogc_template_parse( - "Test\n" - "\n" - " {% block single_source %}\n" - "{% if CHUNDA %}\n" - "bola\n" - "{% else %}\n" - "guda\n" - "{% endif %}\n" - "{% endblock %}\n" - "{% block multiple_sources %}{{ BOLA }}{% endblock %}\n" - "{% block multiple_sources_once %}asd{% endblock %}\n"); - 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_TEMPLATE_BLOCK_STMT); - blogc_assert_template_stmt(stmts->next->next, "\n", - BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next->next->next, "CHUNDA", - BLOGC_TEMPLATE_IF_STMT); - blogc_assert_template_stmt(stmts->next->next->next->next, "\nbola\n", - BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next->next->next->next->next, NULL, - BLOGC_TEMPLATE_ELSE_STMT); - blogc_assert_template_stmt(stmts->next->next->next->next->next->next, - "\nguda\n", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next->next->next->next->next->next->next, - NULL, BLOGC_TEMPLATE_ENDIF_STMT); - blogc_assert_template_stmt(stmts->next->next->next->next->next->next->next->next, - "\n", BLOGC_TEMPLATE_CONTENT_STMT); - b_slist_t *tmp = stmts->next->next->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_TEMPLATE_BLOCK_STMT); - blogc_assert_template_stmt(tmp->next->next->next, "BOLA", - BLOGC_TEMPLATE_VARIABLE_STMT); - blogc_assert_template_stmt(tmp->next->next->next->next, NULL, - BLOGC_TEMPLATE_ENDBLOCK_STMT); - 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); - 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, - NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); - blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next->next, - "\n", BLOGC_TEMPLATE_CONTENT_STMT); - assert_null(tmp->next->next->next->next->next->next->next->next->next->next); - blogc_template_free_stmts(stmts); -} - - -static void -test_template_parse_html(void **state) -{ - b_slist_t *stmts = blogc_template_parse( - "<html>\n" - " <head>\n" - " {% block single_source %}\n" - " <title>My cool blog >> {{ TITLE }}</title>\n" - " {% endblock %}\n" - " {% block multiple_sources %}\n" - " <title>My cool blog - Main page</title>\n" - " {% endblock %}\n" - " </head>\n" - " <body>\n" - " <h1>My cool blog</h1>\n" - " {% block single_source %}\n" - " <h2>{{ TITLE }}</h2>\n" - " {% if DATE %}<h4>Published in: {{ DATE }}</h4>{% endif %}\n" - " <pre>{{ CONTENT }}</pre>\n" - " {% endblock %}\n" - " {% block multiple_sources_once %}<ul>{% endblock %}\n" - " {% block multiple_sources %}<p><a href=\"{{ FILENAME }}.html\">" - "{{ TITLE }}</a>{% if DATE %} - {{ DATE }}{% endif %}</p>{% endblock %}\n" - " {% block multiple_sources_once %}</ul>{% endblock %}\n" - " </body>\n" - "</html>\n"); - assert_non_null(stmts); - blogc_assert_template_stmt(stmts, "<html>\n <head>\n ", - BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next, "single_source", - BLOGC_TEMPLATE_BLOCK_STMT); - blogc_assert_template_stmt(stmts->next->next, - "\n <title>My cool blog >> ", BLOGC_TEMPLATE_CONTENT_STMT); - blogc_assert_template_stmt(stmts->next->next->next, "TITLE", - BLOGC_TEMPLATE_VARIABLE_STMT); - blogc_assert_template_stmt(stmts->next->next->next->next, - "</title>\n ", BLOGC_TEMPLATE_CONTENT_STMT); - - - - - - blogc_template_free_stmts(stmts); -} - - -int -main(void) -{ - const UnitTest tests[] = { - unit_test(test_template_parse), - unit_test(test_template_parse_html), - }; - return run_tests(tests); -} diff --git a/tests/check_template_parser.c b/tests/check_template_parser.c new file mode 100644 index 0000000..7839bbf --- /dev/null +++ b/tests/check_template_parser.c @@ -0,0 +1,222 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file COPYING. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <string.h> +#include "../src/template-parser.h" + + +static void +blogc_assert_template_stmt(b_slist_t *l, const char *value, + const blogc_template_stmt_type_t type) +{ + blogc_template_stmt_t *stmt = l->data; + if (value == NULL) + assert_null(stmt->value); + else + assert_string_equal(stmt->value, value); + assert_int_equal(stmt->type, type); +} + + +static void +test_template_parse(void **state) +{ + const char *a = + "Test\n" + "\n" + " {% block single_source %}\n" + "{% if CHUNDA %}\n" + "bola\n" + "{% endif %}\n" + "{% endblock %}\n" + "{% block multiple_sources %}{{ BOLA }}{% endblock %}\n" + "{% block multiple_sources_once %}asd{% endblock %}\n"; + b_slist_t *stmts = blogc_template_parse(a, strlen(a)); + 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_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(stmts->next->next, "\n", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(stmts->next->next->next, "CHUNDA", + BLOGC_TEMPLATE_IF_STMT); + blogc_assert_template_stmt(stmts->next->next->next->next, "\nbola\n", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(stmts->next->next->next->next->next, + NULL, BLOGC_TEMPLATE_ENDIF_STMT); + blogc_assert_template_stmt(stmts->next->next->next->next->next->next, + "\n", BLOGC_TEMPLATE_CONTENT_STMT); + 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_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next, "BOLA", + BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next, NULL, + BLOGC_TEMPLATE_ENDBLOCK_STMT); + 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); + 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, + NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next->next, + "\n", BLOGC_TEMPLATE_CONTENT_STMT); + assert_null(tmp->next->next->next->next->next->next->next->next->next->next); + blogc_template_free_stmts(stmts); +} + + +static void +test_template_parse_html(void **state) +{ + const char *a = + "<html>\n" + " <head>\n" + " {% block single_source %}\n" + " <title>My cool blog >> {{ TITLE }}</title>\n" + " {% endblock %}\n" + " {% block multiple_sources %}\n" + " <title>My cool blog - Main page</title>\n" + " {% endblock %}\n" + " </head>\n" + " <body>\n" + " <h1>My cool blog</h1>\n" + " {% block single_source %}\n" + " <h2>{{ TITLE }}</h2>\n" + " {% if DATE %}<h4>Published in: {{ DATE }}</h4>{% endif %}\n" + " <pre>{{ CONTENT }}</pre>\n" + " {% endblock %}\n" + " {% block multiple_sources_once %}<ul>{% endblock %}\n" + " {% block multiple_sources %}<p><a href=\"{{ FILENAME }}.html\">" + "{{ TITLE }}</a>{% if DATE %} - {{ DATE }}{% endif %}</p>{% endblock %}\n" + " {% block multiple_sources_once %}</ul>{% endblock %}\n" + " </body>\n" + "</html>\n"; + b_slist_t *stmts = blogc_template_parse(a, strlen(a)); + assert_non_null(stmts); + blogc_assert_template_stmt(stmts, "<html>\n <head>\n ", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(stmts->next, "single_source", + BLOGC_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(stmts->next->next, + "\n <title>My cool blog >> ", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(stmts->next->next->next, "TITLE", + BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(stmts->next->next->next->next, + "</title>\n ", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(stmts->next->next->next->next->next, NULL, + BLOGC_TEMPLATE_ENDBLOCK_STMT); + 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", 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</title>\n ", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next, NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next, + "\n </head>\n <body>\n <h1>My cool blog</h1>\n ", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next, "single_source", + BLOGC_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next, + "\n <h2>", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next, + "TITLE", BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next, + "</h2>\n ", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next, + "DATE", BLOGC_TEMPLATE_IF_STMT); + tmp = tmp->next->next->next->next->next->next->next->next; + blogc_assert_template_stmt(tmp, "<h4>Published in: ", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next, "DATE", BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(tmp->next->next, "</h4>", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next, NULL, + BLOGC_TEMPLATE_ENDIF_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next, "\n <pre>", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next, + "CONTENT", BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next, + "</pre>\n ", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next, + 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_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next, "<ul>", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next, NULL, + BLOGC_TEMPLATE_ENDBLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next, "\n ", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next, + "multiple_sources", BLOGC_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next, + "<p><a href=\"", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next, + "FILENAME", BLOGC_TEMPLATE_VARIABLE_STMT); + tmp = tmp->next->next->next->next->next->next->next->next; + blogc_assert_template_stmt(tmp, ".html\">", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next, "TITLE", + BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(tmp->next->next, "</a>", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next, "DATE", + BLOGC_TEMPLATE_IF_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next, " - ", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next, "DATE", + BLOGC_TEMPLATE_VARIABLE_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next, + NULL, BLOGC_TEMPLATE_ENDIF_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next, + "</p>", BLOGC_TEMPLATE_CONTENT_STMT); + tmp = tmp->next->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_once", + BLOGC_TEMPLATE_BLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next, "</ul>", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next, NULL, + BLOGC_TEMPLATE_ENDBLOCK_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next, + "\n </body>\n</html>\n", BLOGC_TEMPLATE_CONTENT_STMT); + assert_null(tmp->next->next->next->next->next->next); + blogc_template_free_stmts(stmts); +} + + +int +main(void) +{ + const UnitTest tests[] = { + unit_test(test_template_parse), + unit_test(test_template_parse_html), + }; + return run_tests(tests); +} |