From ec86e13f144d4d8de6a93b8b04117ea5028893d1 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Wed, 15 Apr 2015 20:01:17 -0300 Subject: added template grammar --- .gitignore | 3 +- Makefile.am | 29 +++++- src/main.c | 6 ++ src/template-grammar.h | 32 +++++++ src/template-grammar.leg | 200 +++++++++++++++++++++++++++++++++++++++++ tests/check_template_grammar.c | 98 ++++++++++++++++++++ 6 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 src/template-grammar.h create mode 100644 src/template-grammar.leg create mode 100644 tests/check_template_grammar.c diff --git a/.gitignore b/.gitignore index 1d340aa..2751756 100644 --- a/.gitignore +++ b/.gitignore @@ -40,10 +40,11 @@ Makefile.in /blogc # tests +/tests/check_template_grammar /tests/check_utils # leg generated source -/src/template/parser-grammar.c +/src/template-grammar.c # tarballs blogc-*.tar.* diff --git a/Makefile.am b/Makefile.am index 4e213e5..c0cbd6e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,12 +14,14 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ EXTRA_DIST = \ autogen.sh \ README.md \ + src/template-grammar.leg \ $(NULL) CLEANFILES = \ $(NULL) noinst_HEADERS = \ + src/template-grammar.h \ src/utils/utils.h \ $(NULL) @@ -39,6 +41,7 @@ check_PROGRAMS = \ libblogc_la_SOURCES = \ + src/template-grammar.c \ src/utils/slist.c \ src/utils/strings.c \ src/utils/trie.c \ @@ -53,6 +56,8 @@ libblogc_la_LIBADD = \ $(NULL) if USE_LEG +src/%-grammar.c: src/%-grammar.leg + $(AM_V_GEN)$(LEG) -o $@ $< endif blogc_SOURCES = \ @@ -84,9 +89,27 @@ endif if USE_CMOCKA check_PROGRAMS += \ + tests/check_template_grammar \ tests/check_utils \ $(NULL) +tests_check_template_grammar_SOURCES = \ + tests/check_template_grammar.c \ + $(NULL) + +tests_check_template_grammar_CFLAGS = \ + $(CMOCKA_CFLAGS) \ + $(NULL) + +tests_check_template_grammar_LDFLAGS = \ + -no-install \ + $(NULL) + +tests_check_template_grammar_LDADD = \ + $(CMOCKA_LIBS) \ + libblogc.la \ + $(NULL) + tests_check_utils_SOURCES = \ tests/check_utils.c \ $(NULL) @@ -96,7 +119,8 @@ tests_check_utils_CFLAGS = \ $(NULL) tests_check_utils_LDFLAGS = \ - -no-install + -no-install \ + $(NULL) tests_check_utils_LDADD = \ $(CMOCKA_LIBS) \ @@ -119,7 +143,7 @@ valgrind: all $(LIBTOOL) \ --mode=execute \ $(VALGRIND) \ - --tool=memcheck \ + --tool=memcheck --leak-check=full --show-leak-kinds=all \ --leak-check=full \ --leak-resolution=high \ --num-callers=20 \ @@ -145,5 +169,4 @@ endif # Helpers: Cleanup of helper files clean-local: - -rm -rf $(top_builddir)/doc/build/ -rm -rf $(top_builddir)/valgrind-*.xml diff --git a/src/main.c b/src/main.c index f989f62..65e45ee 100644 --- a/src/main.c +++ b/src/main.c @@ -12,10 +12,16 @@ #include +#include "template-grammar.h" + int main(int argc, char **argv) { + b_slist_t *t = blogc_template_parse( + "{{ BOLA }}\n" + "{% block single_source %}\n"); + printf("%s\n", (char*) ((blogc_template_stmt_t*)t->next->next->next->data)->value); printf("Hello, World!\n"); return 0; } diff --git a/src/template-grammar.h b/src/template-grammar.h new file mode 100644 index 0000000..e95fa46 --- /dev/null +++ b/src/template-grammar.h @@ -0,0 +1,32 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015 Rafael G. Martins + * + * This program can be distributed under the terms of the LGPL-2 License. + * See the file COPYING. + */ + +#ifndef _TEMPLATE_GRAMMAR_H +#define _TEMPLATE_GRAMMAR_H + +#include "utils/utils.h" + +typedef enum { + BLOGC_TEMPLATE_IF_STMT, + BLOGC_TEMPLATE_ELSE_STMT, + BLOGC_TEMPLATE_ENDIF_STMT, + BLOGC_TEMPLATE_BLOCK_STMT, + BLOGC_TEMPLATE_ENDBLOCK_STMT, + BLOGC_TEMPLATE_VARIABLE_STMT, + BLOGC_TEMPLATE_CONTENT_STMT, +} blogc_template_stmt_type_t; + +typedef struct { + blogc_template_stmt_type_t type; + char *value; +} blogc_template_stmt_t; + +b_slist_t* blogc_template_parse(const char *tmpl); +void blogc_template_free_stmts(b_slist_t *stmts); + +#endif /* _TEMPLATE_GRAMMAR_H */ diff --git a/src/template-grammar.leg b/src/template-grammar.leg new file mode 100644 index 0000000..63e2a69 --- /dev/null +++ b/src/template-grammar.leg @@ -0,0 +1,200 @@ +# +# blogc: A balde compiler. +# Copyright (C) 2015 Rafael G. Martins +# +# This program can be distributed under the terms of the BSD License. +# See the file COPYING. +# + +%{ + +#include +#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/tests/check_template_grammar.c b/tests/check_template_grammar.c new file mode 100644 index 0000000..ffad8fb --- /dev/null +++ b/tests/check_template_grammar.c @@ -0,0 +1,98 @@ +/* + * 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 "../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); +} + + +int +main(void) +{ + const UnitTest tests[] = { + unit_test(test_template_parse), + }; + return run_tests(tests); +} -- cgit v1.2.3-18-g5258