aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanny van Kooten <dannyvankooten@users.noreply.github.com>2020-03-20 14:01:49 +0100
committerDanny van Kooten <dannyvankooten@users.noreply.github.com>2020-03-20 14:01:49 +0100
commit9f4d4ab24e7a539621bcd810b84201b0127c0870 (patch)
tree5de7ad10ab45653dd55eabd9ff8656b112dc18f9
parent26a1adffd4ac7b4f1390ba0135ede850697c4dfa (diff)
downloadunja-9f4d4ab24e7a539621bcd810b84201b0127c0870.tar.gz
unja-9f4d4ab24e7a539621bcd810b84201b0127c0870.zip
allow negating singular terms & add loop variables
-rw-r--r--src/template.c39
-rw-r--r--tests/test_template.c28
2 files changed, 58 insertions, 9 deletions
diff --git a/src/template.c b/src/template.c
index a1439c4..e8fcf36 100644
--- a/src/template.c
+++ b/src/template.c
@@ -90,7 +90,7 @@ mpc_parser_t *parser_init() {
" number : /[0-9]+/ ;"
" text : /[^{][^{%#]*/;"
" string : '\"' /([^\"])*/ '\"' ;"
- " factor : '(' <expression> ')' | <symbol> | <number> | <string> ;"
+ " factor : <symbol> | <number> | <string> ;"
" term : <factor> (<spaces> ('*' | '/' | '%') <spaces> <factor>)* ;"
" lexp : <term> (<spaces> ('+' | '-') <spaces> <term>)* ;"
" expression: <lexp> <spaces> '>' <spaces> <lexp> "
@@ -99,6 +99,7 @@ mpc_parser_t *parser_init() {
" | <lexp> <spaces> \"<=\" <spaces> <lexp> "
" | <lexp> <spaces> \"!=\" <spaces> <lexp> "
" | <lexp> <spaces> \"==\" <spaces> <lexp> "
+ " | \"not\" <spaces> <lexp> "
" | <lexp> ;"
" print : /{{2}-? */ <expression> / *-?}}/ ;"
" comment : \"{#\" /[^#][^#}]*/ \"#}\" ;"
@@ -329,7 +330,7 @@ void object_free(struct unja_object *obj) {
int object_is_truthy(struct unja_object *obj) {
switch (obj->type) {
case OBJ_NULL: return 0;
- case OBJ_STRING: return strlen(obj->string) > 0;
+ case OBJ_STRING: return strlen(obj->string) > 0 && strcmp(obj->string, "0") != 0;
case OBJ_INT: return obj->integer > 0;
}
@@ -356,6 +357,7 @@ struct unja_object *eval_expression_value(mpc_ast_t* node, struct context *ctx)
if (value == NULL) {
return &null_object;
}
+
return make_string_object(value, NULL);
} else if(strstr(node->tag, "number|")) {
return make_int_object(atoi(node->contents));
@@ -434,9 +436,18 @@ struct unja_object *eval_expression(mpc_ast_t* expr, struct context *ctx) {
return eval_expression_value(expr, ctx);
}
- /* otherwise: with operator */
- int offset = 0;
struct unja_object *result;
+
+ /* singular negated term */
+ if (strcmp(expr->children[0]->contents, "not") == 0) {
+ result = eval_expression_value(expr->children[2], ctx);
+ struct unja_object *negated = make_int_object(!object_to_int(result));
+ object_free(result);
+ return negated;
+ }
+
+ /* otherwise: with operator */
+ unsigned int offset = 0;
mpc_ast_t *left_node = expr->children[0];
struct unja_object *left = eval_expression(left_node, ctx);
@@ -511,12 +522,32 @@ int eval(struct buffer *buf, mpc_ast_t* t, struct context *ctx) {
char *tmp_key = t->children[2]->contents;
char *iterator_key = t->children[4]->contents;
struct vector *list = hashmap_resolve(ctx->vars, iterator_key);
+
+ /* add "loop" variable to context */
+ struct hashmap *loop = hashmap_new();
+ char index[8], first[2], last[2];
+ hashmap_insert(loop, "index", index);
+ hashmap_insert(loop, "first", first);
+ hashmap_insert(loop, "last", last);
+ hashmap_insert(ctx->vars, "loop", loop);
+
+ /* loop over values in vector */
for (int i=0; i < list->size; i++) {
+ /* set loop variable values */
+ sprintf(index, "%d", i);
+ sprintf(first, "%d", i == 0);
+ sprintf(last, "%d", i == (list->size - 1));
hashmap_insert(ctx->vars, tmp_key, list->values[i]);
trim_whitespace = strstr(t->children[5]->contents, "-") ? 1 : 0;
+
+ /* evaluate body */
eval(buf, t->children[6], ctx);
}
+ /* remove "loop" variable from context */
+ hashmap_remove(ctx->vars, "loop");
+ hashmap_free(loop);
+
/* trim trailing whitespace if closing tag has minus sign */
if (strstr(t->children[7]->contents, "-")) {
buf->string = trim_trailing_whitespace(buf->string);
diff --git a/tests/test_template.c b/tests/test_template.c
index d94ddd6..69ff7fe 100644
--- a/tests/test_template.c
+++ b/tests/test_template.c
@@ -3,7 +3,7 @@
START_TESTS
-TEST(text_only) {
+TEST(textvc_only) {
char *input = "Hello world.";
char *output = template_string(input, NULL);
assert_str(output, "Hello world.");
@@ -89,7 +89,6 @@ TEST(expr_add) {
hashmap_free(ctx);
}
-
TEST(expr_op_precedence) {
struct {
char *input;
@@ -162,6 +161,28 @@ TEST(for_block) {
free(output);
}
+
+TEST(for_block_vars) {
+ char *input = "{% for n in names %}"
+ "{{loop.index + 1}}: {{ n }}"
+ "{% if loop.first %} <--{% endif %}"
+ "{% if not loop.last %}\n{% endif %}"
+ "{% endfor %}";
+ struct hashmap *ctx = hashmap_new();
+
+ struct vector *names = vector_new(9);
+ vector_push(names, "John");
+ vector_push(names, "Sally");
+ vector_push(names, "Eric");
+ hashmap_insert(ctx, "names", names);
+
+ char *output = template_string(input, ctx);
+ assert_str(output, "1: John <--\n2: Sally\n3: Eric");
+ vector_free(names);
+ hashmap_free(ctx);
+ free(output);
+}
+
TEST(for_block_whitespace) {
char *input = "\n{%- for n in names -%}\n{{ n }}\n{%- endfor -%}\n";
struct hashmap *ctx = hashmap_new();
@@ -292,7 +313,6 @@ TEST(expr_lte) {
}
}
-
TEST(expr_eq) {
struct {
char *input;
@@ -412,7 +432,6 @@ TEST(buffer_alloc) {
}
TEST(inheritance_depth_1) {
- /* TODO: Check why this fails with files names 1.tmpl */
struct env *env = env_new("./tests/data/inheritance-depth-1/");
char *output = template(env, "one.tmpl", NULL);
assert_str(output, "Header\nChild content\nFooter\n");
@@ -420,7 +439,6 @@ TEST(inheritance_depth_1) {
env_free(env);
}
-
TEST(inheritance_depth_2) {
struct env *env = env_new("./tests/data/inheritance-depth-2/");
char *output = template(env, "two.tmpl", NULL);