From 8d96c02e5118cf7bd656fde9100570a67115d19a Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 29 Dec 2015 00:12:51 +0100 Subject: man: renderer: template-parser: added foreach iterator support --- src/renderer.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/renderer.h | 2 ++ src/template-parser.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++- src/template-parser.h | 2 ++ 4 files changed, 141 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/renderer.c b/src/renderer.c index 3061c43..6cf924e 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -88,6 +88,24 @@ blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local) } +b_slist_t* +blogc_split_list_variable(const char *name, b_trie_t *global, b_trie_t *local) +{ + const char *value = blogc_get_variable(name, global, local); + if (value == NULL) + return NULL; + + b_slist_t *rv = NULL; + + char **tmp = b_str_split(value, ',', 0); + for (unsigned int i = 0; tmp[i] != NULL; i++) + rv = b_slist_append(rv, b_strdup(b_str_strip(tmp[i]))); + b_strv_free(tmp); + + return rv; +} + + char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing) { @@ -106,6 +124,10 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing unsigned int if_count = 0; unsigned int if_skip = 0; + b_slist_t *foreach_var = NULL; + b_slist_t *foreach_var_start = NULL; + b_slist_t *foreach_start = NULL; + bool if_not = false; bool inside_block = false; bool evaluate = false; @@ -174,6 +196,11 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing case BLOGC_TEMPLATE_VARIABLE_STMT: if (stmt->value != NULL) { + if (0 == strcmp(stmt->value, "FOREACH_ITEM")) { // foreach + if (foreach_var != NULL && foreach_var->data != NULL) + b_string_append(str, foreach_var->data); + break; + } config_value = blogc_format_variable(stmt->value, config, inside_block ? tmp_source : NULL); if (config_value != NULL) { @@ -281,9 +308,53 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing case BLOGC_TEMPLATE_ENDIF_STMT: if_count--; break; + + case BLOGC_TEMPLATE_FOREACH_STMT: + if (foreach_var_start == NULL) { + if (stmt->value != NULL) + foreach_var_start = blogc_split_list_variable(stmt->value, + config, inside_block ? tmp_source : NULL); + + if (foreach_var_start != NULL) { + foreach_var = foreach_var_start; + foreach_start = tmp; + } + else { + + // we can just skip anything and walk until the next + // 'endforeach' + while (stmt->type != BLOGC_TEMPLATE_ENDFOREACH_STMT) { + tmp = tmp->next; + stmt = tmp->data; + } + break; + } + } + + if (foreach_var == NULL) { + foreach_start = tmp; + foreach_var = foreach_var_start; + } + break; + + case BLOGC_TEMPLATE_ENDFOREACH_STMT: + if (foreach_start != NULL && foreach_var != NULL) { + foreach_var = foreach_var->next; + if (foreach_var != NULL) { + tmp = foreach_start; + continue; + } + } + foreach_start = NULL; + b_slist_free_full(foreach_var_start, free); + foreach_var_start = NULL; + break; } tmp = tmp->next; } + // no need to free temporary variables here. the template parser makes sure + // that templates are sane and statements are closed. + return b_string_free(str, false); } diff --git a/src/renderer.h b/src/renderer.h index e5cff6e..de26e98 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -15,6 +15,8 @@ const char* blogc_get_variable(const char *name, b_trie_t *global, b_trie_t *local); char* blogc_format_date(const char *date, b_trie_t *global, b_trie_t *local); char* blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local); +b_slist_t* blogc_split_list_variable(const char *name, b_trie_t *global, + b_trie_t *local); char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing); diff --git a/src/template-parser.c b/src/template-parser.c index f6912df..e5c750e 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -32,6 +32,8 @@ typedef enum { TEMPLATE_BLOCK_IF_OPERAND_START, TEMPLATE_BLOCK_IF_STRING_OPERAND, TEMPLATE_BLOCK_IF_VARIABLE_OPERAND, + TEMPLATE_BLOCK_FOREACH_START, + TEMPLATE_BLOCK_FOREACH_VARIABLE, TEMPLATE_BLOCK_END, TEMPLATE_VARIABLE_START, TEMPLATE_VARIABLE, @@ -65,6 +67,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) blogc_template_stmt_operator_t tmp_op = 0; unsigned int if_count = 0; + bool foreach_open = false; b_slist_t *stmts = NULL; blogc_template_stmt_t *stmt = NULL; @@ -201,11 +204,42 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) "statement."); break; } + else if ((current - start == 7) && + (0 == strncmp("foreach", src + start, 7))) + { + if (!foreach_open) { + state = TEMPLATE_BLOCK_FOREACH_START; + type = BLOGC_TEMPLATE_FOREACH_STMT; + start = current; + foreach_open = true; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, "'foreach' statements can't " + "be nested."); + break; + } + else if ((current - start == 10) && + (0 == strncmp("endforeach", src + start, 10))) + { + if (foreach_open) { + state = TEMPLATE_BLOCK_END; + type = BLOGC_TEMPLATE_ENDFOREACH_STMT; + foreach_open = false; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "'endforeach' statement without an open 'foreach' " + "statement."); + break; + } } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, "Invalid statement type: Allowed types are: 'block', " - "'endblock', 'ifdef', 'ifndef' and 'endif'."); + "'endblock', 'ifdef', 'ifndef', 'endif', 'foreach' and " + "'endforeach'."); break; case TEMPLATE_BLOCK_BLOCK_TYPE_START: @@ -337,6 +371,34 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) end2 = current; break; + case TEMPLATE_BLOCK_FOREACH_START: + if (c == ' ') + break; + if (c >= 'A' && c <= 'Z') { + state = TEMPLATE_BLOCK_FOREACH_VARIABLE; + start = current; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid foreach variable name. Must begin with uppercase " + "letter."); + break; + + case TEMPLATE_BLOCK_FOREACH_VARIABLE: + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') + break; + if (c == ' ') { + end = current; + state = TEMPLATE_BLOCK_END; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid foreach variable name. Must be uppercase letter, " + "number or '_'."); + break; + case TEMPLATE_BLOCK_END: if (c == ' ') break; @@ -469,6 +531,9 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) else if (block_state != BLOCK_CLOSED) *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER, "An open block was not closed!"); + else if (foreach_open) + *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER, + "An open 'foreach' statement was not closed!"); } if (*err != NULL) { diff --git a/src/template-parser.h b/src/template-parser.h index d1e9bd6..9da5fff 100644 --- a/src/template-parser.h +++ b/src/template-parser.h @@ -17,6 +17,8 @@ typedef enum { BLOGC_TEMPLATE_IFNDEF_STMT, BLOGC_TEMPLATE_IF_STMT, BLOGC_TEMPLATE_ENDIF_STMT, + BLOGC_TEMPLATE_FOREACH_STMT, + BLOGC_TEMPLATE_ENDFOREACH_STMT, BLOGC_TEMPLATE_BLOCK_STMT, BLOGC_TEMPLATE_ENDBLOCK_STMT, BLOGC_TEMPLATE_VARIABLE_STMT, -- cgit v1.2.3-18-g5258