diff options
Diffstat (limited to 'src/template-parser.c')
-rw-r--r-- | src/template-parser.c | 183 |
1 files changed, 160 insertions, 23 deletions
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) { |