diff options
| author | Rafael G. Martins <rafael@rafaelmartins.eng.br> | 2016-01-30 22:45:46 +0100 | 
|---|---|---|
| committer | Rafael G. Martins <rafael@rafaelmartins.eng.br> | 2016-01-30 22:45:46 +0100 | 
| commit | c4d19f731dc683c5a65327d99f2b416d4781f00b (patch) | |
| tree | 09e8941c5ac840b2a74ca28e5a474b7746e5fe79 /src | |
| parent | 008c96d6518125f2683c1b4e842ca3ed6e865c3d (diff) | |
| parent | 3b0ea0a4908f1702308354ec7cbd8db5c80f87ee (diff) | |
| download | blogc-c4d19f731dc683c5a65327d99f2b416d4781f00b.tar.gz blogc-c4d19f731dc683c5a65327d99f2b416d4781f00b.tar.bz2 blogc-c4d19f731dc683c5a65327d99f2b416d4781f00b.zip  | |
Merge remote-tracking branch 'origin/master' into feature/directives
Diffstat (limited to 'src')
| -rw-r--r-- | src/content-parser.c | 44 | ||||
| -rw-r--r-- | src/content-parser.h | 3 | ||||
| -rw-r--r-- | src/datetime-parser.c | 2 | ||||
| -rw-r--r-- | src/datetime-parser.h | 2 | ||||
| -rw-r--r-- | src/error.c | 2 | ||||
| -rw-r--r-- | src/error.h | 2 | ||||
| -rw-r--r-- | src/file.c | 2 | ||||
| -rw-r--r-- | src/file.h | 2 | ||||
| -rw-r--r-- | src/loader.c | 11 | ||||
| -rw-r--r-- | src/loader.h | 2 | ||||
| -rw-r--r-- | src/main.c | 2 | ||||
| -rw-r--r-- | src/renderer.c | 99 | ||||
| -rw-r--r-- | src/renderer.h | 7 | ||||
| -rw-r--r-- | src/source-parser.c | 2 | ||||
| -rw-r--r-- | src/source-parser.h | 2 | ||||
| -rw-r--r-- | src/template-parser.c | 183 | ||||
| -rw-r--r-- | src/template-parser.h | 9 | ||||
| -rw-r--r-- | src/utils/mem.c | 2 | ||||
| -rw-r--r-- | src/utils/slist.c | 2 | ||||
| -rw-r--r-- | src/utils/strings.c | 49 | ||||
| -rw-r--r-- | src/utils/trie.c | 2 | ||||
| -rw-r--r-- | src/utils/utils.h | 4 | 
22 files changed, 367 insertions, 68 deletions
diff --git a/src/content-parser.c b/src/content-parser.c index 8e98405..e8e169d 100644 --- a/src/content-parser.c +++ b/src/content-parser.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -28,7 +28,7 @@ blogc_slugify(const char *str)          return NULL;      char *new_str = b_strdup(str);      int diff = 'a' - 'A';  // just to avoid magic numbers -    for (unsigned int i = 0; new_str[i] != '\0'; i++) { +    for (size_t i = 0; new_str[i] != '\0'; i++) {          if (new_str[i] >= 'a' && new_str[i] <= 'z')              continue;          if (new_str[i] >= '0' && new_str[i] <= '9') @@ -42,6 +42,40 @@ blogc_slugify(const char *str)  } +char* +blogc_htmlentities(const char *str) +{ +    if (str == NULL) +        return NULL; +    b_string_t *rv = b_string_new(); +    for (size_t i = 0; str[i] != '\0'; i++) { +        switch (str[i]) { +            case '&': +                b_string_append(rv, "&"); +                break; +            case '<': +                b_string_append(rv, "<"); +                break; +            case '>': +                b_string_append(rv, ">"); +                break; +            case '"': +                b_string_append(rv, """); +                break; +            case '\'': +                b_string_append(rv, "'"); +                break; +            case '/': +                b_string_append(rv, "/"); +                break; +            default: +                b_string_append_c(rv, str[i]); +        } +    } +    return b_string_free(rv, false); +} + +  typedef enum {      CONTENT_START_LINE = 1,      CONTENT_EXCERPT_OR_DIRECTIVE, @@ -717,11 +751,13 @@ blogc_content_parse(const char *src, size_t *end_excerpt)                  if (c == '\n' || c == '\r' || is_last) {                      b_string_append(rv, "<pre><code>");                      for (b_slist_t *l = lines; l != NULL; l = l->next) { +                        char *tmp_line = blogc_htmlentities(l->data);                          if (l->next == NULL) -                            b_string_append_printf(rv, "%s", l->data); +                            b_string_append_printf(rv, "%s", tmp_line);                          else -                            b_string_append_printf(rv, "%s%s", l->data, +                            b_string_append_printf(rv, "%s%s", tmp_line,                                  line_ending); +                        free(tmp_line);                      }                      b_string_append_printf(rv, "</code></pre>%s", line_ending);                      b_slist_free_full(lines, free); diff --git a/src/content-parser.h b/src/content-parser.h index 2f6b8b9..6617bb4 100644 --- a/src/content-parser.h +++ b/src/content-parser.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -13,6 +13,7 @@  #include <stdbool.h>  char* blogc_slugify(const char *str); +char* blogc_htmlentities(const char *str);  char* blogc_content_parse_inline(const char *src);  bool blogc_is_ordered_list_item(const char *str, size_t prefix_len);  char* blogc_content_parse(const char *src, size_t *end_excerpt); diff --git a/src/datetime-parser.c b/src/datetime-parser.c index 6a2162d..8785a89 100644 --- a/src/datetime-parser.c +++ b/src/datetime-parser.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/datetime-parser.h b/src/datetime-parser.h index 7f94545..a5087b3 100644 --- a/src/datetime-parser.h +++ b/src/datetime-parser.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/error.c b/src/error.c index e472d1f..59a85a3 100644 --- a/src/error.c +++ b/src/error.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/error.h b/src/error.h index 02ccc96..886d7d3 100644 --- a/src/error.h +++ b/src/error.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/loader.c b/src/loader.c index 8f04dae..baa81fa 100644 --- a/src/loader.c +++ b/src/loader.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -141,11 +141,14 @@ blogc_source_parse_from_files(b_trie_t *conf, b_slist_t *l, blogc_error_t **err)                  b_trie_free(s);                  continue;              } -            char **tags = b_str_split(tags_str, ',', 0); +            char **tags = b_str_split(tags_str, ' ', 0);              bool found = false; -            for (unsigned int i = 0; tags[i] != NULL; i++) -                if (0 == strcmp(b_str_strip(tags[i]), filter_tag)) +            for (unsigned int i = 0; tags[i] != NULL; i++) { +                if (tags[i][0] == '\0') +                    continue; +                if (0 == strcmp(tags[i], filter_tag))                      found = true; +            }              b_strv_free(tags);              if (!found) {                  b_trie_free(s); diff --git a/src/loader.h b/src/loader.h index 610aa42..c432e20 100644 --- a/src/loader.h +++ b/src/loader.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/renderer.c b/src/renderer.c index 3061c43..5e07b0c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -58,8 +58,16 @@ 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) +blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local, +    b_slist_t *foreach_var)  { +    if (0 == strcmp(name, "FOREACH_ITEM")) { +        if (foreach_var != NULL && foreach_var->data != NULL) { +            return b_strdup(foreach_var->data); +        } +        return NULL; +    } +      char *var = NULL;      bool must_format = false;      if (b_str_ends_with(name, "_FORMATTED")) { @@ -88,6 +96,28 @@ 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++) { +        if (tmp[i][0] != '\0')  // ignore empty strings +            rv = b_slist_append(rv, tmp[i]); +        else +            free(tmp[i]); +    } +    free(tmp); + +    return rv; +} + +  char*  blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing)  { @@ -104,7 +134,10 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing      char *defined = NULL;      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; @@ -175,7 +208,7 @@ 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) {                      config_value = blogc_format_variable(stmt->value, -                        config, inside_block ? tmp_source : NULL); +                        config, inside_block ? tmp_source : NULL, foreach_var);                      if (config_value != NULL) {                          b_string_append(str, config_value);                          free(config_value); @@ -203,10 +236,11 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing              case BLOGC_TEMPLATE_IF_STMT:              case BLOGC_TEMPLATE_IFDEF_STMT: +                if_count = 0;                  defined = NULL;                  if (stmt->value != NULL)                      defined = blogc_format_variable(stmt->value, config, -                        inside_block ? tmp_source : NULL); +                        inside_block ? tmp_source : NULL, foreach_var);                  evaluate = false;                  if (stmt->op != 0) {                      // Strings that start with a '"' are actually strings, the @@ -223,7 +257,8 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing                          }                          else {                              defined2 = blogc_format_variable(stmt->value2, -                                config, inside_block ? tmp_source : NULL); +                                config, inside_block ? tmp_source : NULL, +                                foreach_var);                          }                      } @@ -248,7 +283,6 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing                          evaluate = true;                  }                  if (!evaluate) { -                    if_skip = if_count;                      // at this point we can just skip anything, counting the                      // number of 'if's, to know how many 'endif's we need to @@ -264,11 +298,11 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing                              continue;                          }                          if (stmt->type == BLOGC_TEMPLATE_ENDIF_STMT) { -                            if (if_count > if_skip) { +                            if (if_count > 0) {                                  if_count--;                                  continue;                              } -                            if (if_count == if_skip) +                            if (if_count == 0)                                  break;                          }                      } @@ -279,11 +313,56 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing                  break;              case BLOGC_TEMPLATE_ENDIF_STMT: -                if_count--; +                if (if_count > 0) +                    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..15204e6 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -14,7 +14,10 @@  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); +char* blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local, +    b_slist_t *foreach_var); +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/source-parser.c b/src/source-parser.c index db0792c..65fdd4e 100644 --- a/src/source-parser.c +++ b/src/source-parser.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/source-parser.h b/src/source-parser.h index d92b1ce..f359f9e 100644 --- a/src/source-parser.h +++ b/src/source-parser.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/template-parser.c b/src/template-parser.c index f6912df..1d9046e 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -22,6 +22,7 @@ typedef enum {      TEMPLATE_START = 1,      TEMPLATE_OPEN_BRACKET,      TEMPLATE_BLOCK_START, +    TEMPLATE_BLOCK_START_WHITESPACE_CLEANER,      TEMPLATE_BLOCK_TYPE,      TEMPLATE_BLOCK_BLOCK_TYPE_START,      TEMPLATE_BLOCK_BLOCK_TYPE, @@ -32,6 +33,9 @@ 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_WHITESPACE_CLEANER,      TEMPLATE_BLOCK_END,      TEMPLATE_VARIABLE_START,      TEMPLATE_VARIABLE, @@ -65,10 +69,25 @@ 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; +    /* +     * this is a reference to the content of previous node in the singly-linked +     * list. The "correct" solution here would be implement a doubly-linked +     * list, but here are a few reasons to avoid it: +     * +     * - i'm too tired to implement it :P +     * - template parser never walk backwards, then the list itself does not +     *   need to know its previous node. +     */ +    blogc_template_stmt_t *previous = NULL; + +    bool lstrip_next = false; +    char *tmp = NULL; +      blogc_template_parser_state_t state = TEMPLATE_START;      blogc_template_parser_block_state_t block_state = BLOCK_CLOSED;      blogc_template_stmt_type_t type = BLOGC_TEMPLATE_CONTENT_STMT; @@ -83,10 +102,20 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                  if (last) {                      stmt = b_malloc(sizeof(blogc_template_stmt_t));                      stmt->type = type; -                    stmt->value = b_strndup(src + start, src_len - start); +                    if (lstrip_next) { +                        tmp = b_strndup(src + start, src_len - start); +                        stmt->value = b_strdup(b_str_lstrip(tmp)); +                        free(tmp); +                        tmp = NULL; +                        lstrip_next = false; +                    } +                    else { +                        stmt->value = b_strndup(src + start, src_len - start); +                    }                      stmt->op = 0;                      stmt->value2 = NULL;                      stmts = b_slist_append(stmts, stmt); +                    previous = stmt;                      stmt = NULL;                  }                  if (c == '{') { @@ -98,16 +127,26 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)              case TEMPLATE_OPEN_BRACKET:                  if (c == '%' || c == '{') {                      if (c == '%') -                        state = TEMPLATE_BLOCK_START; +                        state = TEMPLATE_BLOCK_START_WHITESPACE_CLEANER;                      else                          state = TEMPLATE_VARIABLE_START;                      if (end > start) {                          stmt = b_malloc(sizeof(blogc_template_stmt_t));                          stmt->type = type; -                        stmt->value = b_strndup(src + start, end - start); +                        if (lstrip_next) { +                            tmp = b_strndup(src + start, end - start); +                            stmt->value = b_strdup(b_str_lstrip(tmp)); +                            free(tmp); +                            tmp = NULL; +                            lstrip_next = false; +                        } +                        else { +                            stmt->value = b_strndup(src + start, end - start); +                        }                          stmt->op = 0;                          stmt->value2 = NULL;                          stmts = b_slist_append(stmts, stmt); +                        previous = stmt;                          stmt = NULL;                      }                      break; @@ -115,6 +154,18 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                  state = TEMPLATE_START;                  break; +            case TEMPLATE_BLOCK_START_WHITESPACE_CLEANER: +                if (c == '-') { +                    if ((previous != NULL) && +                        (previous->type == BLOGC_TEMPLATE_CONTENT_STMT)) +                    { +                        previous->value = b_str_rstrip(previous->value);  // does not need copy +                    } +                    state = TEMPLATE_BLOCK_START; +                    break; +                } +                state = TEMPLATE_BLOCK_START; +              case TEMPLATE_BLOCK_START:                  if (c == ' ')                      break; @@ -123,6 +174,13 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                      start = current;                      break;                  } +                if (c == '-') { +                    *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, +                        src_len, current, +                        "Invalid statement syntax. Duplicated whitespace " +                        "cleaner before statement."); +                    break; +                }                  *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,                      src_len, current,                      "Invalid statement syntax. Must begin with lowercase letter."); @@ -149,7 +207,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                          (0 == strncmp("endblock", src + start, 8)))                      {                          if (block_state != BLOCK_CLOSED) { -                            state = TEMPLATE_BLOCK_END; +                            state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                              type = BLOGC_TEMPLATE_ENDBLOCK_STMT;                              block_state = BLOCK_CLOSED;                              break; @@ -190,7 +248,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                          (0 == strncmp("endif", src + start, 5)))                      {                          if (if_count > 0) { -                            state = TEMPLATE_BLOCK_END; +                            state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                              type = BLOGC_TEMPLATE_ENDIF_STMT;                              if_count--;                              break; @@ -201,11 +259,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_WHITESPACE_CLEANER; +                            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: @@ -230,7 +319,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                      {                          block_state = BLOCK_ENTRY;                          end = current; -                        state = TEMPLATE_BLOCK_END; +                        state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                          break;                      }                      else if ((current - start == 7) && @@ -238,7 +327,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                      {                          block_state = BLOCK_LISTING;                          end = current; -                        state = TEMPLATE_BLOCK_END; +                        state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                          break;                      }                      else if ((current - start == 12) && @@ -246,7 +335,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                      {                          block_state = BLOCK_LISTING_ONCE;                          end = current; -                        state = TEMPLATE_BLOCK_END; +                        state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                          break;                      }                  } @@ -277,7 +366,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                      if (type == BLOGC_TEMPLATE_IF_STMT)                          state = TEMPLATE_BLOCK_IF_OPERATOR_START;                      else -                        state = TEMPLATE_BLOCK_END; +                        state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                      break;                  }                  *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, @@ -304,21 +393,22 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)              case TEMPLATE_BLOCK_IF_OPERAND_START:                  if (c == ' ')                      break; -                if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') { +                if (c >= 'A' && c <= 'Z') {                      state = TEMPLATE_BLOCK_IF_VARIABLE_OPERAND;                      start2 = current;                      break;                  } -                if (c != '"') { -                    op_start = 0; -                    op_end = 0; -                    *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, -                        src_len, current, -                        "Invalid 'if' operand. Must be double-quoted static string."); +                if (c == '"') { +                    state = TEMPLATE_BLOCK_IF_STRING_OPERAND; +                    start2 = current;                      break;                  } -                state = TEMPLATE_BLOCK_IF_STRING_OPERAND; -                start2 = current; +                op_start = 0; +                op_end = 0; +                *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, +                    src_len, current, +                    "Invalid 'if' operand. Must be double-quoted static " +                    "string or variable.");                  break;              case TEMPLATE_BLOCK_IF_STRING_OPERAND: @@ -326,24 +416,67 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                      break;                  if (c == '"' && src[current - 1] == '\\')                      break; -                state = TEMPLATE_BLOCK_END; +                state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                  end2 = current + 1;                  break;             case TEMPLATE_BLOCK_IF_VARIABLE_OPERAND:                  if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')                      break; -                state = TEMPLATE_BLOCK_END; +                state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;                  end2 = current;                  break; -            case TEMPLATE_BLOCK_END: +            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_WHITESPACE_CLEANER; +                    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_WHITESPACE_CLEANER: +                if (c == ' ') +                    break; +                if (c == '-') { +                    lstrip_next = true; +                    state = TEMPLATE_BLOCK_END; +                    break; +                } +                state = TEMPLATE_BLOCK_END; + +            case TEMPLATE_BLOCK_END:                  if (c == '%') {                      state = TEMPLATE_CLOSE_BRACKET;                      break;                  } +                if (c == '-') { +                    *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, +                        src_len, current, +                        "Invalid statement syntax. Duplicated whitespace " +                        "cleaner after statement."); +                    break; +                }                  *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,                      src_len, current,                      "Invalid statement syntax. Must end with '%%}'."); @@ -439,6 +572,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)                          end2 = 0;                      }                      stmts = b_slist_append(stmts, stmt); +                    previous = stmt;                      stmt = NULL;                      state = TEMPLATE_START;                      type = BLOGC_TEMPLATE_CONTENT_STMT; @@ -469,6 +603,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..6cd2c80 100644 --- a/src/template-parser.h +++ b/src/template-parser.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -12,11 +12,18 @@  #include "utils/utils.h"  #include "error.h" +/* + * note: whitespace cleaners are NOT added to ast. we fix strings right during + * template parsing. renderer does not need to care about it, for the sake of + * simplicity. + */  typedef enum {      BLOGC_TEMPLATE_IFDEF_STMT = 1,      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, diff --git a/src/utils/mem.c b/src/utils/mem.c index 7c5e0a2..693d555 100644 --- a/src/utils/mem.c +++ b/src/utils/mem.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/utils/slist.c b/src/utils/slist.c index 3d9b892..9753aa7 100644 --- a/src/utils/slist.c +++ b/src/utils/slist.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/utils/strings.c b/src/utils/strings.c index 40174a1..3151612 100644 --- a/src/utils/strings.c +++ b/src/utils/strings.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -103,22 +103,46 @@ b_str_ends_with(const char *str, const char *suffix)  char* -b_str_strip(char *str) +b_str_lstrip(char *str)  {      if (str == NULL) -        return str; +        return NULL; +    int i; +    size_t str_len = strlen(str); +    for (i = 0; i < str_len; i++) { +        if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') && +            (str[i] != '\r') && (str[i] != '\t') && (str[i] != '\f') && +            (str[i] != '\v')) +        { +            str += i; +            break; +        } +        if (i == str_len - 1) { +            str += str_len; +            break; +        } +    } +    return str; +} + + +char* +b_str_rstrip(char *str) +{ +    if (str == NULL) +        return NULL;      int i;      size_t str_len = strlen(str);      for (i = str_len - 1; i >= 0; i--) { -        if (!isspace(str[i])) { +        if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') && +            (str[i] != '\r') && (str[i] != '\t') && (str[i] != '\f') && +            (str[i] != '\v')) +        {              str[i + 1] = '\0';              break;          } -    } -    str_len = strlen(str); -    for (i = 0; i < str_len; i++) { -        if (!isspace(str[i])) { -            str = str + i; +        if (i == 0) { +            str[0] = '\0';              break;          }      } @@ -126,6 +150,13 @@ b_str_strip(char *str)  } +char* +b_str_strip(char *str) +{ +    return b_str_lstrip(b_str_rstrip(str)); +} + +  char**  b_str_split(const char *str, char c, unsigned int max_pieces)  { diff --git a/src/utils/trie.c b/src/utils/trie.c index db64e1d..2e6a0d8 100644 --- a/src/utils/trie.c +++ b/src/utils/trie.c @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. diff --git a/src/utils/utils.h b/src/utils/utils.h index 5a1505b..dc67497 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,6 +1,6 @@  /*   * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>   *   * This program can be distributed under the terms of the BSD License.   * See the file LICENSE. @@ -50,6 +50,8 @@ char* b_strdup_vprintf(const char *format, va_list ap);  char* b_strdup_printf(const char *format, ...);  bool b_str_starts_with(const char *str, const char *prefix);  bool b_str_ends_with(const char *str, const char *suffix); +char* b_str_lstrip(char *str); +char* b_str_rstrip(char *str);  char* b_str_strip(char *str);  char** b_str_split(const char *str, char c, unsigned int max_pieces);  char* b_str_replace(const char *str, const char search, const char *replace);  | 
