aboutsummaryrefslogtreecommitdiffstats
path: root/src/template-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/template-parser.c')
-rw-r--r--src/template-parser.c183
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) {