aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2015-04-17 23:49:55 -0300
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2015-04-17 23:49:55 -0300
commitbf42a95568f5efffcc87a9b5a7683b7b270a098f (patch)
tree4d3ea13c85bc34b609271ce0bf3b35cdf5a98c6f
parentda54782672331c68fc7c412f4ebe27a9738342eb (diff)
downloadblogc-bf42a95568f5efffcc87a9b5a7683b7b270a098f.tar.gz
blogc-bf42a95568f5efffcc87a9b5a7683b7b270a098f.tar.bz2
blogc-bf42a95568f5efffcc87a9b5a7683b7b270a098f.zip
replaced leg-based parser with handmade parser for templates
yay! no leg parser needed anymore. parsers still needs some work and error handling, though.
-rw-r--r--.gitignore5
-rw-r--r--Makefile.am31
-rw-r--r--README.md2
-rw-r--r--configure.ac31
-rw-r--r--src/main.c8
-rw-r--r--src/output.c2
-rw-r--r--src/template-grammar.leg200
-rw-r--r--src/template-parser.c296
-rw-r--r--src/template-parser.h (renamed from src/template-grammar.h)9
-rw-r--r--tests/check_template_grammar.c145
-rw-r--r--tests/check_template_parser.c222
11 files changed, 534 insertions, 417 deletions
diff --git a/.gitignore b/.gitignore
index 92256ca..3ac375c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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)
diff --git a/README.md b/README.md
index 6919683..5f6f607 100644
--- a/README.md
+++ b/README.md
@@ -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}
")
diff --git a/src/main.c b/src/main.c
index d05c8d6..4e72b6e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
+}